diff --git a/collects/tests/typed-racket/fail/single-letrec.rkt b/collects/tests/typed-racket/fail/single-letrec.rkt new file mode 100644 index 00000000..f7cec773 --- /dev/null +++ b/collects/tests/typed-racket/fail/single-letrec.rkt @@ -0,0 +1,5 @@ +#lang typed/racket + +(: f (Pair 'bad 'worse)) +(define f + (letrec ((y y)) y)) diff --git a/collects/typed-racket/typecheck/tc-expr-unit.rkt b/collects/typed-racket/typecheck/tc-expr-unit.rkt index 58973053..0f7ba022 100644 --- a/collects/typed-racket/typecheck/tc-expr-unit.rkt +++ b/collects/typed-racket/typecheck/tc-expr-unit.rkt @@ -256,6 +256,15 @@ t))] [else (tc-error/expr #:return (ret (Un)) #:stx stx (syntax-e msg))])) +;; check that `expr` doesn't evaluate any references +;; to `name` that aren't under `lambda` +;; value-restriction? : syntax identifier -> boolean +(define (value-restriction? expr name) + (syntax-parse expr + [((~literal #%plain-lambda) . _) #true] + [((~literal case-lambda) . _) #true] + [_ #false])) + ;; tc-expr/check : syntax tc-results -> tc-results (define/cond-contract (tc-expr/check/internal form expected) (--> syntax? tc-results? tc-results?) @@ -356,7 +365,8 @@ [(let-values ([(name ...) expr] ...) . body) (tc/let-values #'((name ...) ...) #'(expr ...) #'body form expected)] [(letrec-values ([(name) expr]) name*) - #:when (and (identifier? #'name*) (free-identifier=? #'name #'name*)) + #:when (and (identifier? #'name*) (free-identifier=? #'name #'name*) + (value-restriction? #'expr #'name)) (match expected [(tc-result1: t) (with-lexical-env/extend (list #'name) (list t) (tc-expr/check #'expr expected))]