diff --git a/collects/racket/contract/private/opt-guts.rkt b/collects/racket/contract/private/opt-guts.rkt index 6968971d4d..6e7bdac63b 100644 --- a/collects/racket/contract/private/opt-guts.rkt +++ b/collects/racket/contract/private/opt-guts.rkt @@ -22,7 +22,8 @@ opt/info-swap-blame opt/info-change-val - opt/unknown) + opt/unknown + combine-two-chaperone?s) ;; a hash table of opters (define opters-table @@ -187,4 +188,17 @@ #f lift-var null - #f))) + #`(chaperone-contract? #,lift-var)))) + +;; combine-two-chaperone?s : (or/c boolean? syntax?) (or/c boolean? syntax?) -> (or/c boolean? syntax?) +(define (combine-two-chaperone?s chaperone-a? chaperone-b?) + (cond + [(and (boolean? chaperone-a?) (boolean? chaperone-b?)) + (and chaperone-a? chaperone-b?)] + [(boolean? chaperone-a?) + (and chaperone-a? chaperone-b?)] + [(boolean? chaperone-b?) + (and chaperone-b? chaperone-a?)] + [else + #`(and #,chaperone-a? #,chaperone-b?)])) + diff --git a/collects/racket/contract/private/opt.rkt b/collects/racket/contract/private/opt.rkt index 276743adbc..4b63f0ca94 100644 --- a/collects/racket/contract/private/opt.rkt +++ b/collects/racket/contract/private/opt.rkt @@ -46,7 +46,10 @@ ;; else the symbol of the lifted variable ;; This is used for contracts with subcontracts (like cons) doing checks. ;; - a list of stronger-ribs -;; - a boolean indicating if this contract is a chaperone contract +;; - a boolean or a syntax object; if it is a boolean, +;; the boolean indicaties if this contract is a chaperone contract +;; if it is a syntax object, then evaluating its contents determines +;; if this is a chaperone contract (define-syntax (define/opter stx) (syntax-case stx () [(_ (for opt/i opt/info stx) expr ...) diff --git a/collects/racket/contract/private/opters.rkt b/collects/racket/contract/private/opters.rkt index 56e7b59f06..62af36d27d 100644 --- a/collects/racket/contract/private/opters.rkt +++ b/collects/racket/contract/private/opters.rkt @@ -63,6 +63,7 @@ [else (let-values ([(next lift superlift partial flat _ this-stronger-ribs this-chaperone?) (opt/i opt/info (car ps))]) + (define next-chaperone? (combine-two-chaperone?s chaperone? this-chaperone?)) (if flat (loop (cdr ps) (cons flat next-ps) @@ -72,7 +73,7 @@ (append this-stronger-ribs stronger-ribs) hos ho-ctc - (and chaperone? this-chaperone?)) + next-chaperone?) (if (< (length hos) 1) (loop (cdr ps) next-ps @@ -82,7 +83,7 @@ (append this-stronger-ribs stronger-ribs) (cons (car ps) hos) next - (and chaperone? this-chaperone?)) + next-chaperone?) (loop (cdr ps) next-ps lift-ps @@ -91,7 +92,7 @@ stronger-ribs (cons (car ps) hos) ho-ctc - (and chaperone? this-chaperone?)))))]))]) + next-chaperone?))))]))]) (with-syntax ((next-ps (with-syntax (((opt-p ...) (reverse opt-ps))) (syntax (or opt-p ...))))) @@ -321,7 +322,7 @@ #f) #f (append stronger-ribs-hd stronger-ribs-tl) - (and hd-chaperone? tl-chaperone?))))) + (combine-two-chaperone?s hd-chaperone? tl-chaperone?))))) (syntax-case stx (cons/c) [(_ hdp tlp) (opt/cons-ctc #'hdp #'tlp)])) @@ -553,7 +554,7 @@ (let-values ([(next lift superlift partial flat _ stronger-ribs chaperone?) (opt/arrow-ctc (syntax->list (syntax (dom ...))) (syntax->list (syntax (rng ...))))]) - (if chaperone? + (if (eq? chaperone? #t) (values next lift superlift partial flat _ stronger-ribs chaperone?) (opt/unknown opt/i opt/info stx))))] [(-> dom ... any) @@ -561,7 +562,7 @@ (opt/unknown opt/i opt/info stx) ;; give up if there is a mandatory keyword (let-values ([(next lift superlift partial flat _ stronger-ribs chaperone?) (opt/arrow-any-ctc (syntax->list (syntax (dom ...))))]) - (if chaperone? + (if (eq? chaperone? #t) (values next lift superlift partial flat _ stronger-ribs chaperone?) (opt/unknown opt/i opt/info stx))))] [(-> dom ... rng) @@ -570,7 +571,7 @@ (let-values ([(next lift superlift partial flat _ stronger-ribs chaperone?) (opt/arrow-ctc (syntax->list (syntax (dom ...))) (list #'rng))]) - (if chaperone? + (if (eq? chaperone? #t) (values next lift superlift partial flat _ stronger-ribs chaperone?) (opt/unknown opt/i opt/info stx))))])) diff --git a/collects/racket/contract/private/struct-dc.rkt b/collects/racket/contract/private/struct-dc.rkt index 6fa467eab5..003aed802e 100644 --- a/collects/racket/contract/private/struct-dc.rkt +++ b/collects/racket/contract/private/struct-dc.rkt @@ -58,7 +58,7 @@ (struct subcontract (field-name ref depended-on?) #:transparent) (struct indep subcontract (ctc) #:transparent) -(struct dep subcontract (dep-proc kind) #:transparent) +(struct dep subcontract (dep-proc flat?) #:transparent) (struct immutable indep () #:transparent) (struct lazy-immutable indep () #:transparent) @@ -74,47 +74,13 @@ (begin-for-syntax ;; exp : syntax ;; lazy? : boolean - ;; type : (or/c '#:impersonator '#:chaperone '#:flat) + ;; type : (or/c '#:chaperone '#:flat) ;; sel-id : identifier? ;; deps : (listof identifier?) (struct clause (exp lazy? sel-id)) (struct dep-clause clause (type deps)) (struct indep-clause clause ())) -(define-syntax (struct/c stx) - (syntax-case stx () - [(_ . args) - (with-syntax ([x (syntax/loc stx (do-struct/c . args))]) - (syntax/loc stx (#%expression x)))])) - -(define (struct/c-name ctc) - '(let ([ctcs (map second - (sort (append (base-struct/c-immutables ctc) (base-struct/c-mutables ctc)) - < #:key first))]) - (apply build-compound-type-name 'struct/c (base-struct/c-name ctc) ctcs))) - -(define (check-struct/c ctc) - '(let ([name (base-struct/c-name ctc)] - [pred? (base-struct/c-predicate ctc)] - [ctc/ref-pairs (map (λ (l) (cons (second l) (third l))) - (append (base-struct/c-immutables ctc) (base-struct/c-mutables ctc)))]) - (λ (val fail [first-order? #f]) - (unless (pred? val) - (fail "expected: ~s, got ~e" name val)) - (when first-order? - (for ([p (in-list ctc/ref-pairs)]) - (let ([c (car p)] [v ((cdr p) val)]) - (unless (contract-first-order-passes? c v) - (fail "expected: ~s, got ~e" (contract-name c) v))))) - #t))) - -(define (struct/c-first-order ctc) - (let ([f (check-struct/c ctc)]) - (λ (val) - (let/ec fail - (f val (λ args (fail #f)) #t))))) - - (define-syntax-rule (cache-λ (id ...) e) (let ([cached unique]) @@ -123,230 +89,6 @@ (set! cached e) cached] [else cached])))) - -(define (struct/c-proj ctc) - (define sub-contracts (base-struct/c-sub-contracts ctc)) - (λ (blame) - (define swapped-blame (blame-swap blame)) - - (define immutable-proj+refs - (for/list ([sub-contract (in-list sub-contracts)] - #:when (immutable? sub-contract)) - (cons - (subcontract-ref sub-contract) - ((contract-struct-projection (indep-ctc immutable)) - blame)))) - - (define init-chaperone-args - (list struct/c-imp-prop-desc - ctc)) - (define init-impersonator-args '()) - - (for ([subcontract (in-list sub-contracts)]) - (cond - [(lazy-immutable? subcontract) - (define proj ((contract-struct-projection (indep-ctc subcontract)) blame)) - (set! init-chaperone-args (list* (subcontract-ref subcontract) - (cache-λ (strct fld) (proj fld)) - init-chaperone-args))] - [(mutable? subcontract) - (define mk-proj (indep-ctc subcontract)) - (define get-proj (mk-proj blame)) - (define set-proj (mk-proj swapped-blame)) - (set! init-impersonator-args (list* (subcontract-ref subcontract) - (λ (strct fld) (get-proj fld)) - (mutable-set subcontract) - (λ (strct fld) (set-proj fld)) - init-impersonator-args))])) - - (λ (val) - (cond - [(and (struct/c-imp-prop-pred? val) - (eq? (struct/c-imp-prop-get val) ctc)) - val] - [else - ;; need to check val is an instance of the right struct - ;(checker val (λ args (apply raise-blame-error blame val args))) - - (define chaperone-args init-chaperone-args) - (define impersonator-args init-impersonator-args) - - (for ([immutable-proj+ref (in-list immutable-proj+refs)]) - (define sel (car immutable-proj+ref)) - (define immutable-proj (cdr immutable-proj+ref)) - (define nv (immutable-proj (sel val))) - (set! chaperone-args (list* sel - (λ (strct fld) nv) - chaperone-args))) - - (for ([sub-contract (in-list sub-contracts)]) - (cond - [(dep-immutable? sub-contract) - (define ctc ((dep-dep-proc sub-contract) val)) - (define ref (subcontract-ref sub-contract)) - (define proj ((contract-struct-projection ctc) blame)) - (cond - [(flat-contract? ctc) - (proj (ref val))] - [else - (define projected (proj (ref val))) - (cond - [(chaperone-contract? ctc) - (set! chaperone-args (list* ref - (λ (strct fld) projected) - chaperone-args))] - [else ;; impersonator contract - (error 'struct/dc - "got an impersonator contract for the field ~a, but it is an immutable field" - (object-name ref))])])] - [(dep-lazy-immutable? sub-contract) - (define ctc ((dep-dep-proc sub-contract) val)) - (define ref (subcontract-ref sub-contract)) - (define proj ((contract-struct-projection ctc) blame)) - (cond - [(chaperone-contract? ctc) - (set! chaperone-args (list* ref - (cache-λ (strct fld) (proj fld)) - chaperone-args))] - [else ;; impersonator contract - (error 'struct/dc - "got an impersonator contract for the field ~a, but it is an immutable field" - (object-name ref))])] - [(dep-mutable? sub-contract) - (define ctc ((dep-dep-proc sub-contract) val)) - (define ref (subcontract-ref sub-contract)) - (define set (dep-mutable-set sub-contract)) - (define get-proj ((contract-struct-projection ctc) blame)) - (define set-proj ((contract-struct-projection ctc) swapped-blame)) - (cond - [(chaperone-contract? ctc) - (set! chaperone-args (list* ref - (λ (strct fld) (get-proj fld)) - set - (λ (strct fld) (set-proj fld)) - chaperone-args))] - [else ;; impersonator contract - (set! impersonator-args (list* ref - (λ (strct fld) (get-proj fld)) - set - (λ (strct fld) (set-proj fld)) - impersonator-args))])])) - - (define chaperoned-val - (if (null? (cddr chaperone-args)) - val - (apply chaperone-struct val chaperone-args))) - (apply impersonate-struct - chaperoned-val - (if (and (null? (cddr chaperone-args)) - (not (null? impersonator-args))) - (append impersonator-args - (list struct/c-imp-prop-desc ctc)) - impersonator-args))])))) - -;; name is symbol -;; predicate is (-> any bool) -;; immutables is (listof (list natural contract selector-proc)) -;; mutables is (listof (list natural contract selector-proc mutator-proc)) -(define-struct base-struct/c (name predicate sub-contracts)) - -(define-struct (flat-struct/c base-struct/c) () - #:property prop:flat-contract - (build-flat-contract-property - #:name struct/c-name - #:first-order struct/c-first-order - #:projection struct/c-proj)) - -(define-struct (chaperone-struct/c base-struct/c) () - #:property prop:chaperone-contract - (parameterize ([skip-projection-wrapper? #t]) - (build-chaperone-contract-property - #:name struct/c-name - #:first-order struct/c-first-order - #:projection struct/c-proj))) - -(define-struct (impersonator-struct/c base-struct/c) () - #:property prop:contract - (build-contract-property - #:name struct/c-name - #:first-order struct/c-first-order - #:projection struct/c-proj)) - -(define-syntax (do-struct/c stx) - (syntax-case stx () - [(_ struct-name args ...) - (and (identifier? (syntax struct-name)) - (struct-info? (syntax-local-value (syntax struct-name) (λ () #f)))) - (let* ([si (extract-struct-info (syntax-local-value (syntax struct-name)))] - [predicate-id (third si)] - [selector-ids (reverse (fourth si))] - [mutator-ids (reverse (fifth si))] - [ctcs (syntax->list #'(args ...))] - [ctc-names (generate-temporaries #'(args ...))]) - (unless (= (length selector-ids) (length ctcs)) - (raise-syntax-error 'struct/c - (format "expected ~a contracts because struct ~a has ~a fields" - (length selector-ids) - (syntax-e #'struct-name) - (length selector-ids)) - stx)) - (unless predicate-id - (raise-syntax-error 'struct/c - (format "could not determine predicate for ~s" (syntax-e #'struct-name)) - stx)) - (unless (andmap values selector-ids) - (raise-syntax-error 'struct/c - (format "could not determine selectors for ~s" (syntax-e #'struct-name)) - stx)) - - (let ([combined-ids (for/list ([n (in-naturals)] - [ctc-name (in-list ctc-names)] - [ref-name (in-list selector-ids)] - [mut-name (in-list mutator-ids)]) - (list n ctc-name ref-name mut-name))]) - (let-values ([(mutables immutables) (partition (λ (l) (fourth l)) combined-ids)]) - (with-syntax ([(ctc-x ...) ctc-names] - [predicate-id predicate-id] - [((imm-count imm-ctc-x imm-ref _) ...) immutables] - [((mut-count mut-ctc-x mut-ref mut-set) ...) mutables]) - (syntax - (let ([ctc-x (coerce-contract 'struct/c args)] ...) - (let ([immutables (list (immutable imm-count imm-ctc-x imm-ref) ...)] - [mutables (list (mutable mut-count mut-ctc-x mut-ref mut-set) ...)]) - (struct/c/proc 'struct-name predicate-id immutables mutables))))))))] - [(_ struct-name anything ...) - (raise-syntax-error 'struct/c "expected a struct identifier" stx (syntax struct-name))])) - -(define (struct/c/proc struct-name predicate sub-contracts) - (for ([sub-contract (in-list sub-contracts)] - #:when (or (immutable? sub-contract) - (lazy-immutable? sub-contract))) - (define imm-ctc (indep-ctc sub-contract)) - (unless (chaperone-contract? imm-ctc) - (error 'struct/c - "expected a chaperone contract for immutable field ~a, got ~e" - (subcontract-field-name sub-contract) - imm-ctc))) - (cond - [(and (not (ormap dep-mutable? sub-contracts)) - (not (ormap mutable? sub-contracts)) - (andmap flat-subcontract? sub-contracts)) - (make-flat-struct/c struct-name predicate sub-contracts)] - [(andmap chaperone-subcontract? sub-contracts) - (make-chaperone-struct/c struct-name predicate sub-contracts)] - [else - (make-impersonator-struct/c struct-name predicate sub-contracts)])) - -(define (flat-subcontract? sc) - (cond - [(indep? sc) (flat-contract? (indep-ctc sc))] - [(dep? sc) (eq? (dep-kind sc) 'flat)])) - -(define (chaperone-subcontract? sc) - (cond - [(indep? sc) (chaperone-contract? (indep-ctc sc))] - [(dep? sc) (or (eq? (dep-kind sc) 'chaperone) - (eq? (dep-kind sc) 'flat))])) (define unique (box #f)) (define (un-dep ctc obj blame immutable-field) @@ -356,7 +98,7 @@ (((contract-projection ctc) blame) obj))) (define (struct/dc-name ctc) - (define info (struct/dc-name-info ctc)) + (define info (base-struct/dc-name-info ctc)) `(struct/dc ,(vector-ref info 0) #; ,@(for/list ([x (in-list (vector-ref info 1))] @@ -367,31 +109,31 @@ '...)]))) (define (struct/dc-first-order ctc) - (struct/dc-pred ctc)) + (base-struct/dc-pred ctc)) (define (struct/dc-proj ctc) - (define pred? (struct/dc-pred ctc)) + (define pred? (base-struct/dc-pred ctc)) (λ (blame) (define blames - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))]) + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))]) (blame-add-context blame (format "the ~a field of" (subcontract-field-name subcontract))))) (define mut-blames - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))]) + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))]) (blame-add-context blame (format "the ~a field of" (subcontract-field-name subcontract)) #:swap? #t))) (define indy-blames - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))]) + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))]) (blame-replace-negative (blame-add-context blame (format "the ~a field of" (subcontract-field-name subcontract))) - (struct/dc-here ctc)))) + (base-struct/dc-here ctc)))) (define mut-indy-blames - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))]) + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))]) (blame-replace-negative (blame-add-context blame (format "the ~a field of" (subcontract-field-name subcontract)) #:swap? #t) - (struct/dc-here ctc)))) + (base-struct/dc-here ctc)))) (define projs - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))] + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))] [blame+ctxt (in-list blames)]) (cond [(indep? subcontract) @@ -399,7 +141,7 @@ ((contract-projection sub-ctc) blame+ctxt)] [else #f]))) (define mut-projs - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))] + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))] [blame+ctxt (in-list mut-blames)]) (cond [(and (indep? subcontract) (mutable? subcontract)) @@ -407,7 +149,7 @@ ((contract-projection sub-ctc) blame+ctxt)] [else #f]))) (define indy-projs - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))] + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))] [blame+ctxt (in-list indy-blames)]) (cond [(indep? subcontract) @@ -415,7 +157,7 @@ ((contract-projection sub-ctc) blame+ctxt)] [else #f]))) (define mut-indy-projs - (for/list ([subcontract (in-list (struct/dc-subcontracts ctc))] + (for/list ([subcontract (in-list (base-struct/dc-subcontracts ctc))] [blame+ctxt (in-list mut-indy-blames)]) (cond [(and (indep? subcontract) (mutable? subcontract)) @@ -430,8 +172,8 @@ [else (unless (pred? v) (raise-blame-error blame v "expected a ~a" - (struct/dc-struct-name ctc))) - (let loop ([subcontracts (struct/dc-subcontracts ctc)] + (base-struct/dc-struct-name ctc))) + (let loop ([subcontracts (base-struct/dc-subcontracts ctc)] [projs projs] [mut-projs mut-projs] [indy-projs indy-projs] @@ -456,12 +198,17 @@ (define mut-blame (car mut-blames)) (define indy-blame (car indy-blames)) (define mut-indy-blame (car mut-indy-blames)) - (define dep-ctc + (define dep-ctc (and (dep? subcontract) - (contract-projection - (coerce-contract - 'struct/dc - (apply (dep-dep-proc subcontract) dep-args))))) + (coerce-contract + 'struct/dc + (apply (dep-dep-proc subcontract) dep-args)))) + (when dep-ctc + (unless (chaperone-contract? dep-ctc) + (error 'struct/dc "expected a chaperone contract for the immutable field ~a, got ~e" + (subcontract-field-name subcontract) + dep-ctc))) + (define dep-ctc-blame-proj (and dep-ctc (contract-projection dep-ctc))) (define new-chaperone-args (cond [(immutable? subcontract) @@ -471,13 +218,7 @@ chaperone-args)] [(lazy-immutable? subcontract) (list* sel - (let ([cache unique]) - (λ (fld v) - (cond - [(eq? cache unique) - (set! cache (proj v)) - cache] - [else cache]))) + (cache-λ (fld v) (proj v)) chaperone-args)] [(mutable? subcontract) (list* sel @@ -486,7 +227,7 @@ (λ (fld v) (mut-proj v)) chaperone-args)] [else - (define proj (dep-ctc blame)) + (define proj (dep-ctc-blame-proj blame)) (cond [(dep-immutable? subcontract) (list* sel @@ -495,17 +236,10 @@ chaperone-args)] [(dep-lazy-immutable? subcontract) (list* sel - (let ([cached unique]) - (λ (fld v) - (cond - [(eq? cached unique) - (set! cached (proj v)) - cached] - [else - cached]))) + (cache-λ (fld v) (proj v)) chaperone-args)] [(dep-mutable? subcontract) - (define mut-proj (dep-ctc mut-blame)) + (define mut-proj (dep-ctc-blame-proj mut-blame)) (list* sel (λ (fld v) (proj v)) (mutable-set subcontract) @@ -516,16 +250,16 @@ (cdr blames) (cdr mut-blames) (cdr indy-blames) (cdr mut-indy-blames) new-chaperone-args (if (subcontract-depended-on? subcontract) - (cons (if dep-ctc - ((dep-ctc indy-blame) ((subcontract-ref subcontract) v)) + (cons (if dep-ctc-blame-proj + ((dep-ctc-blame-proj indy-blame) ((subcontract-ref subcontract) v)) (indy-proj ((subcontract-ref subcontract) v))) dep-args) dep-args))]))]))) #; (begin - (define pred? (struct/dc-pred ctc)) - (define mk-proj ((struct/dc-apply-proj ctc) ctc)) + (define pred? (base-struct/dc-pred ctc)) + (define mk-proj ((base-struct/dc-apply-proj ctc) ctc)) (λ (blame) (define proj (mk-proj blame)) (λ (v) @@ -536,14 +270,14 @@ [else (unless (pred? v) (raise-blame-error blame v "expected a ~a" - (struct/dc-struct-name ctc))) + (base-struct/dc-struct-name ctc))) (proj v)]))))) (define (struct/dc-stronger? this that) (and (struct/dc? that) - (eq? (struct/dc-pred this) (struct/dc-pred that)) - (for/and ([this-subcontract (in-list (struct/dc-subcontracts this))] - [that-subcontract (in-list (struct/dc-subcontracts that))]) + (eq? (base-struct/dc-pred this) (base-struct/dc-pred that)) + (for/and ([this-subcontract (in-list (base-struct/dc-subcontracts this))] + [that-subcontract (in-list (base-struct/dc-subcontracts that))]) (cond [(and (indep? this-subcontract) (indep? that-subcontract)) @@ -565,8 +299,10 @@ (dep-dep-proc this-subcontract) (dep-dep-proc that-subcontract)))] [else #t])))) - -(define-struct struct/dc (subcontracts pred struct-name here name-info) + +(define-struct base-struct/dc (subcontracts pred struct-name here name-info)) + +(define-struct (struct/dc base-struct/dc) () #:property prop:chaperone-contract (parameterize ([skip-projection-wrapper? #t]) (build-chaperone-contract-property @@ -575,6 +311,32 @@ #:projection struct/dc-proj #:stronger struct/dc-stronger?))) +(define-struct (flat-struct/dc base-struct/dc) () + #:property prop:flat-contract + (parameterize ([skip-projection-wrapper? #t]) + (build-flat-contract-property + #:name struct/dc-name + #:first-order struct/dc-first-order + #:projection struct/dc-proj + #:stronger struct/dc-stronger?))) + +(define (build-struct/dc subcontracts pred struct-name here name-info) + (for ([subcontract (in-list subcontracts)]) + (when (indep? subcontract) + (unless (chaperone-contract? (indep-ctc subcontract)) + (error 'struct/dc "expected chaperone contracts, but field ~a has ~e" + (subcontract-field-name subcontract) + (indep-ctc subcontract))))) + (define (flat-subcontract? subcontract) + (cond + [(indep? subcontract) (flat-contract? (indep-ctc subcontract))] + [(dep? subcontract) (dep-flat? subcontract)])) + ((if (andmap flat-subcontract? subcontracts) + make-flat-struct/dc + make-struct/dc) + subcontracts pred struct-name here name-info)) + + (define-for-syntax (get-struct-info id stx) (unless (identifier? id) (raise-syntax-error #f "expected a struct name" stx id)) @@ -825,7 +587,7 @@ (define indep/dep-args (if (dep-clause? clause) (list #`(λ (#,@dep-args) #,(clause-exp clause)) - #`'#,(dep-clause-type clause)) + (eq? (dep-clause-type clause) '#:flat)) (list #`(coerce-contract 'struct/dc #,(clause-exp clause))))) (cons #`(#,subcontract-constructor #,@subcontract-args #,@indep/dep-args @@ -837,11 +599,11 @@ '()) (cdr clauses)))]))) - #`(make-struct/dc (list #,@structs) - #,(list-ref info 2) - 'struct-id - (quote-module-name) - '#(struct-id 'missing-name-information-in-struct/dc)) + #`(build-struct/dc (list #,@structs) + #,(list-ref info 2) + 'struct-id + (quote-module-name) + '#(struct-id 'missing-name-information-in-struct/dc)) #; @@ -887,18 +649,21 @@ )]) me)))))])) -#; (define/opter (-struct/dc opt/i opt/info stx) (syntax-case stx () [(_ struct-id clause ...) - (let () + (let/ec k (define info (get-struct-info #'struct-id stx)) + (define (give-up) + (call-with-values (λ () (opt/unknown opt/i opt/info stx)) + k)) (cond [(ormap values (list-ref info 4)) - ;; any mutable struct, just give up (could generate impersonator code, but - ;; would have to check that the compiled subcontracts are all chaperones/flats) - (opt/unknown opt/i opt/info stx)] + ;; any mutable struct, just give up + (give-up)] [else + (define depended-on-fields (make-free-identifier-mapping)) + (define flat-fields (make-free-identifier-mapping)) (define-values (s-chap-code s-flat-code s-lifts s-super-lifts s-partially-applied can-be-optimized? stronger-ribs chaperone?) (for/fold ([s-chap-code '()] [s-flat-code '()] @@ -914,8 +679,13 @@ (syntax-case clause () [(sel-id #:lazy exp) (values #'sel-id #t #f #'exp)] [(sel-id exp) (values #'sel-id #f #f #'exp)] - [(sel-id #:lazy (dep-id ...) exp) (values #'sel-id #t #'(dep-id ...) #'exp)] - [(sel-id (dep-id ...) exp) (values #'sel-id #f #'(dep-id ...) #'exp)])) + [(sel-id (dep-id ...) #:lazy exp) + (andmap identifier? (syntax->list #'(dep-id ...))) + (values #'sel-id #t #'(dep-id ...) #'exp)] + [(sel-id (dep-id ...) exp) + (andmap identifier? (syntax->list #'(dep-id ...))) + (values #'sel-id #f #'(dep-id ...) #'exp)] + [other (give-up)])) (define-values (this-code this-lifts this-super-lifts this-partially-applied @@ -923,6 +693,11 @@ this-chaperone?) (opt/i opt/info exp)) + (when dep-vars + (for ([dep-var (in-list (syntax->list dep-vars))]) + (free-identifier-mapping-put! depended-on-fields dep-var #t))) + (free-identifier-mapping-put! flat-fields sel-id this-flat?) + (values (cond [(and this-flat? (not lazy?) (not dep-vars)) s-chap-code] @@ -1000,6 +775,15 @@ (and this-can-be-optimized? can-be-optimized?) (append this-stronger-ribs stronger-ribs) (and this-chaperone? chaperone?)))) + + ;; to avoid having to deal with indy-ness, just give up if any + ;; of the fields that are depended on aren't flat + (free-identifier-mapping-for-each + depended-on-fields + (λ (depended-on-id flat?) + (unless (free-identifier-mapping-get flat-fields depended-on-id) + (give-up)))) + (with-syntax ([(stronger-prop-desc stronger-prop-pred? stronger-prop-get) (syntax-local-lift-values-expression 3 diff --git a/collects/scribblings/reference/contracts.scrbl b/collects/scribblings/reference/contracts.scrbl index e5437d0ef8..df5f549ccf 100644 --- a/collects/scribblings/reference/contracts.scrbl +++ b/collects/scribblings/reference/contracts.scrbl @@ -393,12 +393,10 @@ produced. Otherwise, an impersonator contract is produced. ([field-spec [field-id maybe-lazy contract-expr] [field-id (dep-field-id ...) maybe-lazy - maybe-impersonator maybe-flat maybe-dep-state contract-expr]] [maybe-lazy (code:line) #:lazy] - [maybe-impersonator (code:line) #:impersonator] [maybe-flat (code:line) #:flat] [maybe-dep-state (code:line) #:depends-on-state])]{ Produces a contract that recognizes instances of the structure diff --git a/collects/tests/racket/contract-test.rktl b/collects/tests/racket/contract-test.rktl index 76fd58fed2..f360cba621 100644 --- a/collects/tests/racket/contract-test.rktl +++ b/collects/tests/racket/contract-test.rktl @@ -9374,7 +9374,16 @@ 'struct/dc-19 '(let () (struct s (a b)) - (struct/dc s [a (new-∃/c 'α)])) + (struct/dc s [a (new-∃/c 'α)] [b integer?])) + exn:fail?) + + (contract-error-test + 'struct/dc-20 + '(let () + (struct s (a b)) + (contract (struct/dc s [a (b) (new-∃/c 'α)] [b integer?]) + (s 1 2) + 'pos 'neg)) exn:fail?) (test/pos-blame @@ -10476,6 +10485,10 @@ so that propagation occurs. (define alpha (new-∃/c 'alpha)) (struct/c s alpha))) + (ctest #t (chaperone-contract? + (let ([x (struct/dc s [a integer?] [b integer?])]) + (opt/c x)))) + (ctest #t flat-contract? (set/c integer?)) (ctest #f flat-contract? (set/c (-> integer? integer?))) (ctest #t chaperone-contract? (set/c (-> integer? integer?)))