@@ -122,46 +122,48 @@ private def tryRfl (goal : MVarId) (target pre rhs : Expr) : VCGenM (Option (Lis
122122 goal.assign (mkApp3 refl relArgs[0 ]! relArgs[1 ]! pre)
123123 return some []
124124
125- /-- Strategy 5: close `pre ⊑ rhs` against the most recently lifted pure precondition,
126- cached in `Scope.lastLiftedPre?`. This is one defeq check against one hypothesis, not an
127- assumption search.
125+ /-- Strategy 10: close `pre ⊑ φ` on the `Prop` lattice against the most recently lifted pure
126+ precondition, cached in `Scope.lastLiftedPre?`. Runs after lattice decomposition, so `φ` is an
127+ opaque proposition rather than a lattice connective. This is one defeq check against one
128+ hypothesis, not an assumption search.
128129
129130Goals whose RHS contains metavariables are skipped: the defeq check could otherwise assign
130131a post abstraction from an unrelated hypothesis. -/
131132private def tryLiftedHyp (scope : VCGen.Scope) (goal : MVarId) (α pre rhs : Expr) :
132133 VCGenM (Option (List MVarId)) :=
133134 goal.withContext do
135+ unless α.isProp do return none
134136 if rhs.hasExprMVar then return none
135137 let some fvarId := scope.lastLiftedPre? | return none
136138 let some hyp := (← getLCtx).find? fvarId | return none
137- let proof? ← match_expr rhs with
138- | CompleteLattice.ofProp _l _inst φ => do
139- if ← isDefEqS φ hyp.type then
140- pure (some (← mkAppM ``Lean.Order.le_ofProp #[pre, φ, hyp.toExpr]))
141- else
142- pure none
143- | _ => do
144- if α.isProp then
145- if ← isDefEqS rhs hyp.type then
146- pure (some (← mkAppM ``Lean.Order.le_prop #[pre, rhs, hyp.toExpr]))
147- else
148- pure none
149- else
150- pure none
151- let some proof := proof? | return none
139+ unless ← isDefEqS rhs hyp.type do return none
152140 trace[Elab.Tactic.Do.vcgen] "Solved by lifted hypothesis { hyp.userName} "
153- goal.assign proof
141+ goal.assign (← mkAppM ``Lean.Order.le_prop #[pre, rhs, hyp.toExpr])
154142 return some []
155143
156- /-- Strategy 6: lift an embedded pure precondition `⌜φ⌝` into the local context, leaving `⊤`
144+ /-- Close a bare `Prop` residual, such as the subgoal of the `⌜φ⌝` lattice rule, against the
145+ most recently lifted pure precondition. Runs when the target is not a lattice entailment,
146+ just before it would be classified as a VC. -/
147+ private def tryLiftedHypBare (scope : VCGen.Scope) (goal : MVarId) (target : Expr) :
148+ VCGenM (Option (List MVarId)) :=
149+ goal.withContext do
150+ if target.hasExprMVar then return none
151+ let some fvarId := scope.lastLiftedPre? | return none
152+ let some hyp := (← getLCtx).find? fvarId | return none
153+ unless ← isDefEqS target hyp.type do return none
154+ trace[Elab.Tactic.Do.vcgen] "Solved by lifted hypothesis { hyp.userName} "
155+ goal.assign hyp.toExpr
156+ return some []
157+
158+ /-- Strategy 5: lift an embedded pure precondition `⌜φ⌝` into the local context, leaving `⊤`
157159as the residual precondition. Runs before state-argument introduction, which would otherwise
158160leave `⌜φ⌝` applied to the introduced arguments. Returns the new goal and the hypothesis. -/
159161private def tryOfPropPreIntro (goal : MVarId) (pre : Expr) : VCGenM (Option (MVarId × FVarId)) := do
160162 let_expr CompleteLattice.ofProp _l _inst φ := pre | return none
161163 if φ.isTrue then return none
162164 return some (← introPre (ofProp := true ) goal)
163165
164- /-- Strategy 8 : decompose a supported lattice connective on the RHS. -/
166+ /-- Strategy 9 : decompose a supported lattice connective on the RHS. -/
165167private def tryLattice (goal : MVarId) (target rhs : Expr) (preIsTop : Bool) :
166168 VCGenM (Option (List MVarId)) := do
167169 solveLatticeConnective? goal target rhs preIsTop
@@ -197,13 +199,13 @@ private def substOrHoistLet (goal : MVarId) (target : Expr) (info : WPInfo)
197199 | throwError "Failed to intro hoisted let"
198200 return goal
199201
200- /-- Strategy 7a : hoist or zeta-substitute a `let` from the program head. -/
202+ /-- Strategy 11a : hoist or zeta-substitute a `let` from the program head. -/
201203private def tryWPLet (goal : MVarId) (target : Expr) (info : WPInfo) : VCGenM (Option MVarId) := do
202204 let .letE name type val body nondep := info.prog.getAppFn | return none
203205 return some (← substOrHoistLet goal target info name type val body nondep
204206 info.prog.getAppRevArgs)
205207
206- /-- Strategy 7b : split an `ite`/`dite`/match program, or iota-reduce a matcher with a concrete
208+ /-- Strategy 11b : split an `ite`/`dite`/match program, or iota-reduce a matcher with a concrete
207209discriminant. -/
208210private def tryWPMatch (goal : MVarId) (target : Expr) (info : WPInfo) :
209211 VCGenM (Option (List MVarId)) := do
@@ -222,7 +224,7 @@ private def tryWPMatch (goal : MVarId) (target : Expr) (info : WPInfo) :
222224 | .closed => continue
223225 return some simpGoals.toList
224226
225- /-- Strategy 7c : zeta-unfold a local let-bound fvar used as the program head. -/
227+ /-- Strategy 11c : zeta-unfold a local let-bound fvar used as the program head. -/
226228private def tryWPFVarZeta (goal : MVarId) (target : Expr) (info : WPInfo) :
227229 VCGenM (Option MVarId) := do
228230 let f := info.prog.getAppFn
@@ -232,7 +234,7 @@ private def tryWPFVarZeta (goal : MVarId) (target : Expr) (info : WPInfo) :
232234 let prog ← shareCommonInc (val.betaRev info.prog.getAppRevArgs)
233235 return some (← replaceProgDefEq goal target info prog)
234236
235- /-- Strategy 7d : reduce a projection head in the program. -/
237+ /-- Strategy 11d : reduce a projection head in the program. -/
236238private def tryWPHeadReduce (goal : MVarId) (target : Expr) (info : WPInfo) :
237239 VCGenM (Option MVarId) := do
238240 let f := info.prog.getAppFn
@@ -242,7 +244,7 @@ private def tryWPHeadReduce (goal : MVarId) (target : Expr) (info : WPInfo) :
242244 let prog ← betaRevS f' info.prog.getAppRevArgs
243245 return some (← replaceProgDefEq goal target info prog)
244246
245- /-- Strategy 7e : look up a registered `@[spec]` theorem (triple or simp) for the program head
247+ /-- Strategy 11e : look up a registered `@[spec]` theorem (triple or simp) for the program head
246248and apply its cached backward rule. -/
247249private def applySpec (scope : VCGen.Scope) (goal : MVarId) (target : Expr) (info : WPInfo) :
248250 VCGenM SolveResult := do
@@ -293,23 +295,24 @@ The function performs the following steps in order:
2932952. **Target-let handling** : zeta-substitute duplicable top-level lets, otherwise introduce them.
2942963. **Triple unfolding** : If the target is `⦃P⦄ x ⦃Q; E⦄`, unfold into `P ⊑ wp x Q E`.
2952974. **Syntactic rfl** : close `pre ⊑ rhs` by `PartialOrder.rel_refl` when both sides unify.
296- 5. **Lifted-hypothesis discharge** : close `pre ⊑ rhs` against the most recently lifted
297- precondition, cached in `Scope.lastLiftedPre?`.
298- 6. **Embedded pure precondition introduction** : lift a `⌜φ⌝` precondition into the local
298+ 5. **Embedded pure precondition introduction** : lift a `⌜φ⌝` precondition into the local
299299 context, before state-argument introduction would apply it to the introduced arguments.
300- 7 . **State-argument introduction** : If the lattice carrier is a function type
300+ 6 . **State-argument introduction** : If the lattice carrier is a function type
301301 `σ₁ → ... → σₙ → Base`, introduce all excess state arguments.
302- 8. **Bare pure precondition introduction** : on the `Prop` lattice, lift a non-`⊤`
303- precondition into the local context.
304- 9. **EPost projection reduction** : reduce an `EPost.Cons.head` RHS to the projected component.
305- 10. **Lattice decomposition** : decompose `⊓`, `⇨`, `⌜p⌝` and `⊤` RHS connectives.
302+ 7. **Bare pure precondition introduction** : on the `Prop` lattice, replace a `True`
303+ precondition by `⊤` and lift any other precondition into the local context.
304+ 8. **EPost projection reduction** : reduce an `EPost.Cons.head` RHS to the projected component.
305+ 9. **Lattice decomposition** : decompose `⊓`, `⇨`, `⌜p⌝` and `⊤` RHS connectives.
306+ 10. **Lifted-hypothesis discharge** : close a residual `pre ⊑ φ` entailment against the most
307+ recently lifted precondition, cached in `Scope.lastLiftedPre?`.
30630811. **WP decomposition** : when the RHS is `wp e post epost s₁ ... sₙ`, in order:
307309 hoist/zeta program-head lets, split `ite`/`dite`/match, zeta-unfold fvar program heads,
308310 reduce projection heads, and finally apply a registered `@[spec]` theorem.
309311
310- Pure preconditions are lifted as soon as they arise (steps 6 and 8). The lifted hypothesis is
311- cached in `Scope.lastLiftedPre?`, and step 5 closes the handoff VCs of subsequent spec
312- applications against it with a single defeq check.
312+ Pure preconditions are lifted as soon as they arise (steps 5 and 7). The lifted hypothesis is
313+ cached in `Scope.lastLiftedPre?`. Step 10 closes residual entailments against it with a single
314+ defeq check, and bare `Prop` targets, such as the subgoal of the `⌜φ⌝` lattice rule, are
315+ checked against it before being classified as VCs.
313316-/
314317public def solve (scope : VCGen.Scope) (goal : MVarId) : VCGenM SolveResult := goal.withContext do
315318 if ← outOfFuel then return .stop
@@ -329,11 +332,12 @@ public def solve (scope : VCGen.Scope) (goal : MVarId) : VCGenM SolveResult := g
329332 if let some g ← tryTripleUnfold goal target then return .goals scope [g]
330333 if let some g ← tryBareWP goal target then return .goals scope [g]
331334
332- let_expr PartialOrder.rel α inst pre rhs := target | return .noEntailment target
335+ let some (α, inst, pre, rhs) := target.app4? ``PartialOrder.rel
336+ | if let some gs ← tryLiftedHypBare scope goal target then return .goals scope gs
337+ else return .noEntailment target
333338 let preIsTop := pre.isAppOf ``Lean.Order.top && pre.getAppNumArgs == 2
334339
335340 if let some gs ← tryRfl goal target pre rhs then return .goals scope gs
336- if let some gs ← tryLiftedHyp scope goal α pre rhs then return .goals scope gs
337341
338342 -- Phase 2: lift pure preconditions as they arise, introduce excess state arguments, and
339343 -- reduce EPost projections, so that the RHS exposes a lattice connective or a `wp`
@@ -342,6 +346,10 @@ public def solve (scope : VCGen.Scope) (goal : MVarId) : VCGenM SolveResult := g
342346 return .goals { scope with lastLiftedPre? := some h } [g]
343347 if α.isForall then return .goals scope [← introStateArgs preIsTop goal]
344348 if !preIsTop && α.isProp then
349+ if pre.isTrue then
350+ let .goals [g] ← (← read).introRules.truePreIntro.applyChecked goal
351+ | throwError "Failed to apply {.ofConstName ``Lean.Order.true_le} to{indentExpr target}"
352+ return .goals scope [g]
345353 let (g, h) ← introPre (ofProp := false ) goal
346354 return .goals { scope with lastLiftedPre? := some h } [g]
347355 if let some g ← reduceEPostHead? goal target α inst pre rhs then return .goals scope [g]
@@ -350,8 +358,10 @@ public def solve (scope : VCGen.Scope) (goal : MVarId) : VCGenM SolveResult := g
350358 -- (`tryWPMatch`, `tryLattice`) or apply a registered spec (`applySpec`).
351359 let scope ← scope.collectLocalSpecs goal
352360
353- -- Phase 3: lattice connective decomposition.
361+ -- Phase 3: lattice connective decomposition, then lifted-hypothesis discharge of the
362+ -- residual entailments.
354363 if let some gs ← tryLattice goal target rhs preIsTop then return .goals scope gs
364+ if let some gs ← tryLiftedHyp scope goal α pre rhs then return .goals scope gs
355365
356366 -- Phase 4: wp decomposition. The program-shape steps below all consume one unit of fuel
357367 -- (the `stepLimit` config option) when they make progress.
0 commit comments