Skip to content

Commit 23efc8f

Browse files
committed
Add named and local simplify hint databases
Introduce named simplify databases, a head-symbol filter, and proof-local control over which user-reduction rules `simplify`/`cbv` apply. These are all expressed through one `hint` clause, used in three places: after a `simplify`/`cbv` tactic, as a standalone `hint` command, and in the `with hint … (…)` scoped wrapper. Theory-level declarations register lemmas into a database (unchanged): hint simplify {lemma}+ . (* default database *) hint simplify in {db} : {lemma}+ . (* named database *) On the `simplify`/`cbv` tactics, a `hint` clause chooses the rules for that call. The clause is `hint` followed by items disambiguated by their delimiter -- a bare name is a database, `[…]` holds operators, `{…}` holds lemmas: simplify hint d1 d2 (* consult exactly d1, d2 *) simplify hint +d1 -d2 (* +/- a db on the active set *) simplify hint +[f g] (* restrict user rules to f,g *) simplify hint {L} (* add lemma L for this call *) cbv hint d1 {M} The database part is either an unsigned selection (replace) or signed +/- deltas (on the active set), never both; at most one head filter is allowed; and lemma sets `{…}` are add-only -- a clause never removes lemmas from a database (use the head filter to restrict which rules apply). The same clause, written as a standalone command, changes the proof state persistently: hint +d1 -d2 . (* activate / deactivate DBs *) hint {L} . (* add local lemmas *) hint d1 d2 . (* set default databases *) hint +[f g] . (* set default head filter *) hint clear [ {db} ] . (* clear local additions *) hint clear default . (* clear proof-local defaults *) and as a scoped wrapper, in effect only for the wrapped tactic and restored on the resulting subgoals: with hint {clause} ( {tactic} ) . Implementation: the proof-local simplify context lives in a new EcSimplifyContext module (extracted from EcEnv); it is threaded through proof goals so the active DB set, added lemmas, default database, and head filter propagate across subgoals and are consulted by `simplify` and `cbv`. Reduction storage and simplify-hint printing support named databases and head filtering. Document the syntax in doc/tactics/hint-simplify.rst and add regression tests (tests/hint_simplify_db.ec, tests/local_hint_simplify.ec, tests/simplify_head_filter.ec).
1 parent 6ea6bdc commit 23efc8f

24 files changed

Lines changed: 1043 additions & 68 deletions

