Skip to content

Commit 35bfb2a

Browse files
committed
SMT: do not raise an exception on inner let recs, just encode them imprecisely
Fixes #3991
1 parent 684483b commit 35bfb2a

File tree

7 files changed

+39
-57
lines changed

7 files changed

+39
-57
lines changed

src/smtencoding/FStarC.SMTEncoding.Encode.fst

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ let encode_top_level_let :
849849
let toks_fvbs = List.rev toks in
850850
let decls = List.rev decls |> List.flatten in
851851
(*
852+
* GM: Not accurate any more.
852853
* AR: decls are the declarations for the top-level lets
853854
* if one of the let body contains a let rec (inner let rec), we simply return decls at that point, inner let recs are not encoded to the solver yet (see Inner_let_rec below)
854855
* the way it is implemented currently is that, the call to encode the let body throws an exception Inner_let_rec which is caught below in this function
@@ -1154,30 +1155,12 @@ let encode_top_level_let :
11541155
is_smt_reifiable_function env.tcenv t))
11551156
then decls, env_decls
11561157
else
1157-
try
1158-
if not is_rec
1159-
then
1160-
(* Encoding non-recursive definitions *)
1161-
encode_non_rec_lbdef bindings typs toks_fvbs env
1162-
else
1163-
encode_rec_lbdefs bindings typs toks_fvbs env
1164-
with
1165-
| Inner_let_rec names ->
1166-
let plural = List.length names > 1 in
1167-
let r = List.hd names |> snd in
1168-
FStarC.TypeChecker.Err.add_errors
1169-
env.tcenv
1170-
[(Errors.Warning_DefinitionNotTranslated,
1171-
// FIXME
1172-
[Errors.text <| Format.fmt3
1173-
"Definitions of inner let-rec%s %s and %s enclosing top-level letbinding are not encoded to the solver, you will only be able to reason with their types"
1174-
(if plural then "s" else "")
1175-
(List.map fst names |> String.concat ",")
1176-
(if plural then "their" else "its")],
1177-
r,
1178-
Errors.get_ctx () // TODO: fix this, leaking abstraction
1179-
)];
1180-
decls, env_decls //decls are type declarations for the lets, if there is an inner let rec, only those are encoded to the solver
1158+
if not is_rec
1159+
then
1160+
(* Encoding non-recursive definitions *)
1161+
encode_non_rec_lbdef bindings typs toks_fvbs env
1162+
else
1163+
encode_rec_lbdefs bindings typs toks_fvbs env
11811164

11821165
with Let_rec_unencodeable ->
11831166
let msg = bindings |> List.map (fun lb -> show lb.lbname) |> String.concat " and " in

src/smtencoding/FStarC.SMTEncoding.EncodeTerm.fst

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,21 +1465,26 @@ and encode_term (t:typ) (env:env_t) : (term (* encoding of t, expects t
14651465
f, decls@decls'@decls''@(mk_decls fsym tkey_hash f_decls (decls@decls'@decls''))
14661466
end
14671467
1468+
// Inr means a top-level name
14681469
| Tm_let {lbs=(_, {lbname=Inr _}::_)} ->
14691470
failwith "Impossible: already handled by encoding of Sig_let"
14701471
1472+
// A single non-recursive local let
14711473
| Tm_let {lbs=(false, [{lbname=Inl x; lbtyp=t1; lbdef=e1}]); body=e2} ->
14721474
encode_let x t1 e1 e2 env encode_term
14731475
14741476
| Tm_let {lbs=(false, _::_)} ->
14751477
failwith "Impossible: non-recursive let with multiple bindings"
14761478
1477-
| Tm_let {lbs=(_, lbs)} ->
1478-
let names = lbs |> List.map (fun lb ->
1479-
let {lbname = lbname} = lb in
1480-
let x = Inl?.v lbname in (* has to be Inl *)
1481-
(Ident.string_of_id x.ppname, S.range_of_bv x)) in
1482-
raise (Inner_let_rec names)
1479+
// A recursive local let. We encode this imprecisely, just generating a
1480+
// fresh variable.
1481+
| Tm_let {lbs=(true, lbs)} ->
1482+
let f = varops.fresh env.current_module_name "inner_let_rec" in
1483+
let decl = Term.DeclFun (f, [], Term_sort, Some "Inner let rec") in
1484+
mkFreeV <| mk_fv (f, Term_sort), [decl] |> mk_decls_trivial
1485+
1486+
| Tm_let _ ->
1487+
failwith "Impossible: all cases handled above (encode_term)."
14831488
14841489
| Tm_match {scrutinee=e; brs=pats} ->
14851490
encode_match e pats mk_Term_unit env encode_term

