Add support for method contracts to ->2.

Should allow removal of old -> implementation.

Temporarily (almost) duplicates code, which will be fixed by removing the old ->.
This commit is contained in:
Vincent St-Amour 2016-03-29 13:55:28 -05:00
parent 3c074249a0
commit 585ca37c5b
13 changed files with 513 additions and 272 deletions

View File

@ -611,5 +611,33 @@
(begin (set! l (cons 6 l)) #f))
(reverse l))
'(1 2 3 4 5 6))
(contract-error-test
'->-arity-error1
'(contract
(-> any/c any/c)
(lambda (x y) #t)
'pos 'neg)
(lambda (e)
(regexp-match? "a procedure that accepts 1 non-keyword argument"
(exn-message e))))
(contract-error-test
'->-arity-error2
'(contract
(-> any/c)
(lambda (x y) #t)
'pos 'neg)
(lambda (e)
(regexp-match? "a procedure that accepts 0 non-keyword argument"
(exn-message e))))
(contract-error-test
'->-arity-error3
'(contract
(->* (any/c) (#:x any/c) any/c)
(lambda (x) #t)
'pos 'neg)
(lambda (e)
(regexp-match? "a procedure that accepts the #:x keyword argument"
(exn-message e))))
)

View File

@ -2615,4 +2615,33 @@
(init-field [x 0]))
'pos 'neg)])
(equal? (new c%) (new c%)))
#f))
#f)
(contract-error-test
'->m-arity-error-1
'(contract (->m string? string?)
(lambda (y) y)
'pos
'neg)
(lambda (e)
(regexp-match? "a method that accepts 1 non-keyword argument"
(exn-message e))))
(contract-error-test
'->m-arity-error-2
'(contract (->m string?)
(lambda () y)
'pos
'neg)
(lambda (e)
(regexp-match? "a method that accepts 0 non-keyword argument"
(exn-message e))))
(contract-error-test
'->m-arity-error3
'(contract (->*m (any/c) (#:x any/c) any/c)
(lambda (x y) #t)
'pos
'neg)
(lambda (e)
(regexp-match? "a method that accepts the #:x keyword argument"
(exn-message e))))
)

View File

@ -369,7 +369,7 @@
'(let ()
(define o
(contract
(object-contract (field x pos-blame?) (f (->m neg-blame?)))
(object-contract (field x pos-blame?) (f (-> neg-blame?)))
(new (class object% (init-field x) (define/public (f) x) (super-new)) [x 3])
'pos 'neg))
(get-field x o)

View File

@ -16,7 +16,7 @@
"private/basic-opters.rkt" ;; required for effect to install the opters
"private/opt.rkt"
"private/out.rkt"
"private/arrow-val-first.rkt"
(except-in "private/arrow-val-first.rkt" base->?)
"private/orc.rkt"
"private/list.rkt"
"private/and.rkt")

View File