doc/tactics/hint-simplify.rst

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
========================================================================
2+
Hints: `hint simplify`
3+
========================================================================
4+
5+
The `hint simplify` commands manage user reduction rules used by
6+
`simplify`, `cbv`, and tactics that rely on the same simplification
7+
machinery.
8+
9+
Hints can be declared globally in a theory, selected through named
10+
databases, and adjusted locally inside a proof.
11+
12+
.. contents::
13+
:local:
14+
15+
------------------------------------------------------------------------
16+
Global declarations
17+
------------------------------------------------------------------------
18+
19+
Global declarations add lemmas to a simplification database.
20+
21+
.. admonition:: Syntax
22+
23+
`hint simplify {lemma}+ .`
24+
25+
Add the listed lemmas to the default simplification database.
26+
27+
.. admonition:: Syntax
28+
29+
`hint simplify in {db} : {lemma}+ .`
30+
31+
Add the listed lemmas to the named database `{db}`.
32+
33+
These commands are theory-level declarations: once imported, the
34+
corresponding rules are available to simplification.
35+
36+
------------------------------------------------------------------------
37+
The `hint` clause
38+
------------------------------------------------------------------------
39+
40+
`simplify` and `cbv` accept a single `hint` clause that controls which
41+
user-reduction rules are used. The clause is introduced by the `hint`
42+
keyword and is built from an unsigned base database selection followed by
43+
any number of signed items:
44+
45+
.. admonition:: Syntax
46+
47+
`simplify hint {db}* {item}*`
48+
49+
where each `{item}` is one of:
50+
51+
- `+ {db}` / `- {db}` — activate / deactivate a database;
52+
- `+[ {op}+ ]` / `-[ {op}+ ]` — head filter: keep only / drop rules headed
53+
by the listed operators (at most one filter per clause);
54+
- `{ {lemma}+ }` — add lemmas to the default database for this call.
55+
56+
The delimiter disambiguates an item: a bare name is a database, `[…]`
57+
holds operators, `{…}` holds lemmas. Lemma sets are add-only -- a clause
58+
never removes lemmas from a database; use the head filter to restrict
59+
which rules apply. The same clause is accepted by `cbv`.
60+
61+
The database part of a clause is *either* an unsigned selection *or*
62+
signed deltas, never both (mixing them is redundant and rejected). An
63+
unsigned list `hint d1 d2` **replaces** the consulted databases with
64+
exactly `{d1, d2}`. Signed `+ {db}` / `- {db}` instead modify the current
65+
set (the proof-local default, else the active set): `simplify hint +d2`
66+
adds `d2` to it, `simplify hint -d3` removes `d3`.
67+
68+
A head filter performs full simplification (like a bare `simplify`) with
69+
user reduction restricted to the selected operators.
70+
71+
The `hint` clause is independent of the reduction arguments that
72+
`simplify` and `cbv` already accept: a bare `simplify` performs full
73+
simplification, `simplify delta` additionally unfolds all definitions,
74+
`simplify f g` unfolds the operators `f` and `g`, and a keyword-less list
75+
such as `beta zeta` performs only the named reductions. A `hint` clause
76+
may follow any of these (for example `simplify delta hint +[f]`).
77+
78+
------------------------------------------------------------------------
79+
Proof-local commands
80+
------------------------------------------------------------------------
81+
82+
Inside a proof, the simplify configuration can be changed without
83+
modifying the theory-level declarations. The `hint` command takes the
84+
same clause as the `simplify`/`cbv` tactics, and each kind of item has a
85+
persistent effect on the proof state:
86+
87+
.. admonition:: Syntax
88+
89+
`hint {db}* {item}* .`
90+
91+
- `+ {db}` / `- {db}` — activate / deactivate a database (for later bare
92+
`simplify`/`cbv`);
93+
- `{ {lemma}+ }` — add lemmas to the default database;
94+
- an unsigned database list `{db}+` — set the proof-local default
95+
databases used by later `simplify`/`cbv` calls;
96+
- `+[ {op}+ ]` / `-[ {op}+ ]` — set the proof-local default head filter.
97+
98+
As in the tactic clause, the database part is *either* an unsigned
99+
selection *or* signed `+`/`-` deltas, never both.
100+
101+
.. admonition:: Syntax
102+
103+
`hint clear {db}? .`
104+
105+
Clear the local lemma additions: for the default database when `{db}` is
106+
omitted, or for the named database `{db}`. Note that `hint clear default`
107+
is reserved for the form below, so a database named `default` cannot be
108+
cleared this way.
109+
110+
.. admonition:: Syntax
111+
112+
`hint clear default .`
113+
114+
Clear the proof-local default database and head filter.
115+
116+
These proof-local changes are part of the proof state and therefore
117+
follow the usual subgoal branching behavior. Explicit arguments on
118+
`simplify`/`cbv` take precedence over the proof-local defaults.
119+
120+
------------------------------------------------------------------------
121+
Scoped application
122+
------------------------------------------------------------------------
123+
124+
A `hint` command can be used as a scoped wrapper around a tactic, with the
125+
same clause syntax.
126+
127+
.. admonition:: Syntax
128+
129+
`with hint {clause} ( {tactic} )`
130+
131+
For example:
132+
133+
.. code-block:: easycrypt
134+
135+
with hint +ring (simplify).
136+
with hint {fooE} (rewrite foo /=).
137+
with hint ring (cbv).
138+
139+
The wrapped tactic runs with the modified hint configuration, but the
140+
subgoals produced afterwards are restored to the original configuration.
141+
142+
------------------------------------------------------------------------
143+
Example
144+
------------------------------------------------------------------------
145+
146+
.. ecproof::
147+
:title: Simplify hint databases
148+
149+
require import AllCore Int.
150+
151+
op wrap1 (x : int) = x + 1.
152+
op wrap2 (x : int) = x + 2.
153+
op box (x : int) = x.
154+
155+
lemma wrap1E x : box (wrap1 x) = box (x + 1).
156+
proof. smt(). qed.
157+
158+
lemma wrap2E x : box (wrap2 x) = box (x + 2).
159+
proof. smt(). qed.
160+
161+
hint simplify wrap1E.
162+
hint simplify in named : wrap2E.
163+
164+
lemma demo_default (x : int) :
165+
box (wrap1 x) = box (x + 1).
166+
proof.
167+
simplify.
168+
trivial.
169+
qed.
170+
171+
lemma demo_named (x : int) :
172+
box (wrap2 x) = box (x + 2).
173+
proof.
174+
simplify hint named.
175+
trivial.
176+
qed.
177+
178+
lemma demo_local_default (x : int) :
179+
box (wrap2 x) = box (x + 2).
180+
proof.
181+
with hint named (simplify).
182+
trivial.
183+
qed.
184+
185+
lemma demo_head_filter (x : int) :
186+
box (wrap1 x) = box (x + 1).
187+
proof.
188+
simplify hint +[wrap1].
189+
trivial.
190+
qed.

