summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/constraint.ml3
-rw-r--r--src/type_check.ml263
2 files changed, 99 insertions, 167 deletions
diff --git a/src/constraint.ml b/src/constraint.ml
index 1b9447f8..d66705b6 100644
--- a/src/constraint.ml
+++ b/src/constraint.ml
@@ -310,7 +310,8 @@ let conj (x : t) (y : t) : t =
let disj (x : t) (y : t) : t =
BFun (Or, x, y)
-let forall (vars : int list) (x : t) : t = Forall (vars, x)
+let forall (vars : int list) (x : t) : t =
+ if vars = [] then x else Forall (vars, x)
let negate (x : t) : t = Not x
diff --git a/src/type_check.ml b/src/type_check.ml
index cad7dc47..71ed80fa 100644
--- a/src/type_check.ml
+++ b/src/type_check.ml
@@ -344,6 +344,7 @@ module Env : sig
val lookup_id : id -> t -> lvar
val fresh_kid : ?kid:kid -> t -> kid
val expand_synonyms : t -> typ -> typ
+ val canonicalize : t -> typ -> typ
val base_typ_of : t -> typ -> typ
val add_smt_op : id -> string -> t -> t
val get_smt_op : id -> t -> string
@@ -513,7 +514,7 @@ end = struct
else typ_error (id_loc id) ("Could not prove " ^ string_of_list ", " string_of_n_constraint ncs ^ " for type constructor " ^ string_of_id id)
let rec expand_synonyms env (Typ_aux (typ, l) as t) =
- typ_debug ("Expanding synonyms for " ^ string_of_typ t);
+ (* typ_debug ("Expanding synonyms for " ^ string_of_typ t); *)
match typ with
| Typ_tup typs -> Typ_aux (Typ_tup (List.map (expand_synonyms env) typs), l)
| Typ_fn (typ1, typ2, effs) -> Typ_aux (Typ_fn (expand_synonyms env typ1, expand_synonyms env typ2, effs), l)
@@ -563,6 +564,65 @@ end = struct
| Typ_arg_typ typ -> Typ_arg_aux (Typ_arg_typ (expand_synonyms env typ), l)
| arg -> Typ_arg_aux (arg, l)
+ (** Map over all nexps in a type - excluding those in existential constraints **)
+ let rec map_nexps f (Typ_aux (typ_aux, l) as typ) =
+ match typ_aux with
+ | Typ_id _ | Typ_var _ -> typ
+ | Typ_fn (arg_typ, ret_typ, effect) -> Typ_aux (Typ_fn (map_nexps f arg_typ, map_nexps f ret_typ, effect), l)
+ | Typ_tup typs -> Typ_aux (Typ_tup (List.map (map_nexps f) typs), l)
+ | Typ_exist (kids, nc, typ) -> Typ_aux (Typ_exist (kids, nc, map_nexps f typ), l)
+ | Typ_app (id, args) -> Typ_aux (Typ_app (id, List.map (map_nexps_arg f) args), l)
+ and map_nexps_arg f (Typ_arg_aux (arg_aux, l) as arg) =
+ match arg_aux with
+ | Typ_arg_order _ | Typ_arg_typ _ -> arg
+ | Typ_arg_nexp n -> Typ_arg_aux (Typ_arg_nexp (f n), l)
+
+ let canonical env typ =
+ let typ = expand_synonyms env typ in
+ let counter = ref 0 in
+ let complex_nexps = ref KBindings.empty in
+ let simplify_nexp (Nexp_aux (nexp_aux, l) as nexp) =
+ match nexp_aux with
+ | Nexp_var _ | Nexp_constant _ -> nexp
+ | _ ->
+ let kid = Kid_aux (Var ("'c#" ^ string_of_int !counter), l) in
+ complex_nexps := KBindings.add kid nexp !complex_nexps;
+ incr counter;
+ Nexp_aux (Nexp_var kid, l)
+ in
+ let typ = map_nexps (fun nexp -> simplify_nexp (nexp_simp nexp)) typ in
+ let existentials = KBindings.bindings !complex_nexps |> List.map fst in
+ let constrs = List.fold_left (fun ncs (kid, nexp) -> nc_eq (nvar kid) nexp :: ncs) [] (KBindings.bindings !complex_nexps) in
+ existentials, constrs, typ
+
+ let is_canonical env typ =
+ let typ = expand_synonyms env typ in
+ let counter = ref 0 in
+ let simplify_nexp (Nexp_aux (nexp_aux, l) as nexp) =
+ match nexp_aux with
+ | Nexp_var _ | Nexp_constant _ -> nexp
+ | _ -> (incr counter; nexp)
+ in
+ let typ = map_nexps simplify_nexp typ in
+ not (!counter > 0)
+
+ let rec canonicalize env typ =
+ match typ with
+ | Typ_aux (Typ_fn (arg_typ, ret_typ, effects), l) when is_canonical env arg_typ ->
+ Typ_aux (Typ_fn (arg_typ, canonicalize env ret_typ, effects), l)
+ | Typ_aux (Typ_fn _, l) -> typ_error l ("Function type " ^ string_of_typ typ ^ " is not canonical")
+ | _ ->
+ let existentials, constrs, (Typ_aux (typ_aux, l) as typ) = canonical env typ in
+ if existentials = [] then
+ typ
+ else
+ let typ_aux = match typ_aux with
+ | Typ_tup _ | Typ_app _ -> Typ_exist (existentials, List.fold_left nc_and (List.hd constrs) (List.tl constrs), typ)
+ | Typ_exist (kids, nc, typ) -> Typ_exist (kids @ existentials, List.fold_left nc_and nc constrs, typ)
+ | Typ_fn _ | Typ_id _ | Typ_var _ -> assert false (* These must be simple *)
+ in
+ Typ_aux (typ_aux, l)
+
(* Check if a type, order, n-expression or constraint is
well-formed. Throws a type error if the type is badly formed. *)
let rec wf_typ ?exs:(exs=KidSet.empty) env typ =
@@ -1067,80 +1127,6 @@ and simp_typ_aux = function
Typ_exist (kids1 @ kids2, nc_and nc1 nc2, typ)
| typ_aux -> typ_aux
-let order_eq (Ord_aux (ord_aux1, _)) (Ord_aux (ord_aux2, _)) =
- match ord_aux1, ord_aux2 with
- | Ord_inc, Ord_inc -> true
- | Ord_dec, Ord_dec -> true
- | Ord_var kid1, Ord_var kid2 -> Kid.compare kid1 kid2 = 0
- | _, _ -> false
-
-let rec props_subst sv subst props =
- match props with
- | [] -> []
- | ((nexp1, nexp2) :: props) -> (nexp_subst sv subst nexp1, nexp_subst sv subst nexp2) :: props_subst sv subst props
-
-type tnf =
- | Tnf_wild
- | Tnf_id of id
- | Tnf_var of kid
- | Tnf_tup of tnf list
- | Tnf_index_sort of index_sort
- | Tnf_app of id * tnf_arg list
-and tnf_arg =
- | Tnf_arg_nexp of nexp
- | Tnf_arg_typ of tnf
- | Tnf_arg_order of order
- | Tnf_arg_effect of effect
-
-let rec string_of_tnf = function
- | Tnf_wild -> "_"
- | Tnf_id id -> string_of_id id
- | Tnf_var kid -> string_of_kid kid
- | Tnf_tup tnfs -> "(" ^ string_of_list ", " string_of_tnf tnfs ^ ")"
- | Tnf_app (id, args) -> string_of_id id ^ "<" ^ string_of_list ", " string_of_tnf_arg args ^ ">"
- | Tnf_index_sort IS_int -> "INT"
- | Tnf_index_sort (IS_prop (kid, props)) ->
- "{" ^ string_of_kid kid ^ " | " ^ string_of_list " & " (fun (n1, n2) -> string_of_nexp n1 ^ " <= " ^ string_of_nexp n2) props ^ "}"
-and string_of_tnf_arg = function
- | Tnf_arg_nexp n -> string_of_nexp n
- | Tnf_arg_typ tnf -> string_of_tnf tnf
- | Tnf_arg_order o -> string_of_order o
- | Tnf_arg_effect eff -> string_of_effect eff
-
-let rec normalize_typ env (Typ_aux (typ, l)) =
- match typ with
- | Typ_id (Id_aux (Id "int", _)) -> Tnf_index_sort IS_int
- | Typ_id (Id_aux (Id "nat", _)) ->
- let kid = Env.fresh_kid env in Tnf_index_sort (IS_prop (kid, [(nint 0, nvar kid)]))
- | Typ_id v ->
- begin
- try normalize_typ env (Env.get_typ_synonym v env env []) with
- | Not_found -> Tnf_id v
- end
- | Typ_var kid -> Tnf_var kid
- | Typ_tup typs -> Tnf_tup (List.map (normalize_typ env) typs)
- | Typ_app (f, []) -> normalize_typ env (Typ_aux (Typ_id f, l))
- | Typ_app (Id_aux (Id "atom", _), [Typ_arg_aux (Typ_arg_nexp n, _)]) ->
- let kid = Env.fresh_kid env in
- Tnf_index_sort (IS_prop (kid, [(n, nvar kid); (nvar kid, n)]))
- | Typ_app (Id_aux (Id "range", _), [Typ_arg_aux (Typ_arg_nexp n1, _); Typ_arg_aux (Typ_arg_nexp n2, _)]) ->
- let kid = Env.fresh_kid env in
- Tnf_index_sort (IS_prop (kid, [(n1, nvar kid); (nvar kid, n2)]))
- | Typ_app ((Id_aux (Id "vector", _) as vector), args) ->
- Tnf_app (vector, List.map (normalize_typ_arg env) args)
- | Typ_app (id, args) ->
- begin
- try normalize_typ env (Env.get_typ_synonym id env env args) with
- | Not_found -> Tnf_app (id, List.map (normalize_typ_arg env) args)
- end
- | Typ_exist (kids, nc, typ) -> typ_error l "Cannot normalise existential type"
- | Typ_fn _ -> typ_error l ("Cannot normalize function type " ^ string_of_typ (Typ_aux (typ, l)))
-and normalize_typ_arg env (Typ_arg_aux (typ_arg, _)) =
- match typ_arg with
- | Typ_arg_nexp n -> Tnf_arg_nexp n
- | Typ_arg_typ typ -> Tnf_arg_typ (normalize_typ env typ)
- | Typ_arg_order o -> Tnf_arg_order o
-
(* Here's how the constraint generation works for subtyping
X(b,c...) --> {a. Y(a,b,c...)} \subseteq {a. Z(a,b,c...)}
@@ -1253,64 +1239,6 @@ let prove env (NC_aux (nc_aux, _) as nc) =
| NC_true -> true
| _ -> prove_z3 env nc
-let rec subtyp_tnf env tnf1 tnf2 =
- typ_print ("Subset " ^ string_of_list ", " string_of_n_constraint (Env.get_constraints env) ^ " |- " ^ string_of_tnf tnf1 ^ " " ^ string_of_tnf tnf2);
- let module Bindings = Map.Make(Kid) in
- let bindings = ref Bindings.empty in
- let fresh_var kid =
- let n = Bindings.cardinal !bindings in
- bindings := Bindings.add kid n !bindings;
- n
- in
- let var_of kid =
- try Bindings.find kid !bindings with
- | Not_found -> fresh_var kid
- in
- let rec neg_props props =
- match props with
- | [] -> Constraint.literal false
- | [(nexp1, nexp2)] -> Constraint.gt (nexp_constraint env var_of nexp1) (nexp_constraint env var_of nexp2)
- | ((nexp1, nexp2) :: props) ->
- Constraint.disj (Constraint.gt (nexp_constraint env var_of nexp1) (nexp_constraint env var_of nexp2)) (neg_props props)
- in
- let rec pos_props props =
- match props with
- | [] -> Constraint.literal true
- | [(nexp1, nexp2)] -> Constraint.lteq (nexp_constraint env var_of nexp1) (nexp_constraint env var_of nexp2)
- | ((nexp1, nexp2) :: props) ->
- Constraint.conj (Constraint.lteq (nexp_constraint env var_of nexp1) (nexp_constraint env var_of nexp2)) (pos_props props)
- in
- match (tnf1, tnf2) with
- | Tnf_wild, Tnf_wild -> true
- | Tnf_id v1, Tnf_id v2 -> Id.compare v1 v2 = 0
- | Tnf_var kid1, Tnf_var kid2 -> Kid.compare kid1 kid2 = 0
- | Tnf_tup tnfs1, Tnf_tup tnfs2 ->
- begin
- try List.for_all2 (subtyp_tnf env) tnfs1 tnfs2 with
- | Invalid_argument _ -> false
- end
- | Tnf_app (v1, args1), Tnf_app (v2, args2) -> Id.compare v1 v2 = 0 && List.for_all2 (tnf_args_eq env) args1 args2
- | Tnf_index_sort IS_int, Tnf_index_sort IS_int -> true
- | Tnf_index_sort (IS_prop _), Tnf_index_sort IS_int -> true
- | Tnf_index_sort (IS_prop (kid1, prop1)), Tnf_index_sort (IS_prop (kid2, prop2)) ->
- begin
- let kid3 = Env.fresh_kid env in
- let (prop1, prop2) = props_subst kid1 (Nexp_var kid3) prop1, props_subst kid2 (Nexp_var kid3) prop2 in
- let constr = Constraint.conj (nc_constraints env var_of (Env.get_constraints env)) (Constraint.conj (pos_props prop1) (neg_props prop2)) in
- match Constraint.call_z3 constr with
- | Constraint.Unsat -> typ_debug "unsat"; true
- | Constraint.Sat -> typ_debug "sat"; false
- | Constraint.Unknown -> typ_debug "unknown"; false
- end
- | _, _ -> false
-
-and tnf_args_eq env arg1 arg2 =
- match arg1, arg2 with
- | Tnf_arg_nexp n1, Tnf_arg_nexp n2 -> prove env (NC_aux (NC_equal (n1, n2), Parse_ast.Unknown))
- | Tnf_arg_order ord1, Tnf_arg_order ord2 -> order_eq ord1 ord2
- | Tnf_arg_typ tnf1, Tnf_arg_typ tnf2 -> subtyp_tnf env tnf1 tnf2 && subtyp_tnf env tnf2 tnf1
- | _, _ -> assert false
-
(**************************************************************************)
(* 4. Unification *)
(**************************************************************************)
@@ -1759,58 +1687,61 @@ let rec alpha_equivalent env typ1 typ2 =
then (typ_debug "alpha-equivalent"; true)
else (typ_debug "Not alpha-equivalent"; false)
-let rec subtyp l env typ1 typ2 =
- typ_print ("Subtype " ^ string_of_typ typ1 ^ " and " ^ string_of_typ typ2);
- match destruct_exist env typ1, destruct_exist env typ2 with
+let unwrap_exist env typ =
+ match destruct_exist env typ with
+ | Some (kids, nc, typ) -> (kids, nc, typ)
+ | None -> ([], nc_true, typ)
+
+let rec subtyp l env (Typ_aux (typ_aux1, _) as typ1) (Typ_aux (typ_aux2, _) as typ2) =
+ typ_print (("Subtype " |> Util.green |> Util.clear) ^ string_of_typ typ1 ^ " and " ^ string_of_typ typ2);
+ match typ_aux1, typ_aux2 with
+ | Typ_tup typs1, Typ_tup typs2 when List.length typs1 = List.length typs2 ->
+ List.iter2 (subtyp l env) typs1 typs2
+ | _, _ ->
+ match destruct_numeric env typ1, destruct_numeric env typ2 with
(* Ensure alpha equivalent types are always subtypes of one another
- this ensures that we can always re-check inferred types. *)
| _, _ when alpha_equivalent env typ1 typ2 -> ()
- (* Special case for two existentially quantified numeric (atom) types *)
- | Some (kids1, nc1, typ1), Some (_ :: _ :: _ as kids2, nc2, typ2)
- when is_some (destruct_atom_kid env typ1) && is_some (destruct_atom_kid env typ2) ->
- let env = List.fold_left (fun env kid -> Env.add_typ_var kid BK_int env) env kids1 in
- let env = Env.add_constraint nc1 env in
-
- (* Guaranteed to succeed because of the guard *)
- let destruct_some x = match x with Some y -> y | _ -> assert false in
- let atom_kid1 = destruct_some (destruct_atom_kid env typ1) in
- let atom_kid2 = destruct_some (destruct_atom_kid env typ2) in
-
- let kids2 = List.filter (fun kid -> Kid.compare atom_kid2 kid <> 0) kids2 in
- let env = Env.add_typ_var atom_kid2 BK_int env in
- let env = Env.add_constraint (nc_eq (nvar atom_kid1) (nvar atom_kid2)) env in
+ (* Special cases for two numeric (atom) types *)
+ | Some (kids1, nc1, nexp1), Some ([], _, nexp2) ->
+ let env = add_existential kids1 nc1 env in
+ if prove env (nc_eq nexp1 nexp2) then () else typ_raise l (Err_subtype (typ1, typ2, Env.get_constraints env))
+ | Some (kids1, nc1, nexp1), Some (kids2, nc2, nexp2) ->
+ let env = add_existential kids1 nc1 env in
+ let env = add_typ_vars (KidSet.elements (KidSet.inter (nexp_frees nexp2) (KidSet.of_list kids2))) env in
+ let kids2 = KidSet.elements (KidSet.diff (KidSet.of_list kids2) (nexp_frees nexp2)) in
+ let env = Env.add_constraint (nc_eq nexp1 nexp2) env in
let constr var_of =
- Constraint.forall (List.map var_of kids2) (Constraint.negate (nc_constraint env var_of nc2))
+ Constraint.forall (List.map var_of kids2)
+ (nc_constraint env var_of (nc_negate nc2))
in
if prove_z3' env constr then ()
- else typ_error l ("Existential atom subtyping failed")
+ else typ_raise l (Err_subtype (typ1, typ2, Env.get_constraints env))
+ | _, _ ->
+ match destruct_exist env typ1, unwrap_exist env (Env.canonicalize env typ2) with
| Some (kids, nc, typ1), _ ->
- let env = List.fold_left (fun env kid -> Env.add_typ_var kid BK_int env) env kids in
- let env = Env.add_constraint nc env in
- subtyp l env typ1 typ2
- | _, Some (kids, nc, typ2) ->
- typ_debug "XXXXXXX";
- let env = List.fold_left (fun env kid -> Env.add_typ_var kid BK_int env) env kids in
- typ_debug "YYYYYYY";
+ let env = add_existential kids nc env in subtyp l env typ1 typ2
+ | None, (kids, nc, typ2) ->
+ let env = add_typ_vars kids env in
+ let kids' = KidSet.elements (KidSet.diff (KidSet.of_list kids) (typ_frees typ2)) in
let unifiers, existential_kids, existential_nc =
try unify l env typ2 typ1 with
| Unification_error (_, m) -> typ_error l m
in
- typ_debug "ZZZZZZZ";
let nc = List.fold_left (fun nc (kid, uvar) -> nc_subst_uvar kid uvar nc) nc (KBindings.bindings unifiers) in
+ let env = List.fold_left uv_nexp_constraint env (KBindings.bindings unifiers) in
let env = match existential_kids, existential_nc with
| [], None -> env
| _, Some enc ->
let env = List.fold_left (fun env kid -> Env.add_typ_var kid BK_int env) env existential_kids in
- let env = List.fold_left uv_nexp_constraint env (KBindings.bindings unifiers) in
Env.add_constraint enc env
| _, None -> assert false (* Cannot have existential_kids without existential_nc *)
in
- if prove env nc then ()
- else typ_error l ("Could not show " ^ string_of_typ typ1 ^ " is a subset of existential " ^ string_of_typ typ2)
- | _, None ->
- if subtyp_tnf env (normalize_typ env typ1) (normalize_typ env typ2)
- then ()
+ let constr var_of =
+ Constraint.forall (List.map var_of kids')
+ (nc_constraint env var_of (nc_negate nc))
+ in
+ if prove_z3' env constr then ()
else typ_raise l (Err_subtype (typ1, typ2, Env.get_constraints env))
let typ_equality l env typ1 typ2 =