summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast_util.ml9
-rw-r--r--src/constraint.ml35
-rw-r--r--src/constraint.mli4
-rw-r--r--src/process_file.ml4
-rw-r--r--src/sail.ml5
-rw-r--r--src/type_check.ml129
-rw-r--r--src/type_check.mli5
7 files changed, 128 insertions, 63 deletions
diff --git a/src/ast_util.ml b/src/ast_util.ml
index fcfa619e..e4287249 100644
--- a/src/ast_util.ml
+++ b/src/ast_util.ml
@@ -417,11 +417,16 @@ let nc_lteq n1 n2 = NC_aux (NC_bounded_le (n1, n2), Parse_ast.Unknown)
let nc_gteq n1 n2 = NC_aux (NC_bounded_ge (n1, n2), Parse_ast.Unknown)
let nc_lt n1 n2 = nc_lteq (nsum n1 (nint 1)) n2
let nc_gt n1 n2 = nc_gteq n1 (nsum n2 (nint 1))
-let nc_or nc1 nc2 = mk_nc (NC_or (nc1, nc2))
let nc_var kid = mk_nc (NC_var kid)
let nc_true = mk_nc NC_true
let nc_false = mk_nc NC_false
+let nc_or nc1 nc2 =
+ match nc1, nc2 with
+ | _, NC_aux (NC_false, _) -> nc1
+ | NC_aux (NC_false, _), _ -> nc2
+ | _, _ -> mk_nc (NC_or (nc1, nc2))
+
let nc_and nc1 nc2 =
match nc1, nc2 with
| _, NC_aux (NC_true, _) -> nc1
@@ -439,7 +444,7 @@ let arg_kopt (KOpt_aux (KOpt_kind (K_aux (k, _), v), l)) =
| K_order -> arg_order (Ord_aux (Ord_var v, l))
| K_bool -> arg_bool (nc_var v)
| K_type -> arg_typ (mk_typ (Typ_var v))
-
+
let nc_not nc = mk_nc (NC_app (mk_id "not", [arg_bool nc]))
let mk_typschm typq typ = TypSchm_aux (TypSchm_ts (typq, typ), Parse_ast.Unknown)
diff --git a/src/constraint.ml b/src/constraint.ml
index 2c5150a5..5402f6f7 100644
--- a/src/constraint.ml
+++ b/src/constraint.ml
@@ -321,12 +321,13 @@ let call_smt l vars constraints =
Profile.finish_smt t;
result
-let rec solve_unique_smt l vars constraints var =
+let solve_smt l vars constraints var =
let smt_file, smt_var = smtlib_of_constraints ~get_model:true l vars constraints in
let smt_var = pp_sexpr (smt_var var) in
- (* prerr_endline (Printf.sprintf "SMTLIB2 constraints are: \n%s%!" smt_file);
- prerr_endline ("Solving for " ^ smt_var); *)
+ if !opt_smt_verbose then
+ prerr_endline (Printf.sprintf "SMTLIB2 constraints are (solve for %s): \n%s%!" smt_var smt_file)
+ else ();
let rec input_all chan =
try
@@ -355,9 +356,27 @@ let rec solve_unique_smt l vars constraints var =
try
let _ = Str.search_forward (Str.regexp regexp) smt_output 0 in
let result = Big_int.of_string (Str.matched_group 1 smt_output) in
- begin match call_smt l vars (nc_and constraints (nc_neq (nconstant result) (nvar var))) with
- | Unsat -> Some result
- | _ -> None
- end
+ Some result
with
- Not_found -> None
+ | Not_found -> None
+
+let solve_all_smt l vars constraints var =
+ let rec aux results =
+ let constraints = List.fold_left (fun ncs r -> (nc_and ncs (nc_neq (nconstant r) (nvar var)))) constraints results in
+ match solve_smt l vars constraints var with
+ | Some result -> aux (result :: results)
+ | None ->
+ match call_smt l vars constraints with
+ | Unsat -> Some results
+ | _ -> None
+ in
+ aux []
+
+let solve_unique_smt l vars constraints var =
+ match solve_smt l vars constraints var with
+ | Some result ->
+ begin match call_smt l vars (nc_and constraints (nc_neq (nconstant result) (nvar var))) with
+ | Unsat -> Some result
+ | _ -> None
+ end
+ | None -> None
diff --git a/src/constraint.mli b/src/constraint.mli
index fb6e0fcd..b5d6ff6b 100644
--- a/src/constraint.mli
+++ b/src/constraint.mli
@@ -63,4 +63,8 @@ val save_digests : unit -> unit
val call_smt : l -> kind_aux KBindings.t -> n_constraint -> smt_result
+val solve_smt : l -> kind_aux KBindings.t -> n_constraint -> kid -> Big_int.num option
+
+val solve_all_smt : l -> kind_aux KBindings.t -> n_constraint -> kid -> Big_int.num list option
+
val solve_unique_smt : l -> kind_aux KBindings.t -> n_constraint -> kid -> Big_int.num option
diff --git a/src/process_file.ml b/src/process_file.ml
index 52e0cd08..e7bf8d30 100644
--- a/src/process_file.ml
+++ b/src/process_file.ml
@@ -159,7 +159,7 @@ let rec preprocess opts = function
symbols := StringSet.add symbol !symbols;
preprocess opts defs
- | Parse_ast.DEF_pragma ("option", command, l) :: defs ->
+ | (Parse_ast.DEF_pragma ("option", command, l) as opt_pragma) :: defs ->
begin
try
let args = Str.split (Str.regexp " +") command in
@@ -167,7 +167,7 @@ let rec preprocess opts = function
with
| Arg.Bad message | Arg.Help message -> raise (Reporting.err_general l message)
end;
- preprocess opts defs
+ opt_pragma :: preprocess opts defs
| Parse_ast.DEF_pragma ("ifndef", symbol, l) :: defs ->
let then_defs, else_defs, defs = cond_pragma l defs in
diff --git a/src/sail.ml b/src/sail.ml
index 9cf87af8..64ccd341 100644
--- a/src/sail.ml
+++ b/src/sail.ml
@@ -115,8 +115,11 @@ let options = Arg.align ([
( "-smt_solver",
Arg.String (fun s -> Constraint.set_solver (String.trim s)),
"<solver> choose SMT solver. Supported solvers are z3 (default), alt-ergo, cvc4, mathsat, vampire and yices.");
+ ( "-smt_linearize",
+ Arg.Set Type_check.opt_smt_linearize,
+ "(experimental) force linearization for constraints involving exponentials");
( "-latex",
- Arg.Tuple [Arg.Set opt_print_latex; Arg.Clear Type_check.opt_expand_valspec ],
+ Arg.Tuple [Arg.Set opt_print_latex; Arg.Clear Type_check.opt_expand_valspec],
" pretty print the input to LaTeX");
( "-latex_prefix",
Arg.String (fun prefix -> Latex.opt_prefix := prefix),
diff --git a/src/type_check.ml b/src/type_check.ml
index ada04c24..0da7f753 100644
--- a/src/type_check.ml
+++ b/src/type_check.ml
@@ -72,6 +72,10 @@ let opt_no_lexp_bounds_check = ref false
We prefer not to do it for latex output but it is otherwise a good idea. *)
let opt_expand_valspec = ref true
+(* Linearize cases involving power where we would otherwise require
+ the SMT solver to use non-linear arithmetic. *)
+let opt_smt_linearize = ref false
+
let depth = ref 0
let rec indent n = match n with
@@ -247,6 +251,50 @@ and strip_kinded_id_aux = function
and strip_kind = function
| K_aux (k_aux, _) -> K_aux (k_aux, Parse_ast.Unknown)
+let rec typ_nexps (Typ_aux (typ_aux, l)) =
+ match typ_aux with
+ | Typ_internal_unknown -> []
+ | Typ_id v -> []
+ | Typ_var kid -> []
+ | Typ_tup typs -> List.concat (List.map typ_nexps typs)
+ | Typ_app (f, args) -> List.concat (List.map typ_arg_nexps args)
+ | Typ_exist (kids, nc, typ) -> typ_nexps typ
+ | Typ_fn (arg_typs, ret_typ, _) ->
+ List.concat (List.map typ_nexps arg_typs) @ typ_nexps ret_typ
+ | Typ_bidir (typ1, typ2) ->
+ typ_nexps typ1 @ typ_nexps typ2
+and typ_arg_nexps (A_aux (typ_arg_aux, l)) =
+ match typ_arg_aux with
+ | A_nexp n -> [n]
+ | A_typ typ -> typ_nexps typ
+ | A_bool nc -> constraint_nexps nc
+ | A_order ord -> []
+and constraint_nexps (NC_aux (nc_aux, l)) =
+ match nc_aux with
+ | NC_equal (n1, n2) | NC_bounded_ge (n1, n2) | NC_bounded_le (n1, n2) | NC_not_equal (n1, n2) ->
+ [n1; n2]
+ | NC_set _ | NC_true | NC_false | NC_var _ -> []
+ | NC_or (nc1, nc2) | NC_and (nc1, nc2) -> constraint_nexps nc1 @ constraint_nexps nc2
+ | NC_app (_, args) -> List.concat (List.map typ_arg_nexps args)
+
+(* Return a KidSet containing all the type variables appearing in
+ nexp, where nexp occurs underneath a Nexp_exp, i.e. 2^nexp *)
+let rec nexp_power_variables (Nexp_aux (aux, _)) =
+ match aux with
+ | Nexp_times (n1, n2) | Nexp_sum (n1, n2) | Nexp_minus (n1, n2) ->
+ KidSet.union (nexp_power_variables n1) (nexp_power_variables n2)
+ | Nexp_neg n ->
+ nexp_power_variables n
+ | Nexp_id _ | Nexp_var _ | Nexp_constant _ ->
+ KidSet.empty
+ | Nexp_app (_, ns) ->
+ List.fold_left KidSet.union KidSet.empty (List.map nexp_power_variables ns)
+ | Nexp_exp n ->
+ tyvars_of_nexp n
+
+let constraint_power_variables nc =
+ List.fold_left KidSet.union KidSet.empty (List.map nexp_power_variables (constraint_nexps nc))
+
let rec name_pat (P_aux (aux, _)) =
match aux with
| P_id id | P_as (_, id) -> Some ("_" ^ string_of_id id)
@@ -262,7 +310,7 @@ let fresh_existential k =
let named_existential k = function
| Some n -> mk_kopt k (mk_kid n)
| None -> fresh_existential k
-
+
let destruct_exist_plain ?name:(name=None) typ =
match typ with
| Typ_aux (Typ_exist ([kopt], nc, typ), _) ->
@@ -1089,7 +1137,7 @@ end = struct
let add_typ_var l (KOpt_aux (KOpt_kind (K_aux (k, _), v), _)) env =
if KBindings.mem v env.typ_vars then begin
let n = match KBindings.find_opt v env.shadow_vars with Some n -> n | None -> 0 in
- let s_l, s_k = KBindings.find v env.typ_vars in
+ let s_l, s_k = KBindings.find v env.typ_vars in
let s_v = Kid_aux (Var (string_of_kid v ^ "#" ^ string_of_int n), l) in
typ_print (lazy (Printf.sprintf "%stype variable (shadowing %s) %s : %s" adding (string_of_kid s_v) (string_of_kid v) (string_of_kind_aux k)));
{ env with
@@ -1108,11 +1156,34 @@ end = struct
let add_constraint constr env =
wf_constraint env constr;
let (NC_aux (nc_aux, l) as constr) = constraint_simp (expand_constraint_synonyms env constr) in
- match nc_aux with
- | NC_true -> env
- | _ ->
- typ_print (lazy (adding ^ "constraint " ^ string_of_n_constraint constr));
- { env with constraints = constr :: env.constraints }
+ let power_vars = constraint_power_variables constr in
+ if KidSet.cardinal power_vars > 1 && !opt_smt_linearize then
+ typ_error env l ("Cannot add constraint " ^ string_of_n_constraint constr
+ ^ " where more than two variables appear within an exponential")
+ else if KidSet.cardinal power_vars = 1 && !opt_smt_linearize then
+ let v = KidSet.choose power_vars in
+ let constrs = List.fold_left nc_and nc_true (get_constraints env) in
+ begin match Constraint.solve_all_smt l (get_typ_vars env) constrs v with
+ | Some solutions ->
+ typ_print (lazy (Util.("Linearizing " |> red |> clear) ^ string_of_n_constraint constr
+ ^ " for " ^ string_of_kid v ^ " in " ^ Util.string_of_list ", " Big_int.to_string solutions));
+ let linearized =
+ List.fold_left
+ (fun c s -> nc_or c (nc_and (nc_eq (nvar v) (nconstant s)) (constraint_subst v (arg_nexp (nconstant s)) constr)))
+ nc_false solutions
+ in
+ typ_print (lazy (adding ^ "constraint " ^ string_of_n_constraint linearized));
+ { env with constraints = linearized :: env.constraints }
+ | None ->
+ typ_error env l ("Type variable " ^ string_of_kid v
+ ^ " must have a finite number of solutions to add " ^ string_of_n_constraint constr)
+ end
+ else
+ match nc_aux with
+ | NC_true -> env
+ | _ ->
+ typ_print (lazy (adding ^ "constraint " ^ string_of_n_constraint constr));
+ { env with constraints = constr :: env.constraints }
let get_ret_typ env = env.ret_typ
@@ -1326,32 +1397,6 @@ and simp_typ_aux = function
| typ_aux -> typ_aux
-let rec typ_nexps (Typ_aux (typ_aux, l)) =
- match typ_aux with
- | Typ_internal_unknown -> []
- | Typ_id v -> []
- | Typ_var kid -> []
- | Typ_tup typs -> List.concat (List.map typ_nexps typs)
- | Typ_app (f, args) -> List.concat (List.map typ_arg_nexps args)
- | Typ_exist (kids, nc, typ) -> typ_nexps typ
- | Typ_fn (arg_typs, ret_typ, _) ->
- List.concat (List.map typ_nexps arg_typs) @ typ_nexps ret_typ
- | Typ_bidir (typ1, typ2) ->
- typ_nexps typ1 @ typ_nexps typ2
-and typ_arg_nexps (A_aux (typ_arg_aux, l)) =
- match typ_arg_aux with
- | A_nexp n -> [n]
- | A_typ typ -> typ_nexps typ
- | A_bool nc -> constraint_nexps nc
- | A_order ord -> []
-and constraint_nexps (NC_aux (nc_aux, l)) =
- match nc_aux with
- | NC_equal (n1, n2) | NC_bounded_ge (n1, n2) | NC_bounded_le (n1, n2) | NC_not_equal (n1, n2) ->
- [n1; n2]
- | NC_set _ | NC_true | NC_false | NC_var _ -> []
- | NC_or (nc1, nc2) | NC_and (nc1, nc2) -> constraint_nexps nc1 @ constraint_nexps nc2
- | NC_app (_, args) -> List.concat (List.map typ_arg_nexps args)
-
(* Here's how the constraint generation works for subtyping
X(b,c...) --> {a. Y(a,b,c...)} \subseteq {a. Z(a,b,c...)}
@@ -1373,22 +1418,6 @@ this is equivalent to
which is then a problem we can feed to the constraint solver expecting unsat.
*)
-let rec nexp_variable_power (Nexp_aux (aux, _)) =
- match aux with
- | Nexp_times (n1, n2) | Nexp_sum (n1, n2) | Nexp_minus (n1, n2) ->
- nexp_variable_power n1 || nexp_variable_power n2
- | Nexp_neg n ->
- nexp_variable_power n
- | Nexp_id _ | Nexp_var _ | Nexp_constant _ ->
- false
- | Nexp_app (_, ns) ->
- List.exists nexp_variable_power ns
- | Nexp_exp n ->
- not (KidSet.is_empty (tyvars_of_nexp n))
-
-let constraint_variable_power nc =
- List.exists nexp_variable_power (constraint_nexps nc)
-
let prove_smt env (NC_aux (_, l) as nc) =
let vars = Env.get_typ_vars env in
let vars = KBindings.filter (fun _ k -> match k with K_int | K_bool -> true | _ -> false) vars in
@@ -1400,7 +1429,7 @@ let prove_smt env (NC_aux (_, l) as nc) =
(* Work around versions of z3 that are confused by 2^n in
constraints, even when such constraints are irrelevant *)
let ncs' = List.concat (List.map constraint_conj ncs) in
- let ncs' = List.filter (fun nc -> not (constraint_variable_power nc)) ncs' in
+ let ncs' = List.filter (fun nc -> KidSet.is_empty (constraint_power_variables nc)) ncs' in
match Constraint.call_smt l vars (List.fold_left nc_and (nc_not nc) ncs') with
| Constraint.Unsat -> typ_debug (lazy "unsat"); true
| Constraint.Sat | Constraint.Unknown -> typ_debug (lazy "sat/unknown"); false
diff --git a/src/type_check.mli b/src/type_check.mli
index 00439412..cfdfa2c6 100644
--- a/src/type_check.mli
+++ b/src/type_check.mli
@@ -71,6 +71,10 @@ val opt_no_lexp_bounds_check : bool ref
We prefer not to do it for latex output but it is otherwise a good idea. *)
val opt_expand_valspec : bool ref
+(** Linearize cases involving power where we would otherwise require
+ the SMT solver to use non-linear arithmetic. *)
+val opt_smt_linearize : bool ref
+
(** {2 Type errors} *)
type type_error =
@@ -316,6 +320,7 @@ val check_val_spec : Env.t -> 'a val_spec -> tannot def list * Env.t
untrustworthy. *)
val prove : (string * int * int * int) -> Env.t -> n_constraint -> bool
+(** Returns Some c if there is a unique c such that nexp = c *)
val solve_unique : Env.t -> nexp -> Big_int.num option
val canonicalize : Env.t -> typ -> typ