@@ -447,14 +447,11 @@ private def applyFrame (scope : VCGen.Scope) (goal : MVarId) (pre : Expr) (info
447447 | return .notFramed goal info
448448 -- `info.args.take 7` are the program's own `wp` arguments (program type, value, assertions, `WP`
449449 -- instance); `mkAppOptM` synthesizes the remaining instances against the assertion's own
450- -- `CompleteLattice` so the framing shares the structure the program's `wp` uses.
450+ -- `CompleteLattice` so the framing shares the structure the program's `wp` uses. The lattice meet
451+ -- is just the instance `op := (· ⊓ ·)`; its residual folds to `⇨` (see `foldUpperAdjointMeet?`).
451452 let specProof ←
452- if op.eta.getAppFn.isConstOf ``Lean.Order.meet then
453- Meta.mkAppOptM ``Std.Internal.Do.Gadget.meet_wp_imp_le_wp_skipFrame
454- ((info.args.take 7 ).map some ++ #[none, some F])
455- else
456- Meta.mkAppOptM ``Std.Internal.Do.Gadget.wp_imp_le_wp_skipFrame
457- ((info.args.take 7 ).map some ++ #[none, some op, none, some F])
453+ Meta.mkAppOptM ``Std.Internal.Do.Gadget.op_wp_upperAdjoint_le_wp_skipFrame
454+ ((info.args.take 7 ).map some ++ #[none, some op, none, some F])
458455 let some specThm ← mkSpecTheoremFromStx (← getRef) specProof
459456 | throwError "frame: could not build spec from the frame gadget for{indentExpr info.prog}"
460457 let some rule ← (tryMkBackwardRuleFromSpec specThm info).run
@@ -463,6 +460,21 @@ private def applyFrame (scope : VCGen.Scope) (goal : MVarId) (pre : Expr) (info
463460 | throwError "frame: failed to apply rule for{indentExpr info.prog}"
464461 return .framed scope subgoals
465462
463+ /-- Fold a frame residual `upperAdjoint (meet F) b` on the RHS to Heyting `F ⇨ b` (definitionally
464+ equal: `himp` *is* the meet upper adjoint), exposing the meet operand `F` so the `himp` split can
465+ decompose it. This lets the lattice meet be framed by the general gadget like any operator, with no
466+ meet-specific spec; a non-meet operator's residual is left for its registered `impSplit`. -/
467+ private def foldUpperAdjointMeet? (goal : MVarId) (target rhs : Expr) : VCGenM (Option MVarId) := do
468+ unless rhs.isAppOf ``Lean.Order.PreservesSup.upperAdjoint do return none
469+ let args := rhs.getAppArgs
470+ let some slice := args[2 ]? | return none
471+ unless slice.isAppOf ``Lean.Order.meet && slice.getAppNumArgs == 3 do return none
472+ let himpExpr ← Meta.mkAppM ``Lean.Order.himp #[slice.appArg!, args[3 ]!]
473+ let newRhs ← mkAppNS himpExpr (args.extract 4 args.size)
474+ let relArgs := target.getAppArgs
475+ let newTarget ← mkAppNS target.getAppFn (relArgs.set! (relArgs.size - 1 ) newRhs)
476+ return some (← goal.replaceTargetDefEq newTarget)
477+
466478/--
467479The main VC generation step. Operates on a plain `MVarId` with no knowledge of grind.
468480Returns `.goals subgoals` when the goal was decomposed, or a classification result
@@ -522,6 +534,7 @@ public def solve (scope : VCGen.Scope) (goal : MVarId) : VCGenM SolveResult := g
522534 -- Phase 3: shape the `rhs` (reduce an EPost projection, decompose a lattice connective), then
523535 -- discharge a residual entailment against the lifted hypothesis.
524536 if let some g ← reduceEPostHead? goal target α inst pre rhs then return .goals scope [g]
537+ if let some g ← foldUpperAdjointMeet? goal target rhs then return .goals scope [g]
525538 if let some gs ← splitLatticeOp? goal rhs then return .goals scope gs
526539 if let some gs ← liftedHyp? scope goal α pre rhs then return .goals scope gs
527540
0 commit comments