remove obselete code

svn: r14941
This commit is contained in:
Sam Tobin-Hochstadt 2009-05-22 22:05:56 +00:00
parent a8ee2dc161
commit 2a55bec335
3 changed files with 0 additions and 1044 deletions

View File

@ -1,834 +0,0 @@
#lang scheme/unit
(require (only-in "../utils/utils.ss" debug in-syntax printf/log in-pairs rep utils private env [infer r:infer]))
(require "signatures.ss"
stxclass
(for-syntax stxclass)
(rep type-rep effect-rep)
(utils tc-utils)
(private subtype type-utils union type-effect-convenience type-effect-printer resolve-type
type-annotation)
(r:infer infer)
(env type-environments)
(only-in srfi/1 alist-delete)
(only-in scheme/private/class-internal make-object do-make-object)
mzlib/trace mzlib/pretty syntax/kerncase scheme/match
(prefix-in c: scheme/contract)
(for-syntax scheme/base)
(for-template
(only-in '#%kernel [apply k:apply])
"internal-forms.ss" scheme/base
(only-in scheme/private/class-internal make-object do-make-object)))
(require (r:infer constraint-structs))
(import tc-expr^ tc-lambda^ tc-dots^ tc-let^)
(export tc-app^)
;; comparators that inform the type system
(define (comparator? i)
(or (free-identifier=? i #'eq?)
(free-identifier=? i #'equal?)
(free-identifier=? i #'eqv?)
(free-identifier=? i #'=)
(free-identifier=? i #'string=?)))
;; typecheck eq? applications
;; identifier identifier expression expression expression
;; identifier expr expr expr expr -> tc-result
(define (tc/eq comparator v1 v2)
(define (e? i) (free-identifier=? i comparator))
(define (do id val)
(define-syntax alt (syntax-rules () [(_ nm pred ...)
(and (e? #'nm) (or (pred val) ...))]))
(if (or (alt symbol=? symbol?)
(alt string=? string?)
(alt = number?)
(alt eq? boolean? keyword? symbol?)
(alt eqv? boolean? keyword? symbol? number?)
(alt equal? (lambda (x) #t)))
(values (list (make-Restrict-Effect (-val val) id))
(list (make-Remove-Effect (-val val) id)))
(values (list) (list))))
(match (list (tc-expr v1) (tc-expr v2))
[(list (tc-result: id-t (list (Var-True-Effect: id1)) (list (Var-False-Effect: id2))) (tc-result: (Value: val)))
(do id1 val)]
[(list (tc-result: (Value: val)) (tc-result: id-t (list (Var-True-Effect: id1)) (list (Var-False-Effect: id2))))
(do id1 val)]
[_ (values (list) (list))]))
;; typecheck an application:
;; arg-types: the types of the actual parameters
;; arg-effs: the effects of the arguments
;; dom-types: the types of the function's fixed arguments
;; rest-type: the type of the functions's rest parameter, or #f
;; latent-eff: the latent effect of the function
;; arg-stxs: the syntax for each actual parameter, for error reporting
;; [Type] [Type] Maybe[Type] [Syntax] -> (values Listof[Effect] Listof[Effect])
(define (tc-args arg-types arg-thn-effs arg-els-effs dom-types rest-type latent-thn-eff latent-els-eff arg-stxs)
(define (var-true-effect-v e) (match e
[(Var-True-Effect: v) v]))
(define (var-false-effect-v e) (match e
[(Var-False-Effect: v) v]))
;; special case for predicates:
(if (and (not (null? latent-thn-eff))
(not (null? latent-els-eff))
(not rest-type)
;(printf "got to =~n")
(= (length arg-types) (length dom-types) 1)
;(printf "got to var preds~n")
(= (length (car arg-thn-effs)) (length (car arg-els-effs)) 1)
(Var-True-Effect? (caar arg-thn-effs)) ;; thn-effs is a list for each arg
(Var-False-Effect? (caar arg-els-effs)) ;; same with els-effs
(free-identifier=? (var-true-effect-v (caar arg-thn-effs))
(var-false-effect-v (caar arg-els-effs)))
(subtype (car arg-types) (car dom-types)))
;; then this was a predicate application, so we construct the appropriate type effect
(values (map (add-var (var-true-effect-v (caar arg-thn-effs))) latent-thn-eff)
(map (add-var (var-true-effect-v (caar arg-thn-effs))) latent-els-eff))
;; otherwise, we just ignore the effects.
(let loop ([args arg-types] [doms dom-types] [stxs arg-stxs] [arg-count 1])
(cond
[(and (null? args) (null? doms)) (values null null)] ;; here, we just return the empty effect
[(null? args)
(tc-error/delayed
"Insufficient arguments to function application, expected ~a, got ~a"
(length dom-types) (length arg-types))
(values null null)]
[(and (null? doms) rest-type)
(if (subtype (car args) rest-type)
(loop (cdr args) doms (cdr stxs) (add1 arg-count))
(begin
(tc-error/delayed #:stx (car stxs)
"Rest argument had wrong type, expected: ~a and got: ~a"
rest-type (car args))
(values null null)))]
[(null? doms)
(tc-error/delayed "Too many arguments to function, expected ~a, got ~a" (length dom-types) (length arg-types))
(values null null)]
[(subtype (car args) (car doms))
(loop (cdr args) (cdr doms) (cdr stxs) (add1 arg-count))]
[else
(tc-error/delayed
#:stx (car stxs)
"Wrong function argument type, expected ~a, got ~a for argument ~a"
(car doms) (car args) arg-count)
(loop (cdr args) (cdr doms) (cdr stxs) (add1 arg-count))]))))
;(trace tc-args)
(define (stringify-domain dom rst drst [rng #f])
(let ([doms-string (if (null? dom) "" (string-append (stringify dom) " "))]
[rng-string (if rng (format " -> ~a" rng) "")])
(cond [drst
(format "~a~a ... ~a~a" doms-string (car drst) (cdr drst) rng-string)]
[rst
(format "~a~a *~a" doms-string rst rng-string)]
[else (string-append (stringify dom) rng-string)])))
(define (domain-mismatches ty doms rests drests rngs arg-tys tail-ty tail-bound #:expected [expected #f])
(define arguments-str
(stringify-domain arg-tys (if (not tail-bound) tail-ty #f) (if tail-bound (cons tail-ty tail-bound) #f)))
(cond
[(null? doms)
(int-err "How could doms be null: ~a ~a" ty)]
[(= 1 (length doms))
(format "Domain: ~a~nArguments: ~a~n~a"
(stringify-domain (car doms) (car rests) (car drests))
arguments-str
(if expected
(format "Result type: ~a~nExpected result: ~a~n"
(car rngs) expected)
""))]
[else
(format "~a: ~a~nArguments: ~a~n~a"
(if expected "Types" "Domains")
(stringify (if expected
(map stringify-domain doms rests drests rngs)
(map stringify-domain doms rests drests))
"~n\t")
arguments-str
(if expected
(format "Expected result: ~a~n" expected)
""))]))
(define (do-apply-log subst fun arg)
(match* (fun arg)
[('star 'list) (printf/log "Polymorphic apply called with uniform rest arg, list argument\n")]
[('star 'dots) (printf/log "Polymorphic apply called with uniform rest arg, dotted argument\n")]
[('dots 'dots) (printf/log "Polymorphic apply called with non-uniform rest arg, dotted argument\n")])
(log-result subst))
(define (tc/apply f args)
(define f-ty (tc-expr f))
;; produces the first n-1 elements of the list, and the last element
(define (split l)
(let loop ([l l] [acc '()])
(if (null? (cdr l))
(values (reverse acc) (car l))
(loop (cdr l) (cons (car l) acc)))))
(define-values (fixed-args tail) (split (syntax->list args)))
(match f-ty
[(tc-result: (Function: (list (arr: doms rngs rests drests '() thn-effs els-effs) ...)))
(when (null? doms)
(tc-error/expr #:return (ret (Un))
"empty case-lambda given as argument to apply"))
(let ([arg-tys (map tc-expr/t fixed-args)])
(let loop ([doms* doms] [rngs* rngs] [rests* rests] [drests* drests])
(cond [(null? doms*)
(let-values ([(tail-ty tail-bound)
(with-handlers ([exn:fail? (lambda _ (values (tc-expr/t tail) #f))])
(tc/dots tail))])
(tc-error/expr #:return (ret (Un))
(string-append
"Bad arguments to function in apply:~n"
(domain-mismatches f-ty doms rests drests rngs arg-tys tail-ty tail-bound))))]
[(and (car rests*)
(let-values ([(tail-ty tail-bound)
(with-handlers ([exn:fail? (lambda _ (values #f #f))])
(tc/dots tail))])
(and tail-ty
(subtype (apply -lst* arg-tys #:tail (make-Listof tail-ty))
(apply -lst* (car doms*) #:tail (make-Listof (car rests*)))))))
(printf/log "Non-poly apply, ... arg\n")
(ret (car rngs*))]
[(and (car rests*)
(let ([tail-ty (with-handlers ([exn:fail? (lambda _ #f)])
(tc-expr/t tail))])
(and tail-ty
(subtype (apply -lst* arg-tys #:tail tail-ty)
(apply -lst* (car doms*) #:tail (make-Listof (car rests*)))))))
(printf/log (if (memq (syntax->datum f) '(+ - * / max min))
"Simple arithmetic non-poly apply\n"
"Simple non-poly apply\n"))
(ret (car rngs*))]
[(and (car drests*)
(let-values ([(tail-ty tail-bound)
(with-handlers ([exn:fail? (lambda _ (values #f #f))])
(tc/dots tail))])
(and tail-ty
(eq? (cdr (car drests*)) tail-bound)
(subtypes arg-tys (car doms*))
(subtype tail-ty (car (car drests*))))))
(printf/log "Non-poly apply, ... arg\n")
(ret (car rngs*))]
[else (loop (cdr doms*) (cdr rngs*) (cdr rests*) (cdr drests*))])))]
[(tc-result: (Poly: vars (Function: (list (arr: doms rngs rests drests '() thn-effs els-effs) ..1))))
(let*-values ([(arg-tys) (map tc-expr/t fixed-args)]
[(tail-ty tail-bound) (with-handlers ([exn:fail:syntax? (lambda _ (values (tc-expr/t tail) #f))])
(tc/dots tail))])
#;(for-each (lambda (x) (unless (not (Poly? x))
(tc-error "Polymorphic argument of type ~a to polymorphic function in apply not allowed" x)))
(cons tail-ty arg-tys))
(let loop ([doms* doms] [rngs* rngs] [rests* rests] [drests* drests])
(cond [(null? doms*)
(match f-ty
[(tc-result: (Poly-names: _ (Function: (list (arr: doms rngs rests drests '() _ _) ..1))))
(tc-error/expr #:return (ret (Un))
(string-append
"Bad arguments to polymorphic function in apply:~n"
(domain-mismatches f-ty doms rests drests rngs arg-tys tail-ty tail-bound)))])]
;; the actual work, when we have a * function and a list final argument
[(and (car rests*)
(not tail-bound)
(<= (length (car doms*))
(length arg-tys))
(infer/vararg vars
(cons tail-ty arg-tys)
(cons (make-Listof (car rests*))
(car doms*))
(car rests*)
(car rngs*)
(fv (car rngs*))))
=> (lambda (substitution) (ret (subst-all substitution (car rngs*))))]
;; actual work, when we have a * function and ... final arg
[(and (car rests*)
tail-bound
(<= (length (car doms*))
(length arg-tys))
(infer/vararg vars
(cons (make-Listof tail-ty) arg-tys)
(cons (make-Listof (car rests*))
(car doms*))
(car rests*)
(car rngs*)
(fv (car rngs*))))
=> (lambda (substitution) (ret (subst-all substitution (car rngs*))))]
;; ... function, ... arg
[(and (car drests*)
tail-bound
(eq? tail-bound (cdr (car drests*)))
(= (length (car doms*))
(length arg-tys))
(infer vars (cons tail-ty arg-tys) (cons (car (car drests*)) (car doms*)) (car rngs*) (fv (car rngs*))))
=> (lambda (substitution) (ret (subst-all substitution (car rngs*))))]
;; if nothing matches, around the loop again
[else (loop (cdr doms*) (cdr rngs*) (cdr rests*) (cdr drests*))])))]
[(tc-result: (Poly: vars (Function: '())))
(tc-error/expr #:return (ret (Un))
"Function has no cases")]
[(tc-result: (PolyDots: (and vars (list fixed-vars ... dotted-var))
(Function: (list (arr: doms rngs rests drests '() thn-effs els-effs) ..1))))
(let*-values ([(arg-tys) (map tc-expr/t fixed-args)]
[(tail-ty tail-bound) (with-handlers ([exn:fail:syntax? (lambda _ (values (tc-expr/t tail) #f))])
(tc/dots tail))])
(let loop ([doms* doms] [rngs* rngs] [rests* rests] [drests* drests])
(cond [(null? doms*)
(match f-ty
[(tc-result: (PolyDots-names: _ (Function: (list (arr: doms rngs rests drests '() _ _) ..1))))
(tc-error/expr #:return (ret (Un))
(string-append
"Bad arguments to polymorphic function in apply:~n"
(domain-mismatches f-ty doms rests drests rngs arg-tys tail-ty tail-bound)))])]
;; the actual work, when we have a * function and a list final argument
[(and (car rests*)
(not tail-bound)
(<= (length (car doms*))
(length arg-tys))
(infer/vararg vars
(cons tail-ty arg-tys)
(cons (make-Listof (car rests*))
(car doms*))
(car rests*)
(car rngs*)
(fv (car rngs*))))
=> (lambda (substitution)
(do-apply-log substitution 'star 'list)
(ret (subst-all substitution (car rngs*))))]
;; actual work, when we have a * function and ... final arg
[(and (car rests*)
tail-bound
(<= (length (car doms*))
(length arg-tys))
(infer/vararg vars
(cons (make-Listof tail-ty) arg-tys)
(cons (make-Listof (car rests*))
(car doms*))
(car rests*)
(car rngs*)
(fv (car rngs*))))
=> (lambda (substitution)
(do-apply-log substitution 'star 'dots)
(ret (subst-all substitution (car rngs*))))]
;; ... function, ... arg, same bound on ...
[(and (car drests*)
tail-bound
(eq? tail-bound (cdr (car drests*)))
(= (length (car doms*))
(length arg-tys))
(infer vars (cons tail-ty arg-tys) (cons (car (car drests*)) (car doms*)) (car rngs*) (fv (car rngs*))))
=> (lambda (substitution)
(do-apply-log substitution 'dots 'dots)
(ret (subst-all substitution (car rngs*))))]
;; ... function, ... arg, different bound on ...
[(and (car drests*)
tail-bound
(not (eq? tail-bound (cdr (car drests*))))
(= (length (car doms*))
(length arg-tys))
(parameterize ([current-tvars (extend-env (list tail-bound (cdr (car drests*)))
(list (make-DottedBoth (make-F tail-bound))
(make-DottedBoth (make-F (cdr (car drests*)))))
(current-tvars))])
(infer vars (cons tail-ty arg-tys) (cons (car (car drests*)) (car doms*)) (car rngs*) (fv (car rngs*)))))
=> (lambda (substitution)
(define drest-bound (cdr (car drests*)))
(do-apply-log substitution 'dots 'dots)
(ret (substitute-dotted (cadr (assq drest-bound substitution))
tail-bound
drest-bound
(subst-all (alist-delete drest-bound substitution eq?)
(car rngs*)))))]
;; ... function, (List A B C etc) arg
[(and (car drests*)
(not tail-bound)
(eq? (cdr (car drests*)) dotted-var)
(= (length (car doms*))
(length arg-tys))
(untuple tail-ty)
(infer/dots fixed-vars dotted-var (append arg-tys (untuple tail-ty)) (car doms*)
(car (car drests*)) (car rngs*) (fv (car rngs*))))
=> (lambda (substitution)
(define drest-bound (cdr (car drests*)))
(do-apply-log substitution 'dots 'dots)
(ret (subst-all substitution (car rngs*))))]
;; if nothing matches, around the loop again
[else (loop (cdr doms*) (cdr rngs*) (cdr rests*) (cdr drests*))])))]
[(tc-result: (PolyDots: vars (Function: '())))
(tc-error/expr #:return (ret (Un))
"Function has no cases")]
[(tc-result: f-ty) (tc-error/expr #:return (ret (Un))
"Type of argument to apply is not a function type: ~n~a" f-ty)]))
(define (log-result subst)
(define (dmap-length d)
(match d
[(struct dcon (fixed rest)) (length fixed)]
[(struct dcon-exact (fixed rest)) (length fixed)]))
(define (dmap-rest? d)
(match d
[(struct dcon (fixed rest)) rest]
[(struct dcon-exact (fixed rest)) rest]))
(if (list? subst)
(for ([s subst])
(match s
[(list v (list imgs ...) starred)
(printf/log "Instantiated ... variable ~a with ~a types\n" v (length imgs))]
[_ (void)]))
(for* ([(cmap dmap) (in-pairs (cset-maps subst))]
[(k v) (dmap-map dmap)])
(printf/log "Instantiated ... variable ~a with ~a types~a\n" k (dmap-length v)
(if (dmap-rest? v)
" and a starred type"
"")))))
(define-syntax (handle-clauses stx)
(syntax-parse stx
[(_ (lsts ... rngs) f-stx pred infer t argtypes expected)
(with-syntax ([(vars ... rng) (generate-temporaries #'(lsts ... rngs))])
(syntax/loc stx
(or (for/or ([vars lsts] ... [rng rngs]
#:when (pred vars ... rng))
(let ([substitution (infer vars ... rng)])
(and substitution
(log-result substitution)
(ret (or expected
(subst-all substitution rng))))))
(poly-fail t argtypes #:name (and (identifier? f-stx) f-stx) #:expected expected))))]))
(define (poly-fail t argtypes #:name [name #f] #:expected [expected #f])
(match t
[(or (Poly-names: msg-vars (Function: (list (arr: msg-doms msg-rngs msg-rests msg-drests '() _ _) ...)))
(PolyDots-names: msg-vars (Function: (list (arr: msg-doms msg-rngs msg-rests msg-drests '() _ _) ...))))
(let ([fcn-string (if name
(format "function ~a" (syntax->datum name))
"function")])
(if (and (andmap null? msg-doms)
(null? argtypes))
(tc-error/expr #:return (ret (Un))
(string-append
"Could not infer types for applying polymorphic "
fcn-string
"\n"))
(tc-error/expr #:return (ret (Un))
(string-append
"Polymorphic " fcn-string " could not be applied to arguments:~n"
(domain-mismatches t msg-doms msg-rests msg-drests msg-rngs argtypes #f #f #:expected expected)
(if (not (for/and ([t (apply append (map fv/list msg-doms))]) (memq t msg-vars)))
(string-append "Type Variables: " (stringify msg-vars) "\n")
"")))))]))
(define (tc/funapp f-stx args-stx ftype0 argtys expected)
(match-let* ([(list (tc-result: argtypes arg-thn-effs arg-els-effs) ...) argtys])
(let outer-loop ([ftype ftype0]
[argtypes argtypes]
[arg-thn-effs arg-thn-effs]
[arg-els-effs arg-els-effs]
[args args-stx])
(match ftype
;; procedural structs
[(tc-result: (and sty (Struct: _ _ _ (? Type? proc-ty) _ _ _)) thn-eff els-eff)
(outer-loop (ret proc-ty thn-eff els-eff)
(cons (tc-result-t ftype0) argtypes)
(cons (list) arg-thn-effs)
(cons (list) arg-els-effs)
#`(#,(syntax/loc f-stx dummy) #,@args))]
;; mu types, etc
[(tc-result: (? needs-resolving? t) thn-eff els-eff)
(outer-loop (ret (resolve-once t) thn-eff els-eff) argtypes arg-thn-effs arg-els-effs args)]
;; parameters
[(tc-result: (Param: in out))
(match argtypes
[(list) (ret out)]
[(list t)
(if (subtype t in)
(ret -Void)
(tc-error/expr #:return (ret (Un))
"Wrong argument to parameter - expected ~a and got ~a" in t))]
[_ (tc-error/expr #:return (ret (Un))
"Wrong number of arguments to parameter - expected 0 or 1, got ~a"
(length argtypes))])]
;; single clause functions
;; FIXME - error on non-optional keywords
[(tc-result: (and t (Function: (list (arr: dom rng rest #f _ latent-thn-effs latent-els-effs))))
thn-eff els-eff)
(let-values ([(thn-eff els-eff)
(tc-args argtypes arg-thn-effs arg-els-effs dom rest
latent-thn-effs latent-els-effs
(syntax->list args))])
(ret rng thn-eff els-eff))]
;; non-polymorphic case-lambda functions
[(tc-result: (and t (Function: (list (arr: doms rngs rests (and drests #f) '() latent-thn-effs latent-els-effs) ..1)))
thn-eff els-eff)
(let loop ([doms* doms] [rngs rngs] [rests* rests])
(cond [(null? doms*)
(tc-error/expr
#:return (ret (Un))
(string-append "No function domains matched in function application:\n"
(domain-mismatches t doms rests drests rngs argtypes #f #f)))]
[(subtypes/varargs argtypes (car doms*) (car rests*))
(when (car rests*)
(printf/log "Simple varargs function application (~a)\n" (syntax->datum f-stx)))
(ret (car rngs))]
[else (loop (cdr doms*) (cdr rngs) (cdr rests*))]))]
;; simple polymorphic functions, no rest arguments
[(tc-result: (and t
(or (Poly: vars
(Function: (list (arr: doms rngs (and rests #f) (and drests #f) '() thn-effs els-effs) ...)))
(PolyDots: (list vars ...)
(Function: (list (arr: doms rngs (and rests #f) (and drests #f) '() thn-effs els-effs) ...))))))
(handle-clauses (doms rngs) f-stx
(lambda (dom _) (= (length dom) (length argtypes)))
(lambda (dom rng) (infer vars argtypes dom rng (fv rng) expected))
t argtypes expected)]
;; polymorphic varargs
[(tc-result: (and t
(or (Poly: vars (Function: (list (arr: doms rngs rests (and drests #f) '() thn-effs els-effs) ...)))
;; we want to infer the dotted-var here as well, and we don't use these separately
;; so we can just use "vars" instead of (list fixed-vars ... dotted-var)
(PolyDots: vars (Function: (list (arr: doms rngs rests (and drests #f) '() thn-effs els-effs) ...))))))
(printf/log "Polymorphic varargs function application (~a)\n" (syntax->datum f-stx))
(handle-clauses (doms rests rngs) f-stx
(lambda (dom rest rng) (<= (length dom) (length argtypes)))
(lambda (dom rest rng) (infer/vararg vars argtypes dom rest rng (fv rng) expected))
t argtypes expected)]
;; polymorphic ... type
[(tc-result: (and t (PolyDots:
(and vars (list fixed-vars ... dotted-var))
(Function: (list (arr: doms rngs (and #f rests) (cons dtys dbounds) '() thn-effs els-effs) ...)))))
(printf/log "Polymorphic ... function application (~a)\n" (syntax->datum f-stx))
(handle-clauses (doms dtys dbounds rngs) f-stx
(lambda (dom dty dbound rng) (and (<= (length dom) (length argtypes))
(eq? dotted-var dbound)))
(lambda (dom dty dbound rng)
(infer/dots fixed-vars dotted-var argtypes dom dty rng (fv rng) #:expected expected))
t argtypes expected)]
;; Union of function types works if we can apply all of them
[(tc-result: (Union: (list (and fs (Function: _)) ...)) e1 e2)
(match-let ([(list (tc-result: ts) ...) (map (lambda (f) (outer-loop
(ret f e1 e2) argtypes arg-thn-effs arg-els-effs args)) fs)])
(ret (apply Un ts)))]
;; error type is a perfectly good fcn type
[(tc-result: (Error:)) (ret (make-Error))]
[(tc-result: f-ty _ _) (tc-error/expr #:return (ret (Un))
"Cannot apply expression of type ~a, since it is not a function type" f-ty)]))))
;(trace tc/funapp)
(define (tc/app form) (tc/app/internal form #f))
(define (tc/app/check form expected)
(define t (tc/app/internal form expected))
(check-below t expected)
(ret expected))
(define-syntax-class lv-clause
#:transparent
(pattern [(v:id ...) e:expr]))
(define-syntax-class lv-clauses
#:transparent
(pattern (cl:lv-clause ...)
#:with (e ...) #'(cl.e ...)
#:with (vs ...) #'((cl.v ...) ...)))
(define-syntax-class core-expr
#:literals (reverse letrec-syntaxes+values let-values #%plain-app
if letrec-values begin #%plain-lambda set! case-lambda
begin0 with-continuation-mark)
#:transparent
(pattern (let-values cls:lv-clauses body)
#:with (expr ...) #'(cls.e ... body))
(pattern (letrec-values cls:lv-clauses body)
#:with (expr ...) #'(cls.e ... body))
(pattern (letrec-syntaxes+values _ cls:lv-clauses body)
#:with (expr ...) #'(cls.e ... body))
(pattern (#%plain-app expr ...))
(pattern (if expr ...))
(pattern (with-continuation-mark expr ...))
(pattern (begin expr ...))
(pattern (begin0 expr ...))
(pattern (#%plain-lambda _ e)
#:with (expr ...) #'(e))
(pattern (case-lambda [_ expr] ...))
(pattern (set! _ e)
#:with (expr ...) #'(e))
(pattern _
#:with (expr ...) #'()))
;; expr id -> type or #f
;; if there is a binding in stx of the form:
;; (let ([x (reverse name)]) e)
;; where x has a type annotation, return that annotation, otherwise #f
(define (find-annotation stx name)
(define (find s) (find-annotation s name))
(define (match? b)
(syntax-parse b
#:literals (#%plain-app reverse)
[c:lv-clause
#:with (#%plain-app reverse n:id) #'c.e
#:with (v) #'(c.v ...)
#:when (free-identifier=? name #'n)
(type-annotation #'v)]
[_ #f]))
(syntax-parse stx
#:literals (let-values)
[(let-values cls:lv-clauses body)
(or (ormap match? (syntax->list #'cls))
(find #'body))]
[e:core-expr
(ormap find (syntax->list #'(e.expr ...)))]))
(define (check-do-make-object cl pos-args names named-args)
(let* ([names (map syntax-e (syntax->list names))]
[name-assoc (map list names (syntax->list named-args))])
(let loop ([t (tc-expr cl)])
(match t
[(tc-result: (? Mu? t)) (loop (ret (unfold t)))]
[(tc-result: (and c (Class: pos-tys (list (and tnflds (list tnames _ _)) ...) _)))
(unless (= (length pos-tys)
(length (syntax->list pos-args)))
(tc-error/delayed "expected ~a positional arguments, but got ~a"
(length pos-tys) (length (syntax->list pos-args))))
;; use for, since they might be different lengths in error case
(for ([pa (in-syntax pos-args)]
[pt (in-list pos-tys)])
(tc-expr/check pa pt))
(for ([n names]
#:when (not (memq n tnames)))
(tc-error/delayed
"unknown named argument ~a for class~nlegal named arguments are ~a"
n (stringify tnames)))
(for-each (match-lambda
[(list tname tfty opt?)
(let ([s (cond [(assq tname name-assoc) => cadr]
[(not opt?)
(tc-error/delayed "value not provided for named init arg ~a" tname)
#f]
[else #f])])
(if s
;; this argument was present
(tc-expr/check s tfty)
;; this argument wasn't provided, and was optional
#f))])
tnflds)
(ret (make-Instance c))]
[(tc-result: t)
(tc-error/expr #:return (ret (Un)) "expected a class value for object creation, got: ~a" t)]))))
(define (tc-keywords form arities kws kw-args pos-args expected)
(match arities
[(list (arr: dom rng rest #f ktys _ _))
;; assumes that everything is in sorted order
(let loop ([actual-kws kws]
[actuals (map tc-expr/t (syntax->list kw-args))]
[formals ktys])
(match* (actual-kws formals)
[('() '())
(void)]
[(_ '())
(tc-error/expr #:return (ret (Un))
"Unexpected keyword argument ~a" (car actual-kws))]
[('() (cons fst rst))
(match fst
[(Keyword: k _ #t)
(tc-error/expr #:return (ret (Un))
"Missing keyword argument ~a" k)]
[_ (loop actual-kws actuals rst)])]
[((cons k kws-rest) (cons (Keyword: k* t req?) form-rest))
(cond [(eq? k k*) ;; we have a match
(unless (subtype (car actuals) t)
(tc-error/delayed
"Wrong function argument type, expected ~a, got ~a for keyword argument ~a"
t (car actuals) k))
(loop kws-rest (cdr actuals) form-rest)]
[req? ;; this keyword argument was required
(tc-error/delayed "Missing keyword argument ~a" k*)
(loop kws-rest (cdr actuals) form-rest)]
[else ;; otherwise, ignore this formal param, and continue
(loop actual-kws actuals form-rest)])]))
(tc/funapp (car (syntax-e form)) kw-args (ret (make-Function arities)) (map tc-expr (syntax->list pos-args)) expected)]
[_ (int-err "case-lambda w/ keywords not supported")]))
(define (type->list t)
(match t
[(Pair: (Value: (? keyword? k)) b) (cons k (type->list b))]
[(Value: '()) null]
[_ (int-err "bad value in type->list: ~a" t)]))
;; id: identifier
;; sym: a symbol
;; mod: a quoted require spec like 'scheme/base
;; is id the name sym defined in mod?
(define (id-from? id sym mod)
(and (eq? (syntax-e id) sym)
(eq? (module-path-index-resolve (syntax-source-module id))
((current-module-name-resolver) mod #f #f #f))))
(define (tc/app/internal form expected)
(kernel-syntax-case* form #f
(values apply k:apply not list list* call-with-values do-make-object make-object cons
andmap ormap) ;; the special-cased functions
;; special case for delay
[(#%plain-app
mp1
(#%plain-lambda ()
(#%plain-app mp2 (#%plain-app call-with-values (#%plain-lambda () e) list))))
(and (id-from? #'mp1 'make-promise 'scheme/promise)
(id-from? #'mp2 'make-promise 'scheme/promise))
(ret (-Promise (tc-expr/t #'e)))]
;; special cases for classes
[(#%plain-app make-object cl . args)
(check-do-make-object #'cl #'args #'() #'())]
[(#%plain-app do-make-object cl (#%plain-app list . pos-args) (#%plain-app list (#%plain-app cons 'names named-args) ...))
(check-do-make-object #'cl #'pos-args #'(names ...) #'(named-args ...))]
[(#%plain-app do-make-object . args)
(int-err "bad do-make-object : ~a" (syntax->datum #'args))]
;; call-with-values
[(#%plain-app call-with-values prod con)
(match-let* ([(tc-result: prod-t) (tc-expr #'prod)])
(define (values-ty->list t)
(match t
[(Values: ts) ts]
[_ (list t)]))
(match prod-t
[(Function: (list (arr: (list) vals _ #f '() _ _)))
(tc/funapp #'con #'prod (tc-expr #'con) (map ret (values-ty->list vals)) expected)]
[_ (tc-error/expr #:return (ret (Un))
"First argument to call with values must be a function that can accept no arguments, got: ~a"
prod-t)]))]
;; special cases for `values'
;; special case the single-argument version to preserve the effects
[(#%plain-app values arg) (tc-expr #'arg)]
[(#%plain-app values . args)
(let ([tys (map tc-expr/t (syntax->list #'args))])
(ret (-values tys)))]
;; special case for `list'
[(#%plain-app list . args)
(let ([tys (map tc-expr/t (syntax->list #'args))])
(ret (apply -lst* tys)))]
;; special case for `list*'
[(#%plain-app list* . args)
(match-let* ([(list last tys-r ...) (reverse (map tc-expr/t (syntax->list #'args)))]
[tys (reverse tys-r)])
(ret (foldr make-Pair last tys)))]
;; in eq? cases, call tc/eq
[(#%plain-app eq? v1 v2)
(and (identifier? #'eq?) (comparator? #'eq?))
(begin
;; make sure the whole expression is type correct
(tc/funapp #'eq? #'(v1 v2) (tc-expr #'eq?) (map tc-expr (syntax->list #'(v1 v2))) expected)
;; check thn and els with the eq? info
(let-values ([(thn-eff els-eff) (tc/eq #'eq? #'v1 #'v2)])
(ret B thn-eff els-eff)))]
;; special case for `not'
[(#%plain-app not arg)
(match (tc-expr #'arg)
;; if arg was a predicate application, we swap the effects
[(tc-result: t thn-eff els-eff)
(ret B (map var->type-eff els-eff) (map var->type-eff thn-eff))])]
[(#%plain-app k:apply . args)
(tc/app/internal #'(#%plain-app apply . args) expected)]
;; special-er case for (apply values (list x y z))
[(#%plain-app apply values e)
(cond [(with-handlers ([exn:fail? (lambda _ #f)])
(untuple (tc-expr/t #'e)))
=> (lambda (t) (ret (-values t)))]
[else (tc/apply #'values #'(e))])]
;; special case for `apply'
[(#%plain-app apply f . args) (tc/apply #'f #'args)]
;; special case for keywords
[(#%plain-app
(#%plain-app kpe kws num fn)
kw-list
(#%plain-app list . kw-arg-list)
. pos-args)
(eq? (syntax-e #'kpe) 'keyword-procedure-extract)
(match (tc-expr #'fn)
[(tc-result: (Function: arities))
(tc-keywords form arities (type->list (tc-expr/t #'kws)) #'kw-arg-list #'pos-args expected)]
[(tc-result: t) (tc-error/expr #:return (ret (Un))
"Cannot apply expression of type ~a, since it is not a function type" t)])]
;; even more special case for match
[(#%plain-app (letrec-values ([(lp) (#%plain-lambda args . body)]) lp*) . actuals)
(and expected (not (andmap type-annotation (syntax->list #'args))) (free-identifier=? #'lp #'lp*))
(let-loop-check form #'lp #'actuals #'args #'body expected)]
;; or/andmap of ... argument
[(#%plain-app or/andmap f arg)
(and
(identifier? #'or/andmap)
(or (free-identifier=? #'or/andmap #'ormap)
(free-identifier=? #'or/andmap #'andmap))
(with-handlers ([exn:fail? (lambda _ #f)])
(tc/dots #'arg)
#t))
(let-values ([(ty bound) (tc/dots #'arg)])
(parameterize ([current-tvars (extend-env (list bound)
(list (make-DottedBoth (make-F bound)))
(current-tvars))])
(match-let* ([ft (tc-expr #'f)]
[(tc-result: t) (tc/funapp #'f #'(arg) ft (list (ret ty)) #f)])
(ret (Un (-val #f) t)))))]
;; infer for ((lambda
[(#%plain-app (#%plain-lambda (x ...) . body) args ...)
(= (length (syntax->list #'(x ...)))
(length (syntax->list #'(args ...))))
(tc/let-values/check #'((x) ...) #'(args ...) #'body
#'(let-values ([(x) args] ...) . body)
expected)]
;; default case
[(#%plain-app f args ...)
(tc/funapp #'f #'(args ...) (tc-expr #'f) (map tc-expr (syntax->list #'(args ...))) expected)]))
(define (let-loop-check form lp actuals args body expected)
(kernel-syntax-case* #`(#,args #,body #,actuals) #f (null?)
[((val acc ...)
((if (#%plain-app null? val*) thn els))
(actual actuals ...))
(and (free-identifier=? #'val #'val*)
(ormap (lambda (a) (find-annotation #'(if (#%plain-app null? val*) thn els) a))
(syntax->list #'(acc ...))))
(let* ([ts1 (generalize (tc-expr/t #'actual))]
[ann-ts (for/list ([a (in-syntax #'(acc ...))]
[ac (in-syntax #'(actuals ...))])
(or (find-annotation #'(if (#%plain-app null? val*) thn els) a)
(generalize (tc-expr/t ac))))]
[ts (cons ts1 ann-ts)])
;; check that the actual arguments are ok here
(map tc-expr/check (syntax->list #'(actuals ...)) ann-ts)
;; then check that the function typechecks with the inferred types
(tc/rec-lambda/check form args body lp ts expected)
(ret expected))]
;; special case when argument needs inference
[_
(let ([ts (for/list ([ac (syntax->list actuals)]
[f (syntax->list args)])
(or
(type-annotation f #:infer #t)
(generalize (tc-expr/t ac))))])
(tc/rec-lambda/check form args body lp ts expected)
(ret expected))]))
(define (matches? stx)
(let loop ([stx stx] [ress null] [acc*s null])
(syntax-case stx (#%plain-app reverse)
[([(res) (#%plain-app reverse acc*)] . more)
(loop #'more (cons #'res ress) (cons #'acc* acc*s))]
[rest
(syntax->list #'rest)
(begin
;(printf "ress: ~a~n" (map syntax-e ress))
(list (reverse ress) (reverse acc*s) #'rest))]
[_ #f])))
;(trace tc/app/internal)

View File

@ -1,192 +0,0 @@
#lang scheme/unit
(require (rename-in "../utils/utils.ss" [infer r:infer]))
(require "signatures.ss"
(rep type-rep filter-rep object-rep)
(rename-in (types convenience subtype union utils comparison remove-intersect)
[remove *remove])
(env lexical-env)
(r:infer infer)
(utils tc-utils mutated-vars)
syntax/kerncase
mzlib/trace
mzlib/plt-match)
;; if typechecking
(import tc-expr^)
(export tc-if^)
;; combinators for typechecking in the context of effects
;; t/f tells us whether this is the true or the false branch of an if
;; neccessary for handling true/false effects
;; Boolean Expr listof[Effect] option[type] -> TC-Result
(define (tc-expr/eff t/f expr effs expected)
;; this flag represents whether the refinement proves that this expression cannot be executed
(let ([flag (box #f)])
;; this does the operation on the old type
;; type-op : (Type Type -> Type) Type -> _ Type -> Type
(define ((type-op f t) _ old)
(let ([new-t (f old t)])
;; if this operation produces an uninhabitable type, then this expression can't be executed
(when (type-equal? new-t (Un))
(set-box! flag #t))
;; have to return something here, so that we can continue typechecking
new-t))
;; loop : listof[effect] -> tc-result
(let loop ([effs effs])
;; convenience macro for checking the rest of the list
(define-syntax check-rest
(syntax-rules ()
[(check-rest f v)
(with-update-type/lexical f v (loop (cdr effs)))]
[(check-rest f t v)
(check-rest (type-op f t) v)]))
(if (null? effs)
;; base case
(let* ([reachable? (not (unbox flag))])
(unless reachable?
(warn-unreachable expr))
(cond
;; if flag is true, then we don't want to verify that this branch has the appropriate type
;; in particular, it might be (void)
[(and expected reachable?)
(tc-expr/check expr expected)]
;; this code is reachable, but we have no expected type
[reachable?
(tc-expr expr)]
;; otherwise, this code is unreachable
;; and the resulting type should be the empty type
[(check-unreachable-code?)
(tc-expr/check expr Univ)
(ret (Un))]
[else
(ret (Un))]))
;; recursive case
(match (car effs)
;; these effects have no consequence for the typechecking
[(True-Effect:)
(or t/f (set-box! flag #t))
(loop (cdr effs))]
[(False-Effect:)
(and t/f (set-box! flag #t))
(loop (cdr effs))]
;; restrict v to have a type that's a subtype of t
[(Restrict-Effect: t v)
(check-rest restrict t v)]
;; remove t from the type of v
[(Remove-Effect: t v) (check-rest *remove t v)]
;; just replace the type of v with (-val #f)
[(Var-False-Effect: v) (check-rest (lambda (_ old) (-val #f)) v)]
;; v cannot have type (-val #f)
[(Var-True-Effect: v)
(check-rest *remove (-val #f) v)])))))
;; the main function
(define (tc/if-twoarm tst thn els)
;; check in the context of the effects, and return
(match-let* ([(tc-result: tst-ty tst-thn-eff tst-els-eff) (tc-expr tst)]
[(tc-result: thn-ty thn-thn-eff thn-els-eff) (tc-expr/eff #t thn tst-thn-eff #f)]
[(tc-result: els-ty els-thn-eff els-els-eff) (tc-expr/eff #f els tst-els-eff #f)])
(match* (els-ty thn-thn-eff thn-els-eff els-thn-eff els-els-eff)
;; this is the case for `or'
;; the then branch has to be #t
;; the else branch has to be a simple predicate
;; FIXME - can something simpler be done by using demorgan's law?
;; note that demorgan's law doesn't hold for scheme `and' and `or' because they can produce arbitrary values
;; FIXME - mzscheme's or macro doesn't match this!
[(_ (list (True-Effect:)) (list (True-Effect:)) (list (Restrict-Effect: t v)) (list (Remove-Effect: t v*)))
(=> unmatch)
(match (list tst-thn-eff tst-els-eff)
;; check that the test was also a simple predicate
[(list (list (Restrict-Effect: s u)) (list (Remove-Effect: s u*)))
(if (and
;; check that all the predicates are for the for the same identifier
(free-identifier=? u u*)
(free-identifier=? v v*)
(free-identifier=? v u))
;; this is just a very simple or
(ret (Un (-val #t) els-ty)
;; the then and else effects are just the union of the two types
(list (make-Restrict-Effect (Un s t) v))
(list (make-Remove-Effect (Un s t) v)))
;; otherwise, something complicated is happening and we bail
(unmatch))]
;; similarly, bail here
[_ (unmatch)])]
;; this is the case for `and'
[(_ _ _ (list (False-Effect:)) (list (False-Effect:)))
(ret (Un (-val #f) thn-ty)
;; we change variable effects to type effects in the test,
;; because only the boolean result of the test is used
;; whereas, the actual value of the then branch is returned, not just the boolean result
(append (map var->type-eff tst-thn-eff) thn-thn-eff)
;; no else effects for and, because any branch could have been false
(list))]
;; if the else branch can never happen, just use the effect of the then branch
[((Union: (list)) _ _ _ _)
(ret thn-ty thn-thn-eff thn-els-eff)]
;; otherwise this expression has no effects
[(_ _ _ _ _)
(ret (Un thn-ty els-ty))])))
;; checking version
(define (tc/if-twoarm/check tst thn els expected)
;; check in the context of the effects, and return
(match-let* ([(tc-result: tst-ty tst-thn-eff tst-els-eff) (tc-expr tst)]
[(tc-result: thn-ty thn-thn-eff thn-els-eff) (tc-expr/eff #t thn tst-thn-eff expected)]
[(tc-result: els-ty els-thn-eff els-els-eff) (tc-expr/eff #f els tst-els-eff expected)])
(match* (els-ty thn-thn-eff thn-els-eff els-thn-eff els-els-eff)
;; this is the case for `or'
;; the then branch has to be #t
;; the else branch has to be a simple predicate
;; FIXME - can something simpler be done by using demorgan's law?
;; note that demorgan's law doesn't hold for scheme `and' and `or' because they can produce arbitrary values
;; FIXME - mzscheme's or macro doesn't match this!
[(_ (list (True-Effect:)) (list (True-Effect:)) (list (Restrict-Effect: t v)) (list (Remove-Effect: t v*)))
(=> unmatch)
(match (list tst-thn-eff tst-els-eff)
;; check that the test was also a simple predicate
[(list (list (Restrict-Effect: s u)) (list (Remove-Effect: s u*)))
(if (and
;; check that all the predicates are for the for the same identifier
(free-identifier=? u u*)
(free-identifier=? v v*)
(free-identifier=? v u))
;; this is just a very simple or
(let ([t (Un (-val #t) els-ty)])
(check-below t expected)
(ret t
;; the then and else effects are just the union of the two types
(list (make-Restrict-Effect (Un s t) v))
(list (make-Remove-Effect (Un s t) v))))
;; otherwise, something complicated is happening and we bail
(unmatch))]
;; similarly, bail here
[_ (unmatch)])]
;; this is the case for `and'
[(_ _ _ (list (False-Effect:)) (list (False-Effect:)))
(let ([t (Un thn-ty (-val #f))])
(check-below t expected)
(ret t
;; we change variable effects to type effects in the test,
;; because only the boolean result of the test is used
;; whereas, the actual value of the then branch is returned, not just the boolean result
(append (map var->type-eff tst-thn-eff) thn-thn-eff)
;; no else effects for and, because any branch could have been false
(list)))]
;; if the else branch can never happen, just use the effect of the then branch
[((Union: (list)) _ _ _ _)
(ret thn-ty
;; we change variable effects to type effects in the test,
;; because only the boolean result of the test is used
;; whereas, the actual value of the then branch is returned, not just the boolean result
thn-thn-eff
;; no else effects for and, because any branch could have been false
thn-els-eff)]
;; otherwise this expression has no effects
[(_ _ _ _ _)
(let ([t (Un thn-ty els-ty)])
(check-below t expected)
(ret t))])))

View File

@ -1,18 +0,0 @@
#lang scheme/unit
(require "signatures.ss" "../utils/utils.ss")
(require (utils tc-utils))
(import tc-expr^ tc-lambda^ tc-dots^)
(export tc-app^)
(define (tc/app . args)
(int-err "tc/app NYI"))
(define (tc/app/check . args)
(int-err "tc/app/check NYI"))
(define (tc/funapp . args)
(int-err "tc/funapp NYI"))