Better type annotation support.

- Delay more errors for additional reporting.
 - Don't re-check expressions that were checked for inference.
 Closes PR 10098.
This commit is contained in:
Sam Tobin-Hochstadt 2010-05-18 09:11:31 -05:00
parent 2c3db18852
commit 638245e4c5
7 changed files with 77 additions and 32 deletions

View File

@ -0,0 +1,18 @@
#;
(exn-pred 4)
#lang typed/racket
(: bar : (String -> String))
(: bar : (Number -> Number))
(define (bar x)
(+ x 1))
(define: (foo) : Number
(: bar : (Number -> Number))
(define: (bar [x : Number]) : Number
(+ x 1))
(bar 5))
(: baz Number)
(define: baz : Number 7)

View File

@ -0,0 +1,8 @@
#lang typed/scheme/base
(define (f x)
(: g (Integer -> Integer))
(define (g x)
(+ x 2))
(g x))

View File

@ -0,0 +1,8 @@
#lang typed/scheme
(: add (case-lambda (Integer -> Integer) (Integer Integer -> Integer)))
(define add
(case-lambda
[(x) (add x 0)]
[(x y) (+ x y)]))

View File

@ -26,16 +26,19 @@
(free-id-table-set! the-mapping id type)) (free-id-table-set! the-mapping id type))
(define (register-type-if-undefined id type) (define (register-type-if-undefined id type)
(if (free-id-table-ref the-mapping id (lambda _ #f)) (cond [(free-id-table-ref the-mapping id (lambda _ #f))
(tc-error/stx id "Duplicate type annotation for ~a" (syntax-e id)) => (lambda (e)
(register-type id type))) (tc-error/expr #:stx id "Duplicate type annotation for ~a" (syntax-e id))
(when (box? e)
(free-id-table-set! the-mapping id (unbox e))))]
[else (register-type id type)]))
;; add a single type to the mapping ;; add a single type to the mapping
;; identifier type -> void ;; identifier type -> void
(define (register-type/undefined id type) (define (register-type/undefined id type)
;(printf "register-type/undef ~a~n" (syntax-e id)) ;(printf "register-type/undef ~a~n" (syntax-e id))
(if (free-id-table-ref the-mapping id (lambda _ #f)) (if (free-id-table-ref the-mapping id (lambda _ #f))
(tc-error/stx id "Duplicate type annotation for ~a" (syntax-e id)) (void (tc-error/expr #:stx id "Duplicate type annotation for ~a" (syntax-e id)))
(free-id-table-set! the-mapping id (box type)))) (free-id-table-set! the-mapping id (box type))))
;; add a bunch of types to the mapping ;; add a bunch of types to the mapping
@ -61,7 +64,8 @@
(define (finish-register-type id) (define (finish-register-type id)
(unless (maybe-finish-register-type id) (unless (maybe-finish-register-type id)
(tc-error/stx id "Duplicate defintion for ~a" (syntax-e id)))) (tc-error/expr #:stx id "Duplicate defintion for ~a" (syntax-e id)))
(void))
(define (check-all-registered-types) (define (check-all-registered-types)
(free-id-table-for-each (free-id-table-for-each
@ -69,11 +73,13 @@
(lambda (id e) (lambda (id e)
(when (box? e) (when (box? e)
(let ([bnd (identifier-binding id)]) (let ([bnd (identifier-binding id)])
(tc-error/stx id "Declaration for ~a provided, but ~a ~a" (tc-error/expr #:stx id
(syntax-e id) (syntax-e id) "Declaration for ~a provided, but ~a ~a"
(cond [(eq? bnd 'lexical) "is a lexical binding"] ;; should never happen (syntax-e id) (syntax-e id)
[(not bnd) "has no definition"] (cond [(eq? bnd 'lexical) "is a lexical binding"] ;; should never happen
[else "is defined in another module"]))))))) [(not bnd) "has no definition"]
[else "is defined in another module"]))))
(void))))
;; map over the-mapping, producing a list ;; map over the-mapping, producing a list
;; (id type -> T) -> listof[T] ;; (id type -> T) -> listof[T]

View File

@ -37,7 +37,11 @@
;; is let-binding really necessary? - remember to record the bugs! ;; is let-binding really necessary? - remember to record the bugs!
(define (type-annotation stx #:infer [let-binding #f]) (define (type-annotation stx #:infer [let-binding #f])
(define (pt prop) (define (pt prop)
#;(print-size prop) (when (and (identifier? stx)
let-binding
(lookup-type stx (lambda () #f)))
(maybe-finish-register-type stx)
(tc-error/expr #:stx stx "Duplicate type annotation for ~a" (syntax-e stx)))
(if (syntax? prop) (if (syntax? prop)
(parse-type prop) (parse-type prop)
(parse-type/id stx prop))) (parse-type/id stx prop)))
@ -92,14 +96,14 @@
;; get the type annotation of this identifier, otherwise error ;; get the type annotation of this identifier, otherwise error
;; if #:default is provided, return that instead of error ;; if #:default is provided, return that instead of error
;; identifier #:default Type -> Type ;; identifier #:default Type -> Type
(define (get-type stx #:default [default #f]) (define (get-type stx #:default [default #f] #:infer [infer #t])
(parameterize (parameterize
([current-orig-stx stx]) ([current-orig-stx stx])
(cond (cond
[(type-annotation stx #:infer #t)] [(type-annotation stx #:infer infer)]
[default default] [default default]
[(not (syntax-original? stx)) [(not (syntax-original? stx))
(tc-error "untyped var: ~a" (syntax-e stx))] (tc-error "untyped variable: ~a" (syntax-e stx))]
[else [else
(tc-error "no type information on variable ~a" (syntax-e stx))]))) (tc-error "no type information on variable ~a" (syntax-e stx))])))

View File

@ -108,7 +108,7 @@
[(begin (quote-syntax (define-type-alias-internal nm ty)) (#%plain-app values)) [(begin (quote-syntax (define-type-alias-internal nm ty)) (#%plain-app values))
(register-resolved-type-alias #'nm (parse-type #'ty))] (register-resolved-type-alias #'nm (parse-type #'ty))]
[(begin (quote-syntax (:-internal nm ty)) (#%plain-app values)) [(begin (quote-syntax (:-internal nm ty)) (#%plain-app values))
(register-type/undefined #'nm (parse-type #'ty))] (register-type-if-undefined #'nm (parse-type #'ty))]
[_ (void)])) [_ (void)]))
names names
exprs) exprs)

View File

@ -3,7 +3,7 @@
(require (rename-in "../utils/utils.rkt" [infer r:infer])) (require (rename-in "../utils/utils.rkt" [infer r:infer]))
(require syntax/kerncase (require syntax/kerncase
unstable/list unstable/syntax syntax/parse unstable/list unstable/syntax syntax/parse unstable/debug
mzlib/etc mzlib/etc
scheme/match scheme/match
"signatures.rkt" "signatures.rkt"
@ -14,7 +14,7 @@
(types utils convenience) (types utils convenience)
(private parse-type type-annotation type-contract) (private parse-type type-annotation type-contract)
(env type-env init-envs type-name-env type-alias-env lexical-env) (env type-env init-envs type-name-env type-alias-env lexical-env)
unstable/mutated-vars unstable/mutated-vars syntax/id-table
(utils tc-utils) (utils tc-utils)
"provide-handling.rkt" "provide-handling.rkt"
"def-binding.rkt" "def-binding.rkt"
@ -27,6 +27,8 @@
(import tc-expr^ check-subforms^) (import tc-expr^ check-subforms^)
(export typechecker^) (export typechecker^)
(define unann-defs (make-free-id-table))
(define (tc-toplevel/pass1 form) (define (tc-toplevel/pass1 form)
;(printf "form-top: ~a~n" form) ;(printf "form-top: ~a~n" form)
;; first, find the mutated variables: ;; first, find the mutated variables:
@ -115,7 +117,7 @@
(cond (cond
;; if all the variables have types, we stick them into the environment ;; if all the variables have types, we stick them into the environment
[(andmap (lambda (s) (syntax-property s 'type-label)) vars) [(andmap (lambda (s) (syntax-property s 'type-label)) vars)
(let ([ts (map get-type vars)]) (let ([ts (map (λ (x) (get-type x #:infer #f)) vars)])
(for-each register-type-if-undefined vars ts) (for-each register-type-if-undefined vars ts)
(map make-def-binding vars ts))] (map make-def-binding vars ts))]
;; if this already had an annotation, we just construct the binding reps ;; if this already had an annotation, we just construct the binding reps
@ -123,16 +125,13 @@
(for-each finish-register-type vars) (for-each finish-register-type vars)
(map (lambda (s) (make-def-binding s (lookup-type s))) vars)] (map (lambda (s) (make-def-binding s (lookup-type s))) vars)]
;; special case to infer types for top level defines - should handle the multiple values case here ;; special case to infer types for top level defines - should handle the multiple values case here
[(and (= 1 (length vars)) [(= 1 (length vars))
(with-handlers ([exn:fail? (lambda _ #f)]) (match (tc-expr #'expr)
(save-errors!) [(tc-result1: t)
(begin0 (tc-expr #'expr) (register-type (car vars) t)
(restore-errors!)))) (free-id-table-set! unann-defs (car vars) #t)
=> (match-lambda (list (make-def-binding (car vars) t))]
[(tc-result1: t) [t (int-err "~a is not a tc-result" t)])]
(register-type (car vars) t)
(list (make-def-binding (car vars) t))]
[t (int-err "~a is not a tc-result" t)])]
[else [else
(tc-error "Untyped definition : ~a" (map syntax-e vars))]))] (tc-error "Untyped definition : ~a" (map syntax-e vars))]))]
@ -186,10 +185,12 @@
;; definitions just need to typecheck their bodies ;; definitions just need to typecheck their bodies
[(define-values (var ...) expr) [(define-values (var ...) expr)
(begin (let* ([vars (syntax->list #'(var ...))] (let* ([vars (syntax->list #'(var ...))]
[ts (map lookup-type vars)]) [ts (map lookup-type vars)])
(tc-expr/check #'expr (ret ts))) (unless (for/and ([v (syntax->list #'(var ...))])
(void))] (free-id-table-ref unann-defs v (lambda _ #f)))
(tc-expr/check #'expr (ret ts)))
(void))]
;; to handle the top-level, we have to recur into begins ;; to handle the top-level, we have to recur into begins
[(begin) (void)] [(begin) (void)]