diff --git a/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt b/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt index b992b427ac..3c538b504d 100644 --- a/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt +++ b/pkgs/racket-test/tests/racket/contract/flat-contracts.rkt @@ -71,6 +71,9 @@ (test-flat-contract #rx#".x." "axq" "x") (test-flat-contract ''() '() #f) + (test-flat-contract '(if/c integer? even? list?) 2 3) + (test-flat-contract '(if/c integer? even? list?) '() #f) + (test/spec-passed 'any/c '(contract any/c 1 'pos 'neg)) (test-flat-contract 'printable/c (vector (cons 1 (box #f))) (lambda (x) x)) (let () diff --git a/pkgs/racket-test/tests/racket/contract/ifc.rkt b/pkgs/racket-test/tests/racket/contract/ifc.rkt new file mode 100644 index 0000000000..69fa592650 --- /dev/null +++ b/pkgs/racket-test/tests/racket/contract/ifc.rkt @@ -0,0 +1,85 @@ +#lang racket/base +(require "test-util.rkt") + +(parameterize ([current-contract-namespace + (make-basic-contract-namespace)]) + + (test/spec-passed/result + 'if/c1 + '(contract (if/c integer? even? (listof number?)) + 2 + 'pos 'neg) + 2) + + (test/spec-passed/result + 'if/c2 + '(contract (if/c integer? even? (listof number?)) + '() + 'pos 'neg) + '()) + + (test/pos-blame + 'if/c3 + '(contract (if/c integer? even? (listof number?)) + 3 + 'pos 'neg)) + + (test/pos-blame + 'if/c4 + '(contract (if/c integer? even? (listof number?)) + '(#f) + 'pos 'neg)) + + (test/pos-blame + 'if/c5 + '(contract (if/c integer? even? (listof number?)) + #f + 'pos 'neg)) + + (test/pos-blame + 'if/c6 + '(contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1))) + (-> integer? integer?) + number?) + #f + 'pos 'neg)) + + (test/neg-blame + 'if/c7 + '((contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1))) + (-> integer? integer?) + number?) + (λ (x) x) + 'pos 'neg) + #f)) + + (test/spec-passed/result + 'if/c8 + '(contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1))) + (-> integer? integer?) + number?) + 1 + 'pos 'neg) + 1) + + (test/spec-passed/result + 'if/c9 + '(let ([f (λ (x) x)]) + (chaperone-of? + (contract (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1))) + (-> integer? integer?) + number?) + f + 'pos 'neg) + f)) + #t) + + (test/spec-passed/result + 'if/c10 + '(chaperone-contract? + (if/c (λ (x) (and (procedure? x) (procedure-arity-includes? x 1))) + (-> integer? integer?) + number?)) + #t)) + + diff --git a/pkgs/racket-test/tests/racket/contract/name.rkt b/pkgs/racket-test/tests/racket/contract/name.rkt index 384f830148..d81b3b79ed 100644 --- a/pkgs/racket-test/tests/racket/contract/name.rkt +++ b/pkgs/racket-test/tests/racket/contract/name.rkt @@ -32,6 +32,11 @@ (or/c (-> (>=/c 5) (>=/c 5)) boolean?)) (test-name '(or/c boolean? (-> (>=/c 5) (>=/c 5))) (or/c boolean? (-> (>=/c 5) (>=/c 5)))) + + (test-name '(if/c integer? odd? (-> integer? integer?)) + (if/c integer? odd? (-> integer? integer?))) + (test-name '(if/c integer? odd? boolean?) + (if/c integer? odd? boolean?)) (test-name '(first-or/c) (first-or/c)) (test-name '(first-or/c integer? gt0?) (first-or/c integer? (let ([gt0? (lambda (x) (> x 0))]) gt0?))) diff --git a/racket/collects/racket/contract/private/misc.rkt b/racket/collects/racket/contract/private/misc.rkt index edbe1673fc..301f086a6c 100644 --- a/racket/collects/racket/contract/private/misc.rkt +++ b/racket/collects/racket/contract/private/misc.rkt @@ -1999,44 +1999,66 @@ #:stronger stronger? #:list-contract? (list-contract? ctc)))))) -;; (if/c predicate then/c else/c) applies then/c to satisfying -;; predicate, else/c to those that don't. (define (if/c predicate then/c else/c) - #| - Naive version: - (or/c (and/c predicate then/c) - (and/c (not/c predicate) else/c)) - But that applies predicate twice. - |# (unless (procedure? predicate) (raise-type-error 'if/c "procedure?" predicate)) - (unless (contract? then/c) - (raise-type-error 'if/c "contract?" then/c)) - (unless (contract? else/c) - (raise-type-error 'if/c "contract?" else/c)) - (let ([then-ctc (coerce-contract 'if/c then/c)] - [else-ctc (coerce-contract 'if/c else/c)]) - (define name (build-compound-type-name 'if/c predicate then-ctc else-ctc)) - ;; Special case: if both flat contracts, make a flat contract. - (if (and (flat-contract? then-ctc) - (flat-contract? else-ctc)) - ;; flat contract - (let ([then-pred (flat-contract-predicate then-ctc)] - [else-pred (flat-contract-predicate else-ctc)]) - (define (pred x) - (if (predicate x) (then-pred x) (else-pred x))) - (flat-named-contract name pred)) - ;; ho contract - (let ([then-proj (contract-projection then-ctc)] - [then-fo (contract-first-order then-ctc)] - [else-proj (contract-projection else-ctc)] - [else-fo (contract-first-order else-ctc)]) - (define ((proj blame) x) - (if (predicate x) - ((then-proj blame) x) - ((else-proj blame) x))) - (make-contract - #:name name - #:projection proj - #:first-order - (lambda (x) (if (predicate x) (then-fo x) (else-fo x)))))))) + (unless (procedure-arity-includes? predicate 1) + (raise-type-error 'if/c "procedure that accepts 1 argument" predicate)) + (define then-ctc (coerce-contract 'if/c then/c)) + (define else-ctc (coerce-contract 'if/c else/c)) + (cond + [(and (flat-contract? then-ctc) + (flat-contract? else-ctc)) + (define then-pred (flat-contract-predicate then-ctc)) + (define else-pred (flat-contract-predicate else-ctc)) + (define name `(if/c ,(object-name predicate) + ,(contract-name then-pred) + ,(contract-name else-pred))) + (define (pred x) + (if (predicate x) (then-pred x) (else-pred x))) + (flat-named-contract name pred)] + [(and (chaperone-contract? then-ctc) + (chaperone-contract? else-ctc)) + (chaperone-if/c predicate then-ctc else-ctc)] + [else + (impersonator-if/c predicate then-ctc else-ctc)])) + +(define (if/c-first-order ctc) + (define predicate (base-if/c-predicate ctc)) + (define thn (contract-first-order (base-if/c-thn ctc))) + (define els (contract-first-order (base-if/c-els ctc))) + (λ (x) (if (predicate x) (thn x) (els x)))) + +(define (if/c-name ctc) + (define predicate (base-if/c-predicate ctc)) + (define thn (contract-name (base-if/c-thn ctc))) + (define els (contract-name (base-if/c-els ctc))) + `(if/c ,(object-name predicate) ,thn ,els)) + +(define (if/c-late-neg-proj ctc) + (define predicate (base-if/c-predicate ctc)) + (define thn (contract-late-neg-projection (base-if/c-thn ctc))) + (define els (contract-late-neg-projection (base-if/c-els ctc))) + (λ (blame) + (define thn-proj (thn blame)) + (define els-proj (els blame)) + (λ (val neg-party) + (if (predicate val) + (thn-proj val neg-party) + (els-proj val neg-party))))) + +(define-struct base-if/c (predicate thn els) + #:property prop:custom-write custom-write-property-proc) +(define-struct (chaperone-if/c base-if/c) () + #:property prop:chaperone-contract + (build-chaperone-contract-property + #:late-neg-projection if/c-late-neg-proj + #:first-order if/c-first-order + #:name if/c-name)) + +(define-struct (impersonator-if/c base-if/c) () + #:property prop:contract + (build-contract-property + #:late-neg-projection if/c-late-neg-proj + #:first-order if/c-first-order + #:name if/c-name))