Push mixed' contracts down into lazy struct' contracts.

This commit is contained in:
Mike Sperber 2010-08-23 14:40:21 +02:00
parent 225a42b832
commit a4e7ef3594
2 changed files with 212 additions and 61 deletions

View File

@ -63,7 +63,7 @@
=> (lambda (name)
(display "#<signature " port)
(display name port)
(display "#>" port)))
(display ">" port)))
(else
(display "#<signature>" port)))))
@ -138,14 +138,21 @@
(force (signature-arbitrary-promise (force promise))))
#:info-promise
(delay
(make-call-info (force proc-promise) (force args-promise)))
(make-call-info promise (force proc-promise) (force args-promise)))
#:=?-proc
(lambda (this-info other-info)
(and (call-info? other-info)
(eqv? (force proc-promise) (call-info-proc other-info))
(equal? (force args-promise) (call-info-args other-info))))))
(define-struct call-info (proc args) #:transparent)
(define-struct call-info (promise proc args) #:transparent)
; klude to support mixed
(define (real-signature-info sig)
(let ((raw-info (force (signature-info-promise sig))))
(if (call-info? raw-info)
(real-signature-info (force (call-info-promise raw-info)))
(force raw-info))))
(define (make-property-signature name access signature syntax)
(let ((enforce (signature-enforcer signature)))
@ -239,38 +246,80 @@
#f)))
(define (make-mixed-signature name alternative-signatures syntax)
(make-signature
name
(lambda (self obj)
(let loop ((alternative-signatures alternative-signatures))
(cond
((null? alternative-signatures)
(signature-violation obj self #f #f)
obj)
((eq? (car alternative-signatures) self)
(raise
(make-exn:fail:contract
(string->immutable-string
(if name
(format "rekursiver Vertrag: ~a" name)
"rekursiver Vertrag"))
(current-continuation-marks))))
(else
(check-signature (car alternative-signatures)
obj
values
(lambda () (loop (cdr alternative-signatures))))))))
(delay syntax)
#:arbitrary-promise
(delay
(let ((arbitraries (map force (map signature-arbitrary-promise alternative-signatures))))
(if (andmap values arbitraries)
(arbitrary-mixed
(map (lambda (sig arb)
(cons (signature->predicate sig)
arb))
alternative-signatures arbitraries))
#f)))))
(letrec ((alternative-signatures-promise
(delay
(normalize-mixed-signatures mixed-signature alternative-signatures)))
(mixed-signature
(make-signature
name
(lambda (self obj)
(let loop ((alternative-signatures (force alternative-signatures-promise)))
(cond
((null? alternative-signatures)
(signature-violation obj self #f #f)
obj)
((eq? (car alternative-signatures) self)
(raise
(make-exn:fail:contract
(string->immutable-string
(if name
(format "rekursiver Vertrag: ~a" name)
"rekursiver Vertrag"))
(current-continuation-marks))))
(else
(check-signature (car alternative-signatures)
obj
values
(lambda () (loop (cdr alternative-signatures))))))))
(delay syntax)
#:info-promise
(delay (make-mixed-info (force alternative-signatures-promise)))
#:arbitrary-promise
(delay
(let ((arbitraries (map force (map signature-arbitrary-promise (force alternative-signatures-promise)))))
(if (andmap values arbitraries)
(arbitrary-mixed
(map (lambda (sig arb)
(cons (signature->predicate sig)
arb))
(force alternative-signatures-promise) arbitraries))
#f)))
#:=?-proc
(lambda (this-info other-info)
(and (mixed-info? other-info)
(andmap signature=?
(mixed-info-signatures this-info)
(mixed-info-signatures other-info))))
#:<=?-proc
(lambda (this-info other-info)
(and (mixed-info? other-info)
(mixed-signature<=? (mixed-info-signatures this-info)
(mixed-info-signatures other-info)))))))
mixed-signature))
(define (mixed-signature<=? sigs1 sigs2)
(andmap (lambda (sig1)
(ormap (lambda (sig2)
(signature<=? sig1 sig2))
sigs2))
sigs1))
; Flatten out mixed signatures, and fold in in the struct-wrap
; signatures
(define (normalize-mixed-signatures mixed-signature sigs)
(fold-struct-wrap-signatures mixed-signature (flatten-mixed-signatures sigs)))
(define (flatten-mixed-signatures sigs)
(apply append
(map (lambda (sig)
(let ((info (force (signature-info-promise sig))))
(if (mixed-info? info)
(mixed-info-signatures info)
(list sig))))
sigs)))
(define-struct mixed-info (signatures) #:transparent)
(define (check-signature sig val success fail)
((let/ec exit
@ -415,13 +464,36 @@
; The field accessed by ref-proc and set!-proc contains one of these:
(define-struct lazy-wrap-log
;; each contains a list of lists; each element is a list of field signatures
(not-checked checked)
#:transparent)
;; list of lazy-log-not-checked
(not-checked
;; list of lists of field signatures
checked)
#:transparent)
; This situation makes trouble:
; (make-mixed-signature (make-struct-wrap-signature ...) (make-struct-wrap-signature ...) ...)
; We need to push the `mixed' signature inside the struct-wrap
; signature, which is why the struct-map signature has an implicit
; `mixed'.
; To this end, a `lazy-log-not-checked' object tracks a list of
; `mixed' alternatives. The `mixed-signature' field tracks from which
; `mixed' contract the mixture has originally come from.
; (It may be #f, in which case the `field-signatures-list' is a one-element list.)
(define-struct lazy-log-not-checked
(mixed-signature field-signatures-list)
#:transparent)
(define (make-struct-wrap-signature name type-descriptor field-signatures syntax)
(really-make-struct-wrap-signature name type-descriptor #f (list field-signatures) syntax))
; The lists of signatures in `field-signatures-list' form an implicit mixed signature.
(define (really-make-struct-wrap-signature name type-descriptor
mixed-signature field-signatures-list
syntax)
(let ((lazy-wrap-info (lazy-wrap-ref type-descriptor))
(struct-wrap-info (make-struct-wrap-info type-descriptor field-signatures))
(not-checked (make-lazy-log-not-checked mixed-signature field-signatures-list))
(struct-wrap-info (make-struct-wrap-info type-descriptor field-signatures-list))
(predicate (lambda (thing)
(and (struct? thing)
(let-values (((thing-descriptor _) (struct-info thing)))
@ -432,7 +504,7 @@
(wrap-set! (lazy-wrap-info-set!-proc lazy-wrap-info)))
(make-signature
name
(lambda (self thing)
(lambda (self thing)
(if (not (predicate thing))
(signature-violation thing self #f #f)
@ -440,15 +512,21 @@
(cond
((not log)
(wrap-set! thing
(make-lazy-wrap-log (list field-signatures) '())))
((not (let ((check (lambda (wrap-field-signatures)
(andmap signature<=?
wrap-field-signatures
field-signatures))))
(or (ormap check (lazy-wrap-log-not-checked log))
(make-lazy-wrap-log (list not-checked) '())))
((not (let ()
(define (<=? sigs1 sigs2)
(andmap signature<=? sigs1 sigs2))
(define (check wrap-field-signatures)
(ormap (lambda (field-signatures)
(<=? wrap-field-signatures field-signatures))
field-signatures-list))
(or (ormap (lambda (wrap-not-checked)
(andmap check
(lazy-log-not-checked-field-signatures-list wrap-not-checked)))
(lazy-wrap-log-not-checked log))
(ormap check (lazy-wrap-log-checked log)))))
(wrap-set! thing
(make-lazy-wrap-log (cons field-signatures (lazy-wrap-log-not-checked log))
(make-lazy-wrap-log (cons not-checked (lazy-wrap-log-not-checked log))
(lazy-wrap-log-checked log)))))))
thing)
@ -458,21 +536,25 @@
#:=?-proc
(lambda (this-info other-info)
(and (struct-wrap-info? other-info)
(struct-wrap-info-field-signatures other-info)
(struct-wrap-info-field-signatures-list other-info)
(eq? type-descriptor (struct-wrap-info-descriptor other-info))
(andmap signature=?
field-signatures
(struct-wrap-info-field-signatures other-info))))
(andmap (lambda (this-field-signatures)
(andmap (lambda (other-field-signatures)
(andmap signature=? this-field-signatures other-field-signatures))
(struct-wrap-info-field-signatures-list other-info)))
(struct-wrap-info-field-signatures-list this-info))))
#:<=?-proc
(lambda (this-info other-info)
(and (struct-wrap-info? other-info)
(struct-wrap-info-field-signatures other-info)
(struct-wrap-info-field-signatures-list other-info)
(eq? type-descriptor (struct-wrap-info-descriptor other-info))
(andmap signature<=?
field-signatures
(struct-wrap-info-field-signatures other-info))))))))
(andmap (lambda (this-field-signatures)
(ormap (lambda (other-field-signatures)
(andmap signature<=? this-field-signatures other-field-signatures))
(struct-wrap-info-field-signatures-list other-info)))
(struct-wrap-info-field-signatures-list this-info))))))))
(define-struct struct-wrap-info (descriptor field-signatures))
(define-struct struct-wrap-info (descriptor field-signatures-list) #:transparent)
(define (check-struct-wraps! thing)
(let-values (((descriptor skipped?) (struct-info thing)))
@ -489,18 +571,76 @@
(let loop ((field-vals (map (lambda (raw-accessor)
(raw-accessor thing))
raw-accessors))
(field-signatures-list (lazy-wrap-log-not-checked log)))
(if (null? field-signatures-list)
(now-checked '())
(not-checkeds (lazy-wrap-log-not-checked log)))
(if (null? not-checkeds)
(begin
(for-each (lambda (raw-mutator field-val)
(raw-mutator thing field-val))
raw-mutators field-vals)
(wrap-set! thing
(make-lazy-wrap-log '()
(append (lazy-wrap-log-not-checked log)
(append now-checked
(lazy-wrap-log-checked log)))))
(loop (map apply-signature (car field-signatures-list) field-vals)
(cdr field-signatures-list))))))))))
(let ((not-checked (car not-checkeds)))
(let ((field-signatures-list (lazy-log-not-checked-field-signatures-list not-checked))
(mixed-signature (lazy-log-not-checked-mixed-signature not-checked)))
(if (not mixed-signature) ; one-element list
(loop (map apply-signature (car field-signatures-list) field-vals)
(cons (car field-signatures-list) now-checked)
(cdr not-checkeds))
(let inner ((field-signatures-list field-signatures-list)) ; implicit mixed
(if (null? field-signatures-list)
(signature-violation thing mixed-signature #f #f)
(let map ((sigs (car field-signatures-list))
(field-vals field-vals)
(new-field-vals '()))
(if (null? sigs)
(loop (reverse new-field-vals)
(cons (car field-signatures-list) now-checked)
(cdr not-checkeds))
(check-signature (car sigs)
(car field-vals)
(lambda (new-val)
(map (cdr sigs)
(cdr field-vals)
(cons new-val new-field-vals)))
(lambda ()
(inner (cdr field-signatures-list)))))))))))))))))))
; pushes down mixed contracts
(define (fold-struct-wrap-signatures mixed-signature sigs)
(let ((struct-wrap-sigs (make-hasheq))) ; maps a type descriptor to signatures
(define (push-down-struct-wrap-sigs)
(hash-map struct-wrap-sigs
(lambda (type-desc signatures)
(really-make-struct-wrap-signature
(signature-name (car signatures)) type-desc
mixed-signature
(apply append
(map (lambda (sig)
(struct-wrap-info-field-signatures-list (real-signature-info sig)))
signatures))
(signature-syntax (car signatures))))))
(let loop ((sigs sigs)
(vanilla-sigs '()))
(if (null? sigs)
(append (push-down-struct-wrap-sigs)
(reverse vanilla-sigs))
(let* ((sig (car sigs))
(info (real-signature-info sig)))
(if (struct-wrap-info? info)
(let ((type-desc (struct-wrap-info-descriptor info)))
(hash-update! struct-wrap-sigs
type-desc
(lambda (old)
(cons sig old))
(lambda ()
(list sig)))
(loop (cdr sigs) vanilla-sigs))
(loop (cdr sigs) (cons sig vanilla-sigs))))))))
; like apply-signature, but can track more precise blame into the signature itself
(define-syntax apply-signature/blame

View File

@ -348,6 +348,17 @@
(let ((len1 (list-length l1)))
(check-equal? count 10)))))
(test-case
"mixed wrap"
(define-record-procedures-parametric pare pare-of raw-kons pare? (kar kdr))
(define sig1 (signature (pare-of integer boolean)))
(define sig2 (signature (pare-of boolean integer)))
(define sig (signature (mixed sig1 sig2)))
(define/signature x sig (raw-kons #t 15))
(define/signature y sig (raw-kons #t #t))
(check-equal? (kar x) #t)
(check-equal? (say-no (kar y)) 'no))
(test-case
"wrap equality"
(define-record-procedures-parametric pare pare-of raw-kons pare? (kar kdr))