Skip to content

Commit ed5e4a5

Browse files
committed
fix(FPA): Separate semantic triggers upon trigger construction
Currently, the theories are responsible for populating the `semantic` field of triggers when a lemma with semantic triggers is added to the theory (call to `assume_th_elt`). This means that such a lemma lives for some time *without* semantic triggers: in particular, it is initially created using `mk_forall` without semantic triggers. During that call, the guard in `find_particular_subst` preventing the "particular substitution" optimization from triggering for lemmas with semantic trigger is ignored; which means that the "particular substitution" optimization is actually applied to lemmas with semantic triggers. In particular, this optimization makes the lemma `float_of_pos_pow_of_two` in the FPA theory unsound, because it removes the check that `x` is actually a power of two, and causes OCamlPro#1111. This patch makes the `Expr` module responsible for separating syntaxic and semantic triggers, rather than the theory. This ensures that a lemma with semantic triggers never appears as having no semantic triggers. In order to make sure this invariant is properly maintained, the `trigger` type is again made private to the `Expr` module. This required moving from sorting code from the `Matching` module to the `Expr` module, also ensuring that triggers are properly sorted for matching purposes at all times. Fixes OCamlPro#1111
1 parent b464ec3 commit ed5e4a5

File tree

9 files changed

+360
-107
lines changed

9 files changed

+360
-107
lines changed

src/lib/frontend/cnf.ml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,9 @@ let rec make_term quant_basename t =
177177

178178
and make_trigger ~in_theory name quant_basename hyp (e, from_user) =
179179
let content = List.map (make_term quant_basename) e in
180-
let t_depth =
181-
List.fold_left (fun z t -> max z (E.depth t)) 0 content in
182180
(* clean trigger:
183181
remove useless terms in multi-triggers after inlining of lets*)
184-
let trigger =
185-
{ E.content ; t_depth; semantic = []; (* will be set by theories *)
186-
hyp; from_user;
187-
}
188-
in
182+
let trigger = E.mk_trigger ~user:from_user ~hyp content in
189183
E.clean_trigger ~in_theory name trigger
190184

191185
and make_form name_base ~toplevel f loc ~decl_kind : E.t =

src/lib/frontend/d_cnf.ml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,14 +1655,11 @@ and make_trigger ?(loc = Loc.dummy) ~name_base ~decl_kind
16551655
mk_expr ~loc ~name_base ~decl_kind
16561656
in
16571657
let content = List.map mk_expr e in
1658-
let t_depth =
1659-
List.fold_left (fun z t -> max z (E.depth t)) 0 content in
16601658
(* clean trigger:
16611659
remove useless terms in multi-triggers after inlining of lets*)
1662-
let trigger =
1663-
{ E.content; t_depth; semantic = []; (* will be set by theories *)
1664-
hyp; from_user; }
1665-
in
1660+
let trigger = E.mk_trigger ~user:from_user ~hyp content in
1661+
if not in_theory && not (Lists.is_empty trigger.semantic) then
1662+
Errors.typing_error ThSemTriggerError loc;
16661663
E.clean_trigger ~in_theory name trigger
16671664