src/ecCommands.ml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -350,18 +350,11 @@ module HiPrinting = struct
350350
let pr_hint_simplify (fmt : Format.formatter) (env : EcEnv.env) =
351351
let open EcTheory in
352352

353-
let (hint_simplify: (EcEnv.Reduction.topsym * rule list) list) = EcEnv.Reduction.all env in
354-
355-
let hint_simplify = List.filter_map (fun (ts, rl) ->
356-
match ts with
357-
| `Path p -> Some (p, rl)
358-
| _ -> None
359-
) hint_simplify
360-
in
353+
let hint_simplify = EcEnv.Reduction.all env in
361354

362355
let ppe = EcPrinting.PPEnv.ofenv env in
363356

364-
let pp_hint_simplify ppe fmt = (fun (p, (rls : rule list)) ->
357+
let pp_rules ppe fmt = (fun (p, (rls : rule list)) ->
365358
Format.fprintf fmt "@[<b 2>%s:@\n%a@]" (EcPath.basename p)
366359
(EcPrinting.pp_list "@\n" (fun fmt rl ->
367360
begin match rl.rl_cond with
@@ -376,7 +369,21 @@ module HiPrinting = struct
376369
)
377370
in
378371

379-
EcPrinting.pp_by_theory ppe pp_hint_simplify fmt hint_simplify
372+
let pp_db fmt (base, entries) =
373+
let entries = List.filter_map (fun (ts, rl) ->
374+
match ts with
375+
| `Path p -> Some (p, rl)
376+
| _ -> None
377+
) entries in
378+
379+
if not (List.is_empty entries) then
380+
Format.fprintf fmt "@[<v 2>%s:@\n%a@]"
381+
(if base = EcEnv.Reduction.dname then "<default>" else base)
382+
(EcPrinting.pp_by_theory ppe pp_rules) entries
383+
in
384+
385+
EcPrinting.pp_list "@.@." pp_db fmt
386+
(List.filter (fun (_, entries) -> not (List.is_empty entries)) hint_simplify)
380387
end
381388

382389
(* -------------------------------------------------------------------- *)

src/ecCoreGoal.ml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ and pregoal = {
141141
g_uid : handle; (* this goal ID *)
142142
g_hyps : LDecl.hyps; (* goal local environment *)
143143
g_concl : form; (* goal conclusion *)
144+
g_simpl : EcEnv.simplify_context; (* proof-local simplify context *)
144145
}
145146

