summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pretty_print_sail.ml4
-rw-r--r--src/sail.ml3
-rw-r--r--src/type_check.ml116
-rw-r--r--src/type_check.mli13
4 files changed, 109 insertions, 27 deletions
diff --git a/src/pretty_print_sail.ml b/src/pretty_print_sail.ml
index e410eb4b..668e791c 100644
--- a/src/pretty_print_sail.ml
+++ b/src/pretty_print_sail.ml
@@ -81,7 +81,7 @@ let doc_qi (QI_aux(qi,_)) = match qi with
(* typ_doc is the doc for the type being quantified *)
let doc_typquant (TypQ_aux(tq,_)) typ_doc = match tq with
| TypQ_no_forall -> typ_doc
- | TypQ_tq [] -> failwith "TypQ_tq with empty list"
+ | TypQ_tq [] -> typ_doc
| TypQ_tq qlist ->
(* include trailing break because the caller doesn't know if tq is empty *)
doc_op dot
@@ -106,7 +106,7 @@ let doc_lit (L_aux(l,_)) =
| L_bin n -> "0b" ^ n
| L_real r -> r
| L_undef -> "undefined"
- | L_string s -> "\"" ^ s ^ "\"")
+ | L_string s -> "\"" ^ String.escaped s ^ "\"")
let doc_pat, doc_atomic_pat =
let rec pat pa = pat_colons pa
diff --git a/src/sail.ml b/src/sail.ml
index bb2fdaa5..3500b213 100644
--- a/src/sail.ml
+++ b/src/sail.ml
@@ -103,6 +103,9 @@ let options = Arg.align ([
( "-dno_cast",
Arg.Set opt_dno_cast,
" (debug) typecheck without any implicit casting");
+ ( "-no_effects",
+ Arg.Set Type_check.opt_no_effects,
+ " turn off effect checking");
( "-v",
Arg.Set opt_print_version,
" print version");
diff --git a/src/type_check.ml b/src/type_check.ml
index 021ace42..ff5f1512 100644
--- a/src/type_check.ml
+++ b/src/type_check.ml
@@ -47,6 +47,7 @@ open Ast_util
open Big_int
let opt_tc_debug = ref 0
+let opt_no_effects = ref false
let depth = ref 0
let rec indent n = match n with
@@ -83,6 +84,8 @@ let mk_id_typ id = Typ_aux (Typ_id id, Parse_ast.Unknown)
let inc_ord = Ord_aux (Ord_inc, Parse_ast.Unknown)
let dec_ord = Ord_aux (Ord_dec, Parse_ast.Unknown)
+let mk_ord ord_aux = Ord_aux (ord_aux, Parse_ast.Unknown)
+
let int_typ = mk_id_typ (mk_id "int")
let nat_typ = mk_id_typ (mk_id "nat")
let unit_typ = mk_id_typ (mk_id "unit")
@@ -177,6 +180,27 @@ let string_of_index_sort = function
^ string_of_list " & " (fun (x, y) -> string_of_nexp x ^ " <= " ^ string_of_nexp y) constraints
^ "}"
+let quant_items : typquant -> quant_item list = function
+ | TypQ_aux (TypQ_tq qis, _) -> qis
+ | TypQ_aux (TypQ_no_forall, _) -> []
+
+let kopt_kid (KOpt_aux (kopt_aux, _)) =
+ match kopt_aux with
+ | KOpt_none kid | KOpt_kind (_, kid) -> kid
+
+let is_nat_kopt = function
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_nat, _)], _), _), _) -> true
+ | KOpt_aux (KOpt_none _, _) -> true
+ | _ -> false
+
+let is_order_kopt = function
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_order, _)], _), _), _) -> true
+ | _ -> false
+
+let is_typ_kopt = function
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_type, _)], _), _), _) -> true
+ | _ -> false
+
(**************************************************************************)
(* 1. Substitutions *)
(**************************************************************************)
@@ -481,6 +505,7 @@ end = struct
|| Id.compare id (mk_id "bool") = 0
|| Id.compare id (mk_id "real") = 0
|| Id.compare id (mk_id "list") = 0
+ || Id.compare id (mk_id "string") = 0
(* Check if a type, order, or n-expression is well-formed. Throws a
type error if the type is badly formed. FIXME: Add arity to type
@@ -554,8 +579,22 @@ end = struct
else
begin
typ_print ("Adding record " ^ string_of_id id);
+ let rec record_typ_args = function
+ | [] -> []
+ | ((QI_aux (QI_id kopt, _)) :: qis) when is_nat_kopt kopt ->
+ mk_typ_arg (Typ_arg_nexp (nvar (kopt_kid kopt))) :: record_typ_args qis
+ | ((QI_aux (QI_id kopt, _)) :: qis) when is_typ_kopt kopt ->
+ mk_typ_arg (Typ_arg_typ (mk_typ (Typ_var (kopt_kid kopt)))) :: record_typ_args qis
+ | ((QI_aux (QI_id kopt, _)) :: qis) when is_order_kopt kopt ->
+ mk_typ_arg (Typ_arg_order (mk_ord (Ord_var (kopt_kid kopt)))) :: record_typ_args qis
+ | (_ :: qis) -> record_typ_args qis
+ in
+ let rectyp = match record_typ_args (quant_items typq) with
+ | [] -> mk_id_typ id
+ | args -> mk_typ (Typ_app (id, args))
+ in
let fold_accessors accs (typ, fid) =
- let acc_typ = mk_typ (Typ_fn (mk_id_typ id, typ, Effect_aux (Effect_set [], Parse_ast.Unknown))) in
+ let acc_typ = mk_typ (Typ_fn (rectyp, typ, Effect_aux (Effect_set [], Parse_ast.Unknown))) in
typ_print (indent 1 ^ "Adding accessor " ^ string_of_id fid ^ " :: " ^ string_of_bind (typq, acc_typ));
Bindings.add fid (typq, acc_typ) accs
in
@@ -1223,20 +1262,20 @@ let subst_args_unifiers unifiers typ_args =
in
List.fold_left subst_unifier typ_args (KBindings.bindings unifiers)
+let merge_unifiers l kid uvar1 uvar2 =
+ match uvar1, uvar2 with
+ | Some (U_nexp n1), Some (U_nexp n2) ->
+ if nexp_identical n1 n2 then Some (U_nexp n1)
+ else unify_error l ("Multiple non-identical unifiers for " ^ string_of_kid kid
+ ^ ": " ^ string_of_nexp n1 ^ " and " ^ string_of_nexp n2)
+ | Some _, Some _ -> unify_error l "Multiple non-identical non-nexp unifiers"
+ | None, Some u2 -> Some u2
+ | Some u1, None -> Some u1
+ | None, None -> None
+
let unify l env typ1 typ2 =
typ_print ("Unify " ^ string_of_typ typ1 ^ " with " ^ string_of_typ typ2);
let goals = KidSet.inter (KidSet.diff (typ_frees typ1) (typ_frees typ2)) (typ_frees typ1) in
- let merge_unifiers l kid uvar1 uvar2 =
- match uvar1, uvar2 with
- | Some (U_nexp n1), Some (U_nexp n2) ->
- if nexp_identical n1 n2 then Some (U_nexp n1)
- else unify_error l ("Multiple non-identical unifiers for " ^ string_of_kid kid
- ^ ": " ^ string_of_nexp n1 ^ " and " ^ string_of_nexp n2)
- | Some _, Some _ -> unify_error l "Multiple non-identical non-nexp unifiers"
- | None, Some u2 -> Some u2
- | Some u1, None -> Some u1
- | None, None -> None
- in
let rec unify_typ l (Typ_aux (typ1_aux, _) as typ1) (Typ_aux (typ2_aux, _) as typ2) =
typ_debug ("UNIFYING TYPES " ^ string_of_typ typ1 ^ " AND " ^ string_of_typ typ2);
match typ1_aux, typ2_aux with
@@ -1300,6 +1339,11 @@ let unify l env typ1 typ2 =
let typ1, typ2 = Env.expand_synonyms env typ1, Env.expand_synonyms env typ2 in
unify_typ l typ1 typ2
+let merge_uvars l unifiers1 unifiers2 =
+ try KBindings.merge (merge_unifiers l) unifiers1 unifiers2
+ with
+ | Unification_error (_, m) -> typ_error l ("Could not merge unification variables: " ^ m)
+
(**************************************************************************)
(* 5. Type checking expressions *)
(**************************************************************************)
@@ -1342,10 +1386,6 @@ let infer_lit env (L_aux (lit_aux, l) as lit) =
end
| L_undef -> typ_error l "Cannot infer the type of undefined"
-let quant_items : typquant -> quant_item list = function
- | TypQ_aux (TypQ_tq qis, _) -> qis
- | TypQ_aux (TypQ_no_forall, _) -> []
-
let is_nat_kid kid = function
| KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_nat, _)], _), kid'), _) -> Kid.compare kid kid' = 0
| KOpt_aux (KOpt_none kid', _) -> Kid.compare kid kid' = 0
@@ -1409,8 +1449,14 @@ let typ_of_annot (l, tannot) = match tannot with
| Some (_, typ, _) -> typ
| None -> raise (Reporting_basic.err_unreachable l "no type annotation")
+let env_of_annot (l, tannot) = match tannot with
+ | Some (env, _, _) -> env
+ | None -> raise (Reporting_basic.err_unreachable l "no type annotation")
+
let typ_of (E_aux (_, (l, tannot))) = typ_of_annot (l, tannot)
+let env_of (E_aux (_, (l, tannot))) = env_of_annot (l, tannot)
+
let pat_typ_of (P_aux (_, (l, tannot))) = typ_of_annot (l, tannot)
(* Flow typing *)
@@ -1658,7 +1704,7 @@ let rec check_exp env (E_aux (exp_aux, (l, ())) as exp : unit exp) (Typ_aux (typ
begin
match Env.lookup_id id env with
| Union (typq, ctor_typ) ->
- let inferred_exp = infer_funapp' l env id (typq, mk_typ (Typ_fn (unit_typ, ctor_typ, no_effect))) [mk_lit L_unit] (Some typ) in
+ let inferred_exp = fst (infer_funapp' l env id (typq, mk_typ (Typ_fn (unit_typ, ctor_typ, no_effect))) [mk_lit L_unit] (Some typ)) in
annot_exp (E_id id) (typ_of inferred_exp)
| _ -> assert false (* Unreachble due to guard *)
end
@@ -1892,6 +1938,7 @@ and bind_assignment env (LEXP_aux (lexp_aux, _) as lexp) (E_aux (_, (l, ())) as
end
in
let regtyp, inferred_flexp = infer_flexp flexp in
+ typ_debug ("REGTYP: " ^ string_of_typ regtyp ^ " / " ^ string_of_typ (Env.expand_synonyms env regtyp));
match Env.expand_synonyms env regtyp with
| Typ_aux (Typ_id regtyp_id, _) when Env.is_regtyp regtyp_id env ->
let base, top, ranges = Env.get_regtyp regtyp_id env in
@@ -1908,6 +1955,12 @@ and bind_assignment env (LEXP_aux (lexp_aux, _) as lexp) (E_aux (_, (l, ())) as
in
let checked_exp = crule check_exp env exp vec_typ in
annot_assign (annot_lexp (LEXP_field (annot_lexp_effect inferred_flexp regtyp (mk_effect [BE_wreg]), field)) vec_typ) checked_exp, env
+ | Typ_aux (Typ_id rectyp_id, _) | Typ_aux (Typ_app (rectyp_id, _), _) when Env.is_record rectyp_id env ->
+ let (typq, Typ_aux (Typ_fn (rectyp_q, field_typ, _), _)) = Env.get_accessor field env in
+ let unifiers = try unify l env rectyp_q regtyp with Unification_error (l, m) -> typ_error l ("Unification error: " ^ m) in
+ let field_typ' = subst_unifiers unifiers field_typ in
+ let checked_exp = crule check_exp env exp field_typ' in
+ annot_assign (annot_lexp (LEXP_field (annot_lexp_effect inferred_flexp regtyp (mk_effect [BE_wreg]), field)) field_typ') checked_exp, env
| _ -> typ_error l "Field l-expression has invalid type"
end
| LEXP_memory (f, xs) ->
@@ -1981,11 +2034,12 @@ and bind_lexp env (LEXP_aux (lexp_aux, (l, ())) as lexp) typ =
end
| LEXP_vector_range (LEXP_aux (LEXP_id v, _), exp1, exp2) ->
begin
- let is_immutable, vtyp = match Env.lookup_id v env with
+ let is_immutable, is_register, vtyp = match Env.lookup_id v env with
| Unbound -> typ_error l "Cannot assign to element of unbound vector"
| Enum _ -> typ_error l "Cannot vector assign to enumeration element"
- | Local (Immutable, vtyp) -> true, vtyp
- | Local (Mutable, vtyp) | Register vtyp -> false, vtyp
+ | Local (Immutable, vtyp) -> true, false, vtyp
+ | Local (Mutable, vtyp) -> false, false, vtyp
+ | Register vtyp -> false, true, vtyp
in
let access = infer_exp (Env.enable_casts env) (E_aux (E_app (mk_id "vector_subrange", [E_aux (E_id v, (l, ())); exp1; exp2]), (l, ()))) in
let E_aux (E_app (_, [_; inferred_exp1; inferred_exp2]), _) = access in
@@ -1993,6 +2047,9 @@ and bind_lexp env (LEXP_aux (lexp_aux, (l, ())) as lexp) typ =
| Typ_aux (Typ_app (id, [Typ_arg_aux (Typ_arg_typ deref_typ, _)]), _) when string_of_id id = "register" ->
subtyp l env typ deref_typ;
annot_lexp (LEXP_vector_range (annot_lexp_effect (LEXP_id v) vtyp (mk_effect [BE_wreg]), inferred_exp1, inferred_exp2)) typ, env
+ | _ when not is_immutable && is_register ->
+ subtyp l env typ (typ_of access);
+ annot_lexp (LEXP_vector_range (annot_lexp_effect (LEXP_id v) vtyp (mk_effect [BE_wreg]), inferred_exp1, inferred_exp2)) typ, env
| _ when not is_immutable ->
subtyp l env typ (typ_of access);
annot_lexp (LEXP_vector_range (annot_lexp (LEXP_id v) vtyp, inferred_exp1, inferred_exp2)) typ, env
@@ -2077,7 +2134,7 @@ and infer_exp env (E_aux (exp_aux, (l, ())) as exp) =
(* Accessing a field of a record *)
| Typ_aux (Typ_id rectyp, _) as typ when Env.is_record rectyp env ->
begin
- let inferred_acc = infer_funapp' l (Env.no_casts env) field (Env.get_accessor field env) [strip_exp inferred_exp] None in
+ let inferred_acc, _ = infer_funapp' l (Env.no_casts env) field (Env.get_accessor field env) [strip_exp inferred_exp] None in
match inferred_acc with
| E_aux (E_app (field, [inferred_exp]) ,_) -> annot_exp (E_field (inferred_exp, field)) (typ_of inferred_acc)
| _ -> assert false (* Unreachable *)
@@ -2155,10 +2212,17 @@ and infer_exp env (E_aux (exp_aux, (l, ())) as exp) =
annot_exp (E_assert (checked_test, checked_msg)) unit_typ
| _ -> typ_error l ("Cannot infer type of: " ^ string_of_exp exp)
-and infer_funapp l env f xs ret_ctx_typ = infer_funapp' l env f (Env.get_val_spec f env) xs ret_ctx_typ
+and infer_funapp l env f xs ret_ctx_typ = fst (infer_funapp' l env f (Env.get_val_spec f env) xs ret_ctx_typ)
+
+and instantiation_of (E_aux (exp_aux, (l, _)) as exp) =
+ let env = env_of exp in
+ match exp_aux with
+ | E_app (f, xs) -> snd (infer_funapp' l (Env.no_casts env) f (Env.get_val_spec f env) (List.map strip_exp xs) (Some (typ_of exp)))
+ | _ -> invalid_arg ("instantiation_of expected application, got " ^ string_of_exp exp)
and infer_funapp' l env f (typq, f_typ) xs ret_ctx_typ =
let annot_exp exp typ eff = E_aux (exp, (l, Some (env, typ, eff))) in
+ let all_unifiers = ref KBindings.empty in
let rec number n = function
| [] -> []
| (x :: xs) -> (n, x) :: number (n + 1) xs
@@ -2193,6 +2257,7 @@ and infer_funapp' l env f (typq, f_typ) xs ret_ctx_typ =
try
let iarg, unifiers = type_coercion_unify env iarg typ in
typ_debug (string_of_list ", " (fun (kid, uvar) -> string_of_kid kid ^ " => " ^ string_of_uvar uvar) (KBindings.bindings unifiers));
+ all_unifiers := merge_uvars l !all_unifiers unifiers;
let utyps' = List.map (subst_unifiers unifiers) utyps in
let typs' = List.map (subst_unifiers unifiers) typs in
let quants' = List.fold_left (fun qs (kid, uvar) -> instantiate_quants qs kid uvar) quants (KBindings.bindings unifiers) in
@@ -2216,6 +2281,7 @@ and infer_funapp' l env f (typq, f_typ) xs ret_ctx_typ =
typ_debug ("INSTANTIATE RETURN:" ^ string_of_typ ret_typ);
let unifiers = try unify l env ret_typ rct with Unification_error _ -> typ_debug "UERROR"; KBindings.empty in
typ_debug (string_of_list ", " (fun (kid, uvar) -> string_of_kid kid ^ " => " ^ string_of_uvar uvar) (KBindings.bindings unifiers));
+ all_unifiers := merge_uvars l !all_unifiers unifiers;
let typs' = List.map (subst_unifiers unifiers) typs in
let quants' = List.fold_left (fun qs (kid, uvar) -> instantiate_quants qs kid uvar) quants (KBindings.bindings unifiers) in
let ret_typ' = subst_unifiers unifiers ret_typ in
@@ -2237,8 +2303,8 @@ and infer_funapp' l env f (typq, f_typ) xs ret_ctx_typ =
| _ -> typ_error l (string_of_typ f_typ ^ " is not a function type")
in
match ret_ctx_typ with
- | None -> exp
- | Some rct -> type_coercion env exp rct
+ | None -> exp, !all_unifiers
+ | Some rct -> type_coercion env exp rct, !all_unifiers
(**************************************************************************)
(* 6. Effect system *)
@@ -2544,7 +2610,7 @@ let check_fundef env (FD_aux (FD_function (recopt, tannotopt, effectopt, funcls)
[mk_val_spec quant typ id], Env.add_val_spec id (quant, typ) env, eff
else [], env, declared_eff
in
- if equal_effects eff declared_eff
+ if (equal_effects eff declared_eff || !opt_no_effects)
then
vs_def @ [DEF_fundef (FD_aux (FD_function (recopt, tannotopt, effectopt, funcls), (l, None)))], env
else typ_error l ("Effects do not match: " ^ string_of_effect declared_eff ^ " declared and " ^ string_of_effect eff ^ " found")
diff --git a/src/type_check.mli b/src/type_check.mli
index b956e3aa..92465cd5 100644
--- a/src/type_check.mli
+++ b/src/type_check.mli
@@ -45,6 +45,7 @@ open Ast
open Ast_util
val opt_tc_debug : int ref
+val opt_no_effects : bool ref
exception Type_error of l * string;;
@@ -158,6 +159,7 @@ val unit_typ : typ
val string_typ : typ
val real_typ : typ
val vector_typ : nexp -> nexp -> order -> typ -> typ
+val list_typ : typ -> typ
val inc_ord : order
val dec_ord : order
@@ -192,11 +194,22 @@ val env_of_annot : Ast.l * tannot -> Env.t
val typ_of : tannot exp -> typ
val typ_of_annot : Ast.l * tannot -> typ
+val env_of : tannot exp -> Env.t
+
val pat_typ_of : tannot pat -> typ
val effect_of : tannot exp -> effect
val effect_of_annot : tannot -> effect
+type uvar =
+ | U_nexp of nexp
+ | U_order of order
+ | U_effect of effect
+ | U_typ of typ
+
+(* Throws Invalid_argument if the argument is not a E_app expression *)
+val instantiation_of : tannot exp -> uvar KBindings.t
+
val propagate_exp_effect : tannot exp -> tannot exp
(* Fully type-check an AST