(module typechecker-unit (lib "a-unit.ss")
(require (rename (lib "1.ss" "srfi") assoc* assoc)
(lib "kerncase.ss" "syntax")
(lib "struct.ss" "syntax")
(lib "stx.ss" "syntax")
(lib "etc.ss")
"signatures.ss"
"tc-structs.ss"
"subst.ss" "utils.ss" "types.ss" "infer.ss" "infer2.ss"
"types-aux.ss" "subtype.ss" "internal-forms.ss" "remove-intersect.ss" "planet-requires.ss" "type-env.ss" "parse-type.ss" "tc-utils.ss" "type-environments.ss" "base-env.ss" "lexical-env.ss" "type-annotation.ss" "type-name-env.ss" "init-envs.ss"
"effects.ss"
"mutated-vars.ss"
(lib "match.ss"))
(require-for-template (lib "match.ss") "internal-forms.ss")
(require-for-syntax (lib "match.ss") "internal-forms.ss")
(require-libs)
(require (planet "environment.ss" ("cobbe" "environment.plt" 3 0)))
(require-for-template mzscheme)
(import tc-if^ tc-lambda^ tc-app^ tc-let^)
(export typechecker^)
(define (check-type stx e-type ty)
(parameterize ([current-orig-stx stx])
(unless (subtype e-type ty)
(tc-error "body had type ~a, variable had type ~a" e-type ty))))
(define (check-expr stx expr ty)
(check-type stx (tc-expr/t expr) ty))
(define (tc-literal v-stx)
(define (types-of-literals es)
(apply Un (map tc-literal es)))
(define v (syntax-e v-stx))
(cond
[(number? v) N]
[(boolean? v) (make-value v)]
[(null? v) (make-value null)]
[(symbol? v) (make-value v)]
[(string? v) -String]
[(keyword? v) -Keyword]
[(bytes? v) -Bytes]
[(list? v) (make-Listof (types-of-literals v))]
[(vector? v) (make-vec (types-of-literals (vector->list v)))]
[else (begin (printf "checking literal : ~a~n" v) Univ)]))
(define (tc-id id)
(ret (lookup-type/lexical id) (list (make-var-true-effect id)) (list (make-var-false-effect id))))
(define (tc-expr/t e) (tc-result-t (tc-expr e)))
(define (tc-expr form)
(define ty-ann (type-annotation form))
(define (internal-tc-expr form)
(kernel-syntax-case* form #f
(letrec-syntaxes+values)
[(#%datum . #f) (ret (-val #f) (list (make-false-effect)) (list (make-false-effect)))]
[(#%datum . #t) (ret (-val #t) (list (make-true-effect)) (list (make-true-effect)))]
[(quote #f) (ret (-val #f) (list (make-false-effect)) (list (make-false-effect)))]
[(quote #t) (ret (-val #t) (list (make-true-effect)) (list (make-true-effect)))]
[(#%datum . val) (ret (tc-literal #'val))]
[(quote val) (ret (tc-literal #'val))]
[(quote-syntax datum) (ret -Syntax)]
[(with-continuation-mark e1 e2 e3)
(begin (tc-expr #'e1)
(tc-expr #'e2)
(tc-expr #'e3))]
[(lambda formals . body)
(tc/lambda form #'(formals) #'(body))]
[(case-lambda [formals . body] ...)
(tc/lambda form #'(formals ...) #'(body ...))]
[(let-values ([(name ...) expr] ...) . body)
(tc/let-values #'((name ...) ...) #'(expr ...) #'body form)]
[(letrec-values ([(name ...) expr] ...) . body)
(tc/letrec-values #'((name ...) ...) #'(expr ...) #'body form)]
[(set! id val)
(match-let* ([($ tc-result id-t _ _) (tc-id #'id)]
[($ tc-result val-t _ _) (tc-expr #'val)])
(unless (subtype val-t id-t)
(tc-error "Mutation only allowed with compatible types: ~a is not a subtype of ~a" val-t id-t))
(ret -Void))]
[(#%top . id) (tc-id #'id)]
[(#%variable-reference . _)
(tc-error "do not use #%variable-reference")]
[x (identifier? #'x) (tc-id #'x)]
[(#%app . _) (tc/app form)]
[(if tst body) (tc/if-onearm #'tst #'body)]
[(if tst thn els) (tc/if-twoarm #'tst #'thn #'els)]
[(letrec-syntaxes+values stxs vals . body)
(tc-expr (syntax/loc form (letrec-values vals . body)))]
[(begin e . es) (tc-exprs (syntax->list #'(e . es)))]
[(begin0 e . es)
(begin (tc-exprs (syntax->list #'es))
(tc-expr #'e))]
[(#%expression e) (tc-expr #'e)]
[_ (tc-error "cannot typecheck unknown form : ~a~n" (syntax-object->datum form))]))
(parameterize ([current-orig-stx form])
(unless (syntax? form)
(int-err "bad form input to tc-expr: ~a" form))
(match-let ([(and result ($ tc-result result-ty result-thn-eff result-els-eff))
(internal-tc-expr form)])
(cond
[(and ty-ann (subtype result-ty ty-ann)) (ret ty-ann)]
[ty-ann (tc-error "expression had type ~a, but was annotated with type ~a" result-ty ty-ann)]
[else result])))
)
(define (tc-exprs exprs)
(cond [(null? exprs) (ret -Void)]
[(null? (cdr exprs)) (tc-expr (car exprs))]
[else (tc-expr (car exprs))
(tc-exprs (cdr exprs))]))
(define (tc-toplevel/pass1 form)
(find-mutated-vars form)
(parameterize ([current-orig-stx form])
(kernel-syntax-case* form #f (define-type-alias-internal define-typed-struct-internal define-type-internal
require/typed-internal :)
[stx
(syntax-property form 'typechecker:ignore)
(void)]
[(#%expression (quote-syntax (require/typed-internal nm ty)))
(register-type #'nm (parse-type #'ty))]
[(#%expression (quote-syntax (define-type-alias-internal (nm . vars) ty)))
(register-type-name #'nm (parse-type #'(All vars ty)))]
[(#%expression (quote-syntax (define-type-alias-internal nm ty)))
(register-type-name #'nm (parse-type #'ty))]
[(#%expression (quote-syntax (define-typed-struct-internal nm ([fld : ty] ...))))
(tc/struct #'nm (syntax->list #'(fld ...)) (syntax->list #'(ty ...)))]
[(#%expression (quote-syntax (define-typed-struct-internal (vars ...) nm ([fld : ty] ...))))
(tc/poly-struct (syntax->list #'(vars ...)) #'nm (syntax->list #'(fld ...)) (syntax->list #'(ty ...)))]
[(#%expression (quote-syntax (define-type-internal nm top-pred elems ...)))
(let ([variants0 (map (lambda (stx)
(syntax-case stx ()
[(variant maker (fld ty) ...)
(list #'variant #'maker (map cons (syntax->list #'(ty ...)) (syntax->list #'(fld ...))))]))
(syntax->list #'(elems ...)))])
(tc/define-type #'nm #'top-pred variants0))]
[(define-values (var ...) expr)
(andmap (lambda (s) (syntax-property s 'type-label)) (syntax->list #'(var ...)))
(let* ([vars (syntax->list #'(var ...))]
[ts (map get-type vars)])
(for-each register-type vars ts))]
[(define-values . _)
(tc-error "Untyped definition")]
[(begin . rest)
(for-each tc-toplevel/pass1 (syntax->list #'rest))]
[_ (void)])))
(define (tc-toplevel/pass2 form)
(reverse-begin
(do-time
(if (eq? 'define-values (syntax-e (stx-car form)))
(format "In Pass 2 : ~a ~a"
'define-values
(syntax-object->datum (stx-car (stx-cdr form))))
(begin0 (format "In Pass 2 : ~a" (syntax-object->datum (stx-car form))) (stx-car (stx-cdr form)))))
(parameterize ([current-orig-stx form])
(kernel-syntax-case* form #f (define-type-alias-internal define-typed-struct-internal define-type-internal
require/typed-internal)
[stx
(syntax-property form 'typechecker:ignore)
(void)]
[(require . _) (void)]
[(require-for-syntax . _) (void)]
[(require-for-template . _) (void)]
[(provide . _) (void)]
[(define-syntaxes . _) (void)]
[(#%expression (quote-syntax (require/typed-internal . rest))) (void)]
[(#%expression (quote-syntax (define-type-alias-internal . rest))) (void)]
[(#%expression (quote-syntax (define-typed-struct-internal . rest))) (void)]
[(#%expression (quote-syntax (define-type-internal . rest))) (void)]
[(define-values (var ...) expr)
(let* ([vars (syntax->list #'(var ...))]
[ts (map get-type vars)])
(check-expr form #'expr (list->values-ty ts)))]
[(begin) (void)]
[(begin . rest)
(let loop ([l (syntax->list #'rest)])
(if (null? (cdr l))
(tc-toplevel/pass2 (car l))
(begin (tc-toplevel/pass2 (car l))
(loop (cdr l)))))]
[_ (tc-expr form)]))))
(define (tc-toplevel-form form)
(tc-toplevel/pass1 form)
(tc-toplevel/pass2 form))
(define (type-check forms)
(let ([forms (syntax->list forms)])
(do-time "Before Pass1")
(for-each tc-toplevel/pass1 forms)
(do-time "Before Pass2")
(for-each tc-toplevel/pass2 forms)
(do-time "After Pass2")
#`(begin
#,(env-init-code)
#,(tname-env-init-code))))
)