`procedure-arity-includes?' reports #f for keyword-requiring procs

by default; a new optional argument restores the old behavior
  (but the default behavior is consistent with the old docs and with
  the vast majority of existing uses)

 The implementation is ugly for performance reasons. A new primitive
  `prop:arity-incomplete' property determines when to return #f for
  `procedure-arity-includes?' in default mode. A nicer implementation
  would be to redefine `procedure-arity-includes?' at the kw-proc level,
  but the bytecode optimizer's and JIT's treatment of the built-in
  `procedure-arity-includes?' is important. The implementation choice
  could be revisited after cross-module inlining is implemented.

 Closes PR 11978
This commit is contained in:
Matthew Flatt 2011-06-16 11:57:08 -06:00
parent 649fe2f276
commit 56423f330e
13 changed files with 475 additions and 450 deletions

View File

@ -425,7 +425,7 @@ v4 todo:
(and (procedure? x)
(if (base->-dom-rest/c ctc)
(procedure-accepts-and-more? x l)
(procedure-arity-includes? x l))
(procedure-arity-includes? x l #t))
(keywords-match (base->-mandatory-kwds ctc) (base->-optional-kwds ctc) x)
#t))))
@ -1720,8 +1720,8 @@ v4 todo:
(define (procedure-arity-includes?/optionals f base optionals)
(cond
[(zero? optionals) (procedure-arity-includes? f base)]
[else (and (procedure-arity-includes? f (+ base optionals))
[(zero? optionals) (procedure-arity-includes? f base #t)]
[else (and (procedure-arity-includes? f (+ base optionals) #t)
(procedure-arity-includes?/optionals f base (- optionals 1)))]))
(define (keywords-match mandatory-kwds optional-kwds val)

View File

@ -145,7 +145,9 @@
(list (cons prop:arity-string
generate-arity-string)
(cons prop:named-keyword-procedure
(cons name fail-proc)))
(cons name fail-proc))
(cons prop:incomplete-arity
#t))
(current-inspector) fail-proc)])
mk))

View File

@ -134,7 +134,8 @@
procedure-arity procedure-reduce-arity raise-arity-error
procedure->method procedure-rename
chaperone-procedure impersonate-procedure
assq assv assoc)
assq assv assoc
prop:incomplete-arity)
(all-from "reqprov.rkt")
(all-from-except "for.rkt"
define-in-vector-like

View File

@ -63,7 +63,8 @@
hash-iterate-first hash-iterate-next
hash-iterate-value hash-iterate-key
log-message log-level? make-logger logger? current-logger logger-name
make-log-receiver log-receiver?)
make-log-receiver log-receiver?
prop:incomplete-arity)
(rename syntax->datum syntax-object->datum)
(rename datum->syntax datum->syntax-object)
(rename free-identifier=? module-identifier=?)

View File

@ -168,15 +168,21 @@ elements.
(procedure-arity (case-lambda [(x) 0] [(x y) 1]))
]}
@defproc[(procedure-arity-includes? [proc procedure?] [k exact-nonnegative-integer?])
@defproc[(procedure-arity-includes? [proc procedure?]
[k exact-nonnegative-integer?]
[kws-ok? any/c #f])
boolean?]{
Returns @scheme[#t] if the procedure can accept @scheme[k] arguments
when no keyword arguments are supplied, @scheme[#f] otherwise.
Returns @scheme[#t] if the procedure can accept @scheme[k] by-position
arguments, @scheme[#f] otherwise. If @racket[kws-ok?] is @racket[#f],
the result is @racket[#t] only if @racket[proc] has no required
keyword arguments.
@mz-examples[
(procedure-arity-includes? cons 2)
(procedure-arity-includes? display 3)
(procedure-arity-includes? (lambda (x #:y y) x) 1)
(procedure-arity-includes? (lambda (x #:y y) x) 1 #t)
]}
@defproc[(procedure-reduce-arity [proc procedure?]

View File

@ -1992,7 +1992,7 @@
(err/rt-test (procedure-arity-includes? cons 1.0))
(err/rt-test (procedure-arity-includes? 'cons 1))
(arity-test procedure-arity-includes? 2 2)
(arity-test procedure-arity-includes? 2 3)
(newline)
(display ";testing scheme 4 functions; ")

View File

@ -93,7 +93,7 @@
((arity-at-least-value a) . <= . 1))
(and (list? a)
(ormap loop a))))])
(test 1-ok? procedure-arity-includes? (car p) 1)
(test 1-ok? procedure-arity-includes? (car p) 1 #t)
;; While we're here test renaming, etc.:
(test 'other object-name (procedure-rename (car p) 'other))
(test (procedure-arity (car p)) procedure-arity (procedure-rename (car p) 'other))
@ -165,10 +165,10 @@
(let ([p (car p)])
(let-values ([(req allowed) (procedure-keywords p)])
(if (null? allowed)
(if (procedure-arity-includes? p 1)
(if (procedure-arity-includes? p 1 #t)
(list (procedure-reduce-arity p 1) 1 req allowed p)
(list (procedure-reduce-arity p '()) '() req allowed p))
(if (procedure-arity-includes? p 1)
(if (procedure-arity-includes? p 1 #t)
(list (procedure-reduce-keyword-arity p 1 req allowed) 1 req allowed p)
(list (procedure-reduce-keyword-arity p '() req allowed) '() req allowed p))))))
procs)
@ -177,10 +177,10 @@
(let ([p (car p)])
(let-values ([(req allowed) (procedure-keywords p)])
(if (null? allowed)
(if (procedure-arity-includes? p 0)
(if (procedure-arity-includes? p 0 #t)
(list (procedure-reduce-arity p 0) 0 req allowed p)
(list (procedure-reduce-arity p '()) '() req allowed p))
(if (procedure-arity-includes? p 0)
(if (procedure-arity-includes? p 0 #t)
(list (procedure-reduce-keyword-arity p 0 req allowed) 0 req allowed p)
(list (procedure-reduce-keyword-arity p '() req allowed) '() req allowed p))))))
procs)
@ -188,8 +188,7 @@
(map (lambda (p)
(let ([p (car p)])
(let-values ([(req allowed) (procedure-keywords p)])
(if (and (procedure-arity-includes? p 1)
(null? req))
(if (procedure-arity-includes? p 1)
(list* (procedure-reduce-arity p 1) 1 '() '() p
(if (null? allowed)
null
@ -200,8 +199,7 @@
(map (lambda (p)
(let ([p (car p)])
(let-values ([(req allowed) (procedure-keywords p)])
(if (and (procedure-arity-includes? p 0)
(null? req))
(if (procedure-arity-includes? p 0)
(list (procedure-reduce-arity p 0) 0 '() '() p)
(list (procedure-reduce-arity p '()) '() '() '() p)))))
procs)

File diff suppressed because it is too large Load Diff

View File

@ -501,7 +501,7 @@ scheme_init_fun (Scheme_Env *env)
o = scheme_make_folding_prim(scheme_procedure_arity_includes,
"procedure-arity-includes?",
2, 2, 1);
2, 3, 1);
SCHEME_PRIM_PROC_FLAGS(o) |= SCHEME_PRIM_IS_BINARY_INLINED;
scheme_procedure_arity_includes_proc = o;
scheme_add_global_constant("procedure-arity-includes?", o, env);
@ -1854,7 +1854,7 @@ static Scheme_Object *clone_arity(Scheme_Object *a, int delta)
return a;
}
static Scheme_Object *get_or_check_arity(Scheme_Object *p, intptr_t a, Scheme_Object *bign)
static Scheme_Object *get_or_check_arity(Scheme_Object *p, intptr_t a, Scheme_Object *bign, int inc_ok)
/* a == -1 => get arity
a == -2 => check for allowing bignum */
{
@ -1932,6 +1932,10 @@ static Scheme_Object *get_or_check_arity(Scheme_Object *p, intptr_t a, Scheme_Ob
return first;
} else if (type == scheme_proc_struct_type) {
int is_method;
if (!inc_ok
&& scheme_no_arity_property
&& scheme_struct_type_property_ref(scheme_no_arity_property, p))
return scheme_false;
if (scheme_reduced_procedure_struct
&& scheme_is_struct_instance(scheme_reduced_procedure_struct, p)) {
if (a >= 0) {
@ -2183,7 +2187,7 @@ static Scheme_Object *get_or_check_arity(Scheme_Object *p, intptr_t a, Scheme_Ob
Scheme_Object *scheme_get_or_check_arity(Scheme_Object *p, intptr_t a)
{
return get_or_check_arity(p, a, NULL);
return get_or_check_arity(p, a, NULL, 1);
}
int scheme_check_proc_arity2(const char *where, int a,
@ -2200,7 +2204,7 @@ int scheme_check_proc_arity2(const char *where, int a,
if (false_ok && SCHEME_FALSEP(p))
return 1;
if (!SCHEME_PROCP(p) || SCHEME_FALSEP(get_or_check_arity(p, a, NULL))) {
if (!SCHEME_PROCP(p) || SCHEME_FALSEP(get_or_check_arity(p, a, NULL, 1))) {
if (where) {
char buffer[60];
@ -2522,7 +2526,7 @@ static Scheme_Object *object_name(int argc, Scheme_Object **argv)
Scheme_Object *scheme_arity(Scheme_Object *p)
{
return get_or_check_arity(p, -1, NULL);
return get_or_check_arity(p, -1, NULL, 1);
}
static Scheme_Object *procedure_arity(int argc, Scheme_Object *argv[])
@ -2530,7 +2534,7 @@ static Scheme_Object *procedure_arity(int argc, Scheme_Object *argv[])
if (!SCHEME_PROCP(argv[0]))
scheme_wrong_type("procedure-arity", "procedure", 0, argc, argv);
return get_or_check_arity(argv[0], -1, NULL);
return get_or_check_arity(argv[0], -1, NULL, 1);
}
static Scheme_Object *procedure_arity_p(int argc, Scheme_Object *argv[])
@ -2569,6 +2573,7 @@ static Scheme_Object *procedure_arity_p(int argc, Scheme_Object *argv[])
Scheme_Object *scheme_procedure_arity_includes(int argc, Scheme_Object *argv[])
{
intptr_t n;
int inc_ok;
if (!SCHEME_PROCP(argv[0]))
scheme_wrong_type("procedure-arity-includes?", "procedure", 0, argc, argv);
@ -2576,7 +2581,9 @@ Scheme_Object *scheme_procedure_arity_includes(int argc, Scheme_Object *argv[])
n = scheme_extract_index("procedure-arity-includes?", 1, argc, argv, -2, 0);
/* -2 means a bignum */
return get_or_check_arity(argv[0], n, argv[1]);
inc_ok = ((argc > 2) && SCHEME_TRUEP(argv[2]));
return get_or_check_arity(argv[0], n, argv[1], inc_ok);
}
static int is_arity(Scheme_Object *a, int at_least_ok, int list_ok)
@ -2788,7 +2795,7 @@ static Scheme_Object *procedure_reduce_arity(int argc, Scheme_Object *argv[])
a bit complicated, because both the source and target can be
lists that include arity-at-least records. */
orig = get_or_check_arity(argv[0], -1, NULL);
orig = get_or_check_arity(argv[0], -1, NULL, 1);
aty = clone_arity(argv[1], 0);
if (!is_subarity(aty, orig)) {
@ -2816,7 +2823,7 @@ static Scheme_Object *procedure_rename(int argc, Scheme_Object *argv[])
p = scheme_rename_struct_proc(argv[0], argv[1]);
if (p) return p;
aty = get_or_check_arity(argv[0], -1, NULL);
aty = get_or_check_arity(argv[0], -1, NULL, 1);
return make_reduced_proc(argv[0], aty, argv[1], NULL);
}
@ -2828,7 +2835,7 @@ static Scheme_Object *procedure_to_method(int argc, Scheme_Object *argv[])
if (!SCHEME_PROCP(argv[0]))
scheme_wrong_type("procedure->method", "procedure", 0, argc, argv);
aty = get_or_check_arity(argv[0], -1, NULL);
aty = get_or_check_arity(argv[0], -1, NULL, 1);
return make_reduced_proc(argv[0], aty, NULL, scheme_true);
}
@ -2957,8 +2964,8 @@ static Scheme_Object *do_chaperone_procedure(const char *name, const char *whati
if (!SCHEME_PROCP(argv[1]))
scheme_wrong_type(name, "procedure", 1, argc, argv);
orig = get_or_check_arity(val, -1, NULL);
naya = get_or_check_arity(argv[1], -1, NULL);
orig = get_or_check_arity(val, -1, NULL, 1);
naya = get_or_check_arity(argv[1], -1, NULL, 1);
if (!is_subarity(orig, naya))
scheme_raise_exn(MZEXN_FAIL_CONTRACT,
@ -8331,7 +8338,7 @@ static Scheme_Object *time_apply(int argc, Scheme_Object *argv[])
num_rands++;
}
if (SCHEME_FALSEP(get_or_check_arity(argv[0], num_rands, NULL))) {
if (SCHEME_FALSEP(get_or_check_arity(argv[0], num_rands, NULL, 1))) {
char *s;
intptr_t aelen;

View File

@ -13,7 +13,7 @@
#define USE_COMPILED_STARTUP 1
#define EXPECTED_PRIM_COUNT 1022
#define EXPECTED_PRIM_COUNT 1023
#define EXPECTED_UNSAFE_COUNT 76
#define EXPECTED_FLFXNUM_COUNT 68
#define EXPECTED_FUTURES_COUNT 11

View File

@ -406,6 +406,8 @@ extern Scheme_Object *scheme_impersonator_of_property;
extern Scheme_Object *scheme_app_mark_impersonator_property;
extern Scheme_Object *scheme_no_arity_property;
extern Scheme_Object *scheme_reduced_procedure_struct;
/*========================================================================*/

View File

@ -13,12 +13,12 @@
consistently.)
*/
#define MZSCHEME_VERSION "5.1.1.5"
#define MZSCHEME_VERSION "5.1.1.6"
#define MZSCHEME_VERSION_X 5
#define MZSCHEME_VERSION_Y 1
#define MZSCHEME_VERSION_Z 1
#define MZSCHEME_VERSION_W 5
#define MZSCHEME_VERSION_W 6
#define MZSCHEME_VERSION_MAJOR ((MZSCHEME_VERSION_X * 100) + MZSCHEME_VERSION_Y)
#define MZSCHEME_VERSION_MINOR ((MZSCHEME_VERSION_Z * 1000) + MZSCHEME_VERSION_W)

View File

@ -32,6 +32,7 @@ READ_ONLY Scheme_Object *scheme_source_property;
READ_ONLY Scheme_Object *scheme_input_port_property;
READ_ONLY Scheme_Object *scheme_output_port_property;
READ_ONLY Scheme_Object *scheme_equal_property;
READ_ONLY Scheme_Object *scheme_no_arity_property;
READ_ONLY Scheme_Object *scheme_impersonator_of_property;
READ_ONLY Scheme_Object *scheme_make_struct_type_proc;
READ_ONLY Scheme_Object *scheme_current_inspector_proc;
@ -337,6 +338,12 @@ scheme_init_struct (Scheme_Env *env)
scheme_add_global_constant("prop:procedure", proc_property, env);
}
{
REGISTER_SO(scheme_no_arity_property);
scheme_no_arity_property = scheme_make_struct_type_property(scheme_intern_symbol("incomplete-arity"));
scheme_add_global_constant("prop:incomplete-arity", scheme_no_arity_property, env);
}
{
guard = scheme_make_prim_w_arity(check_equal_property_value_ok,
"guard-for-prop:equal+hash",