diff --git a/Directory.Build.props b/Directory.Build.props
index b607c4e9..af6b5b89 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,10 +2,10 @@
$(OtherFlags) --warnon:3559 --warnon:3560 --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen
-
-
-
+
+
+
+
+
+
diff --git a/src/Escalier.Data/Library.fs b/src/Escalier.Data/Library.fs
index 3f1d5919..98b8fd20 100644
--- a/src/Escalier.Data/Library.fs
+++ b/src/Escalier.Data/Library.fs
@@ -108,8 +108,8 @@ module Syntax =
| Null -> "null"
| Undefined -> "undefined"
- // TODO: add SpreadPat?
type ObjPatElem =
+ // TODO: add isMut
| KeyValuePat of
span: Span *
key: string *
@@ -120,6 +120,7 @@ module Syntax =
name: string *
init: option *
isMut: bool
+ // TODO: rename to RestSpreadPat
| RestPat of span: Span * target: Pattern * isMut: bool
type BindingIdent =
@@ -246,8 +247,8 @@ module Type =
Scheme: option }
type ObjPatElem =
- | KeyValuePat of key: string * value: Pattern
- | ShorthandPat of name: string * value: option
+ | KeyValuePat of key: string * value: Pattern * init: option
+ | ShorthandPat of name: string * init: option
| RestPat of target: Pattern
type Pattern =
@@ -262,7 +263,33 @@ module Type =
override this.ToString() =
match this with
| Identifier name -> name
- | _ -> failwith "TODO: Pattern.ToString()"
+ | Object elems ->
+ let elems =
+ List.map
+ (fun elem ->
+ match elem with
+ | KeyValuePat(key, value, init) ->
+ match init with
+ | Some(init) -> $"{key}: {value} = {init}"
+ | None -> $"{key}: {value}"
+ | ShorthandPat(name, init) ->
+ match init with
+ | Some(value) -> $"{name} = {value}"
+ | None -> name
+ | RestPat(target) -> $"...{target}")
+ elems
+
+ let elems = String.concat ", " elems
+ $"{{{elems}}}"
+ | Tuple elems ->
+ let elems =
+ List.map (fun item -> item.ToString()) elems |> String.concat ", "
+
+ $"[{elems}]"
+ | Wildcard -> "_"
+ | Literal lit -> lit.ToString()
+ | Is({ Name = name }, id) -> $"{name} is {id}"
+ | Rest(target) -> $"...{target}"
type FuncParam =
{ Pattern: Pattern
@@ -489,7 +516,10 @@ module Type =
let elems = String.concat ", " elems
$"{{{elems}}}"
- | _ -> failwith "TODO: finish implementing Type.ToString"
+ | Rest t -> $"...{t}"
+ | _ ->
+ printfn "this.Kind = %A" this.Kind
+ failwith "TODO: finish implementing Type.ToString"
type Scheme =
{ TypeParams: option>
diff --git a/src/Escalier.TypeChecker.Tests/Tests.fs b/src/Escalier.TypeChecker.Tests/Tests.fs
index fbf66737..8c4a54b7 100644
--- a/src/Escalier.TypeChecker.Tests/Tests.fs
+++ b/src/Escalier.TypeChecker.Tests/Tests.fs
@@ -474,14 +474,19 @@ let InferObjectRestSpread () =
result {
let src =
"""
- let obj = {a: 5, b: "hello", c: true}
- let {a, ...rest} = obj
+ let obj1 = {a: 5, b: "hello", c: true}
+ let {a, ...rest} = obj1
+ let obj2 = {a, ...rest}
+ let foo = fn({a, ...rest}: {a: number, b: string, c: boolean}) => a
+ foo(obj2)
"""
let! env = inferScript src
Assert.Value(env, "a", "5")
Assert.Value(env, "rest", "{b: \"hello\", c: true}")
+ Assert.Value(env, "obj2", "{a: 5} & {b: \"hello\", c: true}")
}
+ printfn "result = %A" result
Assert.False(Result.isError result)
diff --git a/src/Escalier.TypeChecker/Library.fs b/src/Escalier.TypeChecker/Library.fs
index 06fc7ee9..8bcd6877 100644
--- a/src/Escalier.TypeChecker/Library.fs
+++ b/src/Escalier.TypeChecker/Library.fs
@@ -137,7 +137,17 @@ module rec TypeChecker =
Provenance = None }
| Literal _ -> t
| Wildcard -> t
- | Object _ -> failwith "TODO: foldType - Object"
+ | Object elems ->
+ let elems =
+ List.map
+ (fun elem ->
+ match elem with
+ | Property p -> Property { p with Type = fold p.Type }
+ | _ -> failwith "TODO: foldType - ObjTypeElem")
+ elems
+
+ { Kind = Object(elems)
+ Provenance = None }
| Rest t ->
{ Kind = Rest(fold t)
Provenance = None }
@@ -371,6 +381,8 @@ module rec TypeChecker =
///Unify the two types t1 and t2. Makes the types t1 and t2 the same.
let unify (env: Env) (t1: Type) (t2: Type) : Result =
+ // printfn $"unify {t1} {t2}"
+
result {
match (prune t1).Kind, (prune t2).Kind with
| TypeVar _, _ -> do! bind t1 t2
@@ -465,8 +477,6 @@ module rec TypeChecker =
{ Kind = TypeKind.Object(combinedElems)
Provenance = None }
- printfn "restTypes = %A" restTypes
-
match restTypes with
| [] -> do! unify env t1 objType
| [ restType ] ->
@@ -494,6 +504,47 @@ module rec TypeChecker =
do! unify env newRestType restType
| _ -> return! Error(TypeError.TypeMismatch(t1, t2))
+ | Intersection types, Object allElems ->
+ let mutable combinedElems = []
+ let mutable restTypes = []
+
+ for t in types do
+ match t.Kind with
+ | Object elems -> combinedElems <- combinedElems @ elems
+ | Rest t -> restTypes <- t :: restTypes
+ | _ -> return! Error(TypeError.TypeMismatch(t1, t2))
+
+ let objType =
+ { Kind = TypeKind.Object(combinedElems)
+ Provenance = None }
+
+ match restTypes with
+ | [] -> do! unify env objType t2
+ | [ restType ] ->
+ let objElems, restElems =
+ List.partition
+ (fun (ae: ObjTypeElem) ->
+ List.exists
+ (fun ce ->
+ match ae, ce with
+ | Property ap, Property cp -> ap.Name = cp.Name
+ | _ -> false)
+ combinedElems)
+ allElems
+
+ let newObjType =
+ { Kind = TypeKind.Object(objElems)
+ Provenance = None }
+
+ do! unify env objType newObjType
+
+ let newRestType =
+ { Kind = TypeKind.Object(restElems)
+ Provenance = None }
+
+ do! unify env restType newRestType
+ | _ -> return! Error(TypeError.TypeMismatch(t1, t2))
+
| _, _ ->
let t1' = expandType env t1
@@ -775,6 +826,8 @@ module rec TypeChecker =
{ Kind = makePrimitiveKind "undefined"
Provenance = None }
| ExprKind.Object elems ->
+ let mutable spreadTypes = []
+
let! elems =
List.traverseResultM
(fun (elem: ObjElem) ->
@@ -784,32 +837,44 @@ module rec TypeChecker =
let! t = inferExpr value env nonGeneric
return
- Property
- { Name = key
- Optional = false
- Readonly = false
- Type = t }
+ Some(
+ Property
+ { Name = key
+ Optional = false
+ Readonly = false
+ Type = t }
+ )
| ObjElem.Shorthand(_span, key) ->
let value = getType key env nonGeneric
return
- Property
- { Name = key
- Optional = false
- Readonly = false
- Type = value }
- | ObjElem.Spread(span, value) ->
- return!
- Error(
- TypeError.NotImplemented
- "TODO: inferExpr - ObjElem.Spread"
+ Some(
+ Property
+ { Name = key
+ Optional = false
+ Readonly = false
+ Type = value }
)
+ | ObjElem.Spread(span, value) ->
+ let! t = inferExpr value env nonGeneric
+ spreadTypes <- t :: spreadTypes
+ return None
})
elems
- return
+ let elems = elems |> List.choose id
+
+ let objType =
{ Kind = Object(elems)
Provenance = None }
+
+ match spreadTypes with
+ | [] -> return objType
+ | _ ->
+ return
+ { Kind = Intersection([ objType ] @ spreadTypes)
+ Provenance = None }
+
| _ ->
return!
Error(
@@ -1176,11 +1241,10 @@ module rec TypeChecker =
(fun (elem: Syntax.ObjPatElem) ->
match elem with
| Syntax.ObjPatElem.KeyValuePat(span, key, value, init) ->
- // TODO: init
- ObjPatElem.KeyValuePat(key, patternToPattern value)
+ ObjPatElem.KeyValuePat(key, patternToPattern value, init)
| Syntax.ObjPatElem.ShorthandPat(span, name, init, isMut) ->
- // TODO: init, isMut
- ObjPatElem.ShorthandPat(name, None)
+ // TODO: isMut
+ ObjPatElem.ShorthandPat(name, init)
| Syntax.ObjPatElem.RestPat(span, target, isMut) ->
ObjPatElem.RestPat(patternToPattern target))
elems