diff --git a/typed-racket-lib/typed-racket/typecheck/tc-if.rkt b/typed-racket-lib/typed-racket/typecheck/tc-if.rkt index c00db2f9..a9bdf3cd 100644 --- a/typed-racket-lib/typed-racket/typecheck/tc-if.rkt +++ b/typed-racket-lib/typed-racket/typecheck/tc-if.rkt @@ -1,11 +1,13 @@ #lang racket/unit (require "../utils/utils.rkt" (rep prop-rep) - (types utils prop-ops) + (types abbrev utils prop-ops) (utils tc-utils) (typecheck signatures tc-envops tc-metafunctions) (types type-table) - racket/match) + (private syntax-properties) + racket/match + syntax/parse) ;; if typechecking (import tc-expr^) @@ -17,13 +19,44 @@ (define expected* (and expected (erase-props expected))) (define results-t (with-lexical-env/extend-props (list fs+) - #:unreachable (warn-unreachable thn) + #:unreachable (begin + (handle-unreachable-casted-exprs thn) + (warn-unreachable thn)) (test-position-add-true tst) (tc-expr/check thn expected*))) (define results-u (with-lexical-env/extend-props (list fs-) - #:unreachable (warn-unreachable els) + #:unreachable (begin + (handle-unreachable-casted-exprs els) + (warn-unreachable els)) (test-position-add-false tst) (tc-expr/check els expected*))) (merge-tc-results (list results-t results-u))])) + +;; handle-unreachable-casted-exprs : Any -> Void +;; Traverses stx looking for casted-expr properties. For each one, it +;; calls the function stored in the property, which fills an entry in +;; the cast table with the -Dead-Code type. This is so that the +;; contract-generation pass doesn't throw an internal error. +(define (handle-unreachable-casted-exprs stx) + (syntax-parse stx + [(exp:casted-expr^ e) + ;; fill in this entry in the cast table with the -Dead-Code type + ((attribute exp.value) -Dead-Code) + (void)] + [stx + (define e (syntax-e #'stx)) + (cond + [(pair? e) (handle-unreachable-casted-exprs (car e)) + (handle-unreachable-casted-exprs (cdr e))] + [(box? e) (handle-unreachable-casted-exprs (unbox e))] + [(vector? e) (for ([e (in-vector e)]) + (handle-unreachable-casted-exprs e))] + [(hash? e) (for ([(k v) (in-hash e)]) + (handle-unreachable-casted-exprs k) + (handle-unreachable-casted-exprs v))] + [(struct? e) (for ([e (in-vector (struct->vector e))]) + (handle-unreachable-casted-exprs e))] + [else (void)])])) + diff --git a/typed-racket-lib/typed-racket/types/abbrev.rkt b/typed-racket-lib/typed-racket/types/abbrev.rkt index 2ae46a39..5154af4a 100644 --- a/typed-racket-lib/typed-racket/types/abbrev.rkt +++ b/typed-racket-lib/typed-racket/types/abbrev.rkt @@ -80,6 +80,10 @@ (define (-ne-lst t) (-pair t (-lst t))) +;; For casted-exprs in unreachable code, to fill in the cast table. +;; TODO: This contract normally gets optimized away. Is there away to stop that? +(define -Dead-Code (make-Base 'Dead-Code #'(make-none/c 'dead-code/c) (λ (v) #f))) + ;; Convenient constructor for Values ;; (wraps arg types with Result) (define/cond-contract (-values args) diff --git a/typed-racket-test/succeed/cast-mod.rkt b/typed-racket-test/succeed/cast-mod.rkt index 0a101053..b626b16b 100644 --- a/typed-racket-test/succeed/cast-mod.rkt +++ b/typed-racket-test/succeed/cast-mod.rkt @@ -102,3 +102,20 @@ (check-equal? (f (list (list "a") (list "b") (list "c"))) (list (list "a") (list "b") (list "c")))) +(test-case "cast in dead code" + (check-equal? (if #true 1 (cast 2 Integer)) + 1) + (check-equal? (if #false (cast 1 Integer) 2) + 2) + (check-equal? (if #true 1 (list (cast 2 Integer) (cast 3 Integer))) + 1) + (check-equal? (if #true 1 `#&,(cast 2 Integer)) + 1) + (check-equal? (if #true 1 `#(,(cast 2 Integer) ,(cast 3 Integer))) + 1) + (check-equal? (if #true 1 `#hash([,(cast 2 Integer) . ,(cast 3 Integer)])) + 1) + (check-equal? (if #true 1 `#s(struct ,(cast 2 Integer) ,(cast 3 Integer))) + 1) + ) +