Skip to content

Commit 99683cc

Browse files
sgraf812volodeyka
andauthored
feat: port the mvcgen' tactic to the new Std.Internal.Do meta theory (#14015)
This PR ports the experimental `mvcgen'` tactic to the new `Std.Internal.Do` meta theory, where verification-condition generation works on lattice entailments `pre ⊑ wp x post epost`. Two changes are visible at the proof surface: `mvcgen'` now eagerly introduces all state components as local hypotheses, so more facts reach `grind`; and loop invariants no longer have to restate the exceptional postcondition. Previously the invariant for a loop that can throw had to repeat the exception condition alongside the actual invariant: ```lean theorem throwing_loop_spec : ⦃fun s => ⌜s = 4⌝⦄ throwing_loop ⦃post⟨fun _ _ => ⌜False⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩⦄ := by mvcgen' [throwing_loop] case inv1 => exact post⟨fun (xs, r) s => ⌜r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⌝, fun e s => ⌜e = 42 ∧ s = 4⌝⟩ ``` Now the exceptional postcondition is taken from the surrounding `Triple`, so the invariant case carries only the invariant: ```lean theorem throwing_loop_spec : ⦃fun s => s = 4⦄ throwing_loop ⦃fun _ _ => False; fun e s => e = 42 ∧ s = 4⦄ := by mvcgen' [throwing_loop] case inv1 => exact ⟨fun (xs, r) s => r ≤ 4 ∧ s = 4 ∧ r + xs.suffix.sum > 4⟩ ``` The spec lemmas in `Std.Internal.Do.Triple.SpecLemmas` are registered with `@[spec]` and populate the new spec database, which also gains the `whileM`/`Lean.Loop` specifications, `Invariant.withEarlyReturnNewDo`, and pointwise entailment lemmas for state introduction. The port replays Vladimir Gladshtein's #13978 onto master's file structure. The solver diverges from the legacy algorithm in three points. Reflexivity runs on every entailment before the pure-precondition lifts, closing the leaf VCs where a spec precondition meets an identical goal precondition (`Q x ⊑ wp (pure x) Q E` ends in `Q x ⊑ Q x`) and instantiating spec parameters that occur only in rule premises. Reflexivity no longer assigns synthetic opaque metavariables, so function-typed invariant holes are not silently solved away. Pure preconditions are lifted into the local context as soon as they arise; the lifted hypothesis is cached in `Scope.lastLiftedPre?`, and a targeted assumption step closes the handoff VCs of subsequent spec applications against it. This is the second of two steps that land `vova/new-mvcgen` onto master. The first step (#13954) updated the `@[spec]` builtin attribute and regenerated `stage0`, so this PR reuses master's `stage0` and contains no generated C changes. Invariant suggestion (`invariants?`) yields no suggestions on the new surface; porting the suggestion analysis is deferred until `mvcgen'` replaces the legacy `mvcgen`. --------- Co-authored-by: volodeyka <vovaglad00@gmail.com>
1 parent 4792cd2 commit 99683cc

36 files changed

Lines changed: 1797 additions & 1301 deletions

src/Lean/Elab/Tactic/Do/Internal/VCGen.lean

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public import Lean.Elab.Tactic.Do.Internal.VCGen.Reduce
1010
public import Lean.Elab.Tactic.Do.Internal.VCGen.SpecDB
1111
public import Lean.Elab.Tactic.Do.Internal.VCGen.RuleConstruction
1212
public import Lean.Elab.Tactic.Do.Internal.VCGen.Context
13+
public import Lean.Elab.Tactic.Do.Internal.VCGen.EPost
1314
public import Lean.Elab.Tactic.Do.Internal.VCGen.Util
1415
public import Lean.Elab.Tactic.Do.Internal.VCGen.RuleCache
1516
public import Lean.Elab.Tactic.Do.Internal.VCGen.Entails
@@ -21,12 +22,13 @@ public import Lean.Elab.Tactic.Do.Internal.VCGen.Frontend
2122
The `mvcgen'` tactic, split across the modules above.
2223
2324
- `VCGen.Reduce` — SymM head-redex reducer.
24-
- `VCGen.SpecDB` — `SpecTheoremNew`/`SpecTheoremsNew` + database operations.
25+
- `VCGen.SpecDB` — `SpecTheorem` instantiation, simp-side migration, and `findSpecs` lookup.
2526
- `VCGen.RuleConstruction` — SymM rule constructors from spec/simp/split info.
2627
- `VCGen.Context` — `VCGenM`, its `Context`/`State`, the bundle of pre-built rules.
27-
- `VCGen.Util` — generic VCGenM helpers (`introsSimp`, `repeatAndRfl`, app builders).
28+
- `VCGen.EPost` — exception-postcondition decomposition helpers.
29+
- `VCGen.Util` — generic VCGenM helpers (`introsHygienic`, `simpGoalTelescope`, `solveTrivialConjuncts`).
2830
- `VCGen.RuleCache` — VCGenM cache wrappers around the SymM rule constructors.
29-
- `VCGen.Entails` — entailment-shaped goal decomposition (`solveSPredEntails` etc.).
31+
- `VCGen.Entails` — entailment-shaped goal decomposition (Triple unfolding, state/precondition intro, EPost and lattice steps).
3032
- `VCGen.Solve` — the main `solve` step / `SolveResult`.
3133
- `VCGen.Driver` — the worklist driver (`work`, `emitVC`, `main`, `Result`).
3234
- `VCGen.Frontend` — the `mvcgen'` syntax + tactic elaborator + `mkSpecContext`.

src/Lean/Elab/Tactic/Do/Internal/VCGen/Context.lean

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/-
22
Copyright (c) 2026 Lean FRO LLC. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
4-
Authors: Sebastian Graf
4+
Authors: Sebastian Graf, Vladimir Gladshtein
55
-/
66
module
77

@@ -14,8 +14,7 @@ public import Lean.Meta.Sym.Simp.SimpM
1414
public import Lean.Meta.Tactic.Grind.Types
1515

1616
open Lean Meta Elab Tactic Sym
17-
open Lean.Elab.Tactic.Do Lean.Elab.Tactic.Do.SpecAttr
18-
open Std.Do
17+
open Lean.Elab.Tactic.Do Lean.Elab.Tactic.Do.Internal.SpecAttr
1918

2019
namespace Lean.Elab.Tactic.Do.Internal
2120

@@ -45,48 +44,69 @@ public def UntilPatternThunk.force (ref : IO.Ref UntilPatternThunk) (m : Expr) :
4544
ref.set (.elaborated pat)
4645
return pat
4746

47+
/--
48+
Common metadata for a goal whose right-hand side is a weakest-precondition application
49+
`pre ⊑ wp m Pred EPred monadInst instAL instEAL instWP α prog post epost s₁ ... sₙ`.
50+
-/
51+
public structure VCGen.WPInfo where
52+
/-- The `wp` function head, separated from its explicit core arguments. -/
53+
head : Expr
54+
/-- The ordered core arguments of the `wp` application:
55+
`#[m, Pred, EPred, monadInst, instAL, instEAL, instWP, α, prog, post, epost]`. -/
56+
args : Array Expr
57+
/-- Extra arguments applied after `wp … prog post epost`, usually concrete state arguments. -/
58+
excessArgs : Array Expr
59+
60+
namespace VCGen.WPInfo
61+
62+
/-- Monad type constructor argument of `wp`. -/
63+
public def m (info : WPInfo) : Expr := info.args[0]!
64+
/-- Predicate/lattice type argument of `wp`. -/
65+
public def Pred (info : WPInfo) : Expr := info.args[1]!
66+
/-- Exception postcondition type argument of `wp`. -/
67+
public def EPred (info : WPInfo) : Expr := info.args[2]!
68+
/-- `WPMonad` instance argument of `wp`. -/
69+
public def instWP (info : WPInfo) : Expr := info.args[6]!
70+
/-- Program expression classified by VCGen. -/
71+
public def prog (info : WPInfo) : Expr := info.args[8]!
72+
73+
end VCGen.WPInfo
74+
75+
/-- Pre-built backward rules for the introduction steps of `solve`. -/
76+
public structure VCGen.IntroRules where
77+
/-- The backward rule for `Triple.intro`. Unfolds `⦃P⦄ x ⦃Q; E⦄` into `P ⊑ wp x Q E`. -/
78+
tripleIntro : BackwardRule
79+
/-- The backward rule for `Lean.Order.le_of_forall_le`. Peels one excess state argument
80+
from a function-lattice entailment. -/
81+
stateArgIntro : BackwardRule
82+
/-- The backward rule for `Lean.Order.le_of_imp_top_le`. Introduces a bare pure
83+
precondition on the `Prop` lattice. -/
84+
propPreIntro : BackwardRule
85+
/-- The backward rule for `Lean.Order.ofProp_le`. Introduces an embedded pure
86+
precondition `⌜p⌝` on any complete lattice. -/
87+
ofPropPreIntro : BackwardRule
88+
/-- The backward rule for `Lean.Order.true_le_of_top_le`. Replaces a `True` precondition
89+
with `⊤` on the `Prop` lattice. -/
90+
truePreIntro : BackwardRule
91+
4892
public structure VCGen.Context where
49-
/-- The backward rule for `SPred.entails_cons_intro`. -/
50-
entailsConsIntroRule : BackwardRule
51-
/-- The backward rule for `SPred.entails_nil_pure_intro`. Preferred over `entails_nil_intro`
52-
when the LHS is `⌜φ⌝`, as it unwraps `.down` on the pure assertion. -/
53-
entailsNilPureIntroRule : BackwardRule
54-
/-- The backward rule for `SPred.entails_nil_intro`. Fallback when LHS is not `⌜φ⌝`. -/
55-
entailsNilIntroRule : BackwardRule
56-
/-- The backward rule for `SPred.apply_pure_cons_entails_l`. Peels a state arg from
57-
`SPred.pure (σ::σs) φ s` on the LHS of an entailment. -/
58-
applyPureConsEntailsLRule : BackwardRule
59-
/-- The backward rule for `SPred.apply_pure_cons_entails_r`. Peels a state arg from
60-
`SPred.pure (σ::σs) φ s` on the RHS of an entailment. -/
61-
applyPureConsEntailsRRule : BackwardRule
62-
/-- The backward rule for `SPred.down_pure_intro`. Reduces a target of the form
63-
`(SPred.pure [] φ).down` to `φ`. -/
64-
downPureIntroRule : BackwardRule
65-
/-- The backward rule for `SPred.pure_elim'`. -/
66-
pureElimRule : BackwardRule
67-
/-- The backward rule for `SPred.pure_intro`. -/
68-
pureIntroRule : BackwardRule
69-
/-- The backward rule for `PostCond.entails.rfl`. Tried first to close by reflexivity. -/
70-
postCondEntailsRflRule : BackwardRule
71-
/-- The backward rule for `PostCond.entails.mk`. -/
72-
postCondEntailsMkRule : BackwardRule
73-
/-- The backward rule for `ExceptConds.entails.rfl`. -/
74-
exceptCondsEntailsRflRule : BackwardRule
75-
/-- The backward rule for `ExceptConds.entails.pure`. Closes the exception side for
76-
pure PostShapes, where `ExceptConds.entails` reduces to `True`. -/
77-
exceptCondsEntailsPureRule : BackwardRule
78-
/-- The backward rule for `ExceptConds.entails_false`. -/
79-
exceptCondsEntailsFalseRule : BackwardRule
80-
/-- The backward rule for `ExceptConds.entails_true`. -/
81-
exceptCondsEntailsTrueRule : BackwardRule
82-
/-- The backward rule for `Triple.of_entails_wp`. -/
83-
tripleOfEntailsWPRule : BackwardRule
93+
/-- Pre-built backward rules for the introduction steps of `solve`. -/
94+
introRules : VCGen.IntroRules
95+
/-- The backward rule for `Lean.Order.top_le_prop`. Strips a `(⊤ : Prop) ⊑ ·` wrapper
96+
from a VC before it is emitted. -/
97+
elimPreRule : BackwardRule
8498
/-- The backward rule for `And.intro`. -/
8599
andIntroRule : BackwardRule
100+
/-- The backward rule for `Lean.Order.PartialOrder.rel_refl`. Closes a reflexive
101+
entailment `pre ⊑ pre`. -/
102+
reflRule : BackwardRule
103+
/-- The backward rule for `meet_top_le_of_le`. Cancels a redundant `⊓ ⊤` on the left of an
104+
entailment, turning `P ⊓ ⊤ ⊑ Q` into `P ⊑ Q`. -/
105+
meetTopRule : BackwardRule
86106
/-- User-customizable simp methods used to pre-simplify hypotheses. -/
87107
hypSimpMethods : Option Sym.Simp.Methods := none
88108
/-- The `trivial` config option: when `true` (default), `Driver.emitVC` runs
89-
`repeatAndRfl` to collapse trivial `And.intro` chains; when `false`, the goal is
109+
`solveTrivialConjuncts` to collapse trivial `And.intro` chains; when `false`, the goal is
90110
emitted as-is. -/
91111
trivial : Bool := true
92112
/-- The `jp` config option: when `true`, `tryLetIntro` recognises `__do_jp` lets
@@ -119,18 +139,21 @@ public structure VCGen.Context where
119139

120140
public structure VCGen.Scope where
121141
/-- Spec database in scope: globals plus locals from in-scope hypotheses. -/
122-
specs : SpecTheoremsNew
142+
specs : SpecTheorems
123143
/-- `__do_jp` fvars currently in scope. -/
124144
jps : FVarIdMap JumpSiteInfo := {}
145+
/-- The most recently lifted pure precondition. `tryLiftedHyp` closes handoff VCs against
146+
it without walking the local context. -/
147+
lastLiftedPre? : Option FVarId := none
125148
/-- Index of the next local declaration to consider for local specs. -/
126149
nextDeclIdx : Nat := 0
127150
deriving Inhabited
128151

129152
public structure VCGen.State where
130153
/--
131154
A cache mapping registered SpecThms to their backward rule to apply.
132-
The particular rule depends on the theorem name, the monad and the number of excess state
133-
arguments that the weakest precondition target is applied to.
155+
The particular rule depends on the theorem name, the `WPMonad` instance and the number of
156+
excess state arguments that the weakest precondition target is applied to.
134157
-/
135158
specBackwardRuleCache : Std.HashMap (Name × Expr × Nat) BackwardRule := {}
136159
/--
@@ -140,6 +163,12 @@ public structure VCGen.State where
140163
-/
141164
splitBackwardRuleCache : Std.HashMap (Name × Expr × Nat) BackwardRule := {}
142165
/--
166+
A cache mapping lattice connectives to their backward rule to apply.
167+
The particular rule depends on the rule name, the monad, and the number of excess state
168+
arguments that the weakest precondition target is applied to.
169+
-/
170+
latticeBackwardRuleCache : Std.HashMap (Name × Array Expr × Nat) BackwardRule := {}
171+
/--
143172
Holes of type `Invariant` that have been generated so far.
144173
-/
145174
invariants : Array MVarId := #[]
@@ -178,8 +207,8 @@ public def Scope.registerJP (s : Scope) (fv : FVarId) (info : JumpSiteInfo) : Sc
178207
public def Scope.knownJP? (s : Scope) (fv : FVarId) : Option JumpSiteInfo :=
179208
s.jps.get? fv
180209

181-
public def Scope.insertSpec (s : Scope) (thm : SpecTheoremNew) : Scope :=
182-
{ s with specs := { s.specs with specs := Sym.insertPattern s.specs.specs thm.pattern thm } }
210+
public def Scope.insertSpec (s : Scope) (thm : SpecTheorem) : Scope :=
211+
{ s with specs := s.specs.insert thm }
183212

184213
/-- Walk `goal`'s local context from `scope.nextDeclIdx` onward, registering any
185214
spec-shaped hypotheses as local specs. Advances `nextDeclIdx` to the current
@@ -191,7 +220,7 @@ public def Scope.collectLocalSpecs (scope : Scope) (goal : MVarId) : VCGenM Scop
191220
let scope ← lctx.foldlM (init := scope) (start := scope.nextDeclIdx) fun scope decl => do
192221
if decl.isAuxDecl then return scope
193222
try
194-
if let some thm ← mkSpecTheoremNew (.local decl.fvarId) (eval_prio low) then
223+
if let some thm ← mkSpecTheoremFromLocal decl.fvarId (eval_prio low) then
195224
return scope.insertSpec thm
196225
catch _ => pure ()
197226
return scope

src/Lean/Elab/Tactic/Do/Internal/VCGen/Driver.lean

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ public import Lean.Meta.Sym.Grind
1313

1414
open Lean Meta Elab Tactic Sym
1515
open Lean.Elab.Tactic.Do.SpecAttr
16-
open Std.Do
1716

1817
namespace Lean.Elab.Tactic.Do.Internal
1918

@@ -82,15 +81,18 @@ private def handleInvariantSubgoals (subgoals : List MVarId) : VCGenM (Array MVa
8281
Called when decomposing the goal further did not succeed; in this case we emit a VC for the goal.
8382
Invariant subgoals are handled separately by `handleInvariantSubgoals` directly inside `work`,
8483
so they never reach this path.
84+
If the goal is `(⊤ : Prop) ⊑ φ`, the `⊤ ⊑` wrapper is stripped first.
8585
-/
8686
public def emitVC (goal : Grind.Goal) : VCGenM Unit := do
87-
let goal ← processHypotheses goal
87+
let mvarId ← elimTopPre goal.mvarId
88+
let mut goal := { goal with mvarId }
89+
goal ← processHypotheses goal
8890
if goal.inconsistent then return
89-
-- `trivial`: when false, skip `repeatAndRfl` (which collapses And-chains via rfl);
91+
-- `trivial`: when false, skip `solveTrivialConjuncts` (which collapses And-chains via rfl);
9092
-- emit the goal as-is.
9193
let mvarId ←
9294
if (← read).trivial then
93-
let some mvarId ← repeatAndRfl goal.mvarId | return
95+
let some mvarId ← solveTrivialConjuncts goal.mvarId | return
9496
pure mvarId
9597
else
9698
pure goal.mvarId
@@ -109,20 +111,16 @@ public def work (scope : Scope) (goal : Grind.Goal) : VCGenM Unit := do
109111
let goal := s.goal
110112
if goal.inconsistent then continue
111113
match ← solve s.scope goal.mvarId with
112-
| .stop =>
114+
| .stop _reason =>
113115
emitVC goal
114-
| .noEntailment .. | .noProgramFoundInTarget .. =>
115-
emitVC goal
116-
| .noSpecFoundForProgram prog monad thms =>
117-
if (← read).errorOnMissingSpec then goal.mvarId.withContext do
116+
| .noSpecFoundForProgram prog monad thms => goal.mvarId.withContext do
117+
if (← read).errorOnMissingSpec then
118118
if thms.isEmpty then
119119
throwError "No spec found for program {prog}."
120120
else
121121
throwError "No spec matching the monad {monad} found for program {prog}. Candidates were {thms.map (·.proof)}."
122122
else
123123
emitVC goal
124-
| .noStrategyForProgram prog => goal.mvarId.withContext do
125-
throwError "Did not know how to decompose weakest precondition for {prog}"
126124
| .goals scope subgoals =>
127125
-- Handle invariant subgoals eagerly here, so that VC subgoals popped
128126
-- from the worklist later see the invariant MVar already assigned.
@@ -150,7 +148,7 @@ public structure Result where
150148
inlineHandledInvariants : Std.HashSet Nat := {}
151149

152150
/--
153-
Generate verification conditions for a goal of the form `P ⊢ₛ wp⟦e⟧ Q s₁ ... sₙ` by repeatedly
151+
Generate verification conditions for a goal of the form `pre ⊑ wp e post epost s₁ ... sₙ` by repeatedly
154152
decomposing `e` using registered `@[spec]` theorems.
155153
Return the VCs and invariant goals.
156154
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/-
2+
Copyright (c) 2026 Lean FRO LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Vladimir Gladshtein, Sebastian Graf
5+
-/
6+
module
7+
8+
prelude
9+
public import Lean.Meta.Sym
10+
public import Std.Internal.Do
11+
public import Lean.Meta.Tactic.Replace
12+
13+
open Lean Meta Sym
14+
open Std.Internal.Do Lean.Order
15+
16+
namespace Lean.Elab.Tactic.Do.Internal
17+
18+
namespace VCGen
19+
20+
/-!
21+
Helpers for decomposing exception-postcondition (`EPost`) goals: reducing an `EPost.Cons.head`
22+
projection in the RHS of an entailment to the underlying component. The concrete case
23+
(`epost⟨…⟩`) is handled by `mkEPostAtIndex`/`peelEPostTailChain`; the `⊥` case is handled by
24+
`replaceEPostHeadBot?`, which rewrites `pre ⊑ ⊥.head x₁ … xₙ` to `pre ⊑ ⊥`.
25+
-/
26+
27+
/-- Get the `index`-th component from an `EPost` target. -/
28+
public def mkEPostAtIndex (target : Expr) (index : Nat) : SymM (Option Expr) := do
29+
let mut curr := target
30+
for _ in [:index] do
31+
let_expr EPost.Cons.mk _ _ _ tail := curr | return none
32+
curr := tail
33+
let_expr EPost.Cons.mk _ _ head _ := curr | return none
34+
return head
35+
36+
/-- Peel a chain of `.tail` projections, returning the base `EPost` and the number of tails. -/
37+
public partial def peelEPostTailChain (curr : Expr) (idx : Nat := 0) : Expr × Nat :=
38+
curr.consumeMData.withApp fun fn args =>
39+
if fn.isConstOf ``EPost.Cons.tail && args.size > 0 then
40+
peelEPostTailChain args[args.size - 1]! (idx + 1)
41+
else
42+
(curr, idx)
43+
44+
/--
45+
When the exception postcondition is `⊥`, the RHS of a `pre ⊑ EPost.Cons.head ⊥ x₁ … xₙ` goal is
46+
propositionally—but not definitionally—equal to `⊥`. This rewrites the goal to `pre ⊑ ⊥`,
47+
constructing the equality proof from `EPost.Cons.head_bot` (for the head projection) and
48+
`bot_apply` (for each excess argument).
49+
50+
The proof term is built directly with `mkApp`/`mkConst` and instances extracted from the existing
51+
goal, avoiding `mkAppM`/instance synthesis (which is both expensive and unable to unify `max`-of-
52+
universe-variable instance levels in the abstract-monad setting).
53+
54+
`head`/`args` come from `rhs.withApp`, where `rhs = EPost.Cons.head eh et ⊥ x₁ … xₙ`, and `target`
55+
is the full `pre ⊑ rhs` entailment. Returns the rewritten goal, or `none` if `args[2]` is not `⊥`
56+
or the lattice instances are not in the expected shape (the caller then falls through). -/
57+
public def replaceEPostHeadBot? (goal : MVarId) (target head : Expr) (args : Array Expr) :
58+
SymM (Option MVarId) := do
59+
let some eh := args[0]? | return none
60+
let some et := args[1]? | return none
61+
let some epostArg := args[2]? | return none
62+
-- `epostArg = @bot (EPost.Cons eh et) (scopedCCPO (instCompleteLattice eh et chInst ctInst))`
63+
let_expr Lean.Order.bot _ botCCPOInst := epostArg | return none
64+
unless botCCPOInst.isApp do return none
65+
let clArgs := botCCPOInst.appArg!.getAppArgs
66+
unless clArgs.size ≥ 2 do return none
67+
let chInst := clArgs[clArgs.size - 2]!
68+
let ctInst := clArgs[clArgs.size - 1]!
69+
let [u, v] := head.constLevels! | return none
70+
-- `p0 : EPost.Cons.head ⊥ = (⊥ : eh)`
71+
let headBot := mkApp3 head eh et epostArg
72+
let p0 := mkApp4 (mkConst ``EPost.Cons.head_bot [u, v]) eh et chInst ctInst
73+
let some (_, _, b0) := (← Sym.inferType p0).eq? | return none
74+
-- Fold `bot_apply` over the excess arguments: `acc : EPost.Cons.head ⊥ x₁ … xᵢ = ⊥`.
75+
let mut acc := p0
76+
let mut curHead := headBot
77+
let mut curBot := b0
78+
let mut curTy := eh
79+
let mut curCL := chInst
80+
for x in args.extract 3 args.size do
81+
let .forallE _ σ τ _ := curTy | return none
82+
if τ.hasLooseBVars then return none
83+
unless curCL.isAppOf ``Lean.Order.instCompleteLatticePi do return none
84+
let .lam _ _ τCL _ := curCL.appArg! | return none
85+
let uσ ← Sym.getLevel σ
86+
let uτ ← Sym.getLevel τ
87+
-- `bfa : (⊥ : σ → τ) x = (⊥ : τ)`
88+
let bfa := mkApp4 (mkConst ``Lean.Order.bot_apply [← decLevel uσ, ← decLevel uτ]) σ τ τCL x
89+
let some (_, _, newBot) := (← Sym.inferType bfa).eq? | return none
90+
-- `Eq.trans (congrFun acc x) bfa : curHead x = ⊥`
91+
let cf := mkApp6 (mkConst ``congrFun [uσ, uτ]) σ (.lam `x σ τ .default) curHead curBot acc x
92+
acc := mkApp6 (mkConst ``Eq.trans [uτ]) τ (mkApp curHead x) (mkApp curBot x) newBot cf bfa
93+
curHead := mkApp curHead x
94+
curBot := newBot
95+
curTy := τ
96+
curCL := τCL
97+
-- `acc : rhs = ⊥`; lift through `pre ⊑ ·` and replace the target.
98+
let relArgs := target.getAppArgs
99+
let some α := relArgs[0]? | return none
100+
let some inst := relArgs[1]? | return none
101+
let some pre := relArgs[2]? | return none
102+
let relPre := mkApp3 target.getAppFn α inst pre
103+
let uα ← Sym.getLevel α
104+
let eqProof :=
105+
mkApp6 (mkConst ``congrArg [uα, .succ .zero]) α (mkSort .zero) (mkAppN head args) curBot relPre acc
106+
return some (← goal.replaceTargetEq (mkApp relPre curBot) eqProof)
107+
108+
end VCGen
109+
110+
end Lean.Elab.Tactic.Do.Internal

0 commit comments

Comments
 (0)