|
|
|
@ -6,8 +6,10 @@ type type_error =
|
|
|
|
|
| NotAnArrow of debruijn_type
|
|
|
|
|
| NotAProduct of debruijn_type
|
|
|
|
|
| NotAForall of debruijn_type
|
|
|
|
|
| NotAnEqual of debruijn_type
|
|
|
|
|
| UnboundTermVariable of tevar
|
|
|
|
|
| TwoOccurencesOfSameVariable of string
|
|
|
|
|
| ContextNotAbsurd
|
|
|
|
|
|
|
|
|
|
and arity_requirement = Index of int | Total of int
|
|
|
|
|
|
|
|
|
@ -37,19 +39,59 @@ module TermVarMap =
|
|
|
|
|
module N2DB =
|
|
|
|
|
DeBruijn.Nominal2deBruijn(TermVar)
|
|
|
|
|
|
|
|
|
|
(* We use a union-find to represent equalities between rigid variables
|
|
|
|
|
and types. We need the ability to snapshot and rollback changes, to
|
|
|
|
|
"un-do" the addition of an equality to the environment. *)
|
|
|
|
|
module UF = struct
|
|
|
|
|
include UnionFind.Make(UnionFind.StoreVector)
|
|
|
|
|
type 'a elem = 'a rref
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
module S =
|
|
|
|
|
Structure
|
|
|
|
|
|
|
|
|
|
(* We translate types into union-find graphs to check equality.
|
|
|
|
|
μ-types create cyclic graphs. Each vertex represents a type expression.
|
|
|
|
|
GADTs can add type equalities in the context and we represent
|
|
|
|
|
this by unifying their vertices in the union-find graph. This is
|
|
|
|
|
only possible when they have compatible structures (or one of the
|
|
|
|
|
side has no structure), otherwise the equality is inconsistent. *)
|
|
|
|
|
type vertex =
|
|
|
|
|
structure option UF.elem
|
|
|
|
|
|
|
|
|
|
and structure =
|
|
|
|
|
(* Corresponds to types from the source language (arrow, product, ...). *)
|
|
|
|
|
| SStruct of vertex S.structure
|
|
|
|
|
(* Represents ∀α.τ. We use De Bruijn levels, so we don't need to name
|
|
|
|
|
explicitly the bound variable α. *)
|
|
|
|
|
| SForall of vertex
|
|
|
|
|
(* A De Bruijn level corresponding to a ∀-bound variable. Using levels
|
|
|
|
|
instead of indices makes the equality test easier. *)
|
|
|
|
|
| SForallLevel of int
|
|
|
|
|
|
|
|
|
|
type env = {
|
|
|
|
|
(* A mapping of term variables to types. *)
|
|
|
|
|
types: debruijn_type TermVarMap.t;
|
|
|
|
|
(* A translation environment of TERM variables to TYPE indices. *)
|
|
|
|
|
names: N2DB.env;
|
|
|
|
|
(* The global store of the union-find. *)
|
|
|
|
|
store: structure option UF.store;
|
|
|
|
|
(* A mapping from rigid variables to union-find vertices. *)
|
|
|
|
|
tyvars: structure option UF.elem list;
|
|
|
|
|
(* Is set to false when an inconsistent equation is introduced in the
|
|
|
|
|
environment. It is useful while typechecking pattern-matching. *)
|
|
|
|
|
consistent: bool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type _datatype_env = (unit, debruijn_type) Datatype.Env.t
|
|
|
|
|
|
|
|
|
|
let empty =
|
|
|
|
|
{ types = TermVarMap.empty; names = N2DB.empty }
|
|
|
|
|
let empty = {
|
|
|
|
|
types = TermVarMap.empty;
|
|
|
|
|
names = N2DB.empty;
|
|
|
|
|
store = UF.new_store();
|
|
|
|
|
tyvars = [];
|
|
|
|
|
consistent = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let lookup { types; names } x =
|
|
|
|
|
let lookup { types; names; _ } x =
|
|
|
|
|
try
|
|
|
|
|
(* Obtain the type associated with [x]. *)
|
|
|
|
|
let ty = TermVarMap.find x types in
|
|
|
|
@ -62,32 +104,47 @@ let lookup { types; names } x =
|
|
|
|
|
(* must have been raised by [TermVarMap.find] *)
|
|
|
|
|
raise (TypeError (UnboundTermVariable x))
|
|
|
|
|
|
|
|
|
|
let extend_with_tevar { types; names } x ty =
|
|
|
|
|
let extend_with_tevar { types; names; store; tyvars; consistent } x ty =
|
|
|
|
|
(* Map the name [x] to [ty], and record when it was bound, without
|
|
|
|
|
incrementing the time. *)
|
|
|
|
|
{ types = TermVarMap.add x ty types;
|
|
|
|
|
names = N2DB.slide names x }
|
|
|
|
|
|
|
|
|
|
let extend_with_tyvar { types; names } =
|
|
|
|
|
names = N2DB.slide names x;
|
|
|
|
|
store;
|
|
|
|
|
tyvars;
|
|
|
|
|
consistent; }
|
|
|
|
|
|
|
|
|
|
let extend_with_tyvar { types; names; store; tyvars; consistent } =
|
|
|
|
|
(* Create a fresh vertex for the new type variable *)
|
|
|
|
|
let v = UF.make store None in
|
|
|
|
|
(* Extend the environment of type variables *)
|
|
|
|
|
let tyvars = v :: tyvars in
|
|
|
|
|
(* Increment the time. *)
|
|
|
|
|
{ types; names = N2DB.bump names }
|
|
|
|
|
let names = N2DB.bump names in
|
|
|
|
|
{ types; names ; store; tyvars; consistent }
|
|
|
|
|
|
|
|
|
|
let singleton x ty =
|
|
|
|
|
extend_with_tevar empty x ty
|
|
|
|
|
|
|
|
|
|
let concat env1 env2 =
|
|
|
|
|
let combine _ _ ty2 = Some ty2 in
|
|
|
|
|
{ types = TermVarMap.union combine env1.types env2.types ;
|
|
|
|
|
names = N2DB.concat env1.names env2.names }
|
|
|
|
|
let types = TermVarMap.union combine env1.types env2.types in
|
|
|
|
|
let names = N2DB.concat env1.names env2.names in
|
|
|
|
|
let store = (assert (env1.store == env2.store); env1.store) in
|
|
|
|
|
let tyvars = env2.tyvars @ env1.tyvars in
|
|
|
|
|
let consistent = env1.consistent && env2.consistent in
|
|
|
|
|
{ types; names; store; tyvars; consistent }
|
|
|
|
|
|
|
|
|
|
let concat_disjoint env1 env2 =
|
|
|
|
|
let exception Conflict of TermVarMap.key in
|
|
|
|
|
try
|
|
|
|
|
let combine x _ _ =
|
|
|
|
|
raise (Conflict x) in
|
|
|
|
|
let types = TermVarMap.union combine env1.types env2.types
|
|
|
|
|
and names = N2DB.concat env1.names env2.names in
|
|
|
|
|
Ok { types; names; }
|
|
|
|
|
let types = TermVarMap.union combine env1.types env2.types in
|
|
|
|
|
let names = N2DB.concat env1.names env2.names in
|
|
|
|
|
let store = (assert (env1.store == env2.store); env1.store) in
|
|
|
|
|
let tyvars = env2.tyvars @ env1.tyvars in
|
|
|
|
|
let consistent = env1.consistent && env2.consistent in
|
|
|
|
|
Ok { types; names; store; tyvars; consistent }
|
|
|
|
|
with Conflict x -> Error x
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
@ -130,202 +187,133 @@ let rec as_forall ty =
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* [equal1] groups the easy cases of the type equality test. It does not
|
|
|
|
|
accept arguments of the form [TyMu]. It expects a self parameter [equal]
|
|
|
|
|
that is used to compare the children of the two root nodes. *)
|
|
|
|
|
|
|
|
|
|
let pointwise equal tys1 tys2 =
|
|
|
|
|
try
|
|
|
|
|
List.for_all2 equal tys1 tys2
|
|
|
|
|
with Invalid_argument _ ->
|
|
|
|
|
(* Catching [Invalid_argument] is bad style, but saves a couple calls
|
|
|
|
|
to [List.length]. A cleaner approach would be to use arrays instead
|
|
|
|
|
of lists. *)
|
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
let equal1 equal ty1 ty2 =
|
|
|
|
|
match ty1, ty2 with
|
|
|
|
|
| TyMu _, _
|
|
|
|
|
| _, TyMu _ ->
|
|
|
|
|
assert false
|
|
|
|
|
| TyVar x1, TyVar x2 ->
|
|
|
|
|
Int.equal x1 x2
|
|
|
|
|
| TyArrow (ty1a, ty1b), TyArrow (ty2a, ty2b) ->
|
|
|
|
|
equal ty1a ty2a && equal ty1b ty2b
|
|
|
|
|
| TyForall ((), ty1), TyForall ((), ty2) ->
|
|
|
|
|
equal ty1 ty2
|
|
|
|
|
| TyProduct tys1, TyProduct tys2 ->
|
|
|
|
|
pointwise equal tys1 tys2
|
|
|
|
|
| TyConstr (Datatype.Type tyconstr1, tys1),
|
|
|
|
|
TyConstr (Datatype.Type tyconstr2, tys2) ->
|
|
|
|
|
String.equal tyconstr1 tyconstr2 &&
|
|
|
|
|
pointwise equal tys1 tys2
|
|
|
|
|
| (TyVar _ | TyArrow _ | TyForall _ | TyProduct _ | TyConstr _),
|
|
|
|
|
_ ->
|
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* An unsound approximation of the equality test. (Now unused.) *)
|
|
|
|
|
|
|
|
|
|
(* Comparing two [TyMu] types simply by comparing their bodies would be
|
|
|
|
|
correct, but incomplete: some types that are equal would be considered
|
|
|
|
|
different. *)
|
|
|
|
|
|
|
|
|
|
(* Naively unfolding every [TyMu] type is sound and complete, but leads to
|
|
|
|
|
potential non-termination. The following code avoids non-termination by
|
|
|
|
|
imposing a budget limit. This makes this equality test unsound: two types
|
|
|
|
|
may be considered equal when they are in fact different. *)
|
|
|
|
|
|
|
|
|
|
let rec equal budget ty1 ty2 =
|
|
|
|
|
match ty1, ty2 with
|
|
|
|
|
| (TyMu _ as ty1), ty2
|
|
|
|
|
| ty2, (TyMu _ as ty1) ->
|
|
|
|
|
budget = 0 || equal (budget - 1) (unfold ty1) ty2
|
|
|
|
|
| _, _ ->
|
|
|
|
|
equal1 (equal budget) ty1 ty2
|
|
|
|
|
|
|
|
|
|
let budget =
|
|
|
|
|
4
|
|
|
|
|
|
|
|
|
|
let _unsound_equal ty1 ty2 =
|
|
|
|
|
equal budget ty1 ty2
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* A sound approximation of the equality test. *)
|
|
|
|
|
|
|
|
|
|
(* The System F types produced by an ML type inference algorithm cannot
|
|
|
|
|
contain [TyForall] under [TyMu]. In this restricted setting, deciding
|
|
|
|
|
type equality is relatively easy. We proceed in two layers, as follows:
|
|
|
|
|
|
|
|
|
|
- Initially, we perform a normal (structural) equality test, going
|
|
|
|
|
down under [TyForall] constructors if necessary. As soon as we
|
|
|
|
|
hit a [TyMu] constructor on either side, we switch to the lower
|
|
|
|
|
layer:
|
|
|
|
|
|
|
|
|
|
- In the lower layer, we know that we cannot hit a [TyForall]
|
|
|
|
|
constructor (if we do, we report that the types differ). We
|
|
|
|
|
map each type (separately) to a graph of unification variables
|
|
|
|
|
(where every variable has rigid structure), then we check that
|
|
|
|
|
the two graphs can be unified. *)
|
|
|
|
|
type equality is relatively easy. We map each type to a graph of
|
|
|
|
|
unification variables (where every variable has rigid structure),
|
|
|
|
|
then we check that the two graphs can be unified. *)
|
|
|
|
|
|
|
|
|
|
exception Clash =
|
|
|
|
|
Structure.Iter2
|
|
|
|
|
|
|
|
|
|
module S =
|
|
|
|
|
Structure
|
|
|
|
|
|
|
|
|
|
type vertex =
|
|
|
|
|
structure UnionFind.elem
|
|
|
|
|
|
|
|
|
|
and structure =
|
|
|
|
|
| SRigidVar of DeBruijn.index
|
|
|
|
|
| SStruct of vertex S.structure
|
|
|
|
|
|
|
|
|
|
let rec translate (env : vertex list) (ty : F.debruijn_type) : vertex =
|
|
|
|
|
match ty with
|
|
|
|
|
(* This function assumes that no occurence of forall quantification
|
|
|
|
|
appear under a mu-type. *)
|
|
|
|
|
let rec translate ~inside_mu store (env : vertex list) (ty : F.debruijn_type)
|
|
|
|
|
: vertex
|
|
|
|
|
= match ty with
|
|
|
|
|
|
|
|
|
|
| TyVar x ->
|
|
|
|
|
(* If [x] is bound in [env], then [x] is bound by μ, and we
|
|
|
|
|
already have a vertex that represents it. Otherwise, [x]
|
|
|
|
|
is bound by ∀, and it is regarded as a rigid variable. *)
|
|
|
|
|
let n = List.length env in
|
|
|
|
|
if x < n then
|
|
|
|
|
List.nth env x
|
|
|
|
|
else
|
|
|
|
|
UnionFind.make (SRigidVar (x - n))
|
|
|
|
|
List.nth env x
|
|
|
|
|
|
|
|
|
|
| TyMu ((), ty) ->
|
|
|
|
|
(* Extend the environment with a mapping of this μ-bound variable
|
|
|
|
|
to a fresh vertex. *)
|
|
|
|
|
let dummy : structure = SRigidVar 0 in
|
|
|
|
|
let v1 = UnionFind.make dummy in
|
|
|
|
|
let v1 = UF.make store None in
|
|
|
|
|
let env = v1 :: env in
|
|
|
|
|
(* Translate the body. *)
|
|
|
|
|
let v2 = translate env ty in
|
|
|
|
|
let v2 = translate ~inside_mu:true store env ty in
|
|
|
|
|
(* Unify the vertices [v1] and [v2], keeping [v2]'s structure. *)
|
|
|
|
|
let v = UnionFind.merge (fun _ s2 -> s2) v1 v2 in
|
|
|
|
|
let v = UF.merge store (fun _ s2 -> s2) v1 v2 in
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
|
| TyForall _ ->
|
|
|
|
|
(* Either we have found a ∀ under a μ, which is unexpected, or one
|
|
|
|
|
of the types that we are comparing has more ∀ quantifiers than
|
|
|
|
|
the other. Either way, we report that the types differ. *)
|
|
|
|
|
raise Clash
|
|
|
|
|
| TyForall ((), ty) ->
|
|
|
|
|
if inside_mu then raise Clash;
|
|
|
|
|
(* Extend the environment with a mapping of this forall-bound variable
|
|
|
|
|
to a fresh vertex. *)
|
|
|
|
|
let v1 = UF.make store (Some (SForallLevel (List.length env))) in
|
|
|
|
|
let env = v1 :: env in
|
|
|
|
|
(* Translate the body. *)
|
|
|
|
|
let v2 = translate ~inside_mu store env ty in
|
|
|
|
|
UF.make store (Some (SForall v2))
|
|
|
|
|
|
|
|
|
|
| TyArrow (ty1, ty2) ->
|
|
|
|
|
let v1 = translate env ty1
|
|
|
|
|
and v2 = translate env ty2 in
|
|
|
|
|
UnionFind.make (SStruct (S.TyArrow (v1, v2)))
|
|
|
|
|
let v1 = translate ~inside_mu store env ty1
|
|
|
|
|
and v2 = translate ~inside_mu store env ty2 in
|
|
|
|
|
UF.make store (Some (SStruct (S.TyArrow (v1, v2))))
|
|
|
|
|
|
|
|
|
|
| TyProduct tys ->
|
|
|
|
|
let vs = translate_list env tys in
|
|
|
|
|
UnionFind.make (SStruct (S.TyProduct vs))
|
|
|
|
|
let vs = translate_list ~inside_mu store env tys in
|
|
|
|
|
UF.make store (Some (SStruct (S.TyProduct vs)))
|
|
|
|
|
|
|
|
|
|
| TyConstr (id, tys) ->
|
|
|
|
|
let vs = translate_list env tys in
|
|
|
|
|
UnionFind.make (SStruct (S.TyConstr (id, vs)))
|
|
|
|
|
let vs = translate_list ~inside_mu store env tys in
|
|
|
|
|
UF.make store (Some (SStruct (S.TyConstr (id, vs))))
|
|
|
|
|
|
|
|
|
|
and translate_list env tys =
|
|
|
|
|
List.map (translate env) tys
|
|
|
|
|
| TyEq (ty1, ty2) ->
|
|
|
|
|
let v1 = translate ~inside_mu store env ty1
|
|
|
|
|
and v2 = translate ~inside_mu store env ty2 in
|
|
|
|
|
UF.make store (Some (SStruct (S.TyEq (v1, v2))))
|
|
|
|
|
|
|
|
|
|
and translate_list ~inside_mu store env tys =
|
|
|
|
|
List.map (translate ~inside_mu store env) tys
|
|
|
|
|
|
|
|
|
|
let insert q v1 v2 =
|
|
|
|
|
Stack.push (v1, v2) q
|
|
|
|
|
|
|
|
|
|
let unify_struct q s1 s2 =
|
|
|
|
|
match s1, s2 with
|
|
|
|
|
| SRigidVar x1, SRigidVar x2 ->
|
|
|
|
|
| SForallLevel x1, SForallLevel x2 ->
|
|
|
|
|
if x1 <> x2 then raise Clash
|
|
|
|
|
| SStruct s1, SStruct s2 ->
|
|
|
|
|
Structure.iter2 (insert q) s1 s2
|
|
|
|
|
| SRigidVar _, SStruct _
|
|
|
|
|
| SStruct _, SRigidVar _ ->
|
|
|
|
|
| SForall s1, SForall s2 ->
|
|
|
|
|
insert q s1 s2
|
|
|
|
|
| SForallLevel _, _
|
|
|
|
|
| SStruct _, _
|
|
|
|
|
| SForall _, _ ->
|
|
|
|
|
raise Clash
|
|
|
|
|
|
|
|
|
|
let merge_struct q s1 s2 =
|
|
|
|
|
unify_struct q s1 s2;
|
|
|
|
|
s1
|
|
|
|
|
|
|
|
|
|
let rec unify q (v1 : vertex) (v2 : vertex) =
|
|
|
|
|
let (_ : vertex) = UnionFind.merge (merge_struct q) v1 v2 in
|
|
|
|
|
unify_pending q
|
|
|
|
|
|
|
|
|
|
and unify_pending q =
|
|
|
|
|
(* When we try to perform an equality check, the [mode] tells us if
|
|
|
|
|
one should unify rigid variables with structure (Input mode), that
|
|
|
|
|
is add new equalities in the typing context, or if it is not allowed
|
|
|
|
|
(Check mode). *)
|
|
|
|
|
type mode = Input | Check
|
|
|
|
|
|
|
|
|
|
let merge_struct mode q so1 so2 : structure option =
|
|
|
|
|
match so1, so2 with
|
|
|
|
|
| Some s1, Some s2 ->
|
|
|
|
|
unify_struct q s1 s2;
|
|
|
|
|
so1
|
|
|
|
|
| None, so | so, None ->
|
|
|
|
|
match mode with
|
|
|
|
|
| Input ->
|
|
|
|
|
so
|
|
|
|
|
| Check ->
|
|
|
|
|
raise Clash
|
|
|
|
|
|
|
|
|
|
let rec unify mode store q (v1 : vertex) (v2 : vertex) =
|
|
|
|
|
let (_ : vertex) = UF.merge store (merge_struct mode q) v1 v2 in
|
|
|
|
|
unify_pending mode store q
|
|
|
|
|
|
|
|
|
|
and unify_pending mode store q =
|
|
|
|
|
match Stack.pop q with
|
|
|
|
|
| v1, v2 ->
|
|
|
|
|
unify q v1 v2
|
|
|
|
|
unify mode store q v1 v2
|
|
|
|
|
| exception Stack.Empty ->
|
|
|
|
|
()
|
|
|
|
|
|
|
|
|
|
let unify v1 v2 =
|
|
|
|
|
let unify mode store v1 v2 =
|
|
|
|
|
let q = Stack.create() in
|
|
|
|
|
unify q v1 v2
|
|
|
|
|
|
|
|
|
|
let rec equal ty1 ty2 =
|
|
|
|
|
match ty1, ty2 with
|
|
|
|
|
| (TyMu _ as ty1), ty2
|
|
|
|
|
| ty2, (TyMu _ as ty1) ->
|
|
|
|
|
(* We have hit a μ binder on one side. Switch to layer-1 mode. *)
|
|
|
|
|
begin try
|
|
|
|
|
let env = [] in
|
|
|
|
|
let v1 = translate env ty1
|
|
|
|
|
and v2 = translate env ty2 in
|
|
|
|
|
unify v1 v2;
|
|
|
|
|
true
|
|
|
|
|
with Clash ->
|
|
|
|
|
false
|
|
|
|
|
end
|
|
|
|
|
| _, _ ->
|
|
|
|
|
(* Otherwise, continue in layer-2 mode. *)
|
|
|
|
|
equal1 equal ty1 ty2
|
|
|
|
|
unify mode store q v1 v2
|
|
|
|
|
|
|
|
|
|
let equal mode env ty1 ty2 =
|
|
|
|
|
not env.consistent ||
|
|
|
|
|
try
|
|
|
|
|
let v1 = translate ~inside_mu:false env.store env.tyvars ty1
|
|
|
|
|
and v2 = translate ~inside_mu:false env.store env.tyvars ty2 in
|
|
|
|
|
unify mode env.store v1 v2;
|
|
|
|
|
true
|
|
|
|
|
with Clash ->
|
|
|
|
|
false
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* Re-package the type equality test as a type equality check. *)
|
|
|
|
|
|
|
|
|
|
let (--) ty1 ty2 : unit =
|
|
|
|
|
if not (equal ty1 ty2) then
|
|
|
|
|
let (--) ty1 ty2 env : unit =
|
|
|
|
|
if not (equal Check env ty1 ty2) then
|
|
|
|
|
raise (TypeError (TypeMismatch (ty1, ty2)))
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
@ -349,14 +337,14 @@ let rec typeof datatype_env env (t : debruijn_term) : debruijn_type =
|
|
|
|
|
List.iter on_subterm ts;
|
|
|
|
|
ty
|
|
|
|
|
| Annot (_, t, ty) ->
|
|
|
|
|
typeof env t -- ty;
|
|
|
|
|
(typeof env t -- ty) env;
|
|
|
|
|
ty
|
|
|
|
|
| Abs (_, x, ty1, t) ->
|
|
|
|
|
let ty2 = typeof (extend_with_tevar env x ty1) t in
|
|
|
|
|
TyArrow (ty1, ty2)
|
|
|
|
|
| App (_, t, u) ->
|
|
|
|
|
let ty1, ty2 = as_arrow (typeof env t) in
|
|
|
|
|
typeof env u -- ty1;
|
|
|
|
|
(typeof env u -- ty1) env;
|
|
|
|
|
ty2
|
|
|
|
|
| Let (_, x, t, u) ->
|
|
|
|
|
let env = extend_with_tevar env x (typeof env t) in
|
|
|
|
@ -375,7 +363,7 @@ let rec typeof datatype_env env (t : debruijn_term) : debruijn_type =
|
|
|
|
|
| Variant (_, lbl, dty, t) ->
|
|
|
|
|
let arg_type = typeof_variant datatype_env lbl dty in
|
|
|
|
|
let ty = typeof env t in
|
|
|
|
|
ty -- arg_type;
|
|
|
|
|
(ty -- arg_type) env;
|
|
|
|
|
TyConstr dty
|
|
|
|
|
| LetProd (_, xs, t, u) ->
|
|
|
|
|
let ty = typeof env t in
|
|
|
|
@ -387,8 +375,30 @@ let rec typeof datatype_env env (t : debruijn_term) : debruijn_type =
|
|
|
|
|
| Match (_, ty, t, brs) ->
|
|
|
|
|
let scrutinee_ty = typeof env t in
|
|
|
|
|
let tys = List.map (typeof_branch datatype_env env scrutinee_ty) brs in
|
|
|
|
|
List.iter ((--) ty) tys;
|
|
|
|
|
List.iter (fun ty2 -> (--) ty ty2 env) tys;
|
|
|
|
|
ty
|
|
|
|
|
| Absurd (_, ty) ->
|
|
|
|
|
if env.consistent then
|
|
|
|
|
raise (TypeError (ContextNotAbsurd))
|
|
|
|
|
else
|
|
|
|
|
ty
|
|
|
|
|
| Refl (_, ty) ->
|
|
|
|
|
TyEq (ty, ty)
|
|
|
|
|
| Use (_, t, u) ->
|
|
|
|
|
let store_copy = UF.copy env.store in
|
|
|
|
|
let env = { env with store = store_copy } in
|
|
|
|
|
begin
|
|
|
|
|
match typeof env t with
|
|
|
|
|
| TyEq (ty1, ty2) ->
|
|
|
|
|
if equal Input env ty1 ty2 then
|
|
|
|
|
(* typecheck [u] in a context containing equalities introduced by
|
|
|
|
|
the equality ty1 = ty2 *)
|
|
|
|
|
typeof env u
|
|
|
|
|
else
|
|
|
|
|
typeof { env with consistent = false } u
|
|
|
|
|
| ty ->
|
|
|
|
|
raise (TypeError (NotAnEqual ty))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
and typeof_variant datatype_env lbl (tid, tyargs) =
|
|
|
|
|
let Datatype.{ data_kind ; type_params ; labels_descr; _ } =
|
|
|
|
@ -407,19 +417,19 @@ and typeof_variant datatype_env lbl (tid, tyargs) =
|
|
|
|
|
) tyargs type_params arg_type
|
|
|
|
|
|
|
|
|
|
and typeof_branch datatype_env env scrutinee_ty (pat, rhs) =
|
|
|
|
|
let new_bindings = binding_pattern datatype_env scrutinee_ty pat in
|
|
|
|
|
let new_bindings = binding_pattern datatype_env env scrutinee_ty pat in
|
|
|
|
|
let new_env = concat env new_bindings in
|
|
|
|
|
typeof datatype_env new_env rhs
|
|
|
|
|
|
|
|
|
|
and binding_pattern datatype_env scrutinee_ty pat =
|
|
|
|
|
let binding_pattern = binding_pattern datatype_env in
|
|
|
|
|
and binding_pattern datatype_env env scrutinee_ty pat =
|
|
|
|
|
let binding_pattern = binding_pattern datatype_env env in
|
|
|
|
|
match pat with
|
|
|
|
|
| PVar (_, x) ->
|
|
|
|
|
singleton x scrutinee_ty
|
|
|
|
|
| PWildcard _ ->
|
|
|
|
|
empty
|
|
|
|
|
| PAnnot (_, pat, ty) ->
|
|
|
|
|
scrutinee_ty -- ty;
|
|
|
|
|
(scrutinee_ty -- ty) env;
|
|
|
|
|
binding_pattern ty pat
|
|
|
|
|
| PTuple (_, ps) ->
|
|
|
|
|
let tys = as_product scrutinee_ty in
|
|
|
|
@ -432,7 +442,7 @@ and binding_pattern datatype_env scrutinee_ty pat =
|
|
|
|
|
List.fold_left f empty envs
|
|
|
|
|
| PVariant (_, lbl, dty, pat) ->
|
|
|
|
|
let arg_type = typeof_variant datatype_env lbl dty in
|
|
|
|
|
scrutinee_ty -- TyConstr dty;
|
|
|
|
|
(scrutinee_ty -- TyConstr dty) env;
|
|
|
|
|
binding_pattern arg_type pat
|
|
|
|
|
|
|
|
|
|
let typeof datatype_env t =
|
|
|
|
|