@ -13,13 +13,16 @@
->stct-doms
->stct-rest
->stct-min-arity
->stct-kwd-infos)
->stct-kwd-infos
method?)
(define proc/meth (if method? "a method" "a procedure"))
(let/ec k
(unless (procedure? val)
(k
(λ (neg-party)
(raise-blame-error blame #:missing-party neg-party val
'(expected: "a procedure" given: "~e")
`(expected: ,proc/meth
given: "~e")
val))))
(define-values (actual-mandatory-kwds actual-optional-kwds) (procedure-keywords val))
(define arity (if (list? (procedure-arity val))
@ -46,13 +49,17 @@
(unless matching-arity?
(k
(λ (neg-party)
(define expected-number-of-non-keyword-args*
((if method? sub1 values) expected-number-of-non-keyword-args))
(raise-blame-error blame #:missing-party neg-party val
'(expected:
"a procedure that accepts ~a non-keyword argument~a~a"
`(expected:
,(string-append "a "
proc/meth
" that accepts ~a non-keyword argument~a~a")
given: "~e"
"\n ~a")
expected-number-of-non-keyword-args
(if (= expected-number-of-non-keyword-args 1) "" "s")
expected-number-of-non-keyword-args*
(if (= expected-number-of-non-keyword-args* 1) "" "s")
(if ->stct-rest
" and arbitrarily many more"
"")
@ -63,25 +70,25 @@
(k
(λ (neg-party)
(raise-blame-error blame #:missing-party neg-party val
'(expected:
"a procedure that accepts the ~a keyword argument"
`(expected:
,(string-append proc/meth " that accepts the ~a keyword argument")
given: "~e"
"\n ~a")
kwd
val
(arity-as-string val)))))
(arity-as-string val method?)))))
(define (should-not-have-supplied kwd)
(k
(λ (neg-party)
(raise-blame-error blame #:missing-party neg-party val
'(expected:
"a procedure that does not require the ~a keyword argument"
`(expected:
,(string-append proc/meth " that does not require the ~a keyword argument")
given: "~e"
"\n ~a")
kwd
val
(arity-as-string val)))))
(arity-as-string val method?)))))
(when actual-optional-kwds ;; when all kwds are okay, no checking required
(let loop ([mandatory-kwds actual-mandatory-kwds]
@ -115,13 +122,13 @@
(λ (neg-party)
(raise-blame-error
blame #:missing-party neg-party val
'(expected:
"a procedure that optionally accepts the keyword ~a (this one is mandatory)"
`(expected:
,(string-append proc/meth " that optionally accepts the keyword ~a (this one is mandatory)")
given: "~e"
"\n ~a")
val
kwd
(arity-as-string val)))))
(arity-as-string val method?)))))
(loop new-mandatory-kwds new-all-kwds (cdr kwd-infos))]
[(keyword<? kwd (kwd-info-kwd kwd-info))
(when mandatory?
@ -133,14 +140,15 @@
#f))
(define (arity-as-string v)
(define (arity-as-string v [method? #f])
(define prefix (if (object-name v)
(format "~a accepts: " (object-name v))
(format "accepts: ")))
(string-append prefix (raw-arity-as-string v)))
(string-append prefix (raw-arity-as-string v method?)))
(define (raw-arity-as-string v)
(define (raw-arity-as-string v [method? #f])
(define ar (procedure-arity v))
(define adjust (if method? sub1 values))
(define (plural n) (if (= n 1) "" "s"))
(define-values (man-kwds all-kwds) (procedure-keywords v))
(define opt-kwds (if all-kwds (remove* man-kwds all-kwds) #f))
@ -148,9 +156,11 @@
(define normal-args
(cond
[(null? ar) "no arguments"]
[(number? ar) (format "~a ~aargument~a" ar normal-str (plural ar))]
[(number? ar)
(define ar* (adjust ar))
(format "~a ~aargument~a" ar* normal-str (plural ar*))]
[(arity-at-least? ar) (format "~a or arbitrarily many more ~aarguments"
(arity-at-least-value ar)
(adjust (arity-at-least-value ar))
normal-str)]
[else
(define comma
@ -168,12 +178,12 @@
[(arity-at-least? v)
(list
(format "~a, or arbitrarily many more ~aarguments"
(arity-at-least-value v)
(arity-at-least-value (adjust v))
normal-str))]
[else
(list (format "or ~a ~aarguments" v normal-str))])]
(list (format "or ~a ~aarguments" (adjust v) normal-str))])]
[else
(cons (format "~a~a " (car ar) comma)
(cons (format "~a~a " (adjust (car ar)) comma)
(loop (cdr ar)))])))]))
(cond
[(and (null? man-kwds) (null? opt-kwds))

View File

@ -19,11 +19,10 @@
->-proj
check-pre-cond
check-post-cond
pre-post/desc-result->string)
pre-post/desc-result->string
raise-wrong-number-of-args-error)
(define-for-syntax (build-chaperone-constructor/real this-args
;; (listof (or/c #f stx))
(define-for-syntax (build-chaperone-constructor/real ;; (listof (or/c #f stx))
;; #f => syntactically known to be any/c
mandatory-dom-projs
@ -33,7 +32,8 @@
pre pre/desc
rest
rngs
post post/desc)
post post/desc
method?)
(define (nvars n sym) (generate-temporaries (for/list ([i (in-range n)]) sym)))
(with-syntax ([(mandatory-dom-proj ...) (generate-temporaries mandatory-dom-projs)]
[(optional-dom-proj ...) (generate-temporaries optional-dom-projs)]
@ -51,7 +51,6 @@
(define blame+neg-party (cons blame neg-party))
#,(create-chaperone
#'blame #'neg-party #'blame+neg-party #'blame-party-info #'f #'rng-ctcs
this-args
(for/list ([id (in-list (syntax->list #'(mandatory-dom-proj ...)))]
[mandatory-dom-proj (in-list mandatory-dom-projs)])
(and mandatory-dom-proj id))
@ -65,7 +64,8 @@
pre pre/desc
(if rest (car (syntax->list #'(rest-proj ...))) #f)
(if rngs (syntax->list #'(rng-proj ...)) #f)
post post/desc))))
post post/desc
method?))))
(define (check-pre-cond pre blame neg-party blame+neg-party val)
@ -128,13 +128,13 @@
(define-for-syntax (create-chaperone blame neg-party blame+neg-party blame-party-info
val rng-ctcs
this-args
doms opt-doms
req-kwds opt-kwds
pre pre/desc
dom-rest
rngs
post post/desc)
post post/desc
method?)
(with-syntax ([blame blame]
[blame+neg-party blame+neg-party]
[val val])
@ -152,8 +152,7 @@
[post/desc
(list #`(check-post-cond/desc #,post/desc blame neg-party val))]
[else null])])
(with-syntax ([(this-param ...) this-args]
[(dom-x ...) (generate-temporaries doms)]
(with-syntax ([(dom-x ...) (generate-temporaries doms)]
[(opt-dom-ctc ...) opt-doms]
[(opt-dom-x ...) (generate-temporaries opt-doms)]
[(rest-ctc rest-x) (cons dom-rest (generate-temporaries '(rest)))]
@ -194,9 +193,7 @@
#,rng-checker))
stx))
(let* ([min-method-arity (length doms)]
[max-method-arity (+ min-method-arity (length opt-doms))]
[min-arity (+ (length this-args) min-method-arity)]
(let* ([min-arity (length doms)]
[max-arity (+ min-arity (length opt-doms))]
[req-keywords (map (λ (p) (syntax-e (car p))) req-kwds)]
[opt-keywords (map (λ (p) (syntax-e (car p))) opt-kwds)]
@ -210,13 +207,12 @@
[basic-params
(cond
[dom-rest
#'(this-param ...
dom-x ...
#'(dom-x ...
[opt-dom-x arrow:unspecified-dom] ...
.
rest-x)]
[else
#'(this-param ... dom-x ... [opt-dom-x arrow:unspecified-dom] ...)])]
#'(dom-x ... [opt-dom-x arrow:unspecified-dom] ...)])]
[opt+rest-uses
(for/fold ([i (if dom-rest #'(rest-ctc rest-x neg-party) #'null)])
([o (in-list (reverse
@ -252,24 +248,20 @@
(with-syntax ([kwd-lam-params
(if dom-rest
#'(this-param ...
dom-x ...
#'(dom-x ...
[opt-dom-x arrow:unspecified-dom] ...
kwd-param ... . rest-x)
#'(this-param ...
dom-x ...
#'(dom-x ...
[opt-dom-x arrow:unspecified-dom] ...
kwd-param ...))]
[basic-return
(let ([inner-stx-gen
(if need-apply?
(λ (s) #`(apply values #,@s
this-param ...
dom-projd-args ...
opt+rest-uses))
(λ (s) #`(values
#,@s
this-param ...
dom-projd-args ...)))])
(if rngs
(arrow:check-tail-contract rng-ctcs
@ -286,8 +278,8 @@
(define (inner-stx-gen stuff assume-result-values? do-tail-check?)
(define arg-checking-expressions
(if need-apply?
#'(this-param ... dom-projd-args ... opt+rest-uses)
#'(this-param ... dom-projd-args ...)))
#'(dom-projd-args ... opt+rest-uses)
#'(dom-projd-args ...)))
(define the-call/no-tail-mark
(cond
[(for/and ([dom (in-list doms)])
@ -336,11 +328,9 @@
(if need-apply?
(λ (s k) #`(apply values
#,@s #,@k
this-param ...
dom-projd-args ...
opt+rest-uses))
(λ (s k) #`(values #,@s #,@k
this-param ...
dom-projd-args ...)))]
[outer-stx-gen
(if (null? req-keywords)
@ -393,43 +383,163 @@
(let ()
pre ... kwd-return)))])
(cond
[(and (null? req-keywords) (null? opt-keywords))
#`(arrow:arity-checking-wrapper val
blame neg-party blame+neg-party
basic-lambda
basic-unsafe-lambda
basic-unsafe-lambda/result-values-assumed
basic-unsafe-lambda/result-values-assumed/no-tail
#,(and rngs (length rngs))
void
#,min-method-arity
#,max-method-arity
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...))]
[(pair? req-keywords)
#`(arrow:arity-checking-wrapper val
blame neg-party blame+neg-party
void #t #f #f #f
kwd-lambda
#,min-method-arity
#,max-method-arity
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...))]
[else
#`(arrow:arity-checking-wrapper val
blame neg-party blame+neg-party
basic-lambda #t #f #f #f
kwd-lambda
#,min-method-arity
#,max-method-arity
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...))])))))))))
[(and (null? req-keywords) (null? opt-keywords))
#`(arity-checking-wrapper val
blame neg-party blame+neg-party
basic-lambda
basic-unsafe-lambda
basic-unsafe-lambda/result-values-assumed
basic-unsafe-lambda/result-values-assumed/no-tail
#,(and rngs (length rngs))
void
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...)
#,method?)]
[(pair? req-keywords)
#`(arity-checking-wrapper val
blame neg-party blame+neg-party
void #t #f #f #f
kwd-lambda
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...)
#,method?)]
[else
#`(arity-checking-wrapper val
blame neg-party blame+neg-party
basic-lambda #t #f #f #f
kwd-lambda
#,min-arity
#,(if dom-rest #f max-arity)
'(req-kwd ...)
'(opt-kwd ...)
#,method?)])))))))))
;; should we pass both the basic-lambda and the kwd-lambda?
;; if basic-unsafe-lambda is #f, returns only the one value,
;; namely the chaperone wrapper. Otherwise, returns two values,
;; a procedure and a boolean indicating it the procedure is the
;; basic-unsafe-lambda or not; note that basic-unsafe-lambda might
;; also be #t, but that happens only when we know that basic-lambda
;; can't be chosen (because there are keywords involved)
(define (arity-checking-wrapper val blame neg-party blame+neg-party basic-lambda
basic-unsafe-lambda
basic-unsafe-lambda/result-values-assumed
basic-unsafe-lambda/result-values-assumed/no-tail
contract-result-val-count
kwd-lambda
min-arity max-arity
req-kwd opt-kwd
method?)
;; should not build this unless we are in the 'else' case (and maybe not at all)
(cond
[(arrow:matches-arity-exactly? val min-arity max-arity req-kwd opt-kwd)
(if (and (null? req-kwd) (null? opt-kwd))
(cond
[(impersonator? val)
(if basic-unsafe-lambda
(values basic-lambda #f)
basic-lambda)]
[(and basic-unsafe-lambda
basic-unsafe-lambda/result-values-assumed
(equal? contract-result-val-count
(procedure-result-arity val)))
(if (simple-enough? val)
(values basic-unsafe-lambda/result-values-assumed/no-tail #t)
(values basic-unsafe-lambda/result-values-assumed #t))]
[basic-unsafe-lambda
(values basic-unsafe-lambda #t)]
[else basic-lambda])
(if basic-unsafe-lambda
(values kwd-lambda #f)
kwd-lambda))]
[else
(define-values (vr va) (procedure-keywords val))
(define all-kwds (append req-kwd opt-kwd))
(define (valid-number-of-args? args)
(if max-arity
(<= min-arity (length args) max-arity)
(<= min-arity (length args))))
(define kwd-checker
(if (and (null? req-kwd) (null? opt-kwd))
(λ (kwds kwd-args . args)
(arrow:raise-no-keywords-arg blame #:missing-party neg-party val kwds))
(λ (kwds kwd-args . args)
(with-contract-continuation-mark
blame+neg-party
(let ()
(define args-len (length args))
(unless (valid-number-of-args? args)
(raise-wrong-number-of-args-error
blame #:missing-party neg-party val
args-len min-arity max-arity method?))
;; these two for loops are doing O(n^2) work that could be linear
;; (since the keyword lists are sorted)
(for ([req-kwd (in-list req-kwd)])
(unless (memq req-kwd kwds)
(raise-blame-error (blame-swap blame) #:missing-party neg-party
val
'(expected "keyword argument ~a")
req-kwd)))
(for ([k (in-list kwds)])
(unless (memq k all-kwds)
(raise-blame-error (blame-swap blame) #:missing-party neg-party val
'(received: "unexpected keyword argument ~a")
k)))
(keyword-apply kwd-lambda kwds kwd-args args))))))
(define basic-checker-name
(if (null? req-kwd)
(λ args
(with-contract-continuation-mark
blame+neg-party
(let ()
(unless (valid-number-of-args? args)
(define args-len (length args))
(raise-wrong-number-of-args-error
blame #:missing-party neg-party val
args-len min-arity max-arity method?))
(apply basic-lambda args))))
(λ args
(raise-blame-error (blame-swap blame) #:missing-party neg-party val
"expected required keyword ~a"
(car req-kwd)))))
(define proc
(if (or (not va) (pair? vr) (pair? va))
(make-keyword-procedure kwd-checker basic-checker-name)
basic-checker-name))
(if basic-unsafe-lambda
(values proc #f)
proc)]))
(define (simple-enough? f)
(or (struct-accessor-procedure? f)
(struct-constructor-procedure? f)
(struct-predicate-procedure? f)
(struct-mutator-procedure? f)))
(define (raise-wrong-number-of-args-error
blame #:missing-party [missing-party #f] val
args-len pre-min-arity pre-max-arity method?)
(define min-arity ((if method? sub1 values) pre-min-arity))
(define max-arity ((if method? sub1 values) pre-max-arity))
(define arity-string
(if max-arity
(cond
[(= min-arity max-arity)
(format "~a non-keyword argument~a" min-arity (if (= min-arity 1) "" "s"))]
[(= (+ min-arity 1) max-arity)
(format "~a or ~a non-keyword arguments" min-arity max-arity)]
[else
(format "~a to ~a non-keyword arguments" min-arity max-arity)])
(format "at least ~a non-keyword argument~a" min-arity (if (= min-arity 1) "" "s"))))
(raise-blame-error (blame-swap blame) val
#:missing-party missing-party
'(received: "~a argument~a" expected: "~a")
args-len (if (= args-len 1) "" "s") arity-string))
(define (maybe-cons-kwd c x r neg-party)
(if (eq? arrow:unspecified-dom x)
@ -439,7 +549,7 @@
(define (->-proj chaperone? ctc
;; fields of the 'ctc' struct
min-arity doms kwd-infos rest pre? rngs post?
plus-one-arity-function chaperone-constructor
plus-one-arity-function chaperone-constructor method?
late-neg?)
(define optionals-length (- (length doms) min-arity))
(define mtd? #f) ;; not yet supported for the new contracts
@ -460,7 +570,7 @@
[n (in-naturals 1)])
((get/build-late-neg-projection dom)
(blame-add-context orig-blame
(format "the ~a argument of" (n->th n))
(format "the ~a argument of" (n->th (if method? (sub1 n) n)))
#:swap? #t))))
(define rest-blame
(if (ellipsis-rest-arg-ctc? rest)
@ -532,7 +642,7 @@
[late-neg?
(define (arrow-higher-order:lnp val neg-party)
(cond
[(do-arity-checking orig-blame val doms rest min-arity kwd-infos)
[(do-arity-checking orig-blame val doms rest min-arity kwd-infos method?)
=>
(λ (f)
(f neg-party))]
@ -549,7 +659,7 @@
(define-values (normal-proc proc-with-no-result-checking expected-number-of-results)
(apply plus-one-arity-function orig-blame val plus-one-constructor-args))
(cond
[(do-arity-checking orig-blame val doms rest min-arity kwd-infos)
[(do-arity-checking orig-blame val doms rest min-arity kwd-infos method?)
=>
(λ (neg-party-acceptor)
;; probably don't need to include the wrapped-extra-arrow wrapper

View File

@ -14,34 +14,20 @@
(prefix-in arrow: "arrow.rkt"))
(provide ->2 ->*2
->2-internal ->*2-internal ; for ->m and ->*m
base->? base->-name ; for object-contract
dynamic->*
(for-syntax ->2-handled?
->2-arity-check-only->?
->*2-handled?
(for-syntax ->2-arity-check-only->?
->2*-arity-check-only->?
->-valid-app-shapes
->*-valid-app-shapes)
(rename-out [-predicate/c predicate/c]))
(define-for-syntax (->2-handled? stx)
(syntax-case stx (any values any/c boolean?)
[(_ args ...)
(syntax-parameter-value #'arrow:making-a-method)
#f]
[_ #t]))
(define-for-syntax (->2-arity-check-only->? stx)
(syntax-case stx (any any/c)
[(_ any/c ... any) (- (length (syntax->list stx)) 2)]
[_ #f]))
(define-for-syntax (->*2-handled? stx)
(syntax-case stx (any values any/c)
[(_ args ...)
(syntax-parameter-value #'arrow:making-a-method)
#f]
[_ #t]))
(define-for-syntax (->2*-arity-check-only->? stx)
(syntax-case stx (any any/c)
[(_ (any/c ...) any) (length (syntax->list (cadr (syntax->list stx))))]
@ -106,14 +92,19 @@
(generate-popular-key-ids popular-key-ids)
(define-for-syntax (build-plus-one-arity-function+chaperone-constructor
regular-args
pre-regular-args
optional-args
mandatory-kwds
optional-kwds
pre pre/desc
rest
rngs
post post/desc)
post post/desc
method?)
(define regular-args
(if method?
(cons #'any/c pre-regular-args) ; add `this` argument
pre-regular-args))
(define regular-args/no-any/c
(for/list ([stx (in-list regular-args)])
(syntax-case stx (any/c)
@ -145,9 +136,9 @@
pre pre/desc
rest
rngs
post post/desc)
post post/desc
method?)
(build-chaperone-constructor/real
'() ;; this-args
regular-args/no-any/c
optional-args
mandatory-kwds
@ -155,7 +146,8 @@
pre pre/desc
rest
rngs
post post/desc))]))
post post/desc
method?))]))
(define-syntax (build-populars stx)
(syntax-case stx ()
@ -192,17 +184,16 @@
#f #f
rest
rng-vars
#f #f))
#f #f #f))
(define #,(syntax-local-introduce chaperone-id)
#,(let ([ans (build-chaperone-constructor/real
'() ;; this arg
mans/no-any/c opts
mandatory-kwds
optional-kwds
#f #f
rest
rng-vars
#f #f)])
#f #f #f)])
#;
(when (equal? key (list '(#t) 0 '() '() #f 1))
((dynamic-require 'racket/pretty 'pretty-write) (syntax->datum ans))
@ -222,7 +213,8 @@
pre pre/desc
rest
rngs
post post/desc)
post post/desc
method?)
(with-syntax ([(regb ...) (generate-temporaries regular-args)]
[(optb ...) (generate-temporaries optional-args)]
[(kb ...) (generate-temporaries mandatory-kwds)]
@ -369,10 +361,11 @@
#,(if pre pre #'#f)
'(#,@mandatory-kwds) (list kb ...)
'(#,@optional-kwds) (list okb ...)
#,(length regular-args) (list regb ... optb ...)
#,(length regular-args) (list regb ... optb ...)
#,(if rest #'restb #'#f)
#,(if post post #'#f)
#,(if rngs #'(list rb ...) #'#f))]))
#,(if rngs #'(list rb ...) #'#f)
#,method?)]))
(define body-proc (make-body-proc #t))
(define body-proc/no-range-checking (make-body-proc #f))
(define number-of-rngs (and rngs (with-syntax ([rngs rngs]) (length (syntax->list #'rngs)))))
@ -396,10 +389,11 @@
original-mandatory-kwds kbs
original-optional-kwds okbs
minimum-arg-count rbs rest-ctc
post rngs)
post rngs
method?)
(make-keyword-procedure
(λ (actual-kwds actual-kwd-args neg-party . regular-args)
(check-arg-count minimum-arg-count (length rbs) regular-args f blame neg-party rest-ctc)
(check-arg-count minimum-arg-count (length rbs) regular-args f blame neg-party rest-ctc method?)
(check-keywords original-mandatory-kwds original-optional-kwds actual-kwds f blame neg-party)
(define (mk-call)
(keyword-apply
@ -483,8 +477,9 @@
rngs))
(hash-ref popular-chaperone-key-table key #f))
(define (check-arg-count minimum-arg-count len-rbs regular-args val blame neg-party rest-ctc)
(define (check-arg-count minimum-arg-count len-rbs regular-args val blame neg-party rest-ctc method?)
(define actual-count (length regular-args))
(define adjust (if method? sub1 values))
(cond
[(< actual-count minimum-arg-count)
(raise-blame-error (blame-swap blame) #:missing-party neg-party val
@ -492,14 +487,14 @@
(if (= len-rbs minimum-arg-count)
""
"at least ")
minimum-arg-count)]
(adjust minimum-arg-count))]
[(and (not rest-ctc) (< len-rbs actual-count))
(raise-blame-error (blame-swap blame) #:missing-party neg-party val
'(expected: "~a~a arguments")
(if (= len-rbs minimum-arg-count)
""
"at most ")
len-rbs)]))
(adjust len-rbs))]))
(define (check-keywords mandatory-kwds optional-kwds kwds val blame neg-party)
(let loop ([mandatory-kwds mandatory-kwds]
@ -646,11 +641,18 @@
(define-syntax (->2 stx)
(syntax-case stx ()
[(_ args ...)
(not (->2-handled? stx))
#'(arrow:-> args ...)]
[(_ args ... rng)
[(_ . args)
(let ()
#`(syntax-parameterize
((arrow:making-a-method #f))
#,(quasisyntax/loc stx
(->2-internal -> . args))))]))
(define-syntax (->2-internal stx*)
(syntax-case stx* ()
[(_ orig-> args ... rng)
(let ()
(define stx (syntax/loc stx* (orig-> args ... rng)))
(define this-> (gensym 'this->))
(define-values (regular-args kwds kwd-args let-bindings ellipsis-info)
(parse-arrow-args stx (syntax->list #'(args ...)) this->))
@ -662,24 +664,29 @@
[any #f]
[(values rng ...) (add-pos-obligations (syntax->list #'(rng ...)))]
[rng (add-pos-obligations (list #'rng))]))
(define method? (syntax-parameter-value #'arrow:making-a-method))
(define-values (plus-one-arity-function chaperone-constructor)
(build-plus-one-arity-function+chaperone-constructor
regular-args '() kwds '() #f #f (and ellipsis-info #t) rngs #f #f))
regular-args '() kwds '() #f #f (and ellipsis-info #t) rngs #f #f
method?))
(syntax-property
#`(let #,let-bindings
#,(quasisyntax/loc stx
(build-simple-->
(list #,@regular-args)
'(#,@kwds)
(list #,@kwd-args)
#,(if rngs
#`(list #,@rngs)
#'#f)
#,plus-one-arity-function
#,chaperone-constructor
#,(if ellipsis-info
#`(ellipsis-rest-arg #,(length regular-args) #,@ellipsis-info)
#'#f))))
#`(syntax-parameterize
([arrow:making-a-method #f]) ; subcontracts are not method contracts, even if we ourselves are one
(let #,let-bindings
#,(quasisyntax/loc stx
(build-simple-->
(list #,@regular-args)
'(#,@kwds)
(list #,@kwd-args)
#,(if rngs
#`(list #,@rngs)
#'#f)
#,plus-one-arity-function
#,chaperone-constructor
#,(if ellipsis-info
#`(ellipsis-rest-arg #,(length regular-args) #,@ellipsis-info)
#'#f)
#,method?))))
'racket/contract:contract
(vector this->
;; the -> in the original input to this guy
@ -768,73 +775,81 @@
(syntax->datum #'(optional-dom-kwd ...)))))
(define-syntax (->*2 stx)
(cond
[(->*2-handled? stx)
(define this->* (gensym 'this->*))
(define-values (man-dom man-dom-kwds man-lets
opt-dom opt-dom-kwds opt-lets
rest-ctc pre pre/desc rng-ctcs post post/desc)
(parse->*2 stx this->*))
(with-syntax ([(mandatory-dom ...) man-dom]
[((mandatory-dom-kwd mandatory-dom-kwd-ctc) ...) man-dom-kwds]
[(mandatory-let-bindings ...) man-lets]
[(optional-dom ...) opt-dom]
[((optional-dom-kwd optional-dom-kwd-ctc) ...) opt-dom-kwds]
[(optional-let-bindings ...) opt-lets]
[(pre-x post-x) (generate-temporaries '(pre-cond post-cond))])
(with-syntax ([((kwd dom opt?) ...) #'((mandatory-dom-kwd mandatory-dom-kwd-ctc #f) ...
(optional-dom-kwd optional-dom-kwd-ctc #t) ...)]
[(pre-let-binding ...) (if (or pre pre/desc)
(list #`[pre-x (λ () #,(or pre pre/desc))])
(list))]
[(post-let-binding ...) (if (or post post/desc)
(list #`[post-x (λ () #,(or post post/desc))])
(list))])
(define-values (plus-one-arity-function chaperone-constructor)
(build-plus-one-arity-function+chaperone-constructor
(syntax->list #'(mandatory-dom ...))
(syntax->list #'(optional-dom ...))
(syntax->list #'(mandatory-dom-kwd ...))
(syntax->list #'(optional-dom-kwd ...))
(and pre #'pre-x)
(and pre/desc #'pre-x)
rest-ctc
rng-ctcs
(and post #'post-x)
(and post/desc #'post-x)))
(syntax-property
#`(let (mandatory-let-bindings ...
optional-let-bindings ...
pre-let-binding ...
post-let-binding ...)
(build--> '->*
(list mandatory-dom ...)
(list optional-dom ...)
'(mandatory-dom-kwd ...)
(list mandatory-dom-kwd-ctc ...)
'(optional-dom-kwd ...)
(list optional-dom-kwd-ctc ...)
#,rest-ctc
#,(and pre #t)
#,(if rng-ctcs
#`(list #,@(for/list ([rng-ctc (in-list (syntax->list rng-ctcs))])
(syntax-property rng-ctc
'racket/contract:positive-position
this->*)))
#'#f)
#,(and post #t)
#,plus-one-arity-function
#,chaperone-constructor))
'racket/contract:contract
(vector this->*
;; the -> in the original input to this guy
(list (car (syntax-e stx)))
'()))))]
[else
(syntax-case stx ()
[(_ args ...)
#'(arrow:->* args ...)])]))
(syntax-case stx ()
[(_ . args)
#`(syntax-parameterize
((arrow:making-a-method #f))
#,(quasisyntax/loc stx
(->*2-internal ->* . args)))]))
(define-syntax (->*2-internal stx*)
(define stx (syntax-case stx* () [(_ orig->* . args) (syntax/loc stx* (orig->* . args))]))
(define this->* (gensym 'this->*))
(define-values (man-dom man-dom-kwds man-lets
opt-dom opt-dom-kwds opt-lets
rest-ctc pre pre/desc rng-ctcs post post/desc)
(parse->*2 stx this->*))
(with-syntax ([(mandatory-dom ...) man-dom]
[((mandatory-dom-kwd mandatory-dom-kwd-ctc) ...) man-dom-kwds]
[(mandatory-let-bindings ...) man-lets]
[(optional-dom ...) opt-dom]
[((optional-dom-kwd optional-dom-kwd-ctc) ...) opt-dom-kwds]
[(optional-let-bindings ...) opt-lets]
[(pre-x post-x) (generate-temporaries '(pre-cond post-cond))])
(with-syntax ([((kwd dom opt?) ...) #'((mandatory-dom-kwd mandatory-dom-kwd-ctc #f) ...
(optional-dom-kwd optional-dom-kwd-ctc #t) ...)]
[(pre-let-binding ...) (if (or pre pre/desc)
(list #`[pre-x (λ () #,(or pre pre/desc))])
(list))]
[(post-let-binding ...) (if (or post post/desc)
(list #`[post-x (λ () #,(or post post/desc))])
(list))])
(define method? (syntax-parameter-value #'arrow:making-a-method))
(define-values (plus-one-arity-function chaperone-constructor)
(build-plus-one-arity-function+chaperone-constructor
(syntax->list #'(mandatory-dom ...))
(syntax->list #'(optional-dom ...))
(syntax->list #'(mandatory-dom-kwd ...))
(syntax->list #'(optional-dom-kwd ...))
(and pre #'pre-x)
(and pre/desc #'pre-x)
rest-ctc
rng-ctcs
(and post #'post-x)
(and post/desc #'post-x)
method?))
(syntax-property
#`(let (mandatory-let-bindings ...
optional-let-bindings ...
pre-let-binding ...
post-let-binding ...)
(syntax-parameterize
([arrow:making-a-method #f]) ; subcontracts are not method contracts, even if we ourselves are one
(build--> '->*
(list mandatory-dom ...)
(list optional-dom ...)
'(mandatory-dom-kwd ...)
(list mandatory-dom-kwd-ctc ...)
'(optional-dom-kwd ...)
(list optional-dom-kwd-ctc ...)
#,rest-ctc
#,(and pre #t)
#,(if rng-ctcs
#`(list #,@(for/list ([rng-ctc (in-list (syntax->list rng-ctcs))])
(syntax-property rng-ctc
'racket/contract:positive-position
this->*)))
#'#f)
#,(and post #t)
#,plus-one-arity-function
#,chaperone-constructor
#,method?)))
'racket/contract:contract
(vector this->*
;; the -> in the original input to this guy
(list (car (syntax-e stx)))
'())))))
(define (wrong-number-of-results-blame blame neg-party val reses expected-values)
(define length-reses (length reses))
@ -851,7 +866,8 @@
raw-rngs
plus-one-arity-function
chaperone-constructor
raw-rest-ctc)
raw-rest-ctc
method?)
(build--> '->
raw-regular-doms '()
mandatory-kwds mandatory-raw-kwd-doms
@ -859,16 +875,22 @@
raw-rest-ctc
#f raw-rngs #f
plus-one-arity-function
chaperone-constructor))
chaperone-constructor
method?))
(define (build--> who
raw-regular-doms raw-optional-doms
pre-raw-regular-doms raw-optional-doms
mandatory-kwds mandatory-raw-kwd-doms
optional-kwds optional-raw-kwd-doms
raw-rest-ctc
pre-cond raw-rngs post-cond
plus-one-arity-function
chaperone-constructor)
chaperone-constructor
method?)
(define raw-regular-doms
(if method?
(cons any/c pre-raw-regular-doms) ; `this` argument
pre-raw-regular-doms))
(define regular-doms
(for/list ([dom (in-list (append raw-regular-doms raw-optional-doms))])
(coerce-contract who dom)))
@ -922,13 +944,15 @@
regular-doms kwd-infos rest-ctc pre-cond
rngs post-cond
plus-one-arity-function
chaperone-constructor)]
chaperone-constructor
method?)]
[else
(make-impersonator-> (length raw-regular-doms)
regular-doms kwd-infos rest-ctc pre-cond
rngs post-cond
plus-one-arity-function
chaperone-constructor)]))
chaperone-constructor
method?)]))
(define (dynamic->* #:mandatory-domain-contracts [mandatory-domain-contracts '()]
#:optional-domain-contracts [optional-domain-contracts '()]
@ -1040,7 +1064,7 @@
(make-keyword-procedure
(λ (kwds kwd-args . args)
(check-arg-count min-arity max-arity args f blame neg-party rest-contract)
(check-arg-count min-arity max-arity args f blame neg-party rest-contract #f)
(check-keywords mandatory-keywords optional-keywords kwds f blame neg-party)
(define kwd-results
@ -1090,7 +1114,8 @@
rest-contract
pre-cond range-contracts post-cond
plus-one-arity-function
build-chaperone-constructor))
build-chaperone-constructor
#f)) ; not a method contract
;; min-arity : nat
;; doms : (listof contract?)[len >= min-arity]
@ -1102,8 +1127,10 @@
;; post? : boolean?
;; plus-one-arity-function : procedure? -- special, +1 argument wrapper that accepts neg-party
;; chaperone-constructor ; procedure? -- function that builds a projection tailored to this arrow
;; method? : boolean?
(define-struct base-> (min-arity doms kwd-infos rest pre? rngs post?
plus-one-arity-function chaperone-constructor)
plus-one-arity-function chaperone-constructor
method?)
#:property prop:custom-write custom-write-property-proc)
(define (->-generate ctc)
@ -1198,10 +1225,20 @@
[else
(λ (fuel) (values void '()))]))
(define (base->-name ctc)
;; print-as-method-if-method?: Usually, whether an `->` is printed as `->m` is
;; determined by whether the contract has an implicit `any/c` for the `this`
;; argument.
;; Unfortunately, this is not always the case. `object-contract` creates
;; contracts that *look* like function contracts (i.e. print as `->`), but act
;; like method contracts. Therefore, `object-contract` printing needs to
;; override our behavior.
;; That was probably not good design, but we're stuck with it.
(define ((base->-name print-as-method-if-method?) ctc)
(cond
[(predicate/c? ctc) 'predicate/c]
[else
(define method? (base->-method? ctc))
(define arr (if (and method? print-as-method-if-method?) '->m '->))
(define rngs (base->-rngs ctc))
(define rng-sexp
(cond
@ -1224,23 +1261,25 @@
(for/list ([kwd-info (in-list (base->-kwd-infos ctc))])
(list (kwd-info-kwd kwd-info)
(contract-name (kwd-info-ctc kwd-info))))))
(define doms ((if method? cdr values) (map contract-name (base->-doms ctc))))
(cond
[(ellipsis-rest-arg-ctc? (base->-rest ctc))
`(-> ,@(map contract-name (base->-doms ctc))
,@kwd-args
,(contract-name (*list-ctc-prefix (base->-rest ctc)))
...
,@(for/list ([ctc (in-list (*list-ctc-suffix (base->-rest ctc)))])
(contract-name ctc))
,rng-sexp)]
`(,arr ,@doms
,@kwd-args
,(contract-name (*list-ctc-prefix (base->-rest ctc)))
...
,@(for/list ([ctc (in-list (*list-ctc-suffix (base->-rest ctc)))])
(contract-name ctc))
,rng-sexp)]
[else
`(-> ,@(map contract-name (base->-doms ctc))
,@kwd-args
,rng-sexp)])]
`(,arr ,@doms
,@kwd-args
,rng-sexp)])]
[else
(define (take l n) (reverse (list-tail (reverse l) (- (length l) n))))
(define mandatory-args
`(,@(map contract-name (take (base->-doms ctc) (base->-min-arity ctc)))
`(,@(map contract-name
((if method? cdr values) (take (base->-doms ctc) (base->-min-arity ctc))))
,@(apply
append
(for/list ([kwd-info (base->-kwd-infos ctc)]
@ -1256,21 +1295,21 @@
#:when (not (kwd-info-mandatory? kwd-info)))
(list (kwd-info-kwd kwd-info)
(contract-name (kwd-info-ctc kwd-info)))))))
`(->* ,mandatory-args
,@(if (null? optional-args)
'()
(list optional-args))
,@(if (base->-rest ctc)
(list '#:rest (contract-name (base->-rest ctc)))
(list))
,@(if (base->-pre? ctc)
(list '#:pre '...)
(list))
,rng-sexp
,@(if (base->-post? ctc)
(list '#:post '...)
(list)))])]))
(define arr* (if (and method? print-as-method-if-method?) '->*m '->*))
`(,arr* ,mandatory-args
,@(if (null? optional-args)
'()
(list optional-args))
,@(if (base->-rest ctc)
(list '#:rest (contract-name (base->-rest ctc)))
(list))
,@(if (base->-pre? ctc)
(list '#:pre '...)
(list))
,rng-sexp
,@(if (base->-post? ctc)
(list '#:post '...)
(list)))])]))
(define ((->-first-order ctc) x)
(define l (base->-min-arity ctc))
@ -1303,6 +1342,7 @@
(base->-post? ->stct)
(base->-plus-one-arity-function ->stct)
(base->-chaperone-constructor ->stct)
(base->-method? ->stct)
#f)))
(define late-neg-proj
(λ (->stct)
@ -1316,9 +1356,10 @@
(base->-post? ->stct)
(base->-plus-one-arity-function ->stct)
(base->-chaperone-constructor ->stct)
(base->-method? ->stct)
#t)))
(build-X-property
#:name base->-name
#:name (base->-name #|print-as-method-if-method|# #t)
#:first-order ->-first-order
#:projection
(λ (this)
@ -1399,7 +1440,8 @@
'(expected: "void?" given: "~e")
rng))))
1))
(get-chaperone-constructor))))
(get-chaperone-constructor)
#f))) ; not a method contract
(define (mk-any/c->boolean-contract constructor)
(define (check-result blame neg-party rng)
@ -1462,11 +1504,13 @@
(unless (null? kwds)
(arrow:raise-no-keywords-arg blame #:missing-party neg-party f kwds))
(unless (= 1 (length other))
(arrow:raise-wrong-number-of-args-error
(raise-wrong-number-of-args-error
#:missing-party neg-party
blame f (length other) 1 1 1))
blame f (length other) 1 1 1
#f)) ; not a method contract
(values (rng-checker f blame neg-party) (car other))))])
#f))))
#f))
#f)) ; not a method contract
(define -predicate/c (mk-any/c->boolean-contract predicate/c))
(define any/c->boolean-contract (mk-any/c->boolean-contract make-->))

View File

@ -42,7 +42,8 @@
blame-add-range-context
blame-add-nth-arg-context
raise-no-keywords-arg
raise-wrong-number-of-args-error)
raise-wrong-number-of-args-error
base-->d? ->d-name) ; for object-contract
(define-syntax-parameter making-a-method #f)
(define-syntax-parameter method-contract? #f)
@ -1602,8 +1603,9 @@
optional-kwds
name-wrapper)))
(define (->d-name ctc)
(let* ([name (if (base-->d-mctc? ctc) '->dm '->d)]
;; Re `print-as-method-if-method?`: See comment before `base->-name` in arrow-val-first.rkt
(define ((->d-name print-as-method-if-method?) ctc)
(let* ([name (if (and (base-->d-mctc? ctc) print-as-method-if-method?) '->dm '->d)]
[counting-id 'x]
[ids '(x y z w)]
[next-id
@ -1694,7 +1696,7 @@
#:property prop:contract
(build-contract-property
#:late-neg-projection (late-neg-->d-proj impersonate-procedure)
#:name ->d-name
#:name (->d-name #|print-as-method-if-method?|# #t)
#:first-order ->d-first-order
#:stronger ->d-stronger?))

View File

@ -8,7 +8,7 @@
"blame.rkt"
"prop.rkt"
"misc.rkt"
"arrow.rkt"
(except-in "arrow.rkt" base->?)
"arrow-val-first.rkt")
(provide case->)

View File

@ -1,5 +1,7 @@
#lang racket/base
(require "arrow.rkt"
(require "arrow-val-first.rkt"
"case-arrow.rkt"
(only-in "arrow.rkt" ->d base-->d? ->d-name making-a-method)
"arr-i.rkt"
"guts.rkt"
"prop.rkt"
@ -39,6 +41,15 @@
[_
(raise-syntax-error #f "malformed object-contract clause" stx (car args))])])))
;; similar to `build-compound-type-name`, but handles method contract names
(define (object-contract-sub-name . fs)
(for/list ([sub (in-list fs)])
(cond [(base->? sub) ((base->-name #|print-as-method-if-method?|# #f) sub)] ; covers -> and ->*
[(base-->d? sub) ((->d-name #|print-as-method-if-method?|# #f) sub)]
;; `->i` and `case->` will naturally print correctly, due to the way they handle methods
[(contract-struct? sub) (contract-struct-name sub)]
[else sub])))
(define-struct object-contract (methods method-ctcs fields field-ctcs)
#:property prop:custom-write custom-write-property-proc
#:omit-define-syntaxes
@ -55,7 +66,7 @@
(λ (ctc) `(object-contract ,@(map (λ (fld ctc) (build-compound-type-name 'field fld ctc))
(object-contract-fields ctc)
(object-contract-field-ctcs ctc))
,@(map (λ (mtd ctc) (build-compound-type-name mtd ctc))
,@(map (λ (mtd ctc) (object-contract-sub-name mtd ctc))
(object-contract-methods ctc)
(object-contract-method-ctcs ctc))))
@ -78,9 +89,18 @@
#'(build-object-contract '(method-id ...)
(syntax-parameterize
((making-a-method #t))
(list (let ([method-name method-ctc]) method-name) ...))
(list (let ([method-name (fun->meth method-ctc)]) method-name) ...))
'(field-id ...)
(list field-ctc ...))))]))
(define-syntax (fun->meth stx)
(syntax-case stx ()
[(_ ctc)
(syntax-case #'ctc (->2 ->*2 ->d ->i case->)
[(->2 . args) #'(->m . args)]
[(->*2 . args) #'(->*m . args)]
[(->d . args) #'(->dm . args)]
[(->i . args) #'ctc] ; ->i doesn't reset the `making-a-method` syntax parameter
[(case-> case ...) #'ctc])])) ; neither does case->
(define (build-object-contract methods method-ctcs fields field-ctcs)
(make-object-contract methods

View File

@ -2,9 +2,8 @@
(require "misc.rkt"
"opt.rkt"
"guts.rkt"
"arrow.rkt"
"blame.rkt"
"arrow.rkt"
(except-in "arrow.rkt" base->?)
"arrow-val-first.rkt"
"arrow-higher-order.rkt"
"orc.rkt"

View File

@ -281,12 +281,10 @@
(define-values (arrow? the-valid-app-shapes)
(syntax-case ctrct (->2 ->*2 ->i)
[(->2 . _)
(and (->2-handled? ctrct)
(not (->2-arity-check-only->? ctrct)))
(not (->2-arity-check-only->? ctrct))
(values #t (->-valid-app-shapes ctrct))]
[(->*2 . _)
(values (and (->*2-handled? ctrct)
(not (->2*-arity-check-only->? ctrct)))
(values (not (->2*-arity-check-only->? ctrct))
(->*-valid-app-shapes ctrct))]
[(->i . _) (values #t (->i-valid-app-shapes ctrct))]
[_ (values #f #f)]))

View File

@ -8,7 +8,8 @@
"class-internal.rkt"
"../contract/base.rkt"
"../contract/combinator.rkt"
(only-in "../contract/private/arrow.rkt" making-a-method method-contract?))
(only-in "../contract/private/arrow.rkt" making-a-method method-contract?)
(only-in "../contract/private/arrow-val-first.rkt" ->2-internal ->*2-internal))
(provide make-class/c class/c-late-neg-proj
blame-add-method-context blame-add-field-context blame-add-init-context
@ -25,10 +26,10 @@
;; Shorthand contracts that treat the implicit object argument as if it were
;; contracted with any/c.
(define-syntax-rule (->m . stx)
(syntax-parameterize ([making-a-method #t] [method-contract? #t]) (-> . stx)))
(syntax-parameterize ([making-a-method #t] [method-contract? #t]) (->2-internal ->m . stx)))
(define-syntax-rule (->*m . stx)
(syntax-parameterize ([making-a-method #t] [method-contract? #t]) (->* . stx)))
(syntax-parameterize ([making-a-method #t] [method-contract? #t]) (->*2-internal ->*m . stx)))
(define-syntax-rule (case->m . stx)
(syntax-parameterize ([making-a-method #t] [method-contract? #t]) (case-> . stx)))