Skip to content

Commit e61bb27

Browse files
authored
fix: apply specs stated via reducible abbreviations (#14089)
This PR lets `mvcgen` and `mvcgen'` apply `@[spec]` theorems whose statement is a reducible abbreviation wrapping a Hoare triple, such as `abbrev foo.spec := ⦃P⦄ foo ⦃Q⦄`. Previously the program stayed stuck because the spec, although registered, was discarded at lookup time. The spec database recovers the triple from the proof's type via `SpecProof.instantiate`, which performs no whnf, and then matches the `Triple` head syntactically. For an abbreviation-typed spec the head is the abbreviation constant, so the match failed: `mvcgen` rejected the candidate in `findSpec`, while `mvcgen'` first failed to migrate the entry and then failed to build the backward rule. Reduce the instantiated type with `whnfR` at these three sites before matching `Triple`.
1 parent c64b560 commit e61bb27

3 files changed

Lines changed: 57 additions & 0 deletions

File tree

src/Lean/Elab/Tactic/Do/Attr.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,9 @@ private def mkSpecTheorem (type : Expr) (proof : SpecProof) (prio : Nat) : MetaM
452452
let (levelParams, expr) ← proof.getProof
453453
let type ← instantiateMVars type
454454
let (_, _, type) ← forallMetaTelescope type
455+
-- Reduce reducible abbreviations so a proof whose type is an abbreviation like
456+
-- `abbrev s := ⦃P⦄ prog ⦃Q⦄` is recognized as a triple spec.
457+
let type ← whnfR type
455458
let some _ ← selectProg type | return none
456459
let pattern ← mkSpecPatternFromExpr expr levelParams
457460
return some { pattern, proof, priority := prio }

src/Lean/Elab/Tactic/Do/Spec.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ public def findSpec (database : SpecTheorems) (wp : Expr) : MetaM SpecTheorem :=
3030
trace[Elab.Tactic.Do.spec] "Candidates for {prog}: {candidates.map (·.proof)}"
3131
let specs ← candidates.filterM fun spec => do
3232
let (_, _, _, type) ← spec.proof.instantiate
33+
-- Reduce reducible abbreviations so a proof whose type is an abbreviation like
34+
-- `abbrev s := ⦃P⦄ prog ⦃Q⦄` is recognized as a triple spec.
35+
let type ← whnfR type
3336
trace[Elab.Tactic.Do.spec] "{spec.proof} instantiates to {type}"
3437
let_expr c@Triple m ps instWP α specProg _P _Q := type | throwError "Not a triple: {type}"
3538
let check := isDefEqGuarded wp (mkApp5 (mkConst ``WP.wp c.constLevels!) m ps instWP α specProg)
@@ -177,6 +180,9 @@ public def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag
177180

178181
-- Fully instantiate the specThm without instantiating its MVars to `wp` yet
179182
let (mvars, _, spec, specTy) ← specThm.proof.instantiate
183+
-- Reduce reducible abbreviations so a proof whose type is an abbreviation like
184+
-- `abbrev s := ⦃P⦄ prog ⦃Q⦄` is recognized as a triple spec.
185+
let specTy ← whnfR specTy
180186

181187
-- Instantiation creates `.natural` MVars, which possibly get instantiated by the def eq checks
182188
-- below when they occur in `P` or `Q`.

tests/elab/mvcgenSpecAbbrev.lean

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Std.Internal.Do
2+
import Std.Tactic.Do
3+
4+
/-!
5+
Tests that `mvcgen` and `mvcgen'` apply a `@[spec]` theorem whose *type* is a
6+
reducible abbreviation wrapping a Hoare triple, e.g. `abbrev foo.spec := ⦃P⦄ foo ⦃Q⦄`.
7+
The triple is recovered from the proof's type and must be reduced through the
8+
abbreviation before it is recognized as a `Triple`.
9+
-/
10+
11+
set_option mvcgen.warning false
12+
13+
/-! `mvcgen` over a legacy `Std.Do` triple. `foo` is `irreducible`, so the
14+
postcondition `r = 1` can only be discharged via the registered spec (`r = 0`). -/
15+
section
16+
open Std.Do
17+
18+
@[irreducible] def foo : Id Nat := pure 0
19+
20+
abbrev foo.spec := ⦃⌜True⌝⦄ foo ⦃⇓r => ⌜r = 0⌝⦄
21+
22+
@[spec] theorem foo.spec.proof : foo.spec := by
23+
unfold foo.spec foo; mvcgen <;> grind
24+
25+
example :
26+
⦃⌜True⌝⦄
27+
do let x ← foo; pure (x + 1)
28+
⦃⇓r => ⌜r = 1⌝⦄ := by
29+
mvcgen <;> grind
30+
31+
end
32+
33+
/-! `mvcgen'` over a new-metatheory `Std.Internal.Do` triple. `G` is an `axiom`,
34+
so discharging `wp⟦G⟧` requires the registered spec. -/
35+
section
36+
open Std.Internal.Do Lean.Order
37+
set_option warn.sorry false
38+
39+
axiom G : StateM Nat Unit
40+
41+
abbrev G.spec := ⦃fun (_ : Nat) => True⦄ G ⦃fun _ n => n = n⦄
42+
43+
@[spec] axiom G.spec.proof : G.spec
44+
45+
example : ⦃fun (_ : Nat) => True⦄ G ⦃fun _ n => n = n⦄ := by
46+
mvcgen'
47+
48+
end

0 commit comments

Comments
 (0)