make impersonator properties sensitive to prop:impersonator-of

When a structure type has `prop:inpersonator-of`, follow it
when attemptng to access imperonator properties.

This change fixes a problem with `impersonate-procedure` as
reported by Scott Moore.
This commit is contained in:
Matthew Flatt 2015-03-08 19:22:55 -06:00
parent 092f6bb7e1
commit b923269569
6 changed files with 174 additions and 96 deletions

View File

@ -595,9 +595,19 @@ otherwise the result is a value for which the original structure is an
impersonator (so the original structure is an @racket[impersonator-of?] and impersonator (so the original structure is an @racket[impersonator-of?] and
@racket[equal?] to the result value). The result value must have the @racket[equal?] to the result value). The result value must have the
same @racket[prop:impersonator-of] and @racket[prop:equal+hash] property same @racket[prop:impersonator-of] and @racket[prop:equal+hash] property
values as the original structure, and the property values must be values as the original structure, if any, and the property values must be
inherited from the same structure type (which ensures some consistency inherited from the same structure type (which ensures some consistency
between @racket[impersonator-of?] and @racket[equal?]).} between @racket[impersonator-of?] and @racket[equal?]).
@tech{Impersonator property} predicates and accessors applied to a
structure with the @racket[prop:impersonator-of] property first check
for the property on the immediate structure, and if it is not found,
the value produced by the @racket[prop:impersonator-of] procedure is
checked (recursively).
@history[#:changed "6.1.1.8" @elem{Made @tech{impersonator property}
predicates and accessors sensitive
to @racket[prop:impersonator-of].}]}
@; ------------------------------------------------------------ @; ------------------------------------------------------------
@section{Chaperone Constructors} @section{Chaperone Constructors}

View File