16681665
(** Preprocesses the body of a goal by:

src/lib/reasoners/intervalCalculus.ml

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,64 +2481,20 @@ let instantiate ~do_syntactic_matching match_terms env uf selector =
24812481
env, insts
24822482

24832483

2484-
let separate_semantic_triggers =
2485-
fun th_form ->
2486-
let { E.user_trs; _ } as q =
2487-
match E.form_view th_form with
2488-
| E.Lemma q -> q
2489-
| E.Unit _ | E.Clause _ | E.Literal _ | E.Skolem _
2490-
| E.Let _ | E.Iff _ | E.Xor _ -> assert false
2491-
in
2492-
let r_triggers =
2493-
List.rev_map
2494-
(fun tr ->
2495-
(* because sem-triggers will be set by theories *)
2496-
assert (tr.E.semantic == []);
2497-
let syn, sem =
2498-
List.fold_left
2499-
(fun (syn, sem) t ->
2500-
match E.term_view t with
2501-
| { E.f = Symbols.In (lb, ub); xs = [x]; _ } ->
2502-
syn, (E.Interval (x, lb, ub)) :: sem
2503-
2504-
| { E.f = Symbols.MapsTo x; xs = [t]; _ } ->
2505-
syn, (E.MapsTo (x, t)) :: sem
2506-
2507-
| { E.f = Sy.Op Not_theory_constant; xs = [x]; _ } ->
2508-
syn, (E.NotTheoryConst x) :: sem
2509-
2510-
| { E.f = Sy.Op Is_theory_constant; xs = [x]; _ } ->
2511-
syn, (E.IsTheoryConst x) :: sem
2512-
2513-
| { E.f = Sy.Op Linear_dependency; xs = [x;y]; _ } ->
2514-
syn, (E.LinearDependency(x,y)) :: sem
2515-
2516-
| _ -> t::syn, sem
2517-
)([], []) (List.rev tr.E.content)
2518-
in
2519-
{tr with E.content = syn; semantic = sem}
2520-
)user_trs
2521-
in
2522-
E.mk_forall
2523-
q.E.name q.E.loc q.E.binders (List.rev r_triggers) q.E.main
2524-
~toplevel:true ~decl_kind:E.Dtheory
2525-
25262484
let assume_th_elt t th_elt dep =
25272485
let { Expr.axiom_kind; ax_form; th_name; extends; _ } = th_elt in
25282486
let kd_str =
25292487
if axiom_kind == Util.Propagator then "Th propagator" else "Th CS"
25302488
in
25312489
match extends with
25322490
| Util.NIA | Util.NRA | Util.FPA | Util.RIA ->
2533-
let th_form = separate_semantic_triggers ax_form in
2534-
let th_elt = {th_elt with Expr.ax_form} in
25352491
if get_debug_fpa () >= 2 then
25362492
Printer.print_dbg
25372493
~module_name:"IntervalCalculus" ~function_name:"assume_th_elt"
25382494
"[Theory %s][%s] %a"
2539-
th_name kd_str E.print th_form;
2540-
assert (not (ME.mem th_form t.th_axioms));
2541-
{t with th_axioms = ME.add th_form (th_elt, dep) t.th_axioms}
2495+
th_name kd_str E.print ax_form;
2496+
assert (not (ME.mem ax_form t.th_axioms));
2497+
{t with th_axioms = ME.add ax_form (th_elt, dep) t.th_axioms}
25422498

25432499
| _ -> t
25442500

src/lib/reasoners/matching.ml

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -482,19 +482,10 @@ module Make (X : Arg) : S with type theory = X.t = struct
482482
Debug.match_pats_modulo pat lsubsts;
483483
List.fold_left (match_one_pat mconf env tbox pat) [] lsubsts
484484

485-
let pat_weight s t =
486-
let sf = (Expr.term_view s).f in
487-
let tf = (Expr.term_view t).f in
488-
match sf, tf with
489-
| Symbols.Name _, Symbols.Op _ -> -1
490-
| Symbols.Op _, Symbols.Name _ -> 1
491-
| _ -> Expr.(depth t - depth s)
492-
493485
let matching mconf env tbox pat_info =
494486
let open Matching_types in
495487
let pats = pat_info.trigger in
496488
let pats_list = pats.E.content in
497-
Options.heavy_assert (fun () -> Lists.is_sorted pat_weight pats_list);
498489
Debug.matching pats;
499490
if List.length pats_list > Options.get_max_multi_triggers_size () then
500491
pat_info, []
@@ -610,21 +601,16 @@ module Make (X : Arg) : S with type theory = X.t = struct
610601

611602
module HE = Hashtbl.Make (E)
612603

613-
let sort_pats tr =
614-
let content = List.stable_sort pat_weight tr.E.content in
615-
{ tr with content }
616-
617604
let triggers_of, clear_triggers_of_trs_tbl =
618605
let trs_tbl = HEI.create 101 in
619606
let triggers_of q mconf =
620607
match q.E.user_trs with
621-
| _::_ as l -> List.map sort_pats l
608+
| _::_ as l -> l
622609
| [] ->
623610
try HEI.find trs_tbl (q.E.main, mconf)
624611
with Not_found ->
625612
let trs =
626613
E.make_triggers q.E.main q.E.binders q.E.kind mconf
627-
|> List.map sort_pats
628614
in
629615
HEI.add trs_tbl (q.E.main, mconf) trs;
630616
trs
@@ -641,7 +627,6 @@ module Make (X : Arg) : S with type theory = X.t = struct
641627
with Not_found ->
642628
let trs =
643629
E.resolution_triggers ~is_back:true q
644-
|> List.map sort_pats
645630
in
646631
HE.add trs_tbl q.E.main trs;
647632
trs
@@ -658,7 +643,6 @@ module Make (X : Arg) : S with type theory = X.t = struct
658643
with Not_found ->
659644
let trs =
660645
E.resolution_triggers ~is_back:false q
661-
|> List.map sort_pats
662646
in
663647
HE.add trs_tbl q.E.main trs;
664648
trs

src/lib/structures/expr.ml

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ and semantic_trigger =
9595

9696
and trigger = {
9797
content : t list;
98-
(* this field is filled (with a part of 'content' field) by theories
99-
when assume_th_elt is called *)
10098
semantic : semantic_trigger list;
10199
hyp : t list;
102100
t_depth : int;
@@ -843,6 +841,50 @@ let is_ite s = match s with
843841
| Sy.Op Sy.Tite -> true
844842
| _ -> false
845843

844+
let pat_weight s t =
845+
let sf = (term_view s).f in
846+
let tf = (term_view t).f in
847+
match sf, tf with
848+
| Symbols.Name _, Symbols.Op _ -> -1
849+
| Symbols.Op _, Symbols.Name _ -> 1
850+
| _ -> depth t - depth s
851+
852+
let separate_semantic_triggers content =
853+
let syn, sem =
854+
List.fold_left
855+
(fun (syn, sem) t ->
856+
match term_view t with
857+
| { f = Symbols.In (lb, ub); xs = [x]; _ } ->
858+
syn, (Interval (x, lb, ub)) :: sem
859+
860+
| { f = Symbols.MapsTo x; xs = [t]; _ } ->
861+
syn, (MapsTo (x, t)) :: sem
862+
863+
| { f = Sy.Op Not_theory_constant; xs = [x]; _ } ->
864+
syn, (NotTheoryConst x) :: sem
865+
866+
| { f = Sy.Op Is_theory_constant; xs = [x]; _ } ->
867+
syn, (IsTheoryConst x) :: sem
868+
869+
| { f = Sy.Op Linear_dependency; xs = [x;y]; _ } ->
870+
syn, (LinearDependency(x,y)) :: sem
871+
872+
| _ -> t::syn, sem
873+
)([], []) (List.rev content)
874+
in
875+
syn, sem
876+
877+
let mk_trigger ?user:(from_user = false) ?depth ?(hyp = []) content =
878+
let t_depth =
879+
match depth with
880+
| Some t_depth -> t_depth
881+
| None ->
882+
List.fold_left (fun z t -> max z t.depth) 0 content
883+
in
884+
let content = List.stable_sort pat_weight content in
885+
let content, semantic = separate_semantic_triggers content in
886+
{ content ; semantic ; hyp ; t_depth ; from_user }
887+
846888
let mk_term s l ty =
847889
assert (match s with Sy.Lit _ | Sy.Form _ -> false | _ -> true);
848890
let d = match l with
@@ -1375,9 +1417,11 @@ let rec apply_subst_aux (s_t, s_ty) t =
13751417

13761418
and apply_subst_trigger subst ({ content; _ } as tr) =
13771419
{tr with
1378-
content = List.map (apply_subst_aux subst) content;
1379-
(* semantic_trigger = done on theory side *)
1380-
(* hyp = done on theory side *)
1420+
content =
1421+
List.rev_map (apply_subst_aux subst) content |>
1422+
List.stable_sort pat_weight
1423+
(* semantic_trigger = done on theory side *)
1424+
(* hyp = done on theory side *)
13811425
}
13821426

13831427
(* *1* We should never subst formulas inside termes. We could allow to
@@ -1643,12 +1687,7 @@ let resolution_triggers ~is_back { kind; main = f; binders; _ } =
16431687
| Dpredicate t | Dfunction t ->
16441688
if type_info t != Ty.Tbool then []
16451689
else
1646-
[ { content = [t];
1647-
hyp = [];
1648-
semantic = [];
1649-
t_depth = t.depth;
1650-
from_user = false;
1651-
} ]
1690+
[ mk_trigger ~depth:t.depth [t] ]
16521691
| Dtheory | Dobjective -> []
16531692
| Daxiom
16541693
| Dgoal ->
@@ -1664,12 +1703,7 @@ let resolution_triggers ~is_back { kind; main = f; binders; _ } =
16641703
TSet.exists (cand_is_more_general t) others then
16651704
acc
16661705
else
1667-
{ content = [t];
1668-
hyp = [];
1669-
semantic = [];
1670-
t_depth = t.depth;
1671-
from_user = false;
1672-
} :: acc
1706+
mk_trigger ~depth:t.depth [t] ::acc
16731707
)cand []
16741708

16751709
let free_type_vars_as_types e =
@@ -2212,6 +2246,7 @@ module Triggers = struct
22122246
if sz_l = sz_s then trig
22132247
else
22142248
let content = TMap.fold (fun t _ acc -> t :: acc) res [] in
2249+
let content = List.stable_sort pat_weight content in
22152250
if Options.get_verbose () then
22162251
Printer.print_dbg ~module_name:"Cnf"
22172252
~function_name:"clean_trigger"
@@ -2258,15 +2293,7 @@ module Triggers = struct
22582293
aux vars (STRS.empty, Var.Map.empty) e
22592294

22602295
let triggers_of_list l =
2261-
List.map
2262-
(fun content ->
2263-
{ content;
2264-
semantic = [];
2265-
hyp = [];
2266-
from_user = false;
2267-
t_depth = List.fold_left (fun z t -> max z (depth t)) 0 content
2268-
}
2269-
) l
2296+
List.map mk_trigger l
22702297

22712298
(* Should return false iff lit_view fails with Failure _, but this version
22722299
does not build the literal view. *)

src/lib/structures/expr.mli

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,8 @@ and semantic_trigger =
9191
| IsTheoryConst of t
9292
| LinearDependency of t * t
9393

94-
and trigger = (*private*) {
94+
and trigger = private {
9595
content : t list;
96-
(* this field is filled (with a part of 'content' field) by theories
97-
when assume_th_elt is called *)
9896
semantic : semantic_trigger list;
9997
hyp : t list;
10098
t_depth : int;
@@ -192,6 +190,7 @@ val print_tagged_classes : Format.formatter -> Set.t list -> unit
192190

193191
(** smart constructors for terms *)
194192

193+
val mk_trigger : ?user:bool -> ?depth:int -> ?hyp:t list -> t list -> trigger
195194
val mk_term : Symbols.t -> t list -> Ty.t -> t
196195
val vrai : t
197196
val faux : t

0 commit comments

Comments
 (0)