146147
and goal = {
@@ -394,7 +395,7 @@ module FApi = struct
394395

395396
(* ------------------------------------------------------------------ *)
396397
let tc1_flat ?target (tc : tcenv1) =
397-
let { g_hyps; g_concl } = tc1_current tc in
398+
let { g_hyps; g_concl; _ } = tc1_current tc in
398399
match target with
399400
| None -> (g_hyps, g_concl)
400401
| Some h -> (LDecl.local_hyps h g_hyps, LDecl.hyp_by_id h g_hyps)
@@ -411,6 +412,7 @@ module FApi = struct
411412
let tc1_penv (tc : tcenv1) = tc.tce_penv
412413
let tc1_goal (tc : tcenv1) = snd (tc1_flat tc)
413414
let tc1_env (tc : tcenv1) = LDecl.toenv (tc1_hyps tc)
415+
let tc1_simplify_context (tc : tcenv1) = (tc1_current tc).g_simpl
414416

415417
(* ------------------------------------------------------------------ *)
416418
let tc_handle (tc : tcenv) = tc1_handle tc.tce_tcenv
@@ -461,7 +463,7 @@ module FApi = struct
461463
(* ------------------------------------------------------------------ *)
462464
let pf_newgoal (pe : proofenv) ?vx hyps concl =
463465
let hid = ID.gen () in
464-
let pregoal = { g_uid = hid; g_hyps = hyps; g_concl = concl; } in
466+
let pregoal = { g_uid = hid; g_hyps = hyps; g_concl = concl; g_simpl = EcEnv.SimplifyContext.empty; } in
465467
let goal = { g_goal = pregoal; g_validation = vx; } in
466468
let pe = { pe with pr_goals = ID.Map.add pregoal.g_uid goal pe.pr_goals; } in
467469
(pe, pregoal)
@@ -470,6 +472,8 @@ module FApi = struct
470472
let newgoal (tc : tcenv) ?(hyps : LDecl.hyps option) (concl : form) =
471473
let hyps = ofdfl (fun () -> tc_hyps tc) hyps in
472474
let (pe, pg) = pf_newgoal (tc_penv tc) hyps concl in
475+
let pg = { pg with g_simpl = tc1_simplify_context tc.tce_tcenv } in
476+
let pe = update_goal_map (fun g -> { g with g_goal = pg }) pg.g_uid pe in
473477

474478
let tc = tc_update_tcenv (fun te -> { te with tce_penv = pe }) tc in
475479
let tc = { tc with tce_goals = tc.tce_goals @ [pg.g_uid] } in
@@ -507,6 +511,14 @@ module FApi = struct
507511
let tc = mutate (tcenv_of_tcenv1 tc) vx ?hyps fp in
508512
assert (tc.tce_goals = []); tc.tce_tcenv
509513

514+
let map_pregoal1 (tx : pregoal -> pregoal) (tc : tcenv1) =
515+
let current = tc1_current tc in
516+
let current = tx current in
517+
let tc =
518+
tc1_update_goal_map (fun g -> { g with g_goal = current }) current.g_uid tc
519+
in
520+
{ tc with tce_goal = Some current }
521+
510522
(* ------------------------------------------------------------------ *)
511523
let xmutate (tc : tcenv) (vx : 'a) (fp : form list) =
512524
let (tc, hds) = List.map_fold (fun tc fp -> newgoal tc fp) tc fp in
@@ -990,7 +1002,7 @@ let start (hyps : LDecl.hyps) (goal : form) =
9901002
let uid = ID.gen () in
9911003
let hid = ID.gen () in
9921004

993-
let goal = { g_uid = hid; g_hyps = hyps; g_concl = goal; } in
1005+
let goal = { g_uid = hid; g_hyps = hyps; g_concl = goal; g_simpl = EcEnv.SimplifyContext.empty; } in
9941006
let goal = { g_goal = goal; g_validation = None; } in
9951007
let env = { pr_uid = uid;
9961008
pr_main = hid;

src/ecCoreGoal.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ type pregoal = {
144144
g_uid : handle;
145145
g_hyps : LDecl.hyps;
146146
g_concl : form;
147+
g_simpl : EcEnv.simplify_context;
147148
}
148149

149150
type validation =
@@ -280,6 +281,7 @@ module FApi : sig
280281
* focused goal local context. *)
281282
val mutate : tcenv -> (handle -> validation) -> ?hyps:LDecl.hyps -> form -> tcenv
282283
val mutate1 : tcenv1 -> (handle -> validation) -> ?hyps:LDecl.hyps -> form -> tcenv1
284+
val map_pregoal1 : (pregoal -> pregoal) -> tcenv1 -> tcenv1
283285

284286
(* Same as xmutate, but for an external node resolution depending on
285287
* a unbounded numbers of premises. The ['a] argument is the external
@@ -322,6 +324,7 @@ module FApi : sig
322324
val tc1_hyps : ?target:ident -> tcenv1 -> LDecl.hyps
323325
val tc1_goal : tcenv1 -> form
324326
val tc1_env : tcenv1 -> EcEnv.env
327+
val tc1_simplify_context : tcenv1 -> EcEnv.simplify_context
325328

326329
(* Low-level tactic markers *)
327330
val t_low0 : string -> backward -> backward

0 commit comments

Comments
 (0)