Skip to content

Commit 71e09ca

Browse files
authored
feat: concrete invariant? suggestions based on start and end (#10566)
This PR improves `mvcgen invariants?` to suggest concrete invariants based on how invariants are used in VCs. These suggestions are intentionally simplistic and boil down to "this holds at the start of the loop and this must hold at the end of the loop": ```lean def mySum (l : List Nat) : Nat := Id.run do let mut acc := 0 for x in l do acc := acc + x return acc /-- info: Try this: invariants · ⇓⟨xs, letMuts⟩ => ⌜xs.prefix = [] ∧ letMuts = 0 ∨ xs.suffix = [] ∧ letMuts = l.sum⌝ -/ #guard_msgs (info) in theorem mySum_suggest_invariant (l : List Nat) : mySum l = l.sum := by generalize h : mySum l = r apply Id.of_wp_run_eq h mvcgen invariants? all_goals admit ``` It still is the user's job to weaken this invariant such that it interpolates over all loop iterations, but it *is* a good starting point for iterating. It is also useful because the user does not need to remember the exact syntax.
1 parent e6dd412 commit 71e09ca

File tree

4 files changed

+639
-105
lines changed

4 files changed

+639
-105
lines changed

src/Init/Tactics.lean

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,13 +2176,17 @@ macro (name := mspecMacro) (priority:=low) "mspec" : tactic =>
21762176
`mvcgen` will break down a Hoare triple proof goal like `⦃P⦄ prog ⦃Q⦄` into verification conditions,
21772177
provided that all functions used in `prog` have specifications registered with `@[spec]`.
21782178
2179+
### Verification Conditions and specifications
2180+
21792181
A verification condition is an entailment in the stateful logic of `Std.Do.SPred`
21802182
in which the original program `prog` no longer occurs.
21812183
Verification conditions are introduced by the `mspec` tactic; see the `mspec` tactic for what they
21822184
look like.
21832185
When there's no applicable `mspec` spec, `mvcgen` will try and rewrite an application
21842186
`prog = f a b c` with the simp set registered via `@[spec]`.
21852187
2188+
### Features
2189+
21862190
When used like `mvcgen +noLetElim [foo_spec, bar_def, instBEqFloat]`, `mvcgen` will additionally
21872191
21882192
* add a Hoare triple specification `foo_spec : ... → ⦃P⦄ foo ... ⦃Q⦄` to `spec` set for a
@@ -2191,11 +2195,59 @@ When used like `mvcgen +noLetElim [foo_spec, bar_def, instBEqFloat]`, `mvcgen` w
21912195
* unfold any method of the `instBEqFloat : BEq Float` instance in `prog`.
21922196
* it will no longer substitute away `let`-expressions that occur at most once in `P`, `Q` or `prog`.
21932197
2194-
Furthermore, `mvcgen` tries to close trivial verification conditions by `SPred.entails.rfl` or
2195-
the tactic sequence `try (mpure_intro; trivial)`. The variant `mvcgen_no_trivial` does not do this.
2198+
### Config options
2199+
2200+
`+noLetElim` is just one config option of many. Check out `Lean.Elab.Tactic.Do.VCGen.Config` for all
2201+
options. Of particular note is `stepLimit = some 42`, which is useful for bisecting bugs in
2202+
`mvcgen` and tracing its execution.
2203+
2204+
### Extended syntax
2205+
2206+
Often, `mvcgen` will be used like this:
2207+
```
2208+
mvcgen [...]
2209+
case inv1 => by exact I1
2210+
case inv2 => by exact I2
2211+
all_goals (mleave; try grind)
2212+
```
2213+
There is special syntax for this:
2214+
```
2215+
mvcgen [...] invariants
2216+
· I1
2217+
· I2
2218+
with grind
2219+
```
21962220
2197-
For debugging purposes there is also `mvcgen_step 42` which will do at most 42 VC generation
2198-
steps. This is useful for bisecting issues with the generated VCs.
2221+
### Invariant suggestions
2222+
2223+
`mvcgen` will suggest invariants for you if you use the `invariants?` keyword.
2224+
```
2225+
mvcgen [...] invariants?
2226+
```
2227+
This is useful if you do not recall the exact syntax to construct invariants.
2228+
Furthermore, it will suggest a concrete invariant encoding "this holds at the start of the loop and
2229+
this must hold at the end of the loop" by looking at the corresponding VCs.
2230+
Although the suggested invariant is a good starting point, it is too strong and requires users to
2231+
interpolate it such that the inductive step can be proved. Example:
2232+
```
2233+
def mySum (l : List Nat) : Nat := Id.run do
2234+
let mut acc := 0
2235+
for x in l do
2236+
acc := acc + x
2237+
return acc
2238+
2239+
/--
2240+
info: Try this:
2241+
invariants
2242+
· ⇓⟨xs, letMuts⟩ => ⌜xs.prefix = [] ∧ letMuts = 0 ∨ xs.suffix = [] ∧ letMuts = l.sum⌝
2243+
-/
2244+
#guard_msgs (info) in
2245+
theorem mySum_suggest_invariant (l : List Nat) : mySum l = l.sum := by
2246+
generalize h : mySum l = r
2247+
apply Id.of_wp_run_eq h
2248+
mvcgen invariants?
2249+
all_goals admit
2250+
```
21992251
-/
22002252
macro (name := mvcgenMacro) (priority:=low) "mvcgen" : tactic =>
22012253
Macro.throwError "to use `mvcgen`, please include `import Std.Tactic.Do`"

0 commit comments

Comments
 (0)