src/smtencoding/FStarC.SMTEncoding.Env.fst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ open FStarC.Class.Show
3333

3434
let dbg_PartialApp = Debug.get_toggle "PartialApp"
3535

36-
exception Inner_let_rec of list (string & Range.t) //name of the inner let-rec(s) and their locations
37-
3836
let add_fuel x tl = if (Options.unthrottle_inductives()) then tl else x::tl
3937
let withenv c (a, b) = (a,b,c)
4038
let vargs args = List.filter (function (Inl _, _) -> false | _ -> true) args

src/smtencoding/FStarC.SMTEncoding.Solver.fst

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,33 +1424,17 @@ let encode_and_ask (can_split:bool) (is_retry:bool) use_env_msg tcenv q : (list
14241424
14251425
(* Asks the solver and reports errors. Does quake if needed. *)
14261426
let do_solve (can_split:bool) (is_retry:bool) use_env_msg tcenv q : unit =
1427-
let ans_opt =
1428-
try Some (encode_and_ask can_split is_retry use_env_msg tcenv q) with
1429-
(* Each (potentially splitted) query can fail with this error, raise by encode_query.
1430-
* Note, even though this is a log_issue, the error cannot be turned into a warning
1431-
* nor ignored. *)
1432-
| FStarC.SMTEncoding.Env.Inner_let_rec names ->
1433-
FStarC.TypeChecker.Err.log_issue
1434-
tcenv tcenv.range
1435-
(Errors.Error_NonTopRecFunctionNotFullyEncoded, [
1436-
Errors.text <|
1437-
Format.fmt1
1438-
"Could not encode the query since F* does not support precise smtencoding of inner let-recs yet (in this case %s)"
1439-
(String.concat "," (List.map fst names))]);
1440-
None
1441-
in
1427+
let ans_opt = encode_and_ask can_split is_retry use_env_msg tcenv q in
14421428
match ans_opt with
1443-
| Some (default_settings::_, ans) when not ans.ok ->
1429+
| default_settings::_, ans when not ans.ok ->
14441430
report tcenv default_settings ans
14451431
1446-
| Some (_, ans) when ans.ok ->
1432+
| _, ans when ans.ok ->
14471433
() (* trivial or succeeded *)
14481434
1449-
| Some ([], ans) when not ans.ok ->
1435+
| [], ans when not ans.ok ->
14501436
failwith "impossible: bad answer from encode_and_ask"
14511437
1452-
| None -> () (* already logged an error *)
1453-
14541438
let split_and_solve (retrying:bool) use_env_msg tcenv q : unit =
14551439
if retrying && (Debug.any () || Options.query_stats ()) then begin
14561440
Format.print1 "(%s)\tQuery-stats splitting query because retrying failed query\n"

tests/bug-reports/closed/Bug1622.fst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ let pred (args: list bool) : sprop =
99
| a::q -> let out:sprop = fun s0 -> out s0 in aux q out
1010
in aux args (fun _ -> True)
1111

12-
[@@(expect_failure [142])] //could not encode the query since an inner let-rec aux appears in it
1312
let lemma_pred (args:list bool) : Lemma (pred args true) =
1413
match args with
1514
| [] -> assert_norm (pred args true)
1615
| _ -> admit()
16+
17+
let lemma_pred' (args:list bool) : Lemma (pred args true) =
18+
match args with
19+
| [] -> assert_norm (pred [] true)
20+
| _ -> admit()
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
module Bug2876
22

3-
// Fails since we cannot currently encode inner let-recs.
4-
// The original issue was that this crashed, though, so we are indeed
5-
// testing that we get a proper error.
6-
[@@expect_failure [142]]
3+
// Fails since we encode inner let-recs imprecisely.
4+
[@@expect_failure [19]]
75
let test () =
86
assert ((let rec f (x:nat) : Dv nat = f x in f) == (let rec f (x:nat) : Dv nat = f x in f))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module Bug3991
2+
3+
noeq
4+
type mytc (#a:Type0) (s:a) = | X
5+
6+
let mytc_let_rec : mytc (let rec f x = x in ()) = X
7+
8+
let rec foo (x:int) : int = x
9+
10+
let _ = assert (forall x. 1 + x > x)

0 commit comments

Comments
 (0)