Add augride, which is like augment but enables the contract writer to give

subclasses an idea of whether a method can be augmented (augment) or whether
a method augmentation can be overridden (augride).

svn: r18240
This commit is contained in:
Stevie Strickland 2010-02-21 00:17:42 +00:00
parent 5df617e4d4
commit ffa34e1f7d
3 changed files with 121 additions and 18 deletions

View File

@ -2485,12 +2485,23 @@
(when (eq? flag 'final)
(failed "method ~a is final" m)))))
(for ([m (class/c-augments ctc)])
(let ([index (hash-ref method-ht m #f)])
(unless index
(failed "no public method ~a" m))
(let* ([vec (vector-ref beta-methods index)])
(when (zero? (vector-length vec))
(failed "method ~a has never been augmentable" m))
(when (vector-ref vec (sub1 (vector-length vec)))
(failed "method ~a is currently overrideable, not augmentable" m)))))
(for ([m (class/c-augrides ctc)])
(let ([index (hash-ref method-ht m #f)])
(unless index
(failed "no public method ~a" m))
(let ([vec (vector-ref beta-methods index)])
(when (zero? (vector-length vec))
(failed "method ~a has never been augmentable" m)))))
(failed "method ~a has never been augmentable" m))
(unless (vector-ref vec (sub1 (vector-length vec)))
(failed "method ~a is currently augmentable, not overrideable" m)))))
(for ([s (class/c-supers ctc)])
(let ([index (hash-ref method-ht s #f)])
(unless index
@ -2529,10 +2540,12 @@
[dynamic-features
(append (class/c-overrides ctc)
(class/c-augments ctc)
(class/c-augrides ctc)
(class/c-inherits ctc))]
[dynamic-contracts
(append (class/c-override-contracts ctc)
(class/c-augment-contracts ctc)
(class/c-augride-contracts ctc)
(class/c-inherit-contracts ctc))]
[methods (if (null? (class/c-methods ctc))
(class-methods cls)
@ -2752,12 +2765,15 @@
(vector-set! proj-vec old-idx
(compose (vector-ref proj-vec old-idx) p))))))
;; For augment contracts, we both update the projection and go
;; ahead and apply the projection to the last slot (which will
;; For augment and augride contracts, we both update the projection
;; and go ahead and apply the projection to the last slot (which will
;; only be used by later classes).
(unless (null? (class/c-augments ctc))
(for ([m (in-list (class/c-augments ctc))]
[c (in-list (class/c-augment-contracts ctc))])
(unless (and (null? (class/c-augments ctc))
(null? (class/c-augrides ctc)))
(for ([m (in-list (append (class/c-augments ctc)
(class/c-augrides ctc)))]
[c (in-list (append (class/c-augment-contracts ctc)
(class/c-augride-contracts ctc)))])
(when c
(let* ([i (hash-ref method-ht m)]
[p ((contract-projection c) blame)]
@ -2789,7 +2805,8 @@
(methods method-contracts fields field-contracts
inherits inherit-contracts inherit-fields inherit-field-contracts
supers super-contracts inners inner-contracts
overrides override-contracts augments augment-contracts)
overrides override-contracts augments augment-contracts
augrides augride-contracts)
#:omit-define-syntaxes
#:property prop:contract
(build-contract-property
@ -2824,7 +2841,8 @@
(handle-optional 'super (class/c-supers ctc) (class/c-super-contracts ctc))
(handle-optional 'inner (class/c-inners ctc) (class/c-inner-contracts ctc))
(handle-optional 'override (class/c-overrides ctc) (class/c-override-contracts ctc))
(handle-optional 'augment (class/c-augments ctc) (class/c-augment-contracts ctc))))))
(handle-optional 'augment (class/c-augments ctc) (class/c-augment-contracts ctc))
(handle-optional 'augride (class/c-augrides ctc) (class/c-augride-contracts ctc))))))
#:first-order
(λ (ctc)
(λ (cls)
@ -2851,7 +2869,7 @@
(let-values ([(name ctc) (parse-name-ctc stx)])
(values (cons name names) (cons ctc ctcs)))))
(define (parse-spec stx)
(syntax-case stx (field inherit inherit-field init super inner override augment)
(syntax-case stx (field inherit inherit-field init super inner override augment augride)
[(field f-spec ...)
(let-values ([(names ctcs) (parse-names-ctcs #'(f-spec ...))])
(hash-set! parsed-forms 'fields
@ -2912,6 +2930,15 @@
(append names (hash-ref parsed-forms 'augments null)))
(hash-set! parsed-forms 'augment-contracts
(append ctcs (hash-ref parsed-forms 'augment-contracts null)))))]
[(augride a-spec ...)
(begin
(when object/c?
(raise-syntax-error 'object/c "augride contract not allowed in object/c" stx))
(let-values ([(names ctcs) (parse-names-ctcs #'(a-spec ...))])
(hash-set! parsed-forms 'augrides
(append names (hash-ref parsed-forms 'augrides null)))
(hash-set! parsed-forms 'augride-contracts
(append ctcs (hash-ref parsed-forms 'augride-contracts null)))))]
[m-spec
(let-values ([(name ctc1) (parse-name-ctc #'m-spec)])
(hash-set! parsed-forms 'methods
@ -2943,7 +2970,9 @@
[overrides #`(list #,@(reverse (hash-ref parsed-forms 'overrides null)))]
[override-ctcs #`(list #,@(reverse (hash-ref parsed-forms 'override-contracts null)))]
[augments #`(list #,@(reverse (hash-ref parsed-forms 'augments null)))]
[augment-ctcs #`(list #,@(reverse (hash-ref parsed-forms 'augment-contracts null)))])
[augment-ctcs #`(list #,@(reverse (hash-ref parsed-forms 'augment-contracts null)))]
[augrides #`(list #,@(reverse (hash-ref parsed-forms 'augrides null)))]
[augride-ctcs #`(list #,@(reverse (hash-ref parsed-forms 'augride-contracts null)))])
(syntax/loc stx
(make-class/c methods method-ctcs
fields field-ctcs
@ -2952,7 +2981,8 @@
supers super-ctcs
inners inner-ctcs
overrides override-ctcs
augments augment-ctcs))))]))
augments augment-ctcs
augrides augride-ctcs))))]))
(define (object/c-check-first-order ctc obj blame)
(let/ec return

View File

@ -1467,7 +1467,7 @@ resulting trait is the same as for @scheme[trait-sum], otherwise the
@section{Object and Class Contracts}
@defform/subs[
#:literals (field inherit inherit-field super inner override augment)
#:literals (field inherit inherit-field super inner override augment augride)
(class/c member-spec ...)
@ -1541,10 +1541,12 @@ The internal contracts are as follows:
@scheme[(method-id ...)]). This form can only be used if overriding the method in subclasses
will change the entry point to the dynamic dispatch chain (i.e., the method has never been
augmentable).}
@item{A method contract, tagged with @scheme[augment], describes the behavior provided by
the contracted class for @scheme[method-id] when called directly from subclasses. This form
can only be used if the method has previously been augmentable, which means that no augmenting
or overriding implementation will change the entry point to the dynamic dispatch chain.}
@item{A method contract, tagged with either @scheme[augment] or @scheme[augride], describes the
behavior provided by the contracted class for @scheme[method-id] when called directly from
subclasses. These forms can only be used if the method has previously been augmentable, which
means that no augmenting or overriding implementation will change the entry point to the
dynamic dispatch chain. @scheme[augment] is used when subclasses can augment the method, and
@scheme[augride] is used when subclasses can override the current augmentation.}
]}
@defform/subs[

View File

@ -4410,7 +4410,7 @@
'pos
'neg))
(test/spec-passed
(test/pos-blame
'class/c-first-order-augment-4
'(contract (class/c (augment [m (-> any/c number? number?)]))
(let ([c% (class object% (super-new) (define/pubment (m x) (inner x m x)))])
@ -4431,7 +4431,7 @@
'(contract (class/c (augment [m (-> any/c number? number?)]))
(let* ([c% (class object% (super-new) (define/public (m x) (add1 x)))]
[d% (class c% (super-new) (define/overment (m x) (+ (super m x) (inner x m x))))])
(class d% (super-new) (define/augride (m x) x)))
(class d% (super-new) (define/augment (m x) x)))
'pos
'neg))
@ -4458,6 +4458,77 @@
'neg)])
(class c% (super-new) (inherit m))))
(test/pos-blame
'class/c-first-order-augride-1
'(contract (class/c (augride [m (-> any/c number? number?)]))
object%
'pos
'neg))
(test/pos-blame
'class/c-first-order-augride-2
'(contract (class/c (augride [m (-> any/c number? number?)]))
(class object% (super-new) (define/pubment (m x) (add1 x)))
'pos
'neg))
(test/pos-blame
'class/c-first-order-augride-4
'(contract (class/c (augride [m (-> any/c number? number?)]))
(class object% (super-new) (define/public (m x) (add1 x)))
'pos
'neg))
(test/spec-passed
'class/c-first-order-augride-5
'(contract (class/c (augride [m (-> any/c number? number?)]))
(let ([c% (class object% (super-new) (define/pubment (m x) (inner x m x)))])
(class c% (super-new) (define/augride (m x) (add1 x))))
'pos
'neg))
(test/pos-blame
'class/c-first-order-augride-5
'(contract (class/c (augride [m (-> any/c number? number?)]))
(let ([c% (class object% (super-new) (define/public (m x) (add1 x)))])
(class c% (super-new) (define/override (m x) (add1 (super m x)))))
'pos
'neg))
(test/spec-passed
'class/c-first-order-augride-6
'(contract (class/c (augride [m (-> any/c number? number?)]))
(let* ([c% (class object% (super-new) (define/public (m x) (add1 x)))]
[d% (class c% (super-new) (define/overment (m x) (+ (super m x) (inner x m x))))])
(class d% (super-new) (define/augride (m x) x)))
'pos
'neg))
(test/pos-blame
'class/c-first-order-augride-7
'(contract (class/c (augride m))
object%
'pos
'neg))
(test/spec-passed
'class/c-first-order-augride-8
'(let ([c% (contract (class/c (augride m))
(class (class object% (super-new) (define/pubment (m) 3))
(super-new) (define/augride (m) 4))
'pos
'neg)])
(class c% (super-new) (inherit m))))
(test/pos-blame
'class/c-first-order-augride-9
'(let ([c% (contract (class/c (augride [m (-> any/c number? number?)]))
(class (class object% (super-new) (define/pubment (m) 3))
(super-new) (define/augride (m) 4))
'pos
'neg)])
(class c% (super-new) (inherit m))))
(test/pos-blame
'class/c-first-order-inherit-1
'(let* ([c% (contract (class/c (inherit [m (-> any/c number? number?)]))