implement mlish-core with typed-lang-builder

This commit is contained in:
AlexKnauth 2016-06-20 16:09:18 -04:00
parent 0c2ced8100
commit a4fd3312e5
3 changed files with 2222 additions and 2 deletions

View File

@ -0,0 +1,788 @@
#lang s-exp "../typed-lang-builder/mlish-core.rkt"
(require "rackunit-typechecking.rkt")
;; match on tups
(check-type
(match (tup 1 2) with
[x y -> (+ x y)])
: Int -> 3)
;; tests more or less copied from infer-tests.rkt ------------------------------
(typecheck-fail (λ (x) x) #:with-msg "λ: no expected type, add annotations")
;; top-level defines
(define (f [x : Int] Int) x)
(typecheck-fail (f 1 2) #:with-msg "Wrong number of arguments")
(check-type f : ( Int Int))
(check-type (f 1) : Int 1)
(typecheck-fail (f (λ ([x : Int]) x)))
(define (g [x : X] X) x)
(check-type g : (→/test X X))
;; (inferred) polymorpic instantiation
(check-type (g 1) : Int 1)
(check-type (g #f) : Bool #f) ; different instantiation
(check-type (g add1) : ( Int Int))
(check-type (g +) : ( Int Int Int))
;; function polymorphic in list element
(define-type (List X)
Nil
(Cons X (List X)))
;; arity err
(typecheck-fail (Cons 1) #:with-msg "Cons: Wrong number of arguments")
;; type err
(typecheck-fail (Cons 1 1)
#:with-msg "expected: \\(List Int\\)\n *given: Int")
(typecheck-fail
(match (Cons 1 Nil) with
[Nil -> 1])
#:with-msg "match: clauses not exhaustive; missing: Cons")
(typecheck-fail
(match (Cons 1 Nil) with
[Cons x xs -> 1])
#:with-msg "match: clauses not exhaustive; missing: Nil")
(define (g2 [lst : (List Y)] (List Y)) lst)
(check-type g2 : (→/test (List Y) (List Y)))
(typecheck-fail (g2 1)
#:with-msg
"expected: \\(List Y\\)\n *given: Int")
;; todo? allow polymorphic nil?
(check-type (g2 (Nil {Int})) : (List Int) (Nil {Int}))
(check-type (g2 (Nil {Bool})) : (List Bool) (Nil {Bool}))
(check-type (g2 (Nil {(List Int)})) : (List (List Int)) (Nil {(List Int)}))
(check-type (g2 (Nil {( Int Int)})) : (List ( Int Int)) (Nil {(List ( Int Int))}))
;; annotations unneeded: same as tests above, but without annotations
(check-type (g2 Nil) : (List Int) Nil)
(check-type (g2 Nil) : (List Bool) Nil)
(check-type (g2 Nil) : (List (List Int)) Nil)
(check-type (g2 Nil) : (List ( Int Int)) Nil)
(check-type (g2 (Cons 1 Nil)) : (List Int) (Cons 1 Nil))
(check-type (g2 (Cons "1" Nil)) : (List String) (Cons "1" Nil))
;; mlish cant type this fn (ie, incomplete cases on variant --- what to put for Nil case?)
;(define (g3 [lst : (List X)] → X) (hd lst))
;(check-type g3 : (→ {X} (List X) X))
;(check-type g3 : (→ {A} (List A) A))
;(check-not-type g3 : (→ {A B} (List A) B))
;(typecheck-fail (g3) #:with-msg "Expected.+arguments with type.+List") ; TODO: more precise err msg
;(check-type (g3 (nil {Int})) : Int) ; runtime fail
;(check-type (g3 (nil {Bool})) : Bool) ; runtime fail
;(check-type (g3 (cons 1 nil)) : Int ⇒ 1)
;(check-type (g3 (cons "1" nil)) : String ⇒ "1")
;; recursive fn
(define (recf [x : Int] Int) (recf x))
(check-type recf : ( Int Int))
(define (countdown [x : Int] Int)
(if (zero? x)
0
(countdown (sub1 x))))
(check-type (countdown 0) : Int 0)
(check-type (countdown 10) : Int 0)
(typecheck-fail (countdown "10") #:with-msg "expected: Int\n *given: String")
;; list fns ----------
; map: tests whether match and define properly propagate 'expected-type
(define (map [f : ( X Y)] [lst : (List X)] (List Y))
(match lst with
[Nil -> Nil]
[Cons x xs -> (Cons (f x) (map f xs))]))
(check-type map : (→/test ( X Y) (List X) (List Y)))
(check-type map : (→/test {Y X} ( Y X) (List Y) (List X)))
(check-type map : (→/test ( A B) (List A) (List B)))
(check-not-type map : (→/test ( A B) (List B) (List A)))
(check-not-type map : (→/test ( X X) (List X) (List X))) ; only 1 bound tyvar
; nil without annotation; tests fn-first, left-to-right arg inference
; does work yet, need to add left-to-right inference in #%app
(check-type (map add1 Nil) : (List Int) Nil)
(check-type (map add1 (Cons 1 (Cons 2 (Cons 3 Nil))))
: (List Int) (Cons 2 (Cons 3 (Cons 4 Nil))))
(typecheck-fail (map add1 (Cons "1" Nil))
#:with-msg "expected: Int\n *given: String")
(check-type (map (λ ([x : Int]) (+ x 2)) (Cons 1 (Cons 2 (Cons 3 Nil))))
: (List Int) (Cons 3 (Cons 4 (Cons 5 Nil))))
;; ; doesnt work yet: all lambdas need annotations
;; (check-type (map (λ (x) (+ x 2)) (list 1 2 3)) : (List Int) ⇒ (list 3 4 5))
(define (filter [p? : ( X Bool)] [lst : (List X)] (List X))
(match lst with
[Nil -> Nil]
[Cons x xs -> (if (p? x)
(Cons x (filter p? xs))
(filter p? xs))]))
(define (filter/guard [p? : ( X Bool)] [lst : (List X)] (List X))
(match lst with
[Nil -> Nil]
[Cons x xs #:when (p? x) -> (Cons x (filter p? xs))]
[Cons x xs -> (filter p? xs)]))
(check-type (filter zero? Nil) : (List Int) Nil)
(check-type (filter zero? (Cons 1 (Cons 2 (Cons 3 Nil))))
: (List Int) Nil)
(check-type (filter zero? (Cons 0 (Cons 1 (Cons 2 Nil))))
: (List Int) (Cons 0 Nil))
(check-type (filter (λ (x) (not (zero? x))) (Cons 0 (Cons 1 (Cons 2 Nil))))
: (List Int) (Cons 1 (Cons 2 Nil)))
(check-type (filter/guard zero? Nil) : (List Int) Nil)
(check-type (filter/guard zero? (Cons 1 (Cons 2 (Cons 3 Nil))))
: (List Int) Nil)
(check-type (filter/guard zero? (Cons 0 (Cons 1 (Cons 2 Nil))))
: (List Int) (Cons 0 Nil))
(check-type
(filter/guard (λ (x) (not (zero? x))) (Cons 0 (Cons 1 (Cons 2 Nil))))
: (List Int) (Cons 1 (Cons 2 Nil)))
; doesnt work yet: all lambdas need annotations
;(check-type (filter (λ (x) (not (zero? x))) (list 0 1 2)) : (List Int) ⇒ (list 1 2))
(define (foldr [f : ( X Y Y)] [base : Y] [lst : (List X)] Y)
(match lst with
[Nil -> base]
[Cons x xs -> (f x (foldr f base xs))]))
(define (foldl [f : ( X Y Y)] [acc : Y] [lst : (List X)] Y)
(match lst with
[Nil -> acc]
[Cons x xs -> (foldr f (f x acc) xs)]))
(define (all? [p? : ( X Bool)] [lst : (List X)] Bool)
(match lst with
[Nil -> #t]
[Cons x xs #:when (p? x) -> (all? p? xs)]
[Cons x xs -> #f]))
(define (tails [lst : (List X)] (List (List X)))
(match lst with
[Nil -> (Cons Nil Nil)]
[Cons x xs -> (Cons lst (tails xs))]))
(define (build-list [n : Int] [f : ( Int X)] (List X))
(if (zero? (sub1 n))
(Cons (f 0) Nil)
(Cons (f (sub1 n)) (build-list (sub1 n) f))))
(check-type (build-list 1 add1) : (List Int) (Cons 1 Nil))
(check-type (build-list 3 add1) : (List Int) (Cons 3 (Cons 2 (Cons 1 Nil))))
(check-type (build-list 5 sub1)
: (List Int) (Cons 3 (Cons 2 (Cons 1 (Cons 0 (Cons -1 Nil))))))
(check-type (build-list 5 (λ (x) (add1 (add1 x))))
: (List Int) (Cons 6 (Cons 5 (Cons 4 (Cons 3 (Cons 2 Nil))))))
(define (build-list/comp [i : Int] [n : Int] [nf : ( Int Int)] [f : ( Int X)] (List X))
(if (= i n)
Nil
(Cons (f (nf i)) (build-list/comp (add1 i) n nf f))))
(define built-list-1 (build-list/comp 0 3 (λ (x) (* 2 x)) add1))
(define built-list-2 (build-list/comp 0 3 (λ (x) (* 2 x)) number->string))
(check-type built-list-1 : (List Int) -> (Cons 1 (Cons 3 (Cons 5 Nil))))
(check-type built-list-2 : (List String) -> (Cons "0" (Cons "2" (Cons "4" Nil))))
(define (~>2 [a : A] [f : ( A A)] [g : ( A B)] B)
(g (f a)))
(define ~>2-result-1 (~>2 1 (λ (x) (* 2 x)) add1))
(define ~>2-result-2 (~>2 1 (λ (x) (* 2 x)) number->string))
(check-type ~>2-result-1 : Int -> 3)
(check-type ~>2-result-2 : String -> "2")
(define (append [lst1 : (List X)] [lst2 : (List X)] (List X))
(match lst1 with
[Nil -> lst2]
[Cons x xs -> (Cons x (append xs lst2))]))
;; end infer.rkt tests --------------------------------------------------
;; algebraic data types
(define-type IntList
INil
(ConsI Int IntList))
;; HO, monomorphic
(check-type ConsI : ( Int IntList IntList))
(define (new-cons [c : ( Int IntList IntList)] [x : Int] [xs : IntList]
-> IntList)
(c x xs))
(check-type (new-cons ConsI 1 INil) : IntList -> (ConsI 1 INil))
;; check that ConsI and INil are available as tyvars
(define (f10 [x : INil] [y : ConsI] -> ConsI) y)
(check-type f10 : (→/test X Y Y))
(check-type INil : IntList)
(check-type (ConsI 1 INil) : IntList)
(check-type
(match INil with
[INil -> 1]
[ConsI x xs -> 2]) : Int 1)
(check-type
(match (ConsI 1 INil) with
[INil -> 1]
[ConsI x xs -> 2]) : Int 2)
(typecheck-fail (match 1 with [INil -> 1]))
(typecheck-fail (ConsI #f INil)
#:with-msg
"expected: Int\n *given: Bool")
;; annotated
(check-type (Nil {Int}) : (List Int))
(check-type (Cons {Int} 1 (Nil {Int})) : (List Int))
(check-type (Cons {Int} 1 (Cons 2 (Nil {Int}))) : (List Int))
;; partial annotations
(check-type (Cons 1 (Nil {Int})) : (List Int))
(check-type (Cons 1 (Cons 2 (Nil {Int}))) : (List Int))
(check-type (Cons {Int} 1 Nil) : (List Int))
(check-type (Cons {Int} 1 (Cons 2 Nil)) : (List Int))
(check-type (Cons 1 (Cons {Int} 2 Nil)) : (List Int))
; no annotations
(check-type (Cons 1 Nil) : (List Int))
(check-type (Cons 1 (Cons 2 Nil)) : (List Int))
(define-type (Tree X)
(Leaf X)
(Node (Tree X) (Tree X)))
(check-type (Leaf 10) : (Tree Int))
(check-type (Node (Leaf 10) (Leaf 11)) : (Tree Int))
(typecheck-fail Nil #:with-msg "Nil: no expected type, add annotations")
(typecheck-fail (Cons 1 (Nil {Bool}))
#:with-msg
"expected: \\(List Int\\)\n *given: \\(List Bool\\)")
(typecheck-fail (Cons {Bool} 1 (Nil {Int}))
#:with-msg
(string-append
"Cons: type mismatch\n"
" *expected: +Bool, \\(List Bool\\)\n"
" *given: +Int, \\(List Int\\)\n"
" *expressions: 1, \\(Nil \\(Int\\)\\)"))
(typecheck-fail (Cons {Bool} 1 Nil)
#:with-msg
(string-append
"Cons: type mismatch\n"
" *expected: +Bool, \\(List Bool\\)\n"
" *given: +Int, \\(List Bool\\)\n"
" *expressions: 1, Nil"))
(typecheck-fail (match Nil with [Cons x xs -> 2] [Nil -> 1])
#:with-msg "Nil: no expected type, add annotations")
(check-type
(match (Nil {Int}) with
[Cons x xs -> 2]
[Nil -> 1])
: Int 1)
(check-type
(match (Nil {Int}) with
[Nil -> 1]
[Cons x xs -> 2])
: Int 1)
(check-type
(match (Cons 1 Nil) with
[Nil -> 3]
[Cons y ys -> (+ y 4)])
: Int 5)
(check-type
(match (Cons 1 Nil) with
[Cons y ys -> (+ y 5)]
[Nil -> 3])
: Int 6)
;; check expected-type propagation for other match paterns
(define-type (Option A)
(None)
(Some A))
(define (None* (Option A)) None)
(check-type (match (tup 1 2) with [a b -> None]) : (Option Int) -> None)
(check-type
(match (list 1 2) with
[[] -> None]
[[x y] -> None])
: (Option Int) -> None)
(check-type
(match (list 1 2) with
[[] -> None]
[x :: xs -> None])
: (Option Int) -> None)
(define-type (Pairof A B) (C A B))
(check-type (match (C 1 2) with [C a b -> None]) : (Option Int) -> None)
;; type variable inference
; F should remain valid tyvar, even though it's bound
(define (F [x : X] -> X) x)
(define (tvf1 [x : F] -> F) x)
(check-type tvf1 : (→/test X X))
; G should remain valid tyvar
(define-type (Type1 X) (G X))
(define (tvf5 [x : G] -> G) x)
(check-type tvf5 : (→/test X X))
; TY should not be tyvar, bc it's a valid type
(define-type-alias TY (Pairof Int Int))
(define (tvf2 [x : TY] -> TY) x)
(check-not-type tvf2 : (→/test X X))
; same with Bool
(define (tvf3 [x : Bool] -> Bool) x)
(check-not-type tvf3 : (→/test X X))
;; X in lam should not be a new tyvar
(define (tvf4 [x : X] -> ( X X))
(λ (y) x))
(check-type tvf4 : (→/test X ( X X)))
(check-not-type tvf4 : (→/test X ( Y X)))
(define (tvf6 [x : X] -> ( Y X))
(λ (y) x))
(check-type tvf6 : (→/test X ( Y X)))
;; nested lambdas
(check-type (λ ([x : X]) (λ ([y : X]) y)) : (→/test X ( X X)))
(check-not-type (λ ([x : X]) (λ ([y : X]) y)) : (→/test {X} X (→/test {Y} Y Y)))
(check-type (λ ([x : X]) (λ ([y : Y]) y)) : (→/test {X} X (→/test {Y} Y Y)))
(check-not-type (λ ([x : X]) (λ ([y : Y]) x)) : (→/test X ( X X)))
(check-type
((λ ([x : X]) (λ ([y : Y]) y)) 1)
: (→/test Y Y))
;; TODO?
;; - this fails if polymorphic functions are allowed as HO args
;; - do we want to allow this?
;; - must explicitly instantiate before passing fn
(check-type
((λ ([x : ( X ( Y Y))]) x)
(inst (λ ([x : X]) (inst (λ ([y : Y]) y) Int)) Int))
: ( Int ( Int Int)))
(check-type
((λ ([x : X]) (λ ([y : Y]) (λ ([z : Z]) z))) 1)
: (→/test {Y} Y (→/test {Z} Z Z)))
(check-type (inst Cons (→/test X X))
: ( (→/test X X) (List (→/test X X)) (List (→/test X X))))
(check-type map : (→/test ( X Y) (List X) (List Y)))
(check-type (Cons (λ ([x : X]) x) Nil)
: (List (→/test {X} X X)))
(define (nn [x : X] -> ( (× X ( Y Y))))
(λ () (tup x (λ ([x : Y]) x))))
(typecheck-fail (nn 1) #:with-msg "Could not infer instantiation of polymorphic function nn.")
(check-type (nn 1) : ( (× Int ( String String))))
(check-type (nn 1) : ( (× Int ( (List Int) (List Int)))))
(define (nn2 [x : X] -> ( (× X ( Y Y) (List Z))))
(λ () (tup x (λ ([x : Y]) x) Nil)))
(typecheck-fail (nn2 1) #:with-msg "Could not infer instantiation of polymorphic function nn2.")
(check-type (nn2 1) : ( (× Int ( String String) (List (List Int)))))
(check-type (nn2 1) : ( (× Int ( (List Int) (List Int)) (List String))))
;; test inst order
(check-type ((inst nn2 Int String (List Int)) 1)
: ( (× Int ( String String) (List (List Int)))))
(check-type ((inst nn2 Int (List Int) String) 1)
: ( (× Int ( (List Int) (List Int)) (List String))))
(define (nn3 [x : X] -> ( (× X (Option Y) (Option Z))))
(λ () (tup x None None)))
(check-type (nn3 1) : (→/test (× Int (Option Y) (Option Z))))
(check-type (nn3 1) : ( (× Int (Option String) (Option (List Int)))))
(check-type ((nn3 1)) : (× Int (Option String) (Option (List Int))))
(check-type ((nn3 1)) : (× Int (Option (List Int)) (Option String)))
;; test inst order
(check-type ((inst (nn3 1) String (List Int))) : (× Int (Option String) (Option (List Int))))
(check-type ((inst (nn3 1) (List Int) String)) : (× Int (Option (List Int)) (Option String)))
(define (nn4 -> ( (Option X)))
(λ () (None*)))
(check-type (let ([x (nn4)])
x)
: (→/test (Option X)))
(define (nn5 -> ( (Ref (Option X))))
(λ () (ref (None {X}))))
(typecheck-fail (let ([x (nn5)])
x)
#:with-msg "Could not infer instantiation of polymorphic function nn5.")
(define (nn6 -> ( (Option X)))
(let ([r (((inst nn5 X)))])
(λ () (deref r))))
(check-type (nn6) : (→/test (Option X)))
;; A is covariant, B is invariant.
(define-type (Cps A B)
(cps ( ( A B) B)))
(define (cps* [f : ( ( A B) B)] (Cps A B))
(cps f))
(define (nn7 -> ( (Cps (Option A) B)))
(let ([r (((inst nn5 A)))])
(λ () (cps* (λ (k) (k (deref r)))))))
(typecheck-fail (let ([x (nn7)])
x)
#:with-msg "Could not infer instantiation of polymorphic function nn7.")
(define (nn8 -> ( (Cps (Option A) Int)))
(nn7))
(check-type (let ([x (nn8)])
x)
: (→/test (Cps (Option A) Int)))
(define-type (Result A B)
(Ok A)
(Error B))
(define (ok [a : A] (Result A B))
(Ok a))
(define (error [b : B] (Result A B))
(Error b))
(define (ok-fn [a : A] -> ( (Result A B)))
(λ () (ok a)))
(define (error-fn [b : B] -> ( (Result A B)))
(λ () (error b)))
(check-type (let ([x (ok-fn 1)])
x)
: (→/test (Result Int B)))
(check-type (let ([x (error-fn "bad")])
x)
: (→/test (Result A String)))
(define (nn9 [a : A] -> ( (Result A (Ref B))))
(ok-fn a))
(define (nn10 [a : A] -> ( (Result A (Ref String))))
(nn9 a))
(define (nn11 -> ( (Result (Option A) (Ref String))))
(nn10 (None*)))
(typecheck-fail (let ([x (nn9 1)])
x)
#:with-msg "Could not infer instantiation of polymorphic function nn9.")
(check-type (let ([x (nn10 1)])
x)
: ( (Result Int (Ref String))))
(check-type (let ([x (nn11)])
x)
: (→/test (Result (Option A) (Ref String))))
(check-type (if (zero? (random 2))
(ok 0)
(error "didn't get a zero"))
: (Result Int String))
#|
(define result-if-0
(λ ([b : (Result A1 B1)] [succeed : ( A1 (Result A2 B2))] [fail : ( B1 (Result A2 B2))])
(match b with
[Ok a -> (succeed a)]
[Error b -> (fail b)])))
(check-type result-if-0
: (→/test (Result A1 B1) ( A1 (Result A2 B2)) ( B1 (Result A2 B2))
(Result A2 B2)))
(define (result-if-1 [b : (Result A1 B1)]
( ( A1 (Result A2 B2)) ( B1 (Result A2 B2))
(Result A2 B2)))
(λ ([succeed : ( A1 (Result A2 B2))] [fail : ( B1 (Result A2 B2))])
(result-if-0 b succeed fail)))
(check-type result-if-1
: (→/test (Result A1 B1) ( ( A1 (Result A2 B2)) ( B1 (Result A2 B2))
(Result A2 B2))))
(check-type ((inst result-if-1 Int String (List Int) (List String)) (Ok 1))
: ( ( Int (Result (List Int) (List String)))
( String (Result (List Int) (List String)))
(Result (List Int) (List String))))
(check-type ((inst result-if-1 Int String (List Int) (List String)) (Error "bad"))
: ( ( Int (Result (List Int) (List String)))
( String (Result (List Int) (List String)))
(Result (List Int) (List String))))
(check-type (((inst result-if-1 Int String (List Int) (List String)) (Ok 1))
(λ ([a : Int]) (ok (Cons a Nil)))
(λ ([b : String]) (error (Cons b Nil))))
: (Result (List Int) (List String)))
;; same thing, but without the lambda annotations:
(check-type (((inst result-if-1 Int String (List Int) (List String)) (Ok 1))
(λ (a) (ok (Cons a Nil)))
(λ (b) (error (Cons b Nil))))
: (Result (List Int) (List String)))
(define (result-if-2 [b : (Result A1 B1)]
( ( A1 (Result A2 B2))
( ( B1 (Result A2 B2))
(Result A2 B2))))
(λ ([succeed : ( A1 (Result A2 B2))])
(λ ([fail : ( B1 (Result A2 B2))])
(result-if-0 b succeed fail))))
(check-type result-if-2
: (→/test (Result A1 B1) ( ( A1 (Result A2 B2))
( ( B1 (Result A2 B2))
(Result A2 B2)))))
(check-type ((inst result-if-2 Int String (List Int) (List String)) (Ok 1))
: (→/test ( Int (Result (List Int) (List String)))
( ( String (Result (List Int) (List String)))
(Result (List Int) (List String)))))
(check-type (((inst result-if-2 Int String (List Int) (List String)) (Ok 1))
(λ (a) (Ok (Cons a Nil))))
: (→/test ( String (Result (List Int) (List String)))
(Result (List Int) (List String))))
(check-type ((((inst result-if-2 Int String (List Int) (List String)) (Ok 1))
(λ (a) (Ok (Cons a Nil))))
(λ (b) (Error (Cons b Nil))))
: (Result (List Int) (List String)))
(define (tup* [a : A] [b : B] -> (× A B))
(tup a b))
(define (nn12 -> ( (× (Option A) (Option B))))
(λ () (tup* (None*) (None*))))
(check-type (let ([x (nn12)])
x)
: (→/test (× (Option A) (Option B))))
(define (nn13 -> ( (× (Option A) (Option (Ref B)))))
(nn12))
(typecheck-fail (let ([x (nn13)])
x)
#:with-msg "Could not infer instantiation of polymorphic function nn13.")
;; records and automatically-defined accessors and predicates
(define-type (RecoTest X Y)
(RT1 [x : X] [y : Y] [z : String])
(RT2 [a : Y] [b : X] [c : (List X)])
(RT3 X Y)) ; mixing records and non-records allowed
(check-type RT1-x : (→/test (RecoTest X Y) X))
(check-type RT1-y : (→/test (RecoTest X Y) Y))
(check-type RT1-z : (→/test (RecoTest X Y) String))
(check-type RT2-a : (→/test (RecoTest X Y) Y))
(check-type RT2-b : (→/test (RecoTest X Y) X))
(check-type RT1? : (→/test (RecoTest X Y) Bool))
(check-type RT2? : (→/test (RecoTest X Y) Bool))
(check-type RT3? : (→/test (RecoTest X Y) Bool))
(check-type (RT1-x (RT1 1 #t "2")) : Int -> 1)
(check-type (RT1-y (RT1 1 #t "2")) : Bool -> #t)
(check-type (RT1-z (RT1 1 #t "2")) : String -> "2")
(check-type (RT2-a (RT2 1 #f Nil)) : Int -> 1)
(check-type (RT2-b (RT2 1 #f Nil)) : Bool -> #f)
(check-type (RT2-c (RT2 1 #f Nil)) : (List Bool) -> Nil)
(check-type (RT1? (RT1 1 2 "3")) : Bool -> #t)
(check-type (RT1? (RT2 1 2 Nil)) : Bool -> #f)
(check-type (RT1? (RT3 1 "2")) : Bool -> #f)
(check-type (RT3? (RT3 1 2)) : Bool -> #t)
(check-type (RT3? (RT1 1 2 "3")) : Bool -> #f)
(typecheck-fail RT3-x #:with-msg "unbound identifier")
;; accessors produce runtime exception if given wrong variant
(check-runtime-exn (RT1-x (RT2 1 #f (Cons #t Nil))))
(check-runtime-exn (RT1-y (RT2 1 #f (Cons #t Nil))))
(check-runtime-exn (RT1-z (RT2 1 #f (Cons #t Nil))))
(check-runtime-exn (RT1-x (RT3 1 2)))
(check-runtime-exn (RT2-a (RT1 1 #f "2")))
(check-runtime-exn (RT2-c (RT1 1 #f "2")))
(check-runtime-exn (RT2-c (RT1 1 #f "2")))
(check-runtime-exn (RT2-a (RT3 #f #t)))
;; non-match version
(define (rt-fn [rt : (RecoTest X Y)] -> X)
(if (RT1? rt)
(RT1-x rt)
(if (RT2? rt)
(RT2-b rt)
(match rt with [RT3 x y -> x][RT1 x y z -> x][RT2 a b c -> b]))))
(check-type (rt-fn (RT1 1 #f "3")) : Int -> 1)
(check-type (rt-fn (RT2 #f 2 Nil)) : Int -> 2)
(check-type (rt-fn (RT3 10 20)) : Int -> 10)
;; HO constructors
(check-type RT1 : (→/test X Y String (RecoTest X Y)))
(check-type RT2 : (→/test {X Y} Y X (List X) (RecoTest X Y)))
(check-type RT3 : (→/test X Y (RecoTest X Y)))
(typecheck-fail (for/fold ([x 1]) () "hello")
#:with-msg "for/fold: Type of body and initial accumulator must be the same, given Int and String")
; ext-stlc tests --------------------------------------------------
; tests for stlc extensions
; new literals and base types
(check-type "one" : String) ; literal now supported
(check-type #f : Bool) ; literal now supported
(check-type (λ ([x : Bool]) x) : ( Bool Bool)) ; Bool is now valid type
;; Unit
(check-type (void) : Unit)
(check-type void : ( Unit))
(typecheck-fail
((λ ([x : Unit]) x) 2)
#:with-msg
(expected "Unit" #:given "Int" #:note "Type error applying function"))
(typecheck-fail
((λ ([x : Unit]) x) void)
#:with-msg
(expected "Unit" #:given "(→ Unit)" #:note "Type error applying function"))
(check-type ((λ ([x : Unit]) x) (void)) : Unit)
;; begin
(check-type (begin 1) : Int)
(typecheck-fail (begin) #:with-msg "expected more terms")
;; 2016-03-06: begin terms dont need to be Unit
(check-type (begin 1 2 3) : Int)
#;(typecheck-fail
(begin 1 2 3)
#:with-msg "Expected expression 1 to have Unit type, got: Int")
(check-type (begin (void) 1) : Int 1)
(check-type ((λ ([x : Int]) (begin (void) x)) 1) : Int)
(check-type ((λ ([x : Int]) (begin x)) 1) : Int)
(check-type ((λ ([x : Int]) (begin (begin x))) 1) : Int)
(check-type ((λ ([x : Int]) (begin (void) (begin (void) x))) 1) : Int)
(check-type ((λ ([x : Int]) (begin (begin (void) x))) 1) : Int)
;;ascription
(check-type (ann 1 : Int) : Int 1)
(check-type ((λ ([x : Int]) (ann x : Int)) 10) : Int 10)
(typecheck-fail (ann 1 : Bool) #:with-msg "ann: 1 does not have type Bool")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann Int : Int) #:with-msg "does not have type Int")
; let
(check-type (let () (+ 1 1)) : Int 2)
(check-type (let ([x 10]) (+ 1 2)) : Int)
(check-type (let ([x 10] [y 20]) ((λ ([z : Int] [a : Int]) (+ a z)) x y)) : Int 30)
(typecheck-fail
(let ([x #f]) (+ x 1))
#:with-msg (expected "Int, Int" #:given "Bool, Int"))
(typecheck-fail (let ([x 10] [y (+ x 1)]) (+ x y))
#:with-msg "x: unbound identifier")
(check-type (let* ([x 10] [y (+ x 1)]) (+ x y)) : Int 21)
(typecheck-fail
(let* ([x #t] [y (+ x 1)]) 1)
#:with-msg (expected "Int, Int" #:given "Bool, Int"))
; letrec
(typecheck-fail
(letrec ([(x : Int) #f] [(y : Int) 1]) y)
#:with-msg
"letrec: type check fail, args have wrong type:\n#f has type Bool, expected Int")
(typecheck-fail
(letrec ([(y : Int) 1] [(x : Int) #f]) x)
#:with-msg
"letrec: type check fail, args have wrong type:.+#f has type Bool, expected Int")
(check-type (letrec ([(x : Int) 1] [(y : Int) (+ x 1)]) (+ x y)) : Int 3)
;; recursive
(check-type
(letrec ([(countdown : ( Int String))
(λ (i)
(if (= i 0)
"liftoff"
(countdown (- i 1))))])
(countdown 10)) : String "liftoff")
;; mutually recursive
(check-type
(letrec ([(is-even? : ( Int Bool))
(λ (n)
(or (zero? n)
(is-odd? (sub1 n))))]
[(is-odd? : ( Int Bool))
(λ (n)
(and (not (zero? n))
(is-even? (sub1 n))))])
(is-odd? 11)) : Bool #t)
;; check some more err msgs
(typecheck-fail
(and "1" #f)
#:with-msg "Expected expression \"1\" to have Bool type, got: String")
(typecheck-fail
(and #t "2")
#:with-msg
"Expected expression \"2\" to have Bool type, got: String")
(typecheck-fail
(or "1" #f)
#:with-msg
"Expected expression \"1\" to have Bool type, got: String")
(typecheck-fail
(or #t "2")
#:with-msg
"Expected expression \"2\" to have Bool type, got: String")
;; 2016-03-09: now ok
(check-type (if "true" 1 2) : Int -> 1)
(typecheck-fail
(if #t 1 "2")
#:with-msg
"branches have incompatible types: Int and String")
;; tests from stlc+lit-tests.rkt --------------------------
; most should pass, some failing may now pass due to added types/forms
(check-type 1 : Int)
(check-not-type 1 : ( Int Int))
;(typecheck-fail "one") ; literal now supported
;(typecheck-fail #f) ; literal now supported
(check-type (λ (x y) x) : ( Int Int Int))
(check-not-type (λ ([x : Int]) x) : Int)
(check-type (λ (x) x) : ( Int Int))
(check-type (λ (f) 1) : ( ( Int Int) Int))
(check-type ((λ ([x : Int]) x) 1) : Int 1)
(typecheck-fail
((λ ([x : Bool]) x) 1)
#:with-msg (expected "Bool" #:given "Int"))
;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is now valid type
(typecheck-fail
(λ ([f : Int]) (f 1 2))
#:with-msg
"Expected expression f to have → type, got: Int")
(check-type (λ (f x y) (f x y))
: ( ( Int Int Int) Int Int Int))
(check-type ((λ ([f : ( Int Int Int)] [x : Int] [y : Int]) (f x y)) + 1 2)
: Int 3)
(typecheck-fail
(+ 1 (λ ([x : Int]) x))
#:with-msg (expected "Int, Int" #:given "Int, (→ Int Int)"))
(typecheck-fail
(λ ([x : ( Int Int)]) (+ x x))
#:with-msg (expected "Int, Int" #:given "(→ Int Int), (→ Int Int)"))
(typecheck-fail
((λ ([x : Int] [y : Int]) y) 1)
#:with-msg (expected "Int, Int" #:given "1"
#:note "Wrong number of arguments"))
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)
|#

View File

@ -73,7 +73,7 @@
;; a base type. We also know #'b is not a var, so #'b has
;; to be the same "identifier base type" as #'a.
(unless (and (identifier? #'b) (free-identifier=? #'a #'b))
(type-error #:src (get-orig #'a)
(type-error #:src (get-orig #'b)
#:msg (format "couldn't unify ~~a and ~~a\n expected: ~a\n given: ~a"
(string-join (map type->str (stx-map stx-car orig-cs)) ", ")
(string-join (map type->str (stx-map stx-cadr orig-cs)) ", "))
@ -92,12 +92,13 @@
orig-cs)]
[((~Any tycons1 τ1 ...) (~Any tycons2 τ2 ...))
#:when (typecheck? #'tycons1 #'tycons2)
#:when (stx-length=? #'[τ1 ...] #'[τ2 ...])
(add-constraints Xs
substs
#'((τ1 τ2) ... . rst)
orig-cs)]
[else
(type-error #:src (get-orig #'a)
(type-error #:src (get-orig #'b)
#:msg (format "couldn't unify ~~a and ~~a\n expected: ~a\n given: ~a"
(string-join (map type->str (stx-map stx-car orig-cs)) ", ")
(string-join (map type->str (stx-map stx-cadr orig-cs)) ", "))

File diff suppressed because it is too large Load Diff