diff --git a/collects/data/order.rkt b/collects/data/order.rkt index 47f22cb1b8..1847590105 100644 --- a/collects/data/order.rkt +++ b/collects/data/order.rkt @@ -17,7 +17,8 @@ (define-generics (ordered-dict gen:ordered-dict prop:ordered-dict ordered-dict? #:defined-table dict-def-table ;; private version needs all kw args, in order - #:prop-defined-already? #f) + #:prop-defined-already? #f + #:define-contract #f) (dict-iterate-least ordered-dict) (dict-iterate-greatest ordered-dict) (dict-iterate-least/>? ordered-dict key) diff --git a/collects/racket/generic.rkt b/collects/racket/generic.rkt index 2243a57293..6c676c924f 100644 --- a/collects/racket/generic.rkt +++ b/collects/racket/generic.rkt @@ -1,7 +1,9 @@ #lang racket/base -(require (rename-in "private/generic.rkt" +(require racket/contract/base + racket/contract/combinator + (rename-in "private/generic.rkt" [define-generics define-generics/pre]) - (for-syntax racket/base racket/local)) + (for-syntax racket/base racket/local racket/syntax)) ;; Convenience layer on top of racket/private/generic. ;; To avoid circular dependencies, racket/private/generic cannot use @@ -27,6 +29,121 @@ [gen:name (id "gen:" name-str)]) #'(define-generics/pre (name gen:name prop:name name? #:defined-table defined-table - ;; the following is not public - #:prop-defined-already? #f) + ;; the following are not public + #:prop-defined-already? #f + #:define-contract define-generics-contract) (generic . generics-args) ...)))])) + +;; generate a contract combinator for instances of a generic interface +(define-syntax (define-generics-contract stx) + (syntax-case stx () + [(_ name name? accessor (generic generic-idx) ...) + (with-syntax ([name/c (format-id #'name "~a/c" #'name)]) + #`(define-syntax (name/c stx) + (syntax-case stx () + [(_ [method-id ctc] (... ...)) + (andmap (λ (id) (and (identifier? id) + ;; make sure the ids are all + ;; in the interface + (member (syntax-e id) (list 'generic ...)))) + (syntax->list #'(method-id (... ...)))) + #'(make-generic-instance/c + (quote #,(syntax-e #'name/c)) + name? + accessor + (list 'method-id (... ...)) + (list ctc (... ...)) + (make-immutable-hash + (list (cons 'generic generic-idx) ...)))])))])) + +;; make a generic instance contract +(define (make-generic-instance/c name name? accessor ids ctc-args method-map) + (define ctcs (coerce-contracts 'generic-instance/c ctc-args)) + ;; map method table indices to ids & projections + (define id+ctc-map + (for/hash ([id ids] [ctc ctcs]) + (values (hash-ref method-map id) + (cons id (contract-projection ctc))))) + (cond [(andmap chaperone-contract? ctcs) + (chaperone-generic-instance/c + name name? ids ctcs accessor id+ctc-map method-map)] + [else + (impersonator-generic-instance/c + name name? ids ctcs accessor id+ctc-map method-map)])) + +(define (generic-instance/c-name ctc) + (define method-names + (map (λ (id ctc) (build-compound-type-name id ctc)) + (base-generic-instance/c-ids ctc) + (base-generic-instance/c-ctcs ctc))) + (apply build-compound-type-name + (cons (base-generic-instance/c-name ctc) method-names))) + +;; redirect for use with chaperone-vector +(define ((method-table-redirect ctc blame) vec idx val) + (define id+ctc-map (base-generic-instance/c-id+ctc-map ctc)) + (define maybe-id+ctc (hash-ref id+ctc-map idx #f)) + (cond [maybe-id+ctc + (define id (car maybe-id+ctc)) + (define proj (cdr maybe-id+ctc)) + (define blame-string (format "the ~a method of" id)) + ((proj (blame-add-context blame blame-string)) val)] + [else val])) + +;; projection for generic methods +(define ((generic-instance/c-proj proxy-struct proxy-vector) ctc) + (λ (blame) + ;; for redirecting the method table accessor + (define (redirect struct v) + (proxy-vector + v + (method-table-redirect ctc blame) + (λ (vec i v) v))) + (λ (val) + (unless (contract-first-order-passes? ctc val) + (raise-blame-error + blame val + '(expected: "~s" given: "~e") + (contract-name ctc) + val)) + (define accessor (base-generic-instance/c-accessor ctc)) + (proxy-struct val accessor redirect)))) + +;; recognizes instances of this generic interface +(define ((generic-instance/c-first-order ctc) v) + (cond [((base-generic-instance/c-name? ctc) v) + (define accessor (base-generic-instance/c-accessor ctc)) + (define method-table (accessor v)) + (define ids (base-generic-instance/c-ids ctc)) + (define ctcs (base-generic-instance/c-ctcs ctc)) + (define method-map (base-generic-instance/c-method-map ctc)) + ;; do sub-contract first-order checks + (for/and ([id ids] [ctc ctcs]) + (contract-first-order-passes? + ctc + (vector-ref method-table (hash-ref method-map id))))] + [else #f])) + +;; name - for building ctc name +;; name? - for first-order checks +;; ids - for method names (used to build the ctc name) +;; ctcs - for the contract name +;; accessor - for chaperoning the struct type property +;; id+ctc-map - for chaperoning the method table vector +;; method-map - for first-order checks +(struct base-generic-instance/c + (name name? ids ctcs accessor id+ctc-map method-map)) + +(struct chaperone-generic-instance/c base-generic-instance/c () + #:property prop:chaperone-contract + (build-chaperone-contract-property + #:projection (generic-instance/c-proj chaperone-struct chaperone-vector) + #:first-order generic-instance/c-first-order + #:name generic-instance/c-name)) + +(struct impersonator-generic-instance/c base-generic-instance/c () + #:property prop:contract + (build-contract-property + #:projection (generic-instance/c-proj impersonate-struct impersonate-vector) + #:first-order generic-instance/c-first-order + #:name generic-instance/c-name)) diff --git a/collects/racket/private/dict.rkt b/collects/racket/private/dict.rkt index 863be8e8c0..62c103574a 100644 --- a/collects/racket/private/dict.rkt +++ b/collects/racket/private/dict.rkt @@ -5,7 +5,8 @@ (define-generics (dict gen:dict prop:dict dict? #:defined-table dict-def-table ;; private version needs all kw args, in order - #:prop-defined-already? #f) + #:prop-defined-already? #f + #:define-contract #f) (dict-ref dict key [default]) (dict-set! dict key val) (dict-set dict key val) diff --git a/collects/racket/private/generic.rkt b/collects/racket/private/generic.rkt index ffbc284110..7159a26796 100644 --- a/collects/racket/private/generic.rkt +++ b/collects/racket/private/generic.rkt @@ -1,7 +1,5 @@ #lang racket/base (require racket/local - racket/contract/base - racket/contract/combinator (for-syntax racket/base racket/local racket/syntax) @@ -25,7 +23,12 @@ ;; are we being passed an existing struct property? If so, ;; this kw arg is bound to the struct property accessor, and ;; we don't define the struct property - #:prop-defined-already? defined-already?) + #:prop-defined-already? defined-already? + ;; Passed in by `define-generics` in racket/generic. + ;; This enables us to cut the dependency on racket/contract + ;; for users of this private module. Pass in #f + ;; to avoid defining a contract. + #:define-contract define-generics-contract) (generic . generic-args) ...) (and (identifier? #'header) (identifier? #'name) @@ -139,8 +142,11 @@ [gen (in-vector (get-generics this))]) (values name (not (not gen))))) ;; Define the contract that goes with this generic interface - (define-generics-contract header name? get-generics - (generic generic-idx) ...) + #,@(if (syntax-e #'define-generics-contract) + (list #'(define-generics-contract header name? get-generics + (generic generic-idx) ...)) + ;; don't define a contract when given #f + '()) ;; Define generic functions (define generic (generic-arity-coerce @@ -164,116 +170,3 @@ (raise-argument-error 'generic name-str this)))))) ...)))])) -;; generate a contract combinator for instances of a generic interface -(define-syntax (define-generics-contract stx) - (syntax-case stx () - [(_ name name? accessor (generic generic-idx) ...) - (with-syntax ([name/c (format-id #'name "~a/c" #'name)]) - #`(define-syntax (name/c stx) - (syntax-case stx () - [(_ [method-id ctc] (... ...)) - (andmap (λ (id) (and (identifier? id) - ;; make sure the ids are all - ;; in the interface - (member (syntax-e id) (list 'generic ...)))) - (syntax->list #'(method-id (... ...)))) - #'(make-generic-instance/c - (quote #,(syntax-e #'name/c)) - name? - accessor - (list 'method-id (... ...)) - (list ctc (... ...)) - (make-immutable-hash - (list (cons 'generic generic-idx) ...)))])))])) - -;; make a generic instance contract -(define (make-generic-instance/c name name? accessor ids ctc-args method-map) - (define ctcs (coerce-contracts 'generic-instance/c ctc-args)) - ;; map method table indices to ids & projections - (define id+ctc-map - (for/hash ([id ids] [ctc ctcs]) - (values (hash-ref method-map id) - (cons id (contract-projection ctc))))) - (cond [(andmap chaperone-contract? ctcs) - (chaperone-generic-instance/c - name name? ids ctcs accessor id+ctc-map method-map)] - [else - (impersonator-generic-instance/c - name name? ids ctcs accessor id+ctc-map method-map)])) - -(define (generic-instance/c-name ctc) - (define method-names - (map (λ (id ctc) (build-compound-type-name id ctc)) - (base-generic-instance/c-ids ctc) - (base-generic-instance/c-ctcs ctc))) - (apply build-compound-type-name - (cons (base-generic-instance/c-name ctc) method-names))) - -;; redirect for use with chaperone-vector -(define ((method-table-redirect ctc blame) vec idx val) - (define id+ctc-map (base-generic-instance/c-id+ctc-map ctc)) - (define maybe-id+ctc (hash-ref id+ctc-map idx #f)) - (cond [maybe-id+ctc - (define id (car maybe-id+ctc)) - (define proj (cdr maybe-id+ctc)) - (define blame-string (format "the ~a method of" id)) - ((proj (blame-add-context blame blame-string)) val)] - [else val])) - -;; projection for generic methods -(define ((generic-instance/c-proj proxy-struct proxy-vector) ctc) - (λ (blame) - ;; for redirecting the method table accessor - (define (redirect struct v) - (proxy-vector - v - (method-table-redirect ctc blame) - (λ (vec i v) v))) - (λ (val) - (unless (contract-first-order-passes? ctc val) - (raise-blame-error - blame val - '(expected: "~s" given: "~e") - (contract-name ctc) - val)) - (define accessor (base-generic-instance/c-accessor ctc)) - (proxy-struct val accessor redirect)))) - -;; recognizes instances of this generic interface -(define ((generic-instance/c-first-order ctc) v) - (cond [((base-generic-instance/c-name? ctc) v) - (define accessor (base-generic-instance/c-accessor ctc)) - (define method-table (accessor v)) - (define ids (base-generic-instance/c-ids ctc)) - (define ctcs (base-generic-instance/c-ctcs ctc)) - (define method-map (base-generic-instance/c-method-map ctc)) - ;; do sub-contract first-order checks - (for/and ([id ids] [ctc ctcs]) - (contract-first-order-passes? - ctc - (vector-ref method-table (hash-ref method-map id))))] - [else #f])) - -;; name - for building ctc name -;; name? - for first-order checks -;; ids - for method names (used to build the ctc name) -;; ctcs - for the contract name -;; accessor - for chaperoning the struct type property -;; id+ctc-map - for chaperoning the method table vector -;; method-map - for first-order checks -(struct base-generic-instance/c - (name name? ids ctcs accessor id+ctc-map method-map)) - -(struct chaperone-generic-instance/c base-generic-instance/c () - #:property prop:chaperone-contract - (build-chaperone-contract-property - #:projection (generic-instance/c-proj chaperone-struct chaperone-vector) - #:first-order generic-instance/c-first-order - #:name generic-instance/c-name)) - -(struct impersonator-generic-instance/c base-generic-instance/c () - #:property prop:contract - (build-contract-property - #:projection (generic-instance/c-proj impersonate-struct impersonate-vector) - #:first-order generic-instance/c-first-order - #:name generic-instance/c-name)) diff --git a/collects/racket/stream.rkt b/collects/racket/stream.rkt index 278b6b84f6..bbbf892d35 100644 --- a/collects/racket/stream.rkt +++ b/collects/racket/stream.rkt @@ -41,7 +41,8 @@ (define-generics (-stream gen:stream prop:stream stream? #:defined-table defined-table - #:prop-defined-already? stream-get-generics) + #:prop-defined-already? stream-get-generics + #:define-contract #f) ;; These three are never used for the reasons explained above. ;; We still need the headers for clients who extend racket/stream. (stream-empty? -stream) diff --git a/collects/tests/generic/iterator.rkt b/collects/tests/generic/iterator.rkt index f5f65d6468..04ebd45656 100644 --- a/collects/tests/generic/iterator.rkt +++ b/collects/tests/generic/iterator.rkt @@ -33,7 +33,8 @@ (define-generics (iterator gen:iterator prop:iterator iterator? #:defined-table dummy - #:prop-defined-already? iterator-accessor) + #:prop-defined-already? iterator-accessor + #:define-contract #f) (iterator-first iterator) (iterator-rest iterator) (iterator-continue? iterator))