@ -299,6 +299,15 @@
(lambda (kws kw-args self . args) (lambda (kws kw-args self . args)
(keyword-apply b kws kw-args args))))) (keyword-apply b kws kw-args args)))))
(define (check-proc-prop f mk)
(let-values ([(prop:blue blue? blue-ref) (make-impersonator-property 'blue)])
(define f1 (chaperone-procedure f #f prop:blue "blue"))
(test #t blue? f1)
(test "blue" blue-ref f1)
(define f2 (mk f1))
(test #t blue? f2)
(test "blue" blue-ref f2)))
;; Single argument, no post filter: ;; Single argument, no post filter:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
([chaperone-procedure impersonate-procedure ([chaperone-procedure impersonate-procedure
@ -306,15 +315,18 @@
impersonate-procedure**]) impersonate-procedure**])
(let* ([f (lambda (x) (list x x))] (let* ([f (lambda (x) (list x x))]
[in #f] [in #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x) (lambda (x)
(set! in x) (set! in x)
x))]) x)))]
[f2 (mk f)])
(test '(110 110) f 110) (test '(110 110) f 110)
(test #f values in) (test #f values in)
(test '(111 111) f2 111) (test '(111 111) f2 111)
(test 111 values in))) (test 111 values in)
(check-proc-prop f mk)))
;; Multiple arguments, no post filter: ;; Multiple arguments, no post filter:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -323,15 +335,18 @@
impersonate-procedure**]) impersonate-procedure**])
(let* ([f (lambda (x y) (list x y))] (let* ([f (lambda (x y) (list x y))]
[in #f] [in #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x y) (lambda (x y)
(set! in (vector x y)) (set! in (vector x y))
(values x y)))]) (values x y))))]
[f2 (mk f)])
(test '(1100 1101) f 1100 1101) (test '(1100 1101) f 1100 1101)
(test #f values in) (test #f values in)
(test '(1110 1111) f2 1110 1111) (test '(1110 1111) f2 1110 1111)
(test (vector 1110 1111) values in))) (test (vector 1110 1111) values in)
(check-proc-prop f mk)))
;; Single argument, post filter on single value: ;; Single argument, post filter on single value:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -341,20 +356,23 @@
(let* ([f (lambda (x) (list x x))] (let* ([f (lambda (x) (list x x))]
[in #f] [in #f]
[out #f] [out #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x) (lambda (x)
(set! in x) (set! in x)
(values (lambda (y) (values (lambda (y)
(set! out y) (set! out y)
y) y)
x)))]) x))))]
[f2 (mk f)])
(test '(10 10) f 10) (test '(10 10) f 10)
(test #f values in) (test #f values in)
(test #f values out) (test #f values out)
(test '(11 11) f2 11) (test '(11 11) f2 11)
(test 11 values in) (test 11 values in)
(test '(11 11) values out))) (test '(11 11) values out)
(check-proc-prop f mk)))
;; Multiple arguments, post filter on multiple values: ;; Multiple arguments, post filter on multiple values:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -364,20 +382,23 @@
(let* ([f (lambda (x y z) (values y (list x z)))] (let* ([f (lambda (x y z) (values y (list x z)))]
[in #f] [in #f]
[out #f] [out #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x y z) (lambda (x y z)
(set! in (vector x y z)) (set! in (vector x y z))
(values (lambda (y z) (values (lambda (y z)
(set! out (vector y z)) (set! out (vector y z))
(values y z)) (values y z))
x y z)))]) x y z))))]
[f2 (mk f)])
(test-values '(b (a c)) (lambda () (f 'a 'b 'c))) (test-values '(b (a c)) (lambda () (f 'a 'b 'c)))
(test #f values in) (test #f values in)
(test #f values out) (test #f values out)
(test-values '(b (a c)) (lambda () (f2 'a 'b 'c))) (test-values '(b (a c)) (lambda () (f2 'a 'b 'c)))
(test (vector 'a 'b 'c) values in) (test (vector 'a 'b 'c) values in)
(test (vector 'b '(a c)) values out))) (test (vector 'b '(a c)) values out)
(check-proc-prop f mk)))
;; Optional keyword arguments: ;; Optional keyword arguments:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -386,7 +407,8 @@
impersonate-procedure**/kw]) impersonate-procedure**/kw])
(let* ([f (lambda (x #:a [a 'a] #:b [b 'b]) (list x a b))] (let* ([f (lambda (x #:a [a 'a] #:b [b 'b]) (list x a b))]
[in #f] [in #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x #:a [a 'nope] #:b [b 'nope]) (lambda (x #:a [a 'nope] #:b [b 'nope])
(if (and (eq? a 'nope) (eq? b 'nope)) (if (and (eq? a 'nope) (eq? b 'nope))
@ -395,7 +417,8 @@
(append (append
(if (eq? a 'nope) null (list a)) (if (eq? a 'nope) null (list a))
(if (eq? b 'nope) null (list b))) (if (eq? b 'nope) null (list b)))
x))))]) x)))))]
[f2 (mk f)])
(test '(1 a b) f 1) (test '(1 a b) f 1)
(test '(1 a b) f2 1) (test '(1 a b) f2 1)
(test '(1 2 b) f 1 #:a 2) (test '(1 2 b) f 1 #:a 2)
@ -406,7 +429,8 @@
(test '(1 2 3) f2 1 #:a 2 #:b 3) (test '(1 2 3) f2 1 #:a 2 #:b 3)
(test 1 procedure-arity f2) (test 1 procedure-arity f2)
(test 'f object-name f2) (test 'f object-name f2)
(test-values '(() (#:a #:b)) (lambda () (procedure-keywords f2))))) (test-values '(() (#:a #:b)) (lambda () (procedure-keywords f2)))
(check-proc-prop f mk)))
;; Optional keyword arguments with result chaperone: ;; Optional keyword arguments with result chaperone:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -416,7 +440,8 @@
(let* ([f (lambda (x #:a [a 'a] #:b [b 'b]) (list x a b))] (let* ([f (lambda (x #:a [a 'a] #:b [b 'b]) (list x a b))]
[in #f] [in #f]
[out #f] [out #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x #:a [a 'nope] #:b [b 'nope]) (lambda (x #:a [a 'nope] #:b [b 'nope])
(set! in (list x a b)) (set! in (list x a b))
@ -427,7 +452,8 @@
(append (append
(if (eq? a 'nope) null (list a)) (if (eq? a 'nope) null (list a))
(if (eq? b 'nope) null (list b))) (if (eq? b 'nope) null (list b)))
x))))]) x)))))]
[f2 (mk f)])
(test '(1 a b) f 1) (test '(1 a b) f 1)
(test '(#f #f) list in out) (test '(#f #f) list in out)
(test '(1 a b) f2 1) (test '(1 a b) f2 1)
@ -441,7 +467,8 @@
(test '(1 2 3) f2 1 #:a 2 #:b 3) (test '(1 2 3) f2 1 #:a 2 #:b 3)
(test 1 procedure-arity f2) (test 1 procedure-arity f2)
(test 'f object-name f2) (test 'f object-name f2)
(test-values '(() (#:a #:b)) (lambda () (procedure-keywords f2))))) (test-values '(() (#:a #:b)) (lambda () (procedure-keywords f2)))
(check-proc-prop f mk)))
;; Required keyword arguments: ;; Required keyword arguments:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -450,7 +477,8 @@
impersonate-procedure**/kw]) impersonate-procedure**/kw])
(let* ([f (lambda (x #:a [a 'a] #:b b) (list x a b))] (let* ([f (lambda (x #:a [a 'a] #:b b) (list x a b))]
[in #f] [in #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x #:a [a 'nope] #:b [b 'nope]) (lambda (x #:a [a 'nope] #:b [b 'nope])
(if (and (eq? a 'nope) (eq? b 'nope)) (if (and (eq? a 'nope) (eq? b 'nope))
@ -459,7 +487,8 @@
(append (append
(if (eq? a 'nope) null (list a)) (if (eq? a 'nope) null (list a))
(if (eq? b 'nope) null (list b))) (if (eq? b 'nope) null (list b)))
x))))]) x)))))]
[f2 (mk f)])
(err/rt-test (f 1)) (err/rt-test (f 1))
(err/rt-test (f2 1)) (err/rt-test (f2 1))
(err/rt-test (f 1 #:a 2)) (err/rt-test (f 1 #:a 2))
@ -470,7 +499,8 @@
(test '(1 2 3) f2 1 #:a 2 #:b 3) (test '(1 2 3) f2 1 #:a 2 #:b 3)
(test 1 procedure-arity f2) (test 1 procedure-arity f2)
(test 'f object-name f2) (test 'f object-name f2)
(test-values '((#:b) (#:a #:b)) (lambda () (procedure-keywords f2))))) (test-values '((#:b) (#:a #:b)) (lambda () (procedure-keywords f2)))
(check-proc-prop f mk)))
;; Required keyword arguments: ;; Required keyword arguments:
(as-chaperone-or-impersonator (as-chaperone-or-impersonator
@ -480,7 +510,8 @@
(let* ([f (lambda (x #:a [a 'a] #:b b) (list x a b))] (let* ([f (lambda (x #:a [a 'a] #:b b) (list x a b))]
[in #f] [in #f]
[out #f] [out #f]
[f2 (chaperone-procedure [mk (lambda (f)
(chaperone-procedure
f f
(lambda (x #:a [a 'nope] #:b [b 'nope]) (lambda (x #:a [a 'nope] #:b [b 'nope])
(set! in (list x a b)) (set! in (list x a b))
@ -491,7 +522,8 @@
(append (append
(if (eq? a 'nope) null (list a)) (if (eq? a 'nope) null (list a))
(if (eq? b 'nope) null (list b))) (if (eq? b 'nope) null (list b)))
x))))]) x)))))]
[f2 (mk f)])
(err/rt-test (f 1)) (err/rt-test (f 1))
(err/rt-test (f2 1)) (err/rt-test (f2 1))
(err/rt-test (f 1 #:a 2)) (err/rt-test (f 1 #:a 2))
@ -503,7 +535,8 @@
(test '(1 2 3) f2 1 #:a 2 #:b 3) (test '(1 2 3) f2 1 #:a 2 #:b 3)
(test 1 procedure-arity f2) (test 1 procedure-arity f2)
(test 'f object-name f2) (test 'f object-name f2)
(test-values '((#:b) (#:a #:b)) (lambda () (procedure-keywords f2))))) (test-values '((#:b) (#:a #:b)) (lambda () (procedure-keywords f2)))
(check-proc-prop f mk)))
(err/rt-test ((chaperone-procedure (lambda (x) x) (lambda (y) (values y y))) 1)) (err/rt-test ((chaperone-procedure (lambda (x) x) (lambda (y) (values y y))) 1))
(err/rt-test ((impersonate-procedure (lambda (x) x) (lambda (y) (values y y))) 1)) (err/rt-test ((impersonate-procedure (lambda (x) x) (lambda (y) (values y y))) 1))
@ -1693,6 +1726,10 @@
(test #t impersonator-of? (make-pre-a 17 1) (chaperone-struct (make-pre-a 17 1) pre-a-y #f prop:blue 'color)) (test #t impersonator-of? (make-pre-a 17 1) (chaperone-struct (make-pre-a 17 1) pre-a-y #f prop:blue 'color))
(test #f impersonator-of? (make-pre-a 17 1) (chaperone-struct a-pre-a pre-a-y #f prop:blue 'color)) (test #f impersonator-of? (make-pre-a 17 1) (chaperone-struct a-pre-a pre-a-y #f prop:blue 'color))
(test #t blue? (make-a (chaperone-struct a1 struct:a prop:blue 'color) 2))
(test 'color blue-ref (make-a (chaperone-struct a1 struct:a prop:blue 'color) 2))
(void))) (void)))
;; ---------------------------------------- ;; ----------------------------------------

View File

@ -1708,6 +1708,8 @@
[(okp? n-proc) [(okp? n-proc)
(values (values
(if is-impersonator? (if is-impersonator?
;; Can't use `impersonate-struct` here (due to the immutable field);
;; create a new structure, but preserve properties
((if (okm? n-proc) ((if (okm? n-proc)
make-optional-keyword-method-impersonator make-optional-keyword-method-impersonator
make-optional-keyword-procedure-impersonator) make-optional-keyword-procedure-impersonator)

View File

@ -69,7 +69,6 @@ static int vector_equal (Scheme_Object *vec1, Scheme_Object *orig_vec1,
static int struct_equal (Scheme_Object *s1, Scheme_Object *orig_s1, static int struct_equal (Scheme_Object *s1, Scheme_Object *orig_s1,
Scheme_Object *s2, Scheme_Object *orig_s2, Scheme_Object *s2, Scheme_Object *orig_s2,
Equal_Info *eql); Equal_Info *eql);
static Scheme_Object *apply_impersonator_of(int for_chaperone, Scheme_Object *procs, Scheme_Object *obj);
void scheme_init_true_false(void) void scheme_init_true_false(void)
{ {
@ -699,13 +698,13 @@ int is_equal (Scheme_Object *obj1, Scheme_Object *obj2, Equal_Info *eql)
else else
procs1 = scheme_struct_type_property_ref(scheme_impersonator_of_property, (Scheme_Object *)st1); procs1 = scheme_struct_type_property_ref(scheme_impersonator_of_property, (Scheme_Object *)st1);
if (procs1) if (procs1)
procs1 = apply_impersonator_of(eql->for_chaperone, procs1, obj1); procs1 = scheme_apply_impersonator_of(eql->for_chaperone, procs1, obj1);
if (eql->for_chaperone) if (eql->for_chaperone)
procs2 = NULL; procs2 = NULL;
else { else {
procs2 = scheme_struct_type_property_ref(scheme_impersonator_of_property, (Scheme_Object *)st2); procs2 = scheme_struct_type_property_ref(scheme_impersonator_of_property, (Scheme_Object *)st2);
if (procs2) if (procs2)
procs2 = apply_impersonator_of(eql->for_chaperone, procs2, obj2); procs2 = scheme_apply_impersonator_of(eql->for_chaperone, procs2, obj2);
} }
if (procs1 || procs2) { if (procs1 || procs2) {
@ -997,7 +996,7 @@ int scheme_impersonator_of(Scheme_Object *obj1, Scheme_Object *obj2)
return is_equal(obj1, obj2, &eql); return is_equal(obj1, obj2, &eql);
} }
static Scheme_Object *apply_impersonator_of(int for_chaperone, Scheme_Object *procs, Scheme_Object *obj) Scheme_Object *scheme_apply_impersonator_of(int for_chaperone, Scheme_Object *procs, Scheme_Object *obj)
{ {
Scheme_Object *a[1], *v, *oprocs; Scheme_Object *a[1], *v, *oprocs;

View File

@ -1029,6 +1029,8 @@ Scheme_Object *scheme_chaperone_not_undefined(Scheme_Object *orig_val);
int scheme_is_noninterposing_chaperone(Scheme_Object *obj); int scheme_is_noninterposing_chaperone(Scheme_Object *obj);
Scheme_Object *scheme_apply_impersonator_of(int for_chaperone, Scheme_Object *procs, Scheme_Object *obj);
/*========================================================================*/ /*========================================================================*/
/* syntax objects */ /* syntax objects */
/*========================================================================*/ /*========================================================================*/

View File

@ -1003,6 +1003,8 @@ static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *p
Scheme_Chaperone *px; Scheme_Chaperone *px;
v = args[0]; v = args[0];
while (1) {
if (SCHEME_CHAPERONEP(v)) { if (SCHEME_CHAPERONEP(v)) {
/* Check for property at chaperone level: */ /* Check for property at chaperone level: */
px = (Scheme_Chaperone *)v; px = (Scheme_Chaperone *)v;
@ -1015,6 +1017,19 @@ static Scheme_Object *prop_pred(int argc, Scheme_Object **args, Scheme_Object *p
v = px->val; v = px->val;
} }
if (SAME_TYPE(SCHEME_TYPE(prop), scheme_chaperone_property_type)) {
/* An impersonator property must be at the chaperone level, but can
be via `prop:impersonator-of`: */
Scheme_Object *procs;
procs = scheme_struct_type_property_ref(scheme_impersonator_of_property, v);
if (procs)
v = scheme_apply_impersonator_of(0, procs, v);
else
return scheme_false;
} else
break;
}
if (SCHEME_STRUCTP(v)) if (SCHEME_STRUCTP(v))
stype = ((Scheme_Structure *)v)->stype; stype = ((Scheme_Structure *)v)->stype;
else if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_type_type)) else if (SAME_TYPE(SCHEME_TYPE(v), scheme_struct_type_type))
@ -1158,18 +1173,31 @@ static Scheme_Object *do_chaperone_prop_accessor(const char *who, Scheme_Object
return v; return v;
} }
} }
} else {
if (SAME_TYPE(SCHEME_TYPE(prop), scheme_chaperone_property_type)) {
Scheme_Object *procs;
procs = scheme_struct_type_property_ref(scheme_impersonator_of_property, arg);
if (procs) {
arg = scheme_apply_impersonator_of(0, procs, arg);
/* loop to try again */
} else {
/* an impersonator property lives at the impersonator/chaperone level, only: */
return NULL;
}
} else { } else {
return do_prop_accessor(prop, arg); return do_prop_accessor(prop, arg);
} }
} }
} }
}
static Scheme_Object *prop_accessor(int argc, Scheme_Object **args, Scheme_Object *prim) static Scheme_Object *prop_accessor(int argc, Scheme_Object **args, Scheme_Object *prim)
{ {
Scheme_Object *v; Scheme_Object *v;
v = args[0]; v = args[0];
if (SCHEME_CHAPERONEP(v)) if (SCHEME_CHAPERONEP(v)
|| SAME_TYPE(SCHEME_TYPE(SCHEME_PRIM_CLOSURE_ELS(prim)[0]), scheme_chaperone_property_type))
v = do_chaperone_prop_accessor(((Scheme_Primitive_Proc *)prim)->name, SCHEME_PRIM_CLOSURE_ELS(prim)[0], v = do_chaperone_prop_accessor(((Scheme_Primitive_Proc *)prim)->name, SCHEME_PRIM_CLOSURE_ELS(prim)[0],
v, v); v, v);
else else