From ff8a1e4955fd2a49168b800e03de2031bb746987 Mon Sep 17 00:00:00 2001 From: ayhon Date: Thu, 7 May 2026 17:22:41 +0200 Subject: [PATCH 01/76] refactor: make SelTarget type an distinct inductive type --- Iris/Iris/ProofMode/Patterns/SelPattern.lean | 19 ++++++++++++------- Iris/Iris/ProofMode/Tactics/Clear.lean | 4 +++- Iris/Iris/ProofMode/Tactics/Revert.lean | 4 ++-- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Iris/Iris/ProofMode/Patterns/SelPattern.lean b/Iris/Iris/ProofMode/Patterns/SelPattern.lean index b8c545f6..7dc6e154 100644 --- a/Iris/Iris/ProofMode/Patterns/SelPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SelPattern.lean @@ -51,23 +51,28 @@ partial def SelPat.parse (pats : TSyntaxArray `selPat) : MacroM (List SelPat) := public meta section +inductive SelId where +| lean (id : FVarId) +| pm (uniq : IVarId) +deriving BEq, Hashable, Repr + @[rocq_alias esel_pat] structure SelTarget where - target : IVarId ⊕ FVarId + target : SelId /- Was this target specified explicitly or is it from a glob like ∗? -/ explicit : Bool -/-- Resolve selection patterns to concrete proofmode hypotheses (`.inl`) and Lean locals (`.inr`). -/ +/-- Resolve selection patterns to concrete proofmode hypotheses (`.pm`) and Lean locals (`.lean`). -/ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget) | .ident name => - return [⟨.inl (← hyps.findWithInfo name), true⟩] + return [⟨.pm (← hyps.findWithInfo name), true⟩] | .leanIdent name => do let ldecl ← getLocalDeclFromUserName name.getId - return [⟨.inr ldecl.fvarId, true⟩] + return [⟨.lean ldecl.fvarId, true⟩] | .intuitionistic => - return hyps.intuitionisticIVarIds.map (⟨.inl ·, false⟩) + return hyps.intuitionisticIVarIds.map (⟨.pm ·, false⟩) | .spatial => - return hyps.spatialIVarIds.map (⟨.inl ·, false⟩) + return hyps.spatialIVarIds.map (⟨.pm ·, false⟩) | .pure => do -- `%` selects user-facing Lean pure assumptions, so we keep only `Prop` hypotheses. let mut hyps := #[] @@ -76,7 +81,7 @@ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget continue if ! (← isProp ldecl.type) then continue - hyps := hyps.push (⟨.inr ldecl.fvarId, false⟩) + hyps := hyps.push (⟨.lean ldecl.fvarId, false⟩) return hyps.toList def SelPat.resolve (hyps : Hyps bi e) (pats : List SelPat) : diff --git a/Iris/Iris/ProofMode/Tactics/Clear.lean b/Iris/Iris/ProofMode/Tactics/Clear.lean index 45cf6392..917321f2 100644 --- a/Iris/Iris/ProofMode/Tactics/Clear.lean +++ b/Iris/Iris/ProofMode/Tactics/Clear.lean @@ -52,7 +52,9 @@ elab "iclear" pats:(colGt selPat)+ : tactic => do let pats ← liftMacroM <| SelPat.parse pats ProofModeM.runTactic λ mvar { e, hyps, goal, .. } => do - let (ivars, fvars) := (← SelPat.resolve hyps pats).partitionMap (·.target) + let (ivars, fvars) := (← SelPat.resolve hyps pats).partitionMap fun + | ⟨.pm uniq, _⟩ => .inl uniq + | ⟨.lean id, _⟩ => .inr id -- Clear the selected Iris hypotheses first, updating the proof-mode context and proof term. let mut st : ClearState e goal := { e, hyps, pf := q(fun h => h) } diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 67af068c..7715f949 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -97,8 +97,8 @@ elab "irevert" pats:(colGt selPat)+ : tactic => do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do match target.target with - | .inl ivar => st.revertProofModeHyp ivar - | .inr fvar => st.revertLeanHyp fvar + | .pm ivar => st.revertProofModeHyp ivar + | .lean fvar => st.revertLeanHyp fvar let pf' ← addBIGoalWithoutFVars st.hyps st.goal st.reverted.reverse mvar.assign q($(st.pf) $pf') From 8c8b59f9fa0202f1fc8e2927bc19ffea6ca79a22 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:14:12 +0200 Subject: [PATCH 02/76] refactor: expose `irevert` as a metaprogramming API --- Iris/Iris/ProofMode/Tactics/Revert.lean | 33 ++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 7715f949..c4157974 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -89,16 +89,31 @@ private def RevertState.revertLeanHyp else st.revertLeanForallHyp f α -elab "irevert" pats:(colGt selPat)+ : tactic => do - let pats ← liftMacroM <| SelPat.parse pats - - ProofModeM.runTactic fun mvar { e, hyps, goal, .. } => do - let targets ← SelPat.resolve hyps pats - let init : RevertState e goal := { e, hyps, goal, pf := q(id) } - let st ← targets.reverse.foldlM (init := init) fun st target => do +def iRevertCore (mvar : MVarId) (g : IrisGoal) (targets : List SelTarget) : + ProofModeM (IrisGoal × Expr) := do + let {u, prop, bi, e, hyps, goal} := g + let init : RevertState e goal := { e, hyps, goal, pf := q(id) } + let st ← targets.reverse.foldlM (init := init) fun st target => do match target.target with | .pm ivar => st.revertProofModeHyp ivar | .lean fvar => st.revertLeanHyp fvar - let pf' ← addBIGoalWithoutFVars st.hyps st.goal st.reverted.reverse - mvar.assign q($(st.pf) $pf') + let pf' : Q($(st.e) ⊢ $(st.goal)) ← addBIGoalWithoutFVars st.hyps st.goal st.reverted.reverse + mvar.assign q($(st.pf) $pf') + let g := { + u := u, + bi := bi, + prop := prop, + e := st.e, + hyps := st.hyps, + goal := st.goal + : IrisGoal + } + return (g, pf') + +elab "irevert" pats:(colGt selPat)+ : tactic => do + let pats ← liftMacroM <| SelPat.parse pats + + ProofModeM.runTactic fun mvar g@{hyps, ..} => do + let targets ← SelPat.resolve hyps pats + discard <| iRevertCore mvar g targets From e562c0b0b5c649ac0595646b929a6d604c3c625b Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:16:08 +0200 Subject: [PATCH 03/76] refactor: expose `iintro` as a metaprogramming API --- Iris/Iris/ProofMode/Tactics/Intro.lean | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index 867f738d..f52e7ce6 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -52,7 +52,14 @@ theorem wand_intro_spatial [BI PROP] {P Q A1 A2 : PROP} public meta section open Lean Elab Tactic Meta Qq BI Std -private partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} +/-- +Introduce the hypothesis specified by `pats` into the context given by `P` (structured as `hyps`). +The type of the current goal is given by `Q`. + +This function returns the proof of `P ⊢ Q` to be assigned. The new context is included in the +`goals` directly by the tactic. +-/ +partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) : ProofModeM (Q($P ⊢ $Q)) := do match pats with @@ -63,8 +70,8 @@ private partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} | (ref, .intro (.pure n)) :: pats => withRef ref do let v ← mkFreshLevelMVar - let α : Quoted q(Sort v) ← mkFreshExprMVarQ q(Sort v) - let Φ : Quoted q($α → $prop) ← mkFreshExprMVarQ q($α → $prop) + let α ← mkFreshExprMVarQ q(Sort v) + let Φ ← mkFreshExprMVarQ q($α → $prop) let .some _ ← ProofModeM.trySynthInstanceQ q(FromForall $Q $Φ) | throwError "iintro: {Q} cannot be turned into a universal quantifier or pure hypothesis" let (n, ref) ← getFreshName n @@ -112,7 +119,6 @@ private partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} let pf ← iCasesCore bi hyps A2 pat q(false) A1 (iIntroCore · · pats) return q(wand_intro_spatial (A1 := $A1) (Q := $Q) $pf) - elab "iintro" pats:(colGt introPat)* : tactic => do -- parse syntax let pats ← liftMacroM <| pats.mapM <| IntroPat.parse From 7374db77bc5938a5557b235f0e20ebac454d941c Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:17:04 +0200 Subject: [PATCH 04/76] refactor: expose `iapply` as a metaprogramming API --- Iris/Iris/ProofMode/Tactics/Apply.lean | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Apply.lean b/Iris/Iris/ProofMode/Tactics/Apply.lean index 9f5b6378..db05c54e 100644 --- a/Iris/Iris/ProofMode/Tactics/Apply.lean +++ b/Iris/Iris/ProofMode/Tactics/Apply.lean @@ -34,8 +34,7 @@ Apply a hypothesis `A` to the `goal` by eliminating the wands recursively ## Returns The proof of `hyps ∗ □?p A ⊢ goal` -/ -private partial def iApplyCore {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (hyps : Hyps bi e) (p : Q(Bool)) -(A : Q($prop)) (goal : Q($prop)) : ProofModeM Q($e ∗ □?$p $A ⊢ $goal) := do +partial def iApplyCore {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (hyps : Hyps bi e) (p : Q(Bool)) (A : Q($prop)) (goal : Q($prop)) : ProofModeM Q($e ∗ □?$p $A ⊢ $goal) := do let B ← mkFreshExprMVarQ q($prop) -- if `A := ?B -∗ goal`, add `B` as a new subgoal and conclude `goal` if let some _ ← ProofModeM.trySynthInstanceQ q(IntoWand $p false $A .out $B .in $goal) then @@ -59,8 +58,8 @@ elab "iapply" colGt pmt:pmTerm : tactic => do -- ensure the context can be discarded let LOption.some _ ← trySynthInstanceQ q(TCOr (Affine $e) (Absorbing $goal)) | throwError "iapply: the context {e} is not affine and goal not absorbing" - have rfl : Q($e ∗ □?$p $out ⊣⊢ $e ∗ □?$p $out) := q(.rfl) - mvar.assign q($(pf).trans (assumption (Q := $goal) $(rfl))) + -- have rfl : Q($e ∗ □?$p $out ⊣⊢ $e ∗ □?$p $out) := q(.rfl) + mvar.assign q($(pf).trans (assumption (Q := $goal) .rfl)) -- TODO: Is this better? return -- otherwise, `out` should be a wand, handled by `iApplyCore` let pf' ← iApplyCore hyps' p out goal From 67986f98b9054ade4e915b7a5fb7be1f3b0bae56 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:18:21 +0200 Subject: [PATCH 05/76] feat: add `getDecl?` and `getUsername?` functions for `Hyps` --- Iris/Iris/ProofMode/Expr.lean | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Expr.lean b/Iris/Iris/ProofMode/Expr.lean index f5115840..700f9430 100644 --- a/Iris/Iris/ProofMode/Expr.lean +++ b/Iris/Iris/ProofMode/Expr.lean @@ -22,7 +22,7 @@ open Lean Lean.Expr Lean.Meta Qq structure IVarId where name : Name - deriving Inhabited, BEq, Hashable, Repr + deriving Inhabited, BEq, Hashable, Repr, DecidableEq def mkFreshIVarId [Monad m] [MonadNameGenerator m] : m IVarId := return { name := (← mkFreshId) } @@ -107,6 +107,15 @@ partial def Hyps.find? {u prop bi} (name : Name) : | _, .hyp _ name' ivar p ty _ => if name == name' then (ivar, p, ty) else none | _, .sep _ _ _ _ lhs rhs => rhs.find? name <|> lhs.find? name +partial def Hyps.getDecl? {u prop bi} (uniq : IVarId) {s}: + @Hyps u prop bi s → Option (Name × IVarId × Q(Bool) × Q($prop)) + | .emp _ => none + | .hyp _ name uniq' p ty _ => if uniq == uniq' then (name, uniq, p, ty) else none + | .sep _ _ _ _ lhs rhs => rhs.getDecl? uniq <|> lhs.getDecl? uniq + +def Hyps.getUserName? {u prop bi} (uniq : IVarId) (h : @Hyps u prop bi s) : Option Name := + h.getDecl? uniq |>.map (·.1) + partial def Hyps.spatialIVarIds {u prop bi} : ∀ {s}, @Hyps u prop bi s → List IVarId | _, .emp _ => [] From 4b5bb603324d7f9a8fabdd295b1e5660d928afe6 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:19:01 +0200 Subject: [PATCH 06/76] feat: preliminary `iloeb` tactic implementation --- Iris/Iris/ProofMode/Tactics/Loeb.lean | 137 ++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Iris/Iris/ProofMode/Tactics/Loeb.lean diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean new file mode 100644 index 00000000..5f16b310 --- /dev/null +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -0,0 +1,137 @@ +module + +public meta import Iris.ProofMode.Patterns.SelPattern +public import Iris.ProofMode +meta import Iris.ProofMode.ProofModeM +meta import Iris.ProofMode.Tactics.Revert +meta import Iris.ProofMode.Tactics.Intro +meta import Qq + +namespace Iris.ProofMode + +open Lean Meta Elab.Tactic Qq + +meta section + +def ProofModeM.pruneSolvedGoals : ProofModeM Unit := do + let gs := (←get).goals + let gs ← gs.filterM fun g => not <$> g.isAssigned + modify ({· with goals := gs}) + +def ProofModeM.getUnsolvedGoals : ProofModeM (Array MVarId) := do + pruneSolvedGoals + return (←get).goals + +def ProofModeM.getMainGoal : ProofModeM MVarId := do + let ⟨g :: _⟩ ← getUnsolvedGoals + | throwError "No more proof mode goals!" + return g + +inductive RevertTarget where +| lean (id : FVarId) +| pm (persistent? : Bool) (uniq : IVarId) +deriving BEq, Hashable, Repr + +def RevertTarget.toSelTarget : RevertTarget → SelTarget + | .lean id => .lean id + | .pm _ ivar => .pm ivar + +def ProofModeM.revertingTelescope {α} (mvar : MVarId)(g : IrisGoal)(hs : List RevertTarget) (k : MVarId → IrisGoal → ProofModeM (IrisGoal × α)) : + ProofModeM (IrisGoal × α) := + withTraceNode `iloeb (fun _ => return s!"Reverting telescope") do + let names : List (Syntax × IntroPat) ← hs.mapM fun + | .lean id => do + let name ← Lean.mkIdent <$> id.getUserName + let ident ← `(binderIdent| $name:ident) + return (name, .intro <| .pure ident) + | .pm persistent? uniq => do + let name ← Lean.mkIdent <$> (g.hyps.getUserName? uniq).getM + let ident ← `(binderIdent| $name:ident) + return (name, .intro <| (if persistent? then .intuitionistic else id) <| .one ident) + + -- revert hypothesis in `hs` + let (g, mvar) ← withTraceNode `iloeb (fun _ => return m!"Reverting hypothesis {names.map (·.1.getId)}") do + trace[iloeb] s!"Before revert: {← (liftMetaM ∘ ppExpr =<< inferType (.mvar mvar))}" + let (g, expr) ← iRevertCore mvar g (hs.map RevertTarget.toSelTarget) + ProofModeM.pruneSolvedGoals + trace[iloeb] s!"After revert: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" + trace[iloeb] s!"Revert returned: {←ppExpr expr}" + pure (g, expr.mvarId!) + + -- Run tactic + let ({hyps, goal, ..}, res) ← withTraceNode `iloeb (fun _ => return s!"Running tactic") do + let (g, res) ← k mvar g + ProofModeM.pruneSolvedGoals + pure (g, res) + + -- introduce back the hypethesis in `hs` + withTraceNode `iloeb (fun _ => return m!"Re-introducing hypothesis with {names.map (·.1.getId)}") do + trace[iloeb] s!"Before intros: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" + let mvar ← getMainGoal + let expr ← iIntroCore hyps goal names + ProofModeM.pruneSolvedGoals + mvar.assign expr + trace[iloeb] s!"After intros: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" + return (g, res) + +/-- + Apply Löb induction in the current goal. + + All spatial hypothesis are generalized in the induction hypothesis so that + this one can be included in the intuitionistic context. + + Optionally, other variables can be generalized over through the + `generalizing selPat*` syntax. +-/ +syntax (name := iloeb) "iloeb " " as " binderIdent (" generalizing " (ppSpace colGt selPat)+)? : tactic + +@[inherit_doc iloeb] +elab_rules : tactic +| `(tactic| iloeb as $IH:binderIdent $[generalizing $[$hs:selPat]*]? ) => do + ProofModeM.runTactic fun mvid g@{hyps, ..} => do + let spatialCtx := hyps.spatialUniqs.map (RevertTarget.pm false) + let generalizedHs ← do + let hs := hs.getD #[] + let pats ← Elab.liftMacroM <| SelPat.parse hs + let generalizedHs ← SelPat.resolve hyps pats + generalizedHs.zip hs.toList + |>.filterMapM fun + | (.pm ivar, ref) => do + if spatialCtx.contains (.pm false ivar) then + logWarningAt ref m!"Spatial hypothesis are generalized automatically" + return none + else + return some (RevertTarget.pm true ivar) + | (.lean id, _) => return some (.lean id) + + let _ ← ProofModeM.revertingTelescope mvid g (spatialCtx ++ generalizedHs) fun mvid {u, prop, goal, hyps, ..} => do + trace[iloeb] s!"Goals before tactic : {←(liftM ∘ ppExpr =<< MVarId.getType (←ProofModeM.getMainGoal))}" + let x : Term := ←`(term| @BI.loeb_wand_intuitionistically _) + let ⟨_, hyps', p, out, pf⟩ ← iHave hyps ⟨x, []⟩ true + let pf' ← iApplyCore hyps' p out goal + mvid.assign q($(pf).trans $pf') + trace[iloeb] s!"Goals after applying iloeb: {←(liftM ∘ ppExpr =<< MVarId.getType (←ProofModeM.getMainGoal))}" + + let .some biloeb ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) + | throwError m!"Cannot use `iloeb` if there is no `{←ppExpr q(BI.BILoeb $prop)}` instance available" + (←ProofModeM.getMainGoal).assign biloeb + trace[iloeb] s!"Goals after BILoeb condition dispatch: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" + + let pf ← iModIntroCore hyps' q(iprop(□ (□ ▷ $goal -∗ $goal))) (← `(_)) fun hyps goal => do + addBIGoal hyps goal + let imodGoalId ← ProofModeM.getMainGoal + imodGoalId.assign pf + trace[iloeb] s!"Goals after imodintro: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" + + let introGoalId ← ProofModeM.getMainGoal + let some {goal, hyps, ..} := parseIrisGoal? (←introGoalId.getType) + | throwError "Expected resulting goal to be an Iris goal" + let expr ← iIntroCore hyps goal [(IH, .intro <| .intuitionistic <| .one IH)] + introGoalId.assign expr + trace[iloeb] s!"Goals after introducing {IH}: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" + + let some g := parseIrisGoal? (←MVarId.getType <| ←ProofModeM.getMainGoal) + | throwError "Expected resulting goal to be an Iris goal" + return ⟨g, ()⟩ + +initialize registerTraceClass `iloeb From 65333f5059762200b3a37a8c8e1a87b9969ab109 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:19:38 +0200 Subject: [PATCH 07/76] docs: extend `ihave` docstring on returned `hyps` --- Iris/Iris/ProofMode/Tactics/HaveCore.lean | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/HaveCore.lean b/Iris/Iris/ProofMode/Tactics/HaveCore.lean index aaa849f0..ec208f20 100644 --- a/Iris/Iris/ProofMode/Tactics/HaveCore.lean +++ b/Iris/Iris/ProofMode/Tactics/HaveCore.lean @@ -41,7 +41,9 @@ Assert a hypothesis from either a hypothesis name or a Lean proof term `tm`. ## Returns A tuple containing: - `e'`: Proposition for `hyps'` -- `hyps'`: Updated hypothesis context +- `hyps'`: Updated hypothesis context, which consumes the asserted proposition if it + was contained in the spatial context (or if it was persistent and `keep = true`), + or is returned unchanged if the function term was contained in the Lean context. - `p`: Persistence flag for the output (always `true` for Lean terms, inherited for Iris hypotheses) - `out`: Asserted proposition - `pf`: Proof of `hyps ⊢ hyps' ∗ □?p out` From 58da6e3c1e81a07c5fdd76f3f6dd4f8e20ca7e2d Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:27:50 +0200 Subject: [PATCH 08/76] feat: add iloeb to the tactic prelude --- Iris/Iris/ProofMode/Tactics.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Iris/Iris/ProofMode/Tactics.lean b/Iris/Iris/ProofMode/Tactics.lean index 1ac94998..c86b65e3 100644 --- a/Iris/Iris/ProofMode/Tactics.lean +++ b/Iris/Iris/ProofMode/Tactics.lean @@ -20,3 +20,4 @@ public meta import Iris.ProofMode.Tactics.Rename public meta import Iris.ProofMode.Tactics.Revert public meta import Iris.ProofMode.Tactics.Specialize public meta import Iris.ProofMode.Tactics.Split +public meta import Iris.ProofMode.Tactics.Loeb From 018214c8050b868ac9891d317a3b3d29fb8259b5 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 12:28:11 +0200 Subject: [PATCH 09/76] doc: add copyright header comment in iloeb --- Iris/Iris/ProofMode/Tactics/Loeb.lean | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 5f16b310..e4ae8b06 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -1,3 +1,8 @@ +/- +Copyright (c) 2025 Fernando Leal. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Fernando Leal +-/ module public meta import Iris.ProofMode.Patterns.SelPattern From 0b22f6a8318e7f45b92c22f158f9c29d421ec7cc Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 15:17:04 +0200 Subject: [PATCH 10/76] fix: imports and dangling reference --- Iris/Iris/ProofMode/Tactics/Loeb.lean | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index e4ae8b06..1098a211 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -4,19 +4,15 @@ Released under Apache 2.0 license as described in the file LICENSE. Authors: Fernando Leal -/ module - -public meta import Iris.ProofMode.Patterns.SelPattern -public import Iris.ProofMode -meta import Iris.ProofMode.ProofModeM -meta import Iris.ProofMode.Tactics.Revert -meta import Iris.ProofMode.Tactics.Intro -meta import Qq +public import Iris.ProofMode.Tactics.Apply +public import Iris.ProofMode.Tactics.Intro +public import Iris.ProofMode.Tactics.Revert namespace Iris.ProofMode open Lean Meta Elab.Tactic Qq -meta section +public meta section def ProofModeM.pruneSolvedGoals : ProofModeM Unit := do let gs := (←get).goals @@ -94,7 +90,7 @@ syntax (name := iloeb) "iloeb " " as " binderIdent (" generalizing " (ppSpace co elab_rules : tactic | `(tactic| iloeb as $IH:binderIdent $[generalizing $[$hs:selPat]*]? ) => do ProofModeM.runTactic fun mvid g@{hyps, ..} => do - let spatialCtx := hyps.spatialUniqs.map (RevertTarget.pm false) + let spatialCtx := hyps.spatialIVarIds.map (RevertTarget.pm false) let generalizedHs ← do let hs := hs.getD #[] let pats ← Elab.liftMacroM <| SelPat.parse hs From 5cb61682fac2f64d9af562ac5790a62199c26ad7 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 15:17:49 +0200 Subject: [PATCH 11/76] refactor: move `ProofModeM` methods to `ProofModeM.lean` --- Iris/Iris/ProofMode/ProofModeM.lean | 22 ++++++++++++++++++++++ Iris/Iris/ProofMode/Tactics/Loeb.lean | 14 -------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index e4e1ad63..4ae4d52e 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -123,3 +123,25 @@ def ProofModeM.runTactic (x : MVarId → IrisGoal → ProofModeM α) (s : ProofM replaceMainGoal (nonDep ++ dep).toList return res + +def ProofModeM.pruneSolvedGoals : ProofModeM Unit := do + let gs := (←get).goals + let gs ← gs.filterM fun g => not <$> g.isAssigned + modify ({· with goals := gs}) + +def ProofModeM.getUnsolvedGoals : ProofModeM (Array MVarId) := do + pruneSolvedGoals + return (←get).goals + +def ProofModeM.getMainGoal : ProofModeM MVarId := do + loop (←get).goals 0 +where + loop (goals : Array MVarId) (i : Nat) : ProofModeM MVarId := do + if h: i < goals.size then + if (← goals[i].isAssigned) then + loop goals (i+1) + else + modify ({· with goals:= goals[i...*]}) + return goals[i] + else + throwNoGoalsToBeSolved diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 1098a211..ac9f6b69 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -14,20 +14,6 @@ open Lean Meta Elab.Tactic Qq public meta section -def ProofModeM.pruneSolvedGoals : ProofModeM Unit := do - let gs := (←get).goals - let gs ← gs.filterM fun g => not <$> g.isAssigned - modify ({· with goals := gs}) - -def ProofModeM.getUnsolvedGoals : ProofModeM (Array MVarId) := do - pruneSolvedGoals - return (←get).goals - -def ProofModeM.getMainGoal : ProofModeM MVarId := do - let ⟨g :: _⟩ ← getUnsolvedGoals - | throwError "No more proof mode goals!" - return g - inductive RevertTarget where | lean (id : FVarId) | pm (persistent? : Bool) (uniq : IVarId) From e6622e1a359a27ca1ce5786b1477743734960b15 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 15:18:38 +0200 Subject: [PATCH 12/76] feat: add tests --- Iris/Iris/ProofMode/Tactics/Frame.lean | 4 +- Iris/Iris/ProofMode/Tactics/Loeb.lean | 2 +- Iris/Iris/Tests/Tactics.lean | 92 ++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Frame.lean b/Iris/Iris/ProofMode/Tactics/Frame.lean index 668b107d..a32bc51e 100644 --- a/Iris/Iris/ProofMode/Tactics/Frame.lean +++ b/Iris/Iris/ProofMode/Tactics/Frame.lean @@ -63,7 +63,7 @@ structure FrameResult {u} {prop : Q(Type u)} (bi : Q(BI $prop)) (origE origGoal private def FrameResult.step {u prop bi origE origGoal} : @FrameResult u prop bi origE origGoal → SelTarget → ProofModeM (FrameResult bi origE origGoal) - | st@{hyps, goal, pf, ..}, {explicit, target := .inl ivar} => do + | st@{hyps, goal, pf, ..}, {explicit, target := .pm ivar} => do let ⟨e', hyps', _, out', p, _, hrem⟩ := hyps.remove false ivar let goal' ← mkFreshExprMVarQ q($prop) if let .some _ ← ProofModeM.trySynthInstanceQ q(Frame $p $out' $goal $goal') then @@ -72,7 +72,7 @@ private def FrameResult.step {u prop bi origE origGoal} : throwError "iframe: cannot frame {out'}" else return st - | st@{e, hyps, goal, pf, ..}, {explicit, target := .inr fvar} => do + | st@{e, hyps, goal, pf, ..}, {explicit, target := .lean fvar} => do let ty ← fvar.getType if ! (← Meta.isProp ty) then throwError "iframe: {← fvar.getUserName} is not a Prop" diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index ac9f6b69..1a2b3a09 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -91,7 +91,7 @@ elab_rules : tactic return some (RevertTarget.pm true ivar) | (.lean id, _) => return some (.lean id) - let _ ← ProofModeM.revertingTelescope mvid g (spatialCtx ++ generalizedHs) fun mvid {u, prop, goal, hyps, ..} => do + let _ ← ProofModeM.revertingTelescope mvid g (generalizedHs ++ spatialCtx) fun mvid {u, prop, goal, hyps, ..} => do trace[iloeb] s!"Goals before tactic : {←(liftM ∘ ppExpr =<< MVarId.getType (←ProofModeM.getMainGoal))}" let x : Term := ←`(term| @BI.loeb_wand_intuitionistically _) let ⟨_, hyps', p, out, pf⟩ ← iHave hyps ⟨x, []⟩ true diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 677ffbdc..b4b54dd3 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2166,3 +2166,95 @@ example [BI PROP] [BIAffine PROP] (Q : Nat → PROP) : (Q 0 ⊢ ∃ x, False ∨ iframe end iframe + +section iloeb + +variable {PROP : Type u} [ι₁ : BI PROP] [ι₂ : BILoeb PROP] +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +P Q : PROP +⊢ ⏎ + □IH : ▷ (P -∗ Q) + ⊢ P -∗ Q +-/ +#guard_msgs in +example (P Q : PROP) : + P ⊢ Q := by + iloeb as IH + +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +P₁ P₂ Q : PROP +⊢ ⏎ + □p : P₁ + □IH : ▷ (P₂ -∗ Q) + ⊢ P₂ -∗ Q +-/ +#guard_msgs in +example (P₁ P₂ Q : PROP) : + ⊢ □ P₁ -∗ P₂ -∗ Q := by + iintro #p + iloeb as IH + +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +P₁ P₂ Q : PROP +⊢ ⏎ + □IH : ▷ (□ P₁ -∗ P₂ -∗ Q) + □p : P₁ + ⊢ P₂ -∗ Q +-/ +#guard_msgs in +example (P₁ P₂ Q : PROP) : + ⊢ □ P₁ -∗ P₂ -∗ Q := by + iintro #p + iloeb as IH generalizing p + +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +H₁ : Sort u_1 +P P₁ P₂ Q : PROP +h1 : H₁ +⊢ ⏎ + □IH : ▷ (P -∗ Q) + ∗p : P + ⊢ Q +-/ +#guard_msgs in +example (P₁ P₂ Q : PROP) : + H₁ → ⊢ P -∗ Q := by + iintro %h1 p + iloeb as IH + +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +H₁ : Sort u_1 +P P₁ P₂ Q : PROP +h1✝ h1 : H₁ +⊢ ⏎ + □IH : ▷ ∀ h1, P -∗ Q + ∗p : P + ⊢ Q +-/ +#guard_msgs in +example (P₁ P₂ Q : PROP) : + H₁ → ⊢ P -∗ Q := by + iintro %h1 p + iloeb as IH generalizing %h1 + +end iloeb From d8e847ecc0aed01d0e2317215378ca49d37ae553 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 15:22:14 +0200 Subject: [PATCH 13/76] chore: add TODO justification for opinionated change --- Iris/Iris/ProofMode/Tactics/Intro.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index f52e7ce6..5689c4ba 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -70,8 +70,8 @@ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} | (ref, .intro (.pure n)) :: pats => withRef ref do let v ← mkFreshLevelMVar - let α ← mkFreshExprMVarQ q(Sort v) - let Φ ← mkFreshExprMVarQ q($α → $prop) + let α ← mkFreshExprMVarQ q(Sort v) -- TODO: We used to have the types of these declarations explicitly. + let Φ ← mkFreshExprMVarQ q($α → $prop) -- But why? let .some _ ← ProofModeM.trySynthInstanceQ q(FromForall $Q $Φ) | throwError "iintro: {Q} cannot be turned into a universal quantifier or pure hypothesis" let (n, ref) ← getFreshName n From c875bdc9fac910d47f7bdd090088d429a0ab5087 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 11 May 2026 21:24:29 +0200 Subject: [PATCH 14/76] fix: `uniq` is now `ivar` --- Iris/Iris/ProofMode/Patterns/SelPattern.lean | 2 +- Iris/Iris/ProofMode/Tactics/Loeb.lean | 6 +++--- Iris/Iris/ProofMode/Tactics/Specialize.lean | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Iris/Iris/ProofMode/Patterns/SelPattern.lean b/Iris/Iris/ProofMode/Patterns/SelPattern.lean index 7dc6e154..b70f766c 100644 --- a/Iris/Iris/ProofMode/Patterns/SelPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SelPattern.lean @@ -53,7 +53,7 @@ public meta section inductive SelId where | lean (id : FVarId) -| pm (uniq : IVarId) +| pm (ivar : IVarId) deriving BEq, Hashable, Repr @[rocq_alias esel_pat] diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 1a2b3a09..d710b469 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -16,7 +16,7 @@ public meta section inductive RevertTarget where | lean (id : FVarId) -| pm (persistent? : Bool) (uniq : IVarId) +| pm (persistent? : Bool) (ivar : IVarId) deriving BEq, Hashable, Repr def RevertTarget.toSelTarget : RevertTarget → SelTarget @@ -31,8 +31,8 @@ def ProofModeM.revertingTelescope {α} (mvar : MVarId)(g : IrisGoal)(hs : List R let name ← Lean.mkIdent <$> id.getUserName let ident ← `(binderIdent| $name:ident) return (name, .intro <| .pure ident) - | .pm persistent? uniq => do - let name ← Lean.mkIdent <$> (g.hyps.getUserName? uniq).getM + | .pm persistent? ivar => do + let name ← Lean.mkIdent <$> (hyps.getUserName? ivar).getM let ident ← `(binderIdent| $name:ident) return (name, .intro <| (if persistent? then .intuitionistic else id) <| .one ident) diff --git a/Iris/Iris/ProofMode/Tactics/Specialize.lean b/Iris/Iris/ProofMode/Tactics/Specialize.lean index cef8b53a..57873864 100644 --- a/Iris/Iris/ProofMode/Tactics/Specialize.lean +++ b/Iris/Iris/ProofMode/Tactics/Specialize.lean @@ -107,7 +107,7 @@ private def processWand : let out₂ ← mkFreshExprMVarQ prop let some _ ← ProofModeM.trySynthInstanceQ q(IntoWand $p false $out .out $out₁ .out $out₂) | throwError m!"ispecialize: {out} is not a wand" - let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.inl ·, true⟩)) + let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.pm ·, true⟩)) let pf'' ← res.finish (addBIGoal · · g) let pf := q(specialize_wand_subgoal $out₂ $pf $pf' $pf'') return { e := el', hyps := hypsl', p := q(false), out := out₂, pf } From 5657c05456e5078cca7daf12023c9c68d4f5c50a Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 14:25:03 +0200 Subject: [PATCH 15/76] refactor: have `iRevertCore` accept a continuation --- Iris/Iris/ProofMode/ProofModeM.lean | 11 +++++++++++ Iris/Iris/ProofMode/Tactics/Revert.lean | 25 ++++++++----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index 4ae4d52e..8549b1c2 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -58,6 +58,17 @@ def addBIGoalWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} modify ({goals := ·.goals.push clearedGoalId}) return Expr.mvar clearedGoalId +def runTacticWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} + {e} (hyps : Hyps bi e) (goal : Q($prop)) (toClear : Array FVarId) (name : Name := .anonymous) + (k : ProofModeTactic) : + ProofModeM Q($e ⊢ $goal) := do + let .mvar mvid ← addBIGoalWithoutFVars hyps goal toClear name + | unreachable! + let expr ← mvid.withContext do + k hyps goal + mvid.assign expr + return expr + /-- Add an existing metavariable as a goal to the proof mode state if it is not already assigned or present. -/ def addMVarGoal (m : MVarId) (name : Name := .anonymous) : ProofModeM Unit := do if ← m.isAssignedOrDelayedAssigned then diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index c4157974..394d10d7 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -89,31 +89,22 @@ private def RevertState.revertLeanHyp else st.revertLeanForallHyp f α -def iRevertCore (mvar : MVarId) (g : IrisGoal) (targets : List SelTarget) : - ProofModeM (IrisGoal × Expr) := do - let {u, prop, bi, e, hyps, goal} := g +def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) + (k : ProofModeTactic := addBIGoal) : + ProofModeM Q($e ⊢ $goal) := do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do match target.target with | .pm ivar => st.revertProofModeHyp ivar | .lean fvar => st.revertLeanHyp fvar - let pf' : Q($(st.e) ⊢ $(st.goal)) ← addBIGoalWithoutFVars st.hyps st.goal st.reverted.reverse - mvar.assign q($(st.pf) $pf') - let g := { - u := u, - bi := bi, - prop := prop, - e := st.e, - hyps := st.hyps, - goal := st.goal - : IrisGoal - } - return (g, pf') + let pf' : Q($(st.e) ⊢ $(st.goal)) ← runTacticWithoutFVars st.hyps st.goal st.reverted.reverse (name := .anonymous) k + return q($(st.pf) $pf') elab "irevert" pats:(colGt selPat)+ : tactic => do let pats ← liftMacroM <| SelPat.parse pats - ProofModeM.runTactic fun mvar g@{hyps, ..} => do + ProofModeM.runTactic fun mvar {hyps, goal, ..} => do let targets ← SelPat.resolve hyps pats - discard <| iRevertCore mvar g targets + let expr ← iRevertCore targets hyps goal + mvar.assign expr From ba34106716f725db70dc1129bd3d28b84f96db7d Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 14:25:27 +0200 Subject: [PATCH 16/76] refactor: have `iIntroCore` accept a continuation --- Iris/Iris/ProofMode/Tactics/Intro.lean | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index 5689c4ba..e9b3492d 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -60,13 +60,14 @@ This function returns the proof of `P ⊢ Q` to be assigned. The new context is `goals` directly by the tactic. -/ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} - {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) : + {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) + (k : ProofModeTactic := addBIGoal) : ProofModeM (Q($P ⊢ $Q)) := do match pats with - | [] => addBIGoal hyps Q + | [] => k hyps Q | (ref, .modintro) :: pats => withRef ref do - iModIntroCore hyps Q (← `(_)) (iIntroCore · · pats) + iModIntroCore hyps Q (← `(_)) (iIntroCore · · pats k) | (ref, .intro (.pure n)) :: pats => withRef ref do let v ← mkFreshLevelMVar @@ -79,7 +80,7 @@ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} addLocalVarInfo ref (← getLCtx) x α have B : Q($prop) := Expr.headBeta q($Φ $x) have : $B =Q $Φ $x := ⟨⟩ - let pf : Q(∀ x, $P ⊢ $Φ x) ← mkLambdaFVars #[x] <|← iIntroCore hyps B pats + let pf : Q(∀ x, $P ⊢ $Φ x) ← mkLambdaFVars #[x] <|← iIntroCore hyps B pats k return q(from_forall_intro (Q := $Q) $pf) | (ref, .intro pat) :: pats => withRef ref do @@ -87,7 +88,7 @@ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} let A2 ← mkFreshExprMVarQ q($prop) let fromImp ← ProofModeM.trySynthInstanceQ q(FromImp $Q $A1 $A2) if let (.clear, some _) := (pat, fromImp) then - let pf ← iIntroCore hyps A2 pats + let pf ← iIntroCore hyps A2 pats k return q(imp_intro_drop (Q := $Q) $pf) else let B ← mkFreshExprMVarQ q($prop) @@ -95,7 +96,7 @@ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} | .intuitionistic pat, some _ => let .some _ ← ProofModeM.trySynthInstanceQ q(IntoPersistently false $A1 $B) | throwError "iintro: {A1} not persistent" - let pf ← iCasesCore bi hyps A2 pat q(true) B (iIntroCore · · pats) + let pf ← iCasesCore bi hyps A2 pat q(true) B (iIntroCore · · pats k) return q(imp_intro_intuitionistic (Q := $Q) $pf) | .intuitionistic pat, none => let .some _ ← ProofModeM.trySynthInstanceQ q(FromWand $Q .out $A1 $A2) @@ -104,19 +105,19 @@ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} | throwError "iintro: {A1} not persistent" let .some _ ← trySynthInstanceQ q(TCOr (Affine $A1) (Absorbing $A2)) | throwError "iintro: {A1} not affine and the goal not absorbing" - let pf ← iCasesCore bi hyps A2 pat q(true) B (iIntroCore · · pats) + let pf ← iCasesCore bi hyps A2 pat q(true) B (iIntroCore · · pats k) return q(wand_intro_intuitionistic (A1 := $A1) (Q := $Q) $pf) | _, some _ => -- should always succeed let _ ← ProofModeM.synthInstanceQ q(FromAffinely $B $A1) let .some _ ← trySynthInstanceQ q(TCOr (Persistent $A1) (Intuitionistic $P)) | throwError "iintro: {A1} is not persistent and spatial context is non-empty" - let pf ← iCasesCore bi hyps A2 pat q(false) B (iIntroCore · · pats) + let pf ← iCasesCore bi hyps A2 pat q(false) B (iIntroCore · · pats k) return q(imp_intro_spatial (Q := $Q) $pf) | _, none => let .some _ ← ProofModeM.trySynthInstanceQ q(FromWand $Q .out $A1 $A2) | throwError "iintro: {Q} not a wand" - let pf ← iCasesCore bi hyps A2 pat q(false) A1 (iIntroCore · · pats) + let pf ← iCasesCore bi hyps A2 pat q(false) A1 (iIntroCore · · pats k) return q(wand_intro_spatial (A1 := $A1) (Q := $Q) $pf) elab "iintro" pats:(colGt introPat)* : tactic => do From 8913e35f533ac0a9b008adf84c0c5959c936afd5 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 14:27:28 +0200 Subject: [PATCH 17/76] fix: remove `ProofModeTactic` definition --- Iris/Iris/ProofMode/ProofModeM.lean | 2 +- Iris/Iris/ProofMode/Tactics/Intro.lean | 2 +- Iris/Iris/ProofMode/Tactics/Revert.lean | 3 ++- Iris/Iris/Tests/Tactics.lean | 33 +++++++++++++++++++------ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index 8549b1c2..e9bca4bd 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -60,7 +60,7 @@ def addBIGoalWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} def runTacticWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (hyps : Hyps bi e) (goal : Q($prop)) (toClear : Array FVarId) (name : Name := .anonymous) - (k : ProofModeTactic) : + (k : ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal)) : ProofModeM Q($e ⊢ $goal) := do let .mvar mvid ← addBIGoalWithoutFVars hyps goal toClear name | unreachable! diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index e9b3492d..56e5e291 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -61,7 +61,7 @@ This function returns the proof of `P ⊢ Q` to be assigned. The new context is -/ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) - (k : ProofModeTactic := addBIGoal) : + (k : ∀ {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM (Q($P ⊢ $Q)) := do match pats with | [] => k hyps Q diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 394d10d7..77879958 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -90,7 +90,8 @@ private def RevertState.revertLeanHyp st.revertLeanForallHyp f α def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) - (k : ProofModeTactic := addBIGoal) : + (k : ∀ {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), + ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM Q($e ⊢ $goal) := do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index b4b54dd3..5e9c0b05 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2185,6 +2185,23 @@ example (P Q : PROP) : P ⊢ Q := by iloeb as IH +/-- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +P Q : PROP +⊢ ⏎ + □IH : ▷ (P -∗ Q) + ∗p : P + ⊢ Q +-/ +#guard_msgs in +example (P Q : PROP) : + P ⊢ Q := by + iintro p + iloeb as IH + /-- error: unsolved goals PROP : Type u @@ -2224,8 +2241,8 @@ error: unsolved goals PROP : Type u ι₁ : BI PROP ι₂ : BILoeb PROP -H₁ : Sort u_1 -P P₁ P₂ Q : PROP +H₁ : Prop +P Q : PROP h1 : H₁ ⊢ ⏎ □IH : ▷ (P -∗ Q) @@ -2233,7 +2250,7 @@ h1 : H₁ ⊢ Q -/ #guard_msgs in -example (P₁ P₂ Q : PROP) : +example (H₁ : Prop) (P Q : PROP) : H₁ → ⊢ P -∗ Q := by iintro %h1 p iloeb as IH @@ -2243,16 +2260,16 @@ error: unsolved goals PROP : Type u ι₁ : BI PROP ι₂ : BILoeb PROP -H₁ : Sort u_1 -P P₁ P₂ Q : PROP -h1✝ h1 : H₁ +H₁ : Prop +P Q : PROP +h1 : H₁ ⊢ ⏎ - □IH : ▷ ∀ h1, P -∗ Q + □IH : ▷ ( ⌜H₁⌝ -∗ P -∗ Q) ∗p : P ⊢ Q -/ #guard_msgs in -example (P₁ P₂ Q : PROP) : +example (H₁ : Prop) (P Q : PROP) : H₁ → ⊢ P -∗ Q := by iintro %h1 p iloeb as IH generalizing %h1 From 9b9f425f0a2065c3196aaf96dcc6db06dacb1add Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 14:48:39 +0200 Subject: [PATCH 18/76] fix: uniq is now ivar --- Iris/Iris/ProofMode/Expr.lean | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Iris/Iris/ProofMode/Expr.lean b/Iris/Iris/ProofMode/Expr.lean index 700f9430..b220020a 100644 --- a/Iris/Iris/ProofMode/Expr.lean +++ b/Iris/Iris/ProofMode/Expr.lean @@ -107,14 +107,14 @@ partial def Hyps.find? {u prop bi} (name : Name) : | _, .hyp _ name' ivar p ty _ => if name == name' then (ivar, p, ty) else none | _, .sep _ _ _ _ lhs rhs => rhs.find? name <|> lhs.find? name -partial def Hyps.getDecl? {u prop bi} (uniq : IVarId) {s}: +partial def Hyps.getDecl? {u prop bi} (ivar : IVarId) {s}: @Hyps u prop bi s → Option (Name × IVarId × Q(Bool) × Q($prop)) | .emp _ => none - | .hyp _ name uniq' p ty _ => if uniq == uniq' then (name, uniq, p, ty) else none - | .sep _ _ _ _ lhs rhs => rhs.getDecl? uniq <|> lhs.getDecl? uniq + | .hyp _ name ivar' p ty _ => if ivar == ivar' then (name, ivar, p, ty) else none + | .sep _ _ _ _ lhs rhs => rhs.getDecl? ivar <|> lhs.getDecl? ivar -def Hyps.getUserName? {u prop bi} (uniq : IVarId) (h : @Hyps u prop bi s) : Option Name := - h.getDecl? uniq |>.map (·.1) +def Hyps.getUserName? {u prop bi} (ivar : IVarId) (h : @Hyps u prop bi s) : Option Name := + h.getDecl? ivar |>.map (·.1) partial def Hyps.spatialIVarIds {u prop bi} : ∀ {s}, @Hyps u prop bi s → List IVarId From 1672ec69c11e06f911b716ff80520f7208a500d3 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 15:00:28 +0200 Subject: [PATCH 19/76] feat: simplify `iloeb` using continuations --- Iris/Iris/ProofMode/Tactics/Loeb.lean | 83 +++++++++------------------ 1 file changed, 26 insertions(+), 57 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index d710b469..8a1eb7cf 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -23,9 +23,14 @@ def RevertTarget.toSelTarget : RevertTarget → SelTarget | .lean id => .lean id | .pm _ ivar => .pm ivar -def ProofModeM.revertingTelescope {α} (mvar : MVarId)(g : IrisGoal)(hs : List RevertTarget) (k : MVarId → IrisGoal → ProofModeM (IrisGoal × α)) : - ProofModeM (IrisGoal × α) := - withTraceNode `iloeb (fun _ => return s!"Reverting telescope") do +abbrev ProofModeContinuation := ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), + ProofModeM Q($e ⊢ $goal) + +def ProofModeM.revertingTelescope + {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) + (hs : List RevertTarget) + (k : ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation → ProofModeM Q($e ⊢ $goal)) + : ProofModeM Q($e ⊢ $goal) := do let names : List (Syntax × IntroPat) ← hs.mapM fun | .lean id => do let name ← Lean.mkIdent <$> id.getUserName @@ -35,31 +40,11 @@ def ProofModeM.revertingTelescope {α} (mvar : MVarId)(g : IrisGoal)(hs : List R let name ← Lean.mkIdent <$> (hyps.getUserName? ivar).getM let ident ← `(binderIdent| $name:ident) return (name, .intro <| (if persistent? then .intuitionistic else id) <| .one ident) + trace[iloeb] s!"Calling reverting telescope with {names.map (·.1)} on context {←ppExpr hyps.tm}\n⊢\n{←ppExpr goal}" - -- revert hypothesis in `hs` - let (g, mvar) ← withTraceNode `iloeb (fun _ => return m!"Reverting hypothesis {names.map (·.1.getId)}") do - trace[iloeb] s!"Before revert: {← (liftMetaM ∘ ppExpr =<< inferType (.mvar mvar))}" - let (g, expr) ← iRevertCore mvar g (hs.map RevertTarget.toSelTarget) - ProofModeM.pruneSolvedGoals - trace[iloeb] s!"After revert: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" - trace[iloeb] s!"Revert returned: {←ppExpr expr}" - pure (g, expr.mvarId!) - - -- Run tactic - let ({hyps, goal, ..}, res) ← withTraceNode `iloeb (fun _ => return s!"Running tactic") do - let (g, res) ← k mvar g - ProofModeM.pruneSolvedGoals - pure (g, res) - - -- introduce back the hypethesis in `hs` - withTraceNode `iloeb (fun _ => return m!"Re-introducing hypothesis with {names.map (·.1.getId)}") do - trace[iloeb] s!"Before intros: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" - let mvar ← getMainGoal - let expr ← iIntroCore hyps goal names - ProofModeM.pruneSolvedGoals - mvar.assign expr - trace[iloeb] s!"After intros: {← (←ProofModeM.getUnsolvedGoals).mapM (liftMetaM ∘ ppExpr =<< MVarId.getType ·)}" - return (g, res) + iRevertCore (hs.map RevertTarget.toSelTarget) hyps goal fun hyps goal => do + k hyps goal fun hyps goal => do + iIntroCore hyps goal names /-- Apply Löb induction in the current goal. @@ -75,7 +60,7 @@ syntax (name := iloeb) "iloeb " " as " binderIdent (" generalizing " (ppSpace co @[inherit_doc iloeb] elab_rules : tactic | `(tactic| iloeb as $IH:binderIdent $[generalizing $[$hs:selPat]*]? ) => do - ProofModeM.runTactic fun mvid g@{hyps, ..} => do + ProofModeM.runTactic fun mvid {hyps, goal, ..} => do let spatialCtx := hyps.spatialIVarIds.map (RevertTarget.pm false) let generalizedHs ← do let hs := hs.getD #[] @@ -85,40 +70,24 @@ elab_rules : tactic |>.filterMapM fun | (.pm ivar, ref) => do if spatialCtx.contains (.pm false ivar) then - logWarningAt ref m!"Spatial hypothesis are generalized automatically" + logWarningAt ref m!"Spatial hypothesis are generalized automatically by iloeb" return none else return some (RevertTarget.pm true ivar) | (.lean id, _) => return some (.lean id) - let _ ← ProofModeM.revertingTelescope mvid g (generalizedHs ++ spatialCtx) fun mvid {u, prop, goal, hyps, ..} => do - trace[iloeb] s!"Goals before tactic : {←(liftM ∘ ppExpr =<< MVarId.getType (←ProofModeM.getMainGoal))}" - let x : Term := ←`(term| @BI.loeb_wand_intuitionistically _) - let ⟨_, hyps', p, out, pf⟩ ← iHave hyps ⟨x, []⟩ true - let pf' ← iApplyCore hyps' p out goal - mvid.assign q($(pf).trans $pf') - trace[iloeb] s!"Goals after applying iloeb: {←(liftM ∘ ppExpr =<< MVarId.getType (←ProofModeM.getMainGoal))}" - - let .some biloeb ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) + let expr ← ProofModeM.revertingTelescope hyps goal (generalizedHs ++ spatialCtx) fun {u prop _ _} hyps goal k => do + let .some _ ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) | throwError m!"Cannot use `iloeb` if there is no `{←ppExpr q(BI.BILoeb $prop)}` instance available" - (←ProofModeM.getMainGoal).assign biloeb - trace[iloeb] s!"Goals after BILoeb condition dispatch: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" - - let pf ← iModIntroCore hyps' q(iprop(□ (□ ▷ $goal -∗ $goal))) (← `(_)) fun hyps goal => do - addBIGoal hyps goal - let imodGoalId ← ProofModeM.getMainGoal - imodGoalId.assign pf - trace[iloeb] s!"Goals after imodintro: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" - - let introGoalId ← ProofModeM.getMainGoal - let some {goal, hyps, ..} := parseIrisGoal? (←introGoalId.getType) - | throwError "Expected resulting goal to be an Iris goal" - let expr ← iIntroCore hyps goal [(IH, .intro <| .intuitionistic <| .one IH)] - introGoalId.assign expr - trace[iloeb] s!"Goals after introducing {IH}: {← ((←ProofModeM.getUnsolvedGoals).mapM (liftM ∘ ppExpr =<< MVarId.getType ·))}\n{repr (←ProofModeM.getUnsolvedGoals)}" - - let some g := parseIrisGoal? (←MVarId.getType <| ←ProofModeM.getMainGoal) - | throwError "Expected resulting goal to be an Iris goal" - return ⟨g, ()⟩ + let pf := q(BI.loeb_wand_intuitionistically (P := $goal)) + let pf' ← do + -- We have applied BI.loeb_wand_intuitionistically + let goal := q(iprop(□ (□ ▷ $goal -∗ $goal))) + iModIntroCore hyps goal (← `(_)) fun hyps goal => do + iIntroCore hyps goal [(IH, .intro <| .intuitionistic <| .one IH)] fun hyps goal => do + k hyps goal + return q($(pf').trans $pf) + + mvid.assign expr initialize registerTraceClass `iloeb From b836417cf3908372c0fa7d1e31d881f9fe09f908 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 15:00:41 +0200 Subject: [PATCH 20/76] test: warning when generalizing over spatial hypothesis --- Iris/Iris/Tests/Tactics.lean | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 5e9c0b05..370544ad 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2236,6 +2236,26 @@ example (P₁ P₂ Q : PROP) : iintro #p iloeb as IH generalizing p +/-- +warning: Spatial hypothesis are generalized automatically by iloeb +--- +error: unsolved goals +PROP : Type u +ι₁ : BI PROP +ι₂ : BILoeb PROP +P₁ P₂ Q : PROP +⊢ ⏎ + □p1 : P₁ + □IH : ▷ (P₂ -∗ Q) + ∗p2 : P₂ + ⊢ Q +-/ +#guard_msgs in +example (P₁ P₂ Q : PROP) : + ⊢ □ P₁ -∗ P₂ -∗ Q := by + iintro #p1 p2 + iloeb as IH generalizing p2 + /-- error: unsolved goals PROP : Type u From 2574ceedb731b100b8c0fc6cbbbe2eedd6e64e93 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 15:01:04 +0200 Subject: [PATCH 21/76] docs: add `Hyps` docstring explaining hidden invariants --- Iris/Iris/ProofMode/Expr.lean | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Iris/Iris/ProofMode/Expr.lean b/Iris/Iris/ProofMode/Expr.lean index b220020a..27cec8d3 100644 --- a/Iris/Iris/ProofMode/Expr.lean +++ b/Iris/Iris/ProofMode/Expr.lean @@ -49,6 +49,47 @@ def matchBool (p : Q(Bool)) : ($p =Q true) ⊕' ($p =Q false) := section hyps +/-- +A structured view of an environment `e` as a tree, with nodes + - `emp : Hyps bi iprop(emp)`, for the empty tree + - `sep : Hyps bi iprop(elhs ∗ erhs)`, for branches over separating conjunctions `∗` + - `hyp : Hyps bi iprop(□?p H)`, for a hypothesis `H` at the leaf + +Since the inductive type cannot be indexed by the type of the environment, +we use Qq's `=Q` constraints to ensure the invariant on the shape of the +tree is maintained. This can be seen in the anonymous `_` fields found in +each constructor. + +There are other invariants of the tree that are not represented in its +type, but are documented below. + +### The `tm` field caches a specific function + +In particular, all constructors have a `tm` field which contains +an expression equivalent to `e` up to definitional equality, which computes: + +``` +def tm : Hyps bi e → Expr + | .emp .. => q(iprop( emp )) + | .sep _ lhs rhs .. => q(iprop( $(tm lhs) * $(tm rhs) )) + | .hyp _ _ _ q(false) ty _ => q(iprop( $ty )) + | .hyp _ _ _ q(true) ty _ => q(iprop( □ $ty )) +``` + +This value can be retrieved with the `Hyps.tm` function. + +See https://leanprover.zulipchat.com/#narrow/channel/490604-iris-lean/topic/What.20is.20the.20difference.20between.20.60tm.60.20and.20.60e.60.20in.20.60Hyps.60.3F/near/594308734 + +### The `p` field of `Hyps.hyp` only has literal values. + +Even though `p : Q(Bool)`, we may assume `p = q(true)` or `p = q(false)`. +The reason `p` is then represented as an expression, and not directly a +`Bool`, is that Qq has trouble reasoning about the coercion `Bool` to +`Q(Bool)`. + +See https://leanprover.zulipchat.com/#narrow/channel/490604-iris-lean/topic/What.20is.20the.20difference.20between.20.60tm.60.20and.20.60e.60.20in.20.60Hyps.60.3F/near/594305592 + +-/ inductive Hyps {prop : Q(Type u)} (bi : Q(BI $prop)) : (e : Q($prop)) → Type where | emp (_ : $e =Q emp) : Hyps bi e | sep (tm elhs erhs : Q($prop)) (_ : $e =Q iprop($elhs ∗ $erhs)) From d2a778236b5148b229edc913d85fc5358e10d66c Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 15:19:27 +0200 Subject: [PATCH 22/76] fixup! refactor: make SelTarget type an distinct inductive type --- Iris/Iris/ProofMode/Tactics/Cases.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index d4eb1645..655baa53 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -215,7 +215,7 @@ partial def iCasesCore {P} (hyps : Hyps bi P) (goal : Q($prop)) (pat : iCasesPat | .frame => do let ⟨ivar, hyps'⟩ ← Hyps.addWithInfo bi (← `(binderIdent | _)) p A hyps - let res ← iFrame bi _ hyps' goal [⟨.inl ivar, true⟩] + let res ← iFrame bi _ hyps' goal [⟨.pm ivar, true⟩] res.finish @k | .conjunction [arg] | .disjunction [arg] => iCasesCore hyps goal arg p A @k From ed9a85cb867db9d8f8dafa8817ae5449f3e046c9 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:11:32 +0200 Subject: [PATCH 23/76] review: specialize assumption lemma for `iapply` --- Iris/Iris/ProofMode/Tactics/Apply.lean | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Apply.lean b/Iris/Iris/ProofMode/Tactics/Apply.lean index db05c54e..0e3c6a7e 100644 --- a/Iris/Iris/ProofMode/Tactics/Apply.lean +++ b/Iris/Iris/ProofMode/Tactics/Apply.lean @@ -24,6 +24,11 @@ theorem apply [BI PROP] {p} {P Q Q1 R : PROP} public meta section open Lean Elab Tactic Meta Qq Std +-- Like `ProofMode.assumption`, but specialized for the `iapply` case +theorem apply_assumption [BI PROP] {p : Bool} {P A Q : PROP} [inst : FromAssumption p .in A Q] + [TCOr (Affine P) (Absorbing Q)] : P ∗ □?p A ⊢ Q := + (sep_mono_r inst.1).trans sep_elim_r + /-- Apply a hypothesis `A` to the `goal` by eliminating the wands recursively @@ -59,7 +64,7 @@ elab "iapply" colGt pmt:pmTerm : tactic => do let LOption.some _ ← trySynthInstanceQ q(TCOr (Affine $e) (Absorbing $goal)) | throwError "iapply: the context {e} is not affine and goal not absorbing" -- have rfl : Q($e ∗ □?$p $out ⊣⊢ $e ∗ □?$p $out) := q(.rfl) - mvar.assign q($(pf).trans (assumption (Q := $goal) .rfl)) -- TODO: Is this better? + mvar.assign q($(pf).trans (apply_assumption (Q := $goal))) -- TODO: Is this better? return -- otherwise, `out` should be a wand, handled by `iApplyCore` let pf' ← iApplyCore hyps' p out goal From e9a929783c6e1322eb34673e7b723d46b049f624 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:15:54 +0200 Subject: [PATCH 24/76] =?UTF-8?q?review:=20rename=20`SelId`=20constructors?= =?UTF-8?q?=20(`pm`=E2=86=92`ipm`,=20`lean`=E2=86=92`pure`)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Iris/Iris/ProofMode/Patterns/SelPattern.lean | 16 ++++++++-------- Iris/Iris/ProofMode/Tactics/Clear.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Frame.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Revert.lean | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Iris/Iris/ProofMode/Patterns/SelPattern.lean b/Iris/Iris/ProofMode/Patterns/SelPattern.lean index b70f766c..ef5e0291 100644 --- a/Iris/Iris/ProofMode/Patterns/SelPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SelPattern.lean @@ -52,8 +52,8 @@ partial def SelPat.parse (pats : TSyntaxArray `selPat) : MacroM (List SelPat) := public meta section inductive SelId where -| lean (id : FVarId) -| pm (ivar : IVarId) +| pure (id : FVarId) +| ipm (ivar : IVarId) deriving BEq, Hashable, Repr @[rocq_alias esel_pat] @@ -62,17 +62,17 @@ structure SelTarget where /- Was this target specified explicitly or is it from a glob like ∗? -/ explicit : Bool -/-- Resolve selection patterns to concrete proofmode hypotheses (`.pm`) and Lean locals (`.lean`). -/ +/-- Resolve selection patterns to concrete proofmode hypotheses (`.ipm`) and Lean locals (`.lean`). -/ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget) | .ident name => - return [⟨.pm (← hyps.findWithInfo name), true⟩] + return [⟨.ipm (← hyps.findWithInfo name), true⟩] | .leanIdent name => do let ldecl ← getLocalDeclFromUserName name.getId - return [⟨.lean ldecl.fvarId, true⟩] + return [⟨.pure ldecl.fvarId, true⟩] | .intuitionistic => - return hyps.intuitionisticIVarIds.map (⟨.pm ·, false⟩) + return hyps.intuitionisticIVarIds.map (⟨.ipm ·, false⟩) | .spatial => - return hyps.spatialIVarIds.map (⟨.pm ·, false⟩) + return hyps.spatialIVarIds.map (⟨.ipm ·, false⟩) | .pure => do -- `%` selects user-facing Lean pure assumptions, so we keep only `Prop` hypotheses. let mut hyps := #[] @@ -81,7 +81,7 @@ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget continue if ! (← isProp ldecl.type) then continue - hyps := hyps.push (⟨.lean ldecl.fvarId, false⟩) + hyps := hyps.push (⟨.pure ldecl.fvarId, false⟩) return hyps.toList def SelPat.resolve (hyps : Hyps bi e) (pats : List SelPat) : diff --git a/Iris/Iris/ProofMode/Tactics/Clear.lean b/Iris/Iris/ProofMode/Tactics/Clear.lean index 917321f2..6db3b188 100644 --- a/Iris/Iris/ProofMode/Tactics/Clear.lean +++ b/Iris/Iris/ProofMode/Tactics/Clear.lean @@ -53,8 +53,8 @@ elab "iclear" pats:(colGt selPat)+ : tactic => do ProofModeM.runTactic λ mvar { e, hyps, goal, .. } => do let (ivars, fvars) := (← SelPat.resolve hyps pats).partitionMap fun - | ⟨.pm uniq, _⟩ => .inl uniq - | ⟨.lean id, _⟩ => .inr id + | ⟨.ipm uniq, _⟩ => .inl uniq + | ⟨.pure id, _⟩ => .inr id -- Clear the selected Iris hypotheses first, updating the proof-mode context and proof term. let mut st : ClearState e goal := { e, hyps, pf := q(fun h => h) } diff --git a/Iris/Iris/ProofMode/Tactics/Frame.lean b/Iris/Iris/ProofMode/Tactics/Frame.lean index a32bc51e..c5b64c43 100644 --- a/Iris/Iris/ProofMode/Tactics/Frame.lean +++ b/Iris/Iris/ProofMode/Tactics/Frame.lean @@ -63,7 +63,7 @@ structure FrameResult {u} {prop : Q(Type u)} (bi : Q(BI $prop)) (origE origGoal private def FrameResult.step {u prop bi origE origGoal} : @FrameResult u prop bi origE origGoal → SelTarget → ProofModeM (FrameResult bi origE origGoal) - | st@{hyps, goal, pf, ..}, {explicit, target := .pm ivar} => do + | st@{hyps, goal, pf, ..}, {explicit, target := .ipm ivar} => do let ⟨e', hyps', _, out', p, _, hrem⟩ := hyps.remove false ivar let goal' ← mkFreshExprMVarQ q($prop) if let .some _ ← ProofModeM.trySynthInstanceQ q(Frame $p $out' $goal $goal') then @@ -72,7 +72,7 @@ private def FrameResult.step {u prop bi origE origGoal} : throwError "iframe: cannot frame {out'}" else return st - | st@{e, hyps, goal, pf, ..}, {explicit, target := .lean fvar} => do + | st@{e, hyps, goal, pf, ..}, {explicit, target := .pure fvar} => do let ty ← fvar.getType if ! (← Meta.isProp ty) then throwError "iframe: {← fvar.getUserName} is not a Prop" diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 77879958..6ba8571c 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -96,8 +96,8 @@ def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q( let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do match target.target with - | .pm ivar => st.revertProofModeHyp ivar - | .lean fvar => st.revertLeanHyp fvar + | .ipm ivar => st.revertProofModeHyp ivar + | .pure fvar => st.revertLeanHyp fvar let pf' : Q($(st.e) ⊢ $(st.goal)) ← runTacticWithoutFVars st.hyps st.goal st.reverted.reverse (name := .anonymous) k return q($(st.pf) $pf') From 8a70f42e4444671af911efd47c5205d8a4351a53 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:17:58 +0200 Subject: [PATCH 25/76] review: BI instance doesn't change between tactics --- Iris/Iris/ProofMode/ProofModeM.lean | 2 +- Iris/Iris/ProofMode/Tactics/Intro.lean | 2 +- Iris/Iris/ProofMode/Tactics/Loeb.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Revert.lean | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index e9bca4bd..35c8ddec 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -60,7 +60,7 @@ def addBIGoalWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} def runTacticWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (hyps : Hyps bi e) (goal : Q($prop)) (toClear : Array FVarId) (name : Name := .anonymous) - (k : ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal)) : + (k : ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal)) : ProofModeM Q($e ⊢ $goal) := do let .mvar mvid ← addBIGoalWithoutFVars hyps goal toClear name | unreachable! diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index 56e5e291..6f9fb14a 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -61,7 +61,7 @@ This function returns the proof of `P ⊢ Q` to be assigned. The new context is -/ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) - (k : ∀ {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : + (k : ∀ {e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM (Q($P ⊢ $Q)) := do match pats with | [] => k hyps Q diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 8a1eb7cf..1459603a 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -23,13 +23,13 @@ def RevertTarget.toSelTarget : RevertTarget → SelTarget | .lean id => .lean id | .pm _ ivar => .pm ivar -abbrev ProofModeContinuation := ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), +abbrev ProofModeContinuation := ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) def ProofModeM.revertingTelescope {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) (hs : List RevertTarget) - (k : ∀{u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation → ProofModeM Q($e ⊢ $goal)) + (k : ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation → ProofModeM Q($e ⊢ $goal)) : ProofModeM Q($e ⊢ $goal) := do let names : List (Syntax × IntroPat) ← hs.mapM fun | .lean id => do diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 6ba8571c..60eedb2c 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -90,7 +90,7 @@ private def RevertState.revertLeanHyp st.revertLeanForallHyp f α def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) - (k : ∀ {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), + (k : ∀ {e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM Q($e ⊢ $goal) := do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } From b561b940da82eefd00ac868c1484311c381af1b5 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:30:13 +0200 Subject: [PATCH 26/76] =?UTF-8?q?fixup!=20review:=20rename=20`SelId`=20con?= =?UTF-8?q?structors=20(`pm`=E2=86=92`ipm`,=20`lean`=E2=86=92`pure`)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Iris/Iris/ProofMode/Tactics/Cases.lean | 2 +- Iris/Iris/ProofMode/Tactics/Specialize.lean | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index 655baa53..44709a67 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -215,7 +215,7 @@ partial def iCasesCore {P} (hyps : Hyps bi P) (goal : Q($prop)) (pat : iCasesPat | .frame => do let ⟨ivar, hyps'⟩ ← Hyps.addWithInfo bi (← `(binderIdent | _)) p A hyps - let res ← iFrame bi _ hyps' goal [⟨.pm ivar, true⟩] + let res ← iFrame bi _ hyps' goal [⟨.ipm ivar, true⟩] res.finish @k | .conjunction [arg] | .disjunction [arg] => iCasesCore hyps goal arg p A @k diff --git a/Iris/Iris/ProofMode/Tactics/Specialize.lean b/Iris/Iris/ProofMode/Tactics/Specialize.lean index 57873864..f74619d5 100644 --- a/Iris/Iris/ProofMode/Tactics/Specialize.lean +++ b/Iris/Iris/ProofMode/Tactics/Specialize.lean @@ -107,7 +107,7 @@ private def processWand : let out₂ ← mkFreshExprMVarQ prop let some _ ← ProofModeM.trySynthInstanceQ q(IntoWand $p false $out .out $out₁ .out $out₂) | throwError m!"ispecialize: {out} is not a wand" - let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.pm ·, true⟩)) + let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.ipm ·, true⟩)) let pf'' ← res.finish (addBIGoal · · g) let pf := q(specialize_wand_subgoal $out₂ $pf $pf' $pf'') return { e := el', hyps := hypsl', p := q(false), out := out₂, pf } From 7e6e5439b45b0c3dd76adc78826573c115736347 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:57:01 +0200 Subject: [PATCH 27/76] fixup! review: BI instance doesn't always change between tactics --- Iris/Iris/ProofMode/Tactics/Intro.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index 6f9fb14a..b5a9d5a7 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -61,7 +61,7 @@ This function returns the proof of `P ⊢ Q` to be assigned. The new context is -/ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) - (k : ∀ {e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : + (k : ∀ {prop : Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM (Q($P ⊢ $Q)) := do match pats with | [] => k hyps Q From c7c09acdd2acd5231dab81a4f051c2cd80e01afd Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:57:42 +0200 Subject: [PATCH 28/76] review: add persistence information in `SelTarget` --- Iris/Iris/ProofMode/Expr.lean | 10 +++-- Iris/Iris/ProofMode/Patterns/SelPattern.lean | 14 ++++--- Iris/Iris/ProofMode/Tactics/Cases.lean | 2 +- Iris/Iris/ProofMode/Tactics/Clear.lean | 4 +- Iris/Iris/ProofMode/Tactics/Frame.lean | 4 +- Iris/Iris/ProofMode/Tactics/Loeb.lean | 40 ++++++++------------ Iris/Iris/ProofMode/Tactics/Specialize.lean | 2 +- 7 files changed, 37 insertions(+), 39 deletions(-) diff --git a/Iris/Iris/ProofMode/Expr.lean b/Iris/Iris/ProofMode/Expr.lean index 27cec8d3..205b5bc0 100644 --- a/Iris/Iris/ProofMode/Expr.lean +++ b/Iris/Iris/ProofMode/Expr.lean @@ -479,11 +479,15 @@ def addHypInfo (stx : Syntax) (name : Name) (ivar : IVarId) (prop : Q(Type u)) ( let ty := q(HypMarker $ty) addLocalVarInfo stx (lctx.mkLocalDecl ⟨ivar.name⟩ name ty) (.fvar ⟨ivar.name⟩) ty isBinder +/-- Hyps.findWithInfoPersistent should be used on names obtained from the syntax of a tactic to highlight them correctly. -/ +def Hyps.findWithInfoPersistent {u prop bi} (hyps : @Hyps u prop bi s) (name : Ident) : MetaM (IVarId × Bool) := do + let some (ivar, p, ty) := hyps.find? name.getId | throwError "unknown hypothesis {name}" + addHypInfo name name.getId ivar prop ty + pure (ivar, isTrue p) + /-- Hyps.findWithInfo should be used on names obtained from the syntax of a tactic to highlight them correctly. -/ def Hyps.findWithInfo {u prop bi} (hyps : @Hyps u prop bi s) (name : Ident) : MetaM IVarId := do - let some (ivar, _, ty) := hyps.find? name.getId | throwError "unknown hypothesis {name}" - addHypInfo name name.getId ivar prop ty - pure ivar + (·.1) <$> hyps.findWithInfoPersistent name /-- Hyps.addWithInfo should be used by tactics that introduce a hypothesis based on the name given by the user. -/ def Hyps.addWithInfo {prop : Q(Type u)} (bi : Q(BI $prop)) diff --git a/Iris/Iris/ProofMode/Patterns/SelPattern.lean b/Iris/Iris/ProofMode/Patterns/SelPattern.lean index ef5e0291..1ef3f5ef 100644 --- a/Iris/Iris/ProofMode/Patterns/SelPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SelPattern.lean @@ -61,18 +61,20 @@ structure SelTarget where target : SelId /- Was this target specified explicitly or is it from a glob like ∗? -/ explicit : Bool + persistent? : Bool /-- Resolve selection patterns to concrete proofmode hypotheses (`.ipm`) and Lean locals (`.lean`). -/ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget) - | .ident name => - return [⟨.ipm (← hyps.findWithInfo name), true⟩] + | .ident name => do + let ⟨ivar, persistent?⟩ ← hyps.findWithInfoPersistent name + return [⟨.ipm ivar, true, persistent?⟩] | .leanIdent name => do let ldecl ← getLocalDeclFromUserName name.getId - return [⟨.pure ldecl.fvarId, true⟩] + return [⟨.pure ldecl.fvarId, true, true⟩] | .intuitionistic => - return hyps.intuitionisticIVarIds.map (⟨.ipm ·, false⟩) + return hyps.intuitionisticIVarIds.map (⟨.ipm ·, false, true⟩) | .spatial => - return hyps.spatialIVarIds.map (⟨.ipm ·, false⟩) + return hyps.spatialIVarIds.map (⟨.ipm ·, false, false⟩) | .pure => do -- `%` selects user-facing Lean pure assumptions, so we keep only `Prop` hypotheses. let mut hyps := #[] @@ -81,7 +83,7 @@ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget continue if ! (← isProp ldecl.type) then continue - hyps := hyps.push (⟨.pure ldecl.fvarId, false⟩) + hyps := hyps.push (⟨.pure ldecl.fvarId, false, true⟩) return hyps.toList def SelPat.resolve (hyps : Hyps bi e) (pats : List SelPat) : diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index 44709a67..505ce76a 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -215,7 +215,7 @@ partial def iCasesCore {P} (hyps : Hyps bi P) (goal : Q($prop)) (pat : iCasesPat | .frame => do let ⟨ivar, hyps'⟩ ← Hyps.addWithInfo bi (← `(binderIdent | _)) p A hyps - let res ← iFrame bi _ hyps' goal [⟨.ipm ivar, true⟩] + let res ← iFrame bi _ hyps' goal [⟨.ipm ivar, true, isTrue p⟩] res.finish @k | .conjunction [arg] | .disjunction [arg] => iCasesCore hyps goal arg p A @k diff --git a/Iris/Iris/ProofMode/Tactics/Clear.lean b/Iris/Iris/ProofMode/Tactics/Clear.lean index 6db3b188..34e51be8 100644 --- a/Iris/Iris/ProofMode/Tactics/Clear.lean +++ b/Iris/Iris/ProofMode/Tactics/Clear.lean @@ -53,8 +53,8 @@ elab "iclear" pats:(colGt selPat)+ : tactic => do ProofModeM.runTactic λ mvar { e, hyps, goal, .. } => do let (ivars, fvars) := (← SelPat.resolve hyps pats).partitionMap fun - | ⟨.ipm uniq, _⟩ => .inl uniq - | ⟨.pure id, _⟩ => .inr id + | {target := .ipm uniq, ..} => .inl uniq + | {target := .pure id, ..} => .inr id -- Clear the selected Iris hypotheses first, updating the proof-mode context and proof term. let mut st : ClearState e goal := { e, hyps, pf := q(fun h => h) } diff --git a/Iris/Iris/ProofMode/Tactics/Frame.lean b/Iris/Iris/ProofMode/Tactics/Frame.lean index c5b64c43..85e96568 100644 --- a/Iris/Iris/ProofMode/Tactics/Frame.lean +++ b/Iris/Iris/ProofMode/Tactics/Frame.lean @@ -63,7 +63,7 @@ structure FrameResult {u} {prop : Q(Type u)} (bi : Q(BI $prop)) (origE origGoal private def FrameResult.step {u prop bi origE origGoal} : @FrameResult u prop bi origE origGoal → SelTarget → ProofModeM (FrameResult bi origE origGoal) - | st@{hyps, goal, pf, ..}, {explicit, target := .ipm ivar} => do + | st@{hyps, goal, pf, ..}, {explicit, target := .ipm ivar, ..} => do let ⟨e', hyps', _, out', p, _, hrem⟩ := hyps.remove false ivar let goal' ← mkFreshExprMVarQ q($prop) if let .some _ ← ProofModeM.trySynthInstanceQ q(Frame $p $out' $goal $goal') then @@ -72,7 +72,7 @@ private def FrameResult.step {u prop bi origE origGoal} : throwError "iframe: cannot frame {out'}" else return st - | st@{e, hyps, goal, pf, ..}, {explicit, target := .pure fvar} => do + | st@{e, hyps, goal, pf, ..}, {explicit, target := .pure fvar, ..} => do let ty ← fvar.getType if ! (← Meta.isProp ty) then throwError "iframe: {← fvar.getUserName} is not a Prop" diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 1459603a..0c20b7b5 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -14,35 +14,26 @@ open Lean Meta Elab.Tactic Qq public meta section -inductive RevertTarget where -| lean (id : FVarId) -| pm (persistent? : Bool) (ivar : IVarId) -deriving BEq, Hashable, Repr - -def RevertTarget.toSelTarget : RevertTarget → SelTarget - | .lean id => .lean id - | .pm _ ivar => .pm ivar - -abbrev ProofModeContinuation := ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), +abbrev ProofModeContinuation(u : Level) := ∀ {prop : Q(Type u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) def ProofModeM.revertingTelescope {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) - (hs : List RevertTarget) - (k : ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation → ProofModeM Q($e ⊢ $goal)) + (hs : List SelTarget) + (k : ∀ {prop : Q(Type u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation u → ProofModeM Q($e ⊢ $goal)) : ProofModeM Q($e ⊢ $goal) := do let names : List (Syntax × IntroPat) ← hs.mapM fun - | .lean id => do + | {target := .pure id, ..} => do let name ← Lean.mkIdent <$> id.getUserName let ident ← `(binderIdent| $name:ident) return (name, .intro <| .pure ident) - | .pm persistent? ivar => do + | {target := .ipm ivar, persistent?, ..} => do let name ← Lean.mkIdent <$> (hyps.getUserName? ivar).getM let ident ← `(binderIdent| $name:ident) return (name, .intro <| (if persistent? then .intuitionistic else id) <| .one ident) trace[iloeb] s!"Calling reverting telescope with {names.map (·.1)} on context {←ppExpr hyps.tm}\n⊢\n{←ppExpr goal}" - iRevertCore (hs.map RevertTarget.toSelTarget) hyps goal fun hyps goal => do + iRevertCore hs hyps goal fun hyps goal => do k hyps goal fun hyps goal => do iIntroCore hyps goal names @@ -61,30 +52,31 @@ syntax (name := iloeb) "iloeb " " as " binderIdent (" generalizing " (ppSpace co elab_rules : tactic | `(tactic| iloeb as $IH:binderIdent $[generalizing $[$hs:selPat]*]? ) => do ProofModeM.runTactic fun mvid {hyps, goal, ..} => do - let spatialCtx := hyps.spatialIVarIds.map (RevertTarget.pm false) + let spatialCtx := hyps.spatialIVarIds.map ({target := .ipm ·, explicit := false, persistent? := false}) let generalizedHs ← do let hs := hs.getD #[] let pats ← Elab.liftMacroM <| SelPat.parse hs let generalizedHs ← SelPat.resolve hyps pats generalizedHs.zip hs.toList - |>.filterMapM fun - | (.pm ivar, ref) => do - if spatialCtx.contains (.pm false ivar) then + |>.filterMapM fun (tgt, ref) => + match tgt.target with + | .ipm ivar => do + if spatialCtx.map (·.target) |>.contains (.ipm ivar) then logWarningAt ref m!"Spatial hypothesis are generalized automatically by iloeb" return none else - return some (RevertTarget.pm true ivar) - | (.lean id, _) => return some (.lean id) + return some ({target := .ipm ivar, explicit := false, persistent? := true}) + | .pure id => return some ({target := .pure id, explicit := false, persistent? := true}) - let expr ← ProofModeM.revertingTelescope hyps goal (generalizedHs ++ spatialCtx) fun {u prop _ _} hyps goal k => do - let .some _ ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) + let expr ← ProofModeM.revertingTelescope hyps goal (generalizedHs ++ spatialCtx) fun {prop _ _} hyps goal k => do + let some _ ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) | throwError m!"Cannot use `iloeb` if there is no `{←ppExpr q(BI.BILoeb $prop)}` instance available" let pf := q(BI.loeb_wand_intuitionistically (P := $goal)) let pf' ← do -- We have applied BI.loeb_wand_intuitionistically let goal := q(iprop(□ (□ ▷ $goal -∗ $goal))) iModIntroCore hyps goal (← `(_)) fun hyps goal => do - iIntroCore hyps goal [(IH, .intro <| .intuitionistic <| .one IH)] fun hyps goal => do + iIntroCore hyps goal [(IH, .intro <| .intuitionistic <| .one IH)] fun hyps goal => k hyps goal return q($(pf').trans $pf) diff --git a/Iris/Iris/ProofMode/Tactics/Specialize.lean b/Iris/Iris/ProofMode/Tactics/Specialize.lean index f74619d5..c67f7459 100644 --- a/Iris/Iris/ProofMode/Tactics/Specialize.lean +++ b/Iris/Iris/ProofMode/Tactics/Specialize.lean @@ -107,7 +107,7 @@ private def processWand : let out₂ ← mkFreshExprMVarQ prop let some _ ← ProofModeM.trySynthInstanceQ q(IntoWand $p false $out .out $out₁ .out $out₂) | throwError m!"ispecialize: {out} is not a wand" - let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.ipm ·, true⟩)) + let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.ipm ·, true, kind != .spatial⟩)) let pf'' ← res.finish (addBIGoal · · g) let pf := q(specialize_wand_subgoal $out₂ $pf $pf' $pf'') return { e := el', hyps := hypsl', p := q(false), out := out₂, pf } From 4b69bd6318c5fe78a81bce962a93401ee718eb1d Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 17:04:01 +0200 Subject: [PATCH 29/76] review: remove stale definitions --- Iris/Iris/ProofMode/ProofModeM.lean | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index 35c8ddec..efd9d41d 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -134,25 +134,3 @@ def ProofModeM.runTactic (x : MVarId → IrisGoal → ProofModeM α) (s : ProofM replaceMainGoal (nonDep ++ dep).toList return res - -def ProofModeM.pruneSolvedGoals : ProofModeM Unit := do - let gs := (←get).goals - let gs ← gs.filterM fun g => not <$> g.isAssigned - modify ({· with goals := gs}) - -def ProofModeM.getUnsolvedGoals : ProofModeM (Array MVarId) := do - pruneSolvedGoals - return (←get).goals - -def ProofModeM.getMainGoal : ProofModeM MVarId := do - loop (←get).goals 0 -where - loop (goals : Array MVarId) (i : Nat) : ProofModeM MVarId := do - if h: i < goals.size then - if (← goals[i].isAssigned) then - loop goals (i+1) - else - modify ({· with goals:= goals[i...*]}) - return goals[i] - else - throwNoGoalsToBeSolved From 02f35ed5984de93083fc9fe17a0125c4c9967587 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 17:04:17 +0200 Subject: [PATCH 30/76] review: add negative test when no BILoeb instance is available using `iloeb` --- Iris/Iris/Tests/Tactics.lean | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 370544ad..ecd97524 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2294,4 +2294,11 @@ example (H₁ : Prop) (P Q : PROP) : iintro %h1 p iloeb as IH generalizing %h1 +variable {PROP : Type u} [ι₁ : BI PROP] in +/-- error: Cannot use `iloeb` if there is no `BILoeb PROP` instance available -/ +#guard_msgs in +example (H₁ : Prop) (P Q : PROP) : + H₁ → ⊢ P -∗ Q := by + iloeb as IH + end iloeb From 678a03cc717b36b5de1023af40f5ca799ea5a026 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 17:16:52 +0200 Subject: [PATCH 31/76] =?UTF-8?q?refactor:=20push=20`persistent=3F`=20in?= =?UTF-8?q?=20`SelTarget`=20inside=20`=C3=ACpm`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Iris/Iris/ProofMode/Patterns/SelPattern.lean | 19 +++++++++---------- Iris/Iris/ProofMode/Tactics/Cases.lean | 2 +- Iris/Iris/ProofMode/Tactics/Clear.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Frame.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Loeb.lean | 16 +++++++--------- Iris/Iris/ProofMode/Tactics/Revert.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Specialize.lean | 2 +- 7 files changed, 24 insertions(+), 27 deletions(-) diff --git a/Iris/Iris/ProofMode/Patterns/SelPattern.lean b/Iris/Iris/ProofMode/Patterns/SelPattern.lean index 1ef3f5ef..0bc642fc 100644 --- a/Iris/Iris/ProofMode/Patterns/SelPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SelPattern.lean @@ -51,30 +51,29 @@ partial def SelPat.parse (pats : TSyntaxArray `selPat) : MacroM (List SelPat) := public meta section -inductive SelId where +inductive SelTarget.Kind where | pure (id : FVarId) -| ipm (ivar : IVarId) +| ipm (ivar : IVarId) (persistent? : Bool) deriving BEq, Hashable, Repr @[rocq_alias esel_pat] structure SelTarget where - target : SelId + kind : SelTarget.Kind /- Was this target specified explicitly or is it from a glob like ∗? -/ explicit : Bool - persistent? : Bool /-- Resolve selection patterns to concrete proofmode hypotheses (`.ipm`) and Lean locals (`.lean`). -/ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget) | .ident name => do let ⟨ivar, persistent?⟩ ← hyps.findWithInfoPersistent name - return [⟨.ipm ivar, true, persistent?⟩] + return [⟨.ipm ivar persistent?, true⟩] | .leanIdent name => do let ldecl ← getLocalDeclFromUserName name.getId - return [⟨.pure ldecl.fvarId, true, true⟩] + return [⟨.pure ldecl.fvarId, true⟩] | .intuitionistic => - return hyps.intuitionisticIVarIds.map (⟨.ipm ·, false, true⟩) + return hyps.intuitionisticIVarIds.map (⟨.ipm · true, false⟩) | .spatial => - return hyps.spatialIVarIds.map (⟨.ipm ·, false, false⟩) + return hyps.spatialIVarIds.map (⟨.ipm · true, false⟩) | .pure => do -- `%` selects user-facing Lean pure assumptions, so we keep only `Prop` hypotheses. let mut hyps := #[] @@ -83,7 +82,7 @@ def SelPat.resolveOne (hyps : Hyps bi e) : SelPat → ProofModeM (List SelTarget continue if ! (← isProp ldecl.type) then continue - hyps := hyps.push (⟨.pure ldecl.fvarId, false, true⟩) + hyps := hyps.push (⟨.pure ldecl.fvarId, false⟩) return hyps.toList def SelPat.resolve (hyps : Hyps bi e) (pats : List SelPat) : @@ -91,7 +90,7 @@ def SelPat.resolve (hyps : Hyps bi e) (pats : List SelPat) : -- we want to remove duplicates; and if an pattern is first explicitly specified and then non-explicitly, -- we want to remove the non-explicit version (but not the other way around) return (← pats.flatMapM (SelPat.resolveOne hyps)).eraseDupsBy - (λ snd fst => snd.target == fst.target && (fst.explicit == snd.explicit || fst.explicit)) + (λ snd fst => snd.kind == fst.kind && (fst.explicit == snd.explicit || fst.explicit)) end diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index 505ce76a..b2c167e9 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -215,7 +215,7 @@ partial def iCasesCore {P} (hyps : Hyps bi P) (goal : Q($prop)) (pat : iCasesPat | .frame => do let ⟨ivar, hyps'⟩ ← Hyps.addWithInfo bi (← `(binderIdent | _)) p A hyps - let res ← iFrame bi _ hyps' goal [⟨.ipm ivar, true, isTrue p⟩] + let res ← iFrame bi _ hyps' goal [⟨.ipm ivar (isTrue p), true⟩] res.finish @k | .conjunction [arg] | .disjunction [arg] => iCasesCore hyps goal arg p A @k diff --git a/Iris/Iris/ProofMode/Tactics/Clear.lean b/Iris/Iris/ProofMode/Tactics/Clear.lean index 34e51be8..a00f6e07 100644 --- a/Iris/Iris/ProofMode/Tactics/Clear.lean +++ b/Iris/Iris/ProofMode/Tactics/Clear.lean @@ -53,8 +53,8 @@ elab "iclear" pats:(colGt selPat)+ : tactic => do ProofModeM.runTactic λ mvar { e, hyps, goal, .. } => do let (ivars, fvars) := (← SelPat.resolve hyps pats).partitionMap fun - | {target := .ipm uniq, ..} => .inl uniq - | {target := .pure id, ..} => .inr id + | {kind := .ipm uniq _, ..} => .inl uniq + | {kind := .pure id, ..} => .inr id -- Clear the selected Iris hypotheses first, updating the proof-mode context and proof term. let mut st : ClearState e goal := { e, hyps, pf := q(fun h => h) } diff --git a/Iris/Iris/ProofMode/Tactics/Frame.lean b/Iris/Iris/ProofMode/Tactics/Frame.lean index 85e96568..4a0ebfad 100644 --- a/Iris/Iris/ProofMode/Tactics/Frame.lean +++ b/Iris/Iris/ProofMode/Tactics/Frame.lean @@ -63,7 +63,7 @@ structure FrameResult {u} {prop : Q(Type u)} (bi : Q(BI $prop)) (origE origGoal private def FrameResult.step {u prop bi origE origGoal} : @FrameResult u prop bi origE origGoal → SelTarget → ProofModeM (FrameResult bi origE origGoal) - | st@{hyps, goal, pf, ..}, {explicit, target := .ipm ivar, ..} => do + | st@{hyps, goal, pf, ..}, {explicit, kind := .ipm ivar _, ..} => do let ⟨e', hyps', _, out', p, _, hrem⟩ := hyps.remove false ivar let goal' ← mkFreshExprMVarQ q($prop) if let .some _ ← ProofModeM.trySynthInstanceQ q(Frame $p $out' $goal $goal') then @@ -72,7 +72,7 @@ private def FrameResult.step {u prop bi origE origGoal} : throwError "iframe: cannot frame {out'}" else return st - | st@{e, hyps, goal, pf, ..}, {explicit, target := .pure fvar, ..} => do + | st@{e, hyps, goal, pf, ..}, {explicit, kind := .pure fvar, ..} => do let ty ← fvar.getType if ! (← Meta.isProp ty) then throwError "iframe: {← fvar.getUserName} is not a Prop" diff --git a/Iris/Iris/ProofMode/Tactics/Loeb.lean b/Iris/Iris/ProofMode/Tactics/Loeb.lean index 0c20b7b5..22948d0a 100644 --- a/Iris/Iris/ProofMode/Tactics/Loeb.lean +++ b/Iris/Iris/ProofMode/Tactics/Loeb.lean @@ -23,11 +23,11 @@ def ProofModeM.revertingTelescope (k : ∀ {prop : Q(Type u)}{bi : Q(BI $prop)}{e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeContinuation u → ProofModeM Q($e ⊢ $goal)) : ProofModeM Q($e ⊢ $goal) := do let names : List (Syntax × IntroPat) ← hs.mapM fun - | {target := .pure id, ..} => do + | {kind := .pure id, ..} => do let name ← Lean.mkIdent <$> id.getUserName let ident ← `(binderIdent| $name:ident) return (name, .intro <| .pure ident) - | {target := .ipm ivar, persistent?, ..} => do + | {kind := .ipm ivar persistent?, ..} => do let name ← Lean.mkIdent <$> (hyps.getUserName? ivar).getM let ident ← `(binderIdent| $name:ident) return (name, .intro <| (if persistent? then .intuitionistic else id) <| .one ident) @@ -52,21 +52,19 @@ syntax (name := iloeb) "iloeb " " as " binderIdent (" generalizing " (ppSpace co elab_rules : tactic | `(tactic| iloeb as $IH:binderIdent $[generalizing $[$hs:selPat]*]? ) => do ProofModeM.runTactic fun mvid {hyps, goal, ..} => do - let spatialCtx := hyps.spatialIVarIds.map ({target := .ipm ·, explicit := false, persistent? := false}) + let spatialCtx : List SelTarget := hyps.spatialIVarIds.map ({kind := .ipm · false, explicit := false}) let generalizedHs ← do let hs := hs.getD #[] let pats ← Elab.liftMacroM <| SelPat.parse hs let generalizedHs ← SelPat.resolve hyps pats generalizedHs.zip hs.toList |>.filterMapM fun (tgt, ref) => - match tgt.target with - | .ipm ivar => do - if spatialCtx.map (·.target) |>.contains (.ipm ivar) then + match tgt.kind with + | .ipm _ false => do logWarningAt ref m!"Spatial hypothesis are generalized automatically by iloeb" return none - else - return some ({target := .ipm ivar, explicit := false, persistent? := true}) - | .pure id => return some ({target := .pure id, explicit := false, persistent? := true}) + | .ipm _ true + | .pure _ => return some tgt let expr ← ProofModeM.revertingTelescope hyps goal (generalizedHs ++ spatialCtx) fun {prop _ _} hyps goal k => do let some _ ← ProofModeM.trySynthInstanceQ q(BI.BILoeb $prop) diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 60eedb2c..a2fc6039 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -95,8 +95,8 @@ def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q( ProofModeM Q($e ⊢ $goal) := do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do - match target.target with - | .ipm ivar => st.revertProofModeHyp ivar + match target.kind with + | .ipm ivar _ => st.revertProofModeHyp ivar | .pure fvar => st.revertLeanHyp fvar let pf' : Q($(st.e) ⊢ $(st.goal)) ← runTacticWithoutFVars st.hyps st.goal st.reverted.reverse (name := .anonymous) k diff --git a/Iris/Iris/ProofMode/Tactics/Specialize.lean b/Iris/Iris/ProofMode/Tactics/Specialize.lean index c67f7459..d71ce8bf 100644 --- a/Iris/Iris/ProofMode/Tactics/Specialize.lean +++ b/Iris/Iris/ProofMode/Tactics/Specialize.lean @@ -107,7 +107,7 @@ private def processWand : let out₂ ← mkFreshExprMVarQ prop let some _ ← ProofModeM.trySynthInstanceQ q(IntoWand $p false $out .out $out₁ .out $out₂) | throwError m!"ispecialize: {out} is not a wand" - let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.ipm ·, true, kind != .spatial⟩)) + let res ← iFrame bi _ hypsr' out₁ (frameIVars.map (⟨.ipm · (kind != .spatial), true⟩)) let pf'' ← res.finish (addBIGoal · · g) let pf := q(specialize_wand_subgoal $out₂ $pf $pf' $pf'') return { e := el', hyps := hypsl', p := q(false), out := out₂, pf } From a55e95f4626933d8d661ac1dadf63421bcc524a3 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 17:30:14 +0200 Subject: [PATCH 32/76] fix: warnings --- Iris/Iris/ProofMode/Tactics/Intro.lean | 2 +- Iris/Iris/ProofMode/Tactics/Revert.lean | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Intro.lean b/Iris/Iris/ProofMode/Tactics/Intro.lean index b5a9d5a7..cd44a705 100644 --- a/Iris/Iris/ProofMode/Tactics/Intro.lean +++ b/Iris/Iris/ProofMode/Tactics/Intro.lean @@ -61,7 +61,7 @@ This function returns the proof of `P ⊢ Q` to be assigned. The new context is -/ partial def iIntroCore {prop : Q(Type u)} {bi : Q(BI $prop)} {P} (hyps : Hyps bi P) (Q : Q($prop)) (pats : List (Syntax × IntroPat)) - (k : ∀ {prop : Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal) := addBIGoal) : + (k : ∀ {prop : Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}, Hyps bi e → (goal: Q($prop)) → ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM (Q($P ⊢ $Q)) := do match pats with | [] => k hyps Q diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index a2fc6039..e077a320 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -90,8 +90,7 @@ private def RevertState.revertLeanHyp st.revertLeanForallHyp f α def iRevertCore (targets : List SelTarget) {u : Level}{prop: Q(Type $u)}{bi : Q(BI $prop)}{e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)) - (k : ∀ {e : Q($prop)}(hyps : Hyps bi e)(goal: Q($prop)), - ProofModeM Q($e ⊢ $goal) := addBIGoal) : + (k : ∀ {e : Q($prop)}, Hyps bi e → (goal: Q($prop)) → ProofModeM Q($e ⊢ $goal) := addBIGoal) : ProofModeM Q($e ⊢ $goal) := do let init : RevertState e goal := { e, hyps, goal, pf := q(id) } let st ← targets.reverse.foldlM (init := init) fun st target => do From 1638a598728b770ac843b21817c20cb271b0b4f7 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 6 May 2026 16:00:02 +0200 Subject: [PATCH 33/76] feat: add WeakestPre notation --- Iris/Iris/BI/WeakestPre.lean | 300 +++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 Iris/Iris/BI/WeakestPre.lean diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean new file mode 100644 index 00000000..0063990c --- /dev/null +++ b/Iris/Iris/BI/WeakestPre.lean @@ -0,0 +1,300 @@ +module + +public import Iris.Std.CoPset +public meta import Iris.Std.Rewrite +public import Std +meta import Lean +public import Lean + +public section + +#check Lean.PrettyPrinter.Unexpander + +open Lean + +inductive Stuckness where +| NotStuck +| MaybeStuck + +namespace Stuckness + +@[simp] +instance instLE: LE Stuckness where + le x y := ¬ (x = .MaybeStuck ∧ y = .NotStuck) + +instance : Std.IsPreorder Stuckness where + le_refl := by + simp only [LE.le, not_and, forall_eq, reduceCtorEq, not_false_eq_true] + le_trans := by + simp only [LE.le]; grind only [Stuckness] + +end Stuckness + +class Wp (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) where + wp : A → CoPset → Expr → (Val → PROP) → PROP + +class TotalWP (PROP Expr) (Val : outParam (Type _)) (A : Type _) where + totalWp : A → CoPset → Expr → (Val → PROP) → PROP + +syntax wp_expr := + term:max (" @ " term:max (" ; " term:max) <|> ((" ? ")? )) <|> (" ? ")? + +declare_syntax_cat wp_postcond_inner +syntax ident ", " term : wp_postcond_inner +syntax term : wp_postcond_inner + +declare_syntax_cat wp_postcond +syntax " {{ " wp_postcond_inner " }} " : wp_postcond +syntax " [{ " wp_postcond_inner " }] " : wp_postcond +syntax " ⦃ " wp_postcond_inner " ⦄ " : wp_postcond -- Are spaces outside of parens used in the pp? +syntax " 〖 " wp_postcond_inner " 〗 " : wp_postcond + +syntax (name := wp) "WP " wp_expr wp_postcond : term + +/- This section checks whether the syntax is recognized correctly for all combinations -/ +section testNotation +set_option trace.Elab.info false + +/-- Suppress all `info` level messages from a command -/ +elab "#no_info" "in" cmd:command : command => do + Lean.Elab.Command.elabCommandTopLevel cmd + modify fun st => {st with + messages := {st.messages with + unreported := st.messages.unreported.filter (¬ ·.severity matches MessageSeverity.information) + } + } + +#no_info in #check_failure WP e @ s ; E {{ Φ }} +#no_info in #check_failure WP e @ E {{ Φ }} +#no_info in #check_failure WP e @ E ? {{ Φ }} +#no_info in #check_failure WP e {{ Φ }} +#no_info in #check_failure WP e ? {{ Φ }} + +#no_info in #check_failure WP e @ s ; E {{v, Φ }} +#no_info in #check_failure WP e @ E {{v, Φ }} +#no_info in #check_failure WP e @ E ? {{v, Φ }} +#no_info in #check_failure WP e {{v, Φ }} +#no_info in #check_failure WP e ? {{v, Φ }} + +#no_info in #check_failure WP e @ s ; E ⦃ Φ ⦄ +#no_info in #check_failure WP e @ E ⦃ Φ ⦄ +#no_info in #check_failure WP e @ E ? ⦃ Φ ⦄ +#no_info in #check_failure WP e ⦃ Φ ⦄ +#no_info in #check_failure WP e ? ⦃ Φ ⦄ + +#no_info in #check_failure WP e @ s ; E ⦃v, Φ ⦄ +#no_info in #check_failure WP e @ E ⦃v, Φ ⦄ +#no_info in #check_failure WP e @ E ? ⦃v, Φ ⦄ +#no_info in #check_failure WP e ⦃v, Φ ⦄ +#no_info in #check_failure WP e ? ⦃v, Φ ⦄ + +#no_info in #check_failure WP e @ s ; E [{ Φ }] +#no_info in #check_failure WP e @ E [{ Φ }] +#no_info in #check_failure WP e @ E ? [{ Φ }] +#no_info in #check_failure WP e [{ Φ }] +#no_info in #check_failure WP e ? [{ Φ }] + +#no_info in #check_failure WP e @ s ; E [{v, Φ }] +#no_info in #check_failure WP e @ E [{v, Φ }] +#no_info in #check_failure WP e @ E ? [{v, Φ }] +#no_info in #check_failure WP e [{v, Φ }] +#no_info in #check_failure WP e ? [{v, Φ }] + +#no_info in #check_failure WP e @ s ; E 〖 Φ 〗 +#no_info in #check_failure WP e @ E 〖 Φ 〗 +#no_info in #check_failure WP e @ E ? 〖 Φ 〗 +#no_info in #check_failure WP e 〖 Φ 〗 +#no_info in #check_failure WP e ? 〖 Φ 〗 + +#no_info in #check_failure WP e @ s ; E 〖v, Φ 〗 +#no_info in #check_failure WP e @ E 〖v, Φ 〗 +#no_info in #check_failure WP e @ E ? 〖v, Φ 〗 +#no_info in #check_failure WP e 〖v, Φ 〗 +#no_info in #check_failure WP e ? 〖v, Φ 〗 + +end testNotation + +open Lean in +meta def parseWpExpr : Lean.TSyntax `wp_expr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun + | `(wp_expr| $e @ $s ; $E) => + return (e, s, E) + | `(wp_expr| $e @ $E) => + return (e, ←`(Stuckness.NotStuck), E) + | `(wp_expr| $e @ $E ?) => + return (e, ←`(Stuckness.MaybeStuck), E) + | `(wp_expr| $e:term) => + return (e, ←`(Stuckness.NotStuck), ←`(⊤)) + | `(wp_expr| $e:term ?) => + return (e, ←`(Stuckness.MaybeStuck), ←`(⊤)) + | _ => Lean.Macro.throwUnsupported + +open Lean in +meta def parseWpPostcondInner (stx : TSyntax `wp_postcond_inner) : MacroM (TSyntax `term) := do + match stx with + | `(wp_postcond_inner| $v:ident, $Φ:term) => `(fun $v => $Φ) + | `(wp_postcond_inner| $Φ:term) => return Φ + | _ => Macro.throwUnsupported + +open Lean in +meta def parseWpPostcond (stx : TSyntax `wp_postcond) : MacroM (TSyntax `term × Bool) := do + match stx with + | `(wp_postcond| {{ $inner:wp_postcond_inner }}) + | `(wp_postcond| ⦃ $inner:wp_postcond_inner ⦄) => + return (←parseWpPostcondInner inner, false) + | `(wp_postcond| [{ $inner:wp_postcond_inner }]) + | `(wp_postcond| 〖 $inner:wp_postcond_inner 〗) => + return (←parseWpPostcondInner inner, true) + | _ => Macro.throwUnsupported (α := TSyntax `term × Bool) + +@[macro wp] +meta def wpMacro : Lean.Macro := fun stx => do + match stx with + | `(WP $expr $postcond) => + let (e, s, E) ← parseWpExpr expr + let (Φ, useTotal?) ← parseWpPostcond postcond + if useTotal? then + `(TotalWP.totalWp $s $E $e $Φ) + else + `(Wp.wp $s $E $e $Φ) + | _ => Lean.Macro.throwUnsupported + +section testElab +set_option linter.unusedVariables false + +variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) +variable [Wp PROP Expr Val A] +variable [Wp PROP Expr Val Stuckness] +variable [TotalWP PROP Expr Val A] +variable [TotalWP PROP Expr Val Stuckness] + +variable (e : Expr)(s : A)(E : CoPset) +variable (Φ : PROP) + +/-- info: Wp.wp s E e fun v => Φ : PROP -/ +#guard_msgs in #check WP e @ s ; E {{v, Φ }} +/-- info: Wp.wp Stuckness.NotStuck E e fun v => Φ : PROP -/ +#guard_msgs in #check WP e @ E {{ v, Φ}} +/-- info: Wp.wp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ +#guard_msgs in #check WP e {{v, Φ }} +/-- info: Wp.wp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ }} + +/-- info: TotalWP.totalWp s E e fun v => Φ : PROP -/ +#guard_msgs in #check WP e @ s ; E [{v, Φ }] +/-- info: TotalWP.totalWp Stuckness.NotStuck E e fun v => Φ : PROP -/ +#guard_msgs in #check WP e @ E [{ v, Φ}] +/-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ +#guard_msgs in #check WP e [{v, Φ }] +/-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ }] + +variable (Φ : Val → PROP) + +/-- info: Wp.wp s E e Φ : PROP -/ +#guard_msgs in #check WP e @ s ; E {{ Φ }} +/-- info: Wp.wp Stuckness.NotStuck E e Φ : PROP -/ +#guard_msgs in #check WP e @ E {{ Φ}} +/-- info: Wp.wp Stuckness.NotStuck ⊤ e Φ : PROP -/ +#guard_msgs in #check WP e {{ Φ }} +/-- info: Wp.wp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ +#guard_msgs in #check WP e ? {{ Φ }} + +/-- info: TotalWP.totalWp s E e Φ : PROP -/ +#guard_msgs in #check WP e @ s ; E [{ Φ }] +/-- info: TotalWP.totalWp Stuckness.NotStuck E e Φ : PROP -/ +#guard_msgs in #check WP e @ E [{ Φ}] +/-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e Φ : PROP -/ +#guard_msgs in #check WP e [{ Φ }] +/-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ +#guard_msgs in #check WP e ? [{ Φ }] + +end testElab + +meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TSyntax `wp_postcond_inner) + | `(fun $v:ident => $Φ:term) => `(wp_postcond_inner|$v:ident, $Φ:term) + | `($Φ:term) => `(wp_postcond_inner| $Φ:term) + +open Lean in +meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax `wp_expr) := do + match s, E with + | `(Stuckness.NotStuck), `(⊤) => `(wp_expr| $e:term) + | `(Stuckness.NotStuck), E => `(wp_expr| $e:term @ $E:term) + | `(Stuckness.MaybeStuck), `(⊤) => `(wp_expr| $e:term ?) + | `(Stuckness.MaybeStuck), E => `(wp_expr| $e:term @ $E:term ?) + | s, E => `(wp_expr| $e:term @ $s:term ; $E:term) + +@[app_unexpander Wp.wp] +meta def unexpanderWp : PrettyPrinter.Unexpander + | `($_wp $s $E $e $Φ) => do + let wp_expr ← makeWpExpr s E e + let wp_postcond_inner ← unexpandWpPostcondInner Φ + `(WP $wp_expr {{ $wp_postcond_inner }}) + | _ => throw () + +@[app_unexpander TotalWP.totalWp] +meta def unexpanderTotalWp : PrettyPrinter.Unexpander + | `($_wp $s $E $e $Φ) => do + let wp_expr ← makeWpExpr s E e + let wp_postcond_inner ← unexpandWpPostcondInner Φ + `(WP $wp_expr [{ $wp_postcond_inner }]) + | _ => throw () + +section testUnexpand +set_option linter.unusedVariables false + +variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) +variable [Wp PROP Expr Val A] +variable [Wp PROP Expr Val Stuckness] +variable [TotalWP PROP Expr Val A] +variable [TotalWP PROP Expr Val Stuckness] + +variable (e : Expr)(s : A)(E : CoPset) +variable (Φ : PROP) + +/-- info: WP e @ s ; E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E {{v, Φ }} +/-- info: WP e @ E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E {{ v, Φ}} +/-- info: WP e @ E ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? {{ v, Φ}} +/-- info: WP e {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e {{v, Φ }} +/-- info: WP e ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ }} + +/-- info: WP e @ s ; E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E [{v, Φ }] +/-- info: WP e @ E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E [{ v, Φ}] +/-- info: WP e @ E ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? [{ v, Φ}] +/-- info: WP e [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e [{v, Φ }] +/-- info: WP e ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ }] + +variable (Φ : Val → PROP) + +/-- info: WP e @ s ; E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E {{ Φ }} +/-- info: WP e @ E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E {{ Φ}} +/-- info: WP e @ E ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? {{ Φ}} +/-- info: WP e {{ Φ }} : PROP -/ +#guard_msgs in #check WP e {{ Φ }} +/-- info: WP e ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{ Φ }} + +/-- info: WP e @ s ; E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E [{ Φ }] +/-- info: WP e @ E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E [{ Φ}] +/-- info: WP e @ E ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? [{ Φ}] +/-- info: WP e [{ Φ }] : PROP -/ +#guard_msgs in #check WP e [{ Φ }] +/-- info: WP e ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{ Φ }] + +end testUnexpand From 6f4e724711be4b5b9ea10a0be7ef5dfa2bf63c1c Mon Sep 17 00:00:00 2001 From: ayhon Date: Thu, 7 May 2026 12:08:22 +0200 Subject: [PATCH 34/76] wip --- Iris/Iris/BI/Updates.lean | 50 ++- Iris/Iris/BI/WeakestPre.lean | 491 +++++++++++++++++++++---- Iris/Iris/ProgramLogic/WeakestPre.lean | 356 ++++++++++++++++++ 3 files changed, 821 insertions(+), 76 deletions(-) create mode 100644 Iris/Iris/ProgramLogic/WeakestPre.lean diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index 07618cf3..6963c8d8 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -105,7 +105,7 @@ class BIUpdate (PROP : Type _) [BI PROP] extends BUpd PROP where frame_r {P R : PROP} : (|==> P) ∗ R ⊢ |==> (P ∗ R) class BIFUpdate (PROP : Type _) [BI PROP] extends FUpd PROP where - [ne {E1 E2 : CoPset} : OFE.NonExpansive (FUpd.fupd E1 E2 (PROP := PROP))] + [ne {E1 E2 : CoPset} : OFE.NonExpansive (iprop(|={E1,E2}=> · : PROP))] subset {E1 E2 : CoPset} : E2 ⊆ E1 → ⊢ |={E1,E2}=> |={E2,E1}=> (emp : PROP) except0 {E1 E2 : CoPset} {P : PROP} : (◇ |={E1,E2}=> P) ⊢ |={E1,E2}=> P mono {E1 E2 : CoPset} {P Q : PROP} : (P ⊢ Q) → (|={E1,E2}=> P) ⊢ |={E1,E2}=> Q @@ -311,6 +311,54 @@ variable [BI PROP] [BIFUpdate PROP] open BIFUpdate LawfulSet +theorem step_fupdN_contractive {E1 E2 : CoPset} {n : Nat} [ι : BILaterContractive PROP] : + OFE.Contractive (iprop(|={E1}[E2]▷=>^[n + 1] · : PROP)) where + distLater_dist := by + intro i x y xy_i + induction n with + | zero => + dsimp only [Nat.repeat] + apply BIFUpdate.ne.ne + apply ι.distLater_dist + intros j ji + apply BIFUpdate.ne.ne + apply xy_i j ji + | succ n IH => + dsimp only [Nat.repeat] + apply BIFUpdate.ne.ne + apply later_ne.ne + apply BIFUpdate.ne.ne + assumption + +theorem step_fupdN_ne {E1 E2 : CoPset} {n : Nat} : + OFE.NonExpansive (iprop(|={E1}[E2]▷=>^[n] · : PROP)) where + ne := by + intro i x y xy_i + induction n with + | zero => simp only [Nat.repeat, xy_i] + | succ n IH => + dsimp only [Nat.repeat] + apply BIFUpdate.ne.ne + apply later_ne.ne + apply BIFUpdate.ne.ne + assumption + +theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : + (|={Eo}[Ei]▷=>^[n] P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=>^[n] Q) := by + refine wand_intro' ?_ + induction n with + | zero => + dsimp [Nat.repeat] + exact wand_elim_l + | succ n IH => + dsimp [Nat.repeat] + calc iprop((P -∗ Q) ∗ |={Eo,Ei}=> ▷ |={Ei,Eo}=> _) + _ ⊢ |={Eo,Ei}=> (P -∗ Q) ∗ ▷ |={Ei,Eo}=> _ := (fupd_frame_l ..) + _ ⊢ |={Eo,Ei}=> (▷ (P -∗ Q)) ∗ ▷ |={Ei,Eo}=> _ := mono (sep_mono (later_intro) .rfl) + _ ⊢ |={Eo,Ei}=> ▷ ((P -∗ Q) ∗ |={Ei,Eo}=> _) := mono (later_sep.2) + _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> ((P -∗ Q) ∗ _) := mono (later_mono (fupd_frame_l ..)) + _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> _ := mono (later_mono (mono IH)) + @[rocq_alias step_fupd_fupd] theorem step_fupd_fupd {Eo Ei : CoPset} {P : PROP} : (|={Eo}[Ei]▷=> P) ⊣⊢ (|={Eo}[Ei]▷=> |={Eo}=> P) := ⟨mono <| later_mono <| mono fupd_intro, mono <| later_mono BIFUpdate.trans⟩ diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 0063990c..e37cce09 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -1,11 +1,22 @@ module public import Iris.Std.CoPset +public import Iris.BI +public meta import Iris.BI +public import Iris.BI.BIBase public meta import Iris.Std.Rewrite public import Std meta import Lean public import Lean +public import Iris.BI.BI +public import Iris.BI.Classes +public import Iris.BI.DerivedLaws +public import Iris.BI.DerivedLawsLater +public import Iris.BI.Extensions +public import Iris.BI.SIProp +public meta import Iris.Std.RocqPorting + public section #check Lean.PrettyPrinter.Unexpander @@ -39,17 +50,21 @@ class TotalWP (PROP Expr) (Val : outParam (Type _)) (A : Type _) where syntax wp_expr := term:max (" @ " term:max (" ; " term:max) <|> ((" ? ")? )) <|> (" ? ")? -declare_syntax_cat wp_postcond_inner -syntax ident ", " term : wp_postcond_inner -syntax term : wp_postcond_inner +declare_syntax_cat wpPostcondInner +syntax ident ", " term : wpPostcondInner +syntax term : wpPostcondInner + +declare_syntax_cat wpPostcond +syntax " {{ " wpPostcondInner " }} " : wpPostcond +syntax " [{ " wpPostcondInner " }] " : wpPostcond +syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond -- Are spaces outside of parens used in the pp? +syntax " 〖 " wpPostcondInner " 〗 " : wpPostcond + +syntax (name := wp) "WP " wp_expr wpPostcond : term -declare_syntax_cat wp_postcond -syntax " {{ " wp_postcond_inner " }} " : wp_postcond -syntax " [{ " wp_postcond_inner " }] " : wp_postcond -syntax " ⦃ " wp_postcond_inner " ⦄ " : wp_postcond -- Are spaces outside of parens used in the pp? -syntax " 〖 " wp_postcond_inner " 〗 " : wp_postcond +syntax texanPostcond := (ident+ ", ")? " RET " term:min "; " term -syntax (name := wp) "WP " wp_expr wp_postcond : term +syntax (name := texanTriple) "{{{ " term " }}} " wp_expr " {{{ " texanPostcond " }}}" : term /- This section checks whether the syntax is recognized correctly for all combinations -/ section testNotation @@ -64,53 +79,264 @@ elab "#no_info" "in" cmd:command : command => do } } -#no_info in #check_failure WP e @ s ; E {{ Φ }} -#no_info in #check_failure WP e @ E {{ Φ }} -#no_info in #check_failure WP e @ E ? {{ Φ }} -#no_info in #check_failure WP e {{ Φ }} -#no_info in #check_failure WP e ? {{ Φ }} - -#no_info in #check_failure WP e @ s ; E {{v, Φ }} -#no_info in #check_failure WP e @ E {{v, Φ }} -#no_info in #check_failure WP e @ E ? {{v, Φ }} -#no_info in #check_failure WP e {{v, Φ }} -#no_info in #check_failure WP e ? {{v, Φ }} - -#no_info in #check_failure WP e @ s ; E ⦃ Φ ⦄ -#no_info in #check_failure WP e @ E ⦃ Φ ⦄ -#no_info in #check_failure WP e @ E ? ⦃ Φ ⦄ -#no_info in #check_failure WP e ⦃ Φ ⦄ -#no_info in #check_failure WP e ? ⦃ Φ ⦄ - -#no_info in #check_failure WP e @ s ; E ⦃v, Φ ⦄ -#no_info in #check_failure WP e @ E ⦃v, Φ ⦄ -#no_info in #check_failure WP e @ E ? ⦃v, Φ ⦄ -#no_info in #check_failure WP e ⦃v, Φ ⦄ -#no_info in #check_failure WP e ? ⦃v, Φ ⦄ - -#no_info in #check_failure WP e @ s ; E [{ Φ }] -#no_info in #check_failure WP e @ E [{ Φ }] -#no_info in #check_failure WP e @ E ? [{ Φ }] -#no_info in #check_failure WP e [{ Φ }] -#no_info in #check_failure WP e ? [{ Φ }] - -#no_info in #check_failure WP e @ s ; E [{v, Φ }] -#no_info in #check_failure WP e @ E [{v, Φ }] -#no_info in #check_failure WP e @ E ? [{v, Φ }] -#no_info in #check_failure WP e [{v, Φ }] -#no_info in #check_failure WP e ? [{v, Φ }] - -#no_info in #check_failure WP e @ s ; E 〖 Φ 〗 -#no_info in #check_failure WP e @ E 〖 Φ 〗 -#no_info in #check_failure WP e @ E ? 〖 Φ 〗 -#no_info in #check_failure WP e 〖 Φ 〗 -#no_info in #check_failure WP e ? 〖 Φ 〗 - -#no_info in #check_failure WP e @ s ; E 〖v, Φ 〗 -#no_info in #check_failure WP e @ E 〖v, Φ 〗 -#no_info in #check_failure WP e @ E ? 〖v, Φ 〗 -#no_info in #check_failure WP e 〖v, Φ 〗 -#no_info in #check_failure WP e ? 〖v, Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E {{ Φ }} +-/ +#guard_msgs in #check_failure WP e @ s ; E {{ Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E {{ Φ }} +-/ +#guard_msgs in #check_failure WP e @ E {{ Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? {{ Φ }} +-/ +#guard_msgs in #check_failure WP e @ E ? {{ Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e {{ Φ }} +-/ +#guard_msgs in #check_failure WP e {{ Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e ? {{ Φ }} +-/ +#guard_msgs in #check_failure WP e ? {{ Φ }} + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E {{ v, Φ }} +-/ +#guard_msgs in #check_failure WP e @ s ; E {{v, Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E {{ v, Φ }} +-/ +#guard_msgs in #check_failure WP e @ E {{v, Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? {{ v, Φ }} +-/ +#guard_msgs in #check_failure WP e @ E ? {{v, Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e {{ v, Φ }} +-/ +#guard_msgs in #check_failure WP e {{v, Φ }} +/-- +info: elaboration function for `wp` has not been implemented + WP e ? {{ v, Φ }} +-/ +#guard_msgs in #check_failure WP e ? {{v, Φ }} + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E ⦃ Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ s ; E ⦃ Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ⦃ Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ E ⦃ Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? ⦃ Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ E ? ⦃ Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e ⦃ Φ ⦄ +-/ +#guard_msgs in #check_failure WP e ⦃ Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e ? ⦃ Φ ⦄ +-/ +#guard_msgs in #check_failure WP e ? ⦃ Φ ⦄ + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E ⦃ v, Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ s ; E ⦃v, Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ⦃ v, Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ E ⦃v, Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? ⦃ v, Φ ⦄ +-/ +#guard_msgs in #check_failure WP e @ E ? ⦃v, Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e ⦃ v, Φ ⦄ +-/ +#guard_msgs in #check_failure WP e ⦃v, Φ ⦄ +/-- +info: elaboration function for `wp` has not been implemented + WP e ? ⦃ v, Φ ⦄ +-/ +#guard_msgs in #check_failure WP e ? ⦃v, Φ ⦄ + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E [{ Φ }] +-/ +#guard_msgs in #check_failure WP e @ s ; E [{ Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E [{ Φ }] +-/ +#guard_msgs in #check_failure WP e @ E [{ Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? [{ Φ }] +-/ +#guard_msgs in #check_failure WP e @ E ? [{ Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e [{ Φ }] +-/ +#guard_msgs in #check_failure WP e [{ Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e ? [{ Φ }] +-/ +#guard_msgs in #check_failure WP e ? [{ Φ }] + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E [{ v, Φ }] +-/ +#guard_msgs in #check_failure WP e @ s ; E [{v, Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E [{ v, Φ }] +-/ +#guard_msgs in #check_failure WP e @ E [{v, Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? [{ v, Φ }] +-/ +#guard_msgs in #check_failure WP e @ E ? [{v, Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e [{ v, Φ }] +-/ +#guard_msgs in #check_failure WP e [{v, Φ }] +/-- +info: elaboration function for `wp` has not been implemented + WP e ? [{ v, Φ }] +-/ +#guard_msgs in #check_failure WP e ? [{v, Φ }] + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E 〖 Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ s ; E 〖 Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E 〖 Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ E 〖 Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? 〖 Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ E ? 〖 Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e 〖 Φ 〗 +-/ +#guard_msgs in #check_failure WP e 〖 Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e ? 〖 Φ 〗 +-/ +#guard_msgs in #check_failure WP e ? 〖 Φ 〗 + +/-- +info: elaboration function for `wp` has not been implemented + WP e @ s ; E 〖 v, Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ s ; E 〖v, Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E 〖 v, Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ E 〖v, Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e @ E ? 〖 v, Φ 〗 +-/ +#guard_msgs in #check_failure WP e @ E ? 〖v, Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e 〖 v, Φ 〗 +-/ +#guard_msgs in #check_failure WP e 〖v, Φ 〗 +/-- +info: elaboration function for `wp` has not been implemented + WP e ? 〖 v, Φ 〗 +-/ +#guard_msgs in #check_failure WP e ? 〖v, Φ 〗 + +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ s ; E {{{ x y, RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ x y , RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ E {{{ x y, RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ E {{{ x y , RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ E ? {{{ x y, RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ x y , RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e {{{ x y, RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e {{{ x y , RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e ? {{{ x y, RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e ? {{{ x y , RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ s ; E {{{ RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ E {{{ RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ E {{{ RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e @ E ? {{{ RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e {{{ RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e {{{ RET pat ; Q }}} +/-- +info: elaboration function for `texanTriple` has not been implemented + {{{ P }}} e ? {{{ RET pat; Q }}} +-/ +#guard_msgs in #check_failure {{{ P }}} e ? {{{ RET pat ; Q }}} end testNotation @@ -129,20 +355,20 @@ meta def parseWpExpr : Lean.TSyntax `wp_expr → Lean.MacroM (TSyntax `term × T | _ => Lean.Macro.throwUnsupported open Lean in -meta def parseWpPostcondInner (stx : TSyntax `wp_postcond_inner) : MacroM (TSyntax `term) := do +meta def parseWpPostcondInner (stx : TSyntax `wpPostcondInner) : MacroM (TSyntax `term) := do match stx with - | `(wp_postcond_inner| $v:ident, $Φ:term) => `(fun $v => $Φ) - | `(wp_postcond_inner| $Φ:term) => return Φ + | `(wpPostcondInner| $v:ident, $Φ:term) => `(fun $v => $Φ) + | `(wpPostcondInner| $Φ:term) => return Φ | _ => Macro.throwUnsupported open Lean in -meta def parseWpPostcond (stx : TSyntax `wp_postcond) : MacroM (TSyntax `term × Bool) := do +meta def parseWpPostcond (stx : TSyntax `wpPostcond) : MacroM (TSyntax `term × Bool) := do match stx with - | `(wp_postcond| {{ $inner:wp_postcond_inner }}) - | `(wp_postcond| ⦃ $inner:wp_postcond_inner ⦄) => + | `(wpPostcond| {{ $inner:wpPostcondInner }}) + | `(wpPostcond| ⦃ $inner:wpPostcondInner ⦄) => return (←parseWpPostcondInner inner, false) - | `(wp_postcond| [{ $inner:wp_postcond_inner }]) - | `(wp_postcond| 〖 $inner:wp_postcond_inner 〗) => + | `(wpPostcond| [{ $inner:wpPostcondInner }]) + | `(wpPostcond| 〖 $inner:wpPostcondInner 〗) => return (←parseWpPostcondInner inner, true) | _ => Macro.throwUnsupported (α := TSyntax `term × Bool) @@ -158,10 +384,78 @@ meta def wpMacro : Lean.Macro := fun stx => do `(Wp.wp $s $E $e $Φ) | _ => Lean.Macro.throwUnsupported +-- syntax (name := underExtraBinders) "underExtraBinders(" term:min ")" : term +-- syntax (name := sourceExtraBinders) "sourceExtraBinders(" term ")" : term + +-- private meta partial def findSource : Syntax → Option Syntax +-- | .missing => none +-- | .node _ ``sourceExtraBinders args => do +-- let #[_, inner, _] := args | none +-- return inner +-- | .node _ _ args => do +-- let #[res] := args.filterMap findSource | none +-- return res +-- | .ident _ _ _ _ => none +-- | .atom _ _ => none + +-- private meta partial def collectUnresolvedIds : Syntax → Elab.Term.TermElabM (Array Ident) +-- | .missing => return {} +-- | .node _ _ args => do +-- args.flatMapM collectUnresolvedIds +-- | id@(.ident ..) => do +-- try +-- let _ ← Elab.Term.elabIdent id .none +-- return {} +-- catch +-- | .error _ md => +-- let msg ← md.toString +-- if msg.startsWith "Unknown identifier" then +-- return #[⟨id⟩] +-- else +-- return {} +-- | _ => return {} +-- | .atom _ _ => +-- return {} + +-- elab "sourceExtraBinders(" t:term ")" : term => Elab.Term.elabTerm t .none + +-- @[term_elab underExtraBinders] +-- meta def elabUnderExtraBinders: Elab.Term.TermElab := fun stx ty? => do +-- match stx with +-- | `(underExtraBinders( $t:term ) ) => +-- if let .some s := findSource t then +-- -- logInfo s!"Found! {s.getArgs.size} {repr s}" +-- let ids ← collectUnresolvedIds s +-- logInfo s!"Collected identifiers {ids}" +-- let stx ← `(∀ $ids*, $t) +-- Elab.Term.elabTerm stx ty? +-- else +-- Elab.Term.elabTerm t ty? +-- | _ => unreachable! + +-- macro_rules +-- | `(iprop(underExtraBinders($t))) => `(underExtraBinders(iprop($t))) +-- | `(iprop(sourceExtraBinders($t))) => `(sourceExtraBinders(iprop($t))) + +-- @[macro texanTriple] +-- meta def wpTexanTriple : Lean.Macro +-- | `({{{ $P:term }}} $expr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q }}}) => do +-- let xs : TSyntaxArray `ident := xs.getD #[] +-- `(iprop(∀ Φ, $P -∗ ▷ (∀ $xs*, underExtraBinders($Q → Φ sourceExtraBinders($pat))) -∗ (WP $expr {{ Φ }}))) +-- | _ => Lean.Macro.throwUnsupported + +@[macro texanTriple] +meta def wpTexanTriple : Lean.Macro + | `({{{ $P:term }}} $expr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q }}}) => do + let xs : TSyntaxArray `ident := xs.getD #[] + `(iprop(∀ Φ, $P -∗ ▷ (∀ $xs*, $Q → Φ $pat) -∗ (WP $expr {{ Φ }}))) + | _ => Lean.Macro.throwUnsupported + section testElab set_option linter.unusedVariables false variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) +variable [ι : Iris.BI PROP] variable [Wp PROP Expr Val A] variable [Wp PROP Expr Val Stuckness] variable [TotalWP PROP Expr Val A] @@ -173,7 +467,7 @@ variable (Φ : PROP) /-- info: Wp.wp s E e fun v => Φ : PROP -/ #guard_msgs in #check WP e @ s ; E {{v, Φ }} /-- info: Wp.wp Stuckness.NotStuck E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ E {{ v, Φ}} +#guard_msgs in #check WP e @ E {{ v, Φ }} /-- info: Wp.wp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ #guard_msgs in #check WP e {{v, Φ }} /-- info: Wp.wp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ @@ -182,7 +476,7 @@ variable (Φ : PROP) /-- info: TotalWP.totalWp s E e fun v => Φ : PROP -/ #guard_msgs in #check WP e @ s ; E [{v, Φ }] /-- info: TotalWP.totalWp Stuckness.NotStuck E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ E [{ v, Φ}] +#guard_msgs in #check WP e @ E [{ v, Φ }] /-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ #guard_msgs in #check WP e [{v, Φ }] /-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ @@ -193,7 +487,7 @@ variable (Φ : Val → PROP) /-- info: Wp.wp s E e Φ : PROP -/ #guard_msgs in #check WP e @ s ; E {{ Φ }} /-- info: Wp.wp Stuckness.NotStuck E e Φ : PROP -/ -#guard_msgs in #check WP e @ E {{ Φ}} +#guard_msgs in #check WP e @ E {{ Φ }} /-- info: Wp.wp Stuckness.NotStuck ⊤ e Φ : PROP -/ #guard_msgs in #check WP e {{ Φ }} /-- info: Wp.wp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ @@ -202,17 +496,46 @@ variable (Φ : Val → PROP) /-- info: TotalWP.totalWp s E e Φ : PROP -/ #guard_msgs in #check WP e @ s ; E [{ Φ }] /-- info: TotalWP.totalWp Stuckness.NotStuck E e Φ : PROP -/ -#guard_msgs in #check WP e @ E [{ Φ}] +#guard_msgs in #check WP e @ E [{ Φ }] /-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e Φ : PROP -/ #guard_msgs in #check WP e [{ Φ }] /-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ #guard_msgs in #check WP e ? [{ Φ }] + +variable (P : PROP) (Q : PROP) (v : Nat) (s : Stuckness) [Wp PROP Expr Nat Stuckness] + +-- Can we do away with the `x .. y` by obtaining the identifiers directly from `RET pat`? + +/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp s E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ s ; E {{{ x y , RET (x+1) ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ E {{{ x y , RET (x+1) ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ E ? {{{ x y , RET (x+1) ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e {{{ x y , RET (x+1) ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e ? {{{ x y , RET (x+1) ; Q }}} + +#check iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp s E e Φ) +/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp s E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ s ; E {{{ RET 0 ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ E {{{ RET 0 ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ E ? {{{ RET pat ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e {{{ RET pat ; Q }}} +/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e ? {{{ RET pat ; Q }}} + + end testElab -meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TSyntax `wp_postcond_inner) - | `(fun $v:ident => $Φ:term) => `(wp_postcond_inner|$v:ident, $Φ:term) - | `($Φ:term) => `(wp_postcond_inner| $Φ:term) +meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TSyntax `wpPostcondInner) + | `(fun $v:ident => $Φ:term) => `(wpPostcondInner|$v:ident, $Φ:term) + | `($Φ:term) => `(wpPostcondInner| $Φ:term) open Lean in meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax `wp_expr) := do @@ -227,16 +550,16 @@ meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax ` meta def unexpanderWp : PrettyPrinter.Unexpander | `($_wp $s $E $e $Φ) => do let wp_expr ← makeWpExpr s E e - let wp_postcond_inner ← unexpandWpPostcondInner Φ - `(WP $wp_expr {{ $wp_postcond_inner }}) + let wpPostcondInner ← unexpandWpPostcondInner Φ + `(WP $wp_expr {{ $wpPostcondInner }}) | _ => throw () @[app_unexpander TotalWP.totalWp] meta def unexpanderTotalWp : PrettyPrinter.Unexpander | `($_wp $s $E $e $Φ) => do let wp_expr ← makeWpExpr s E e - let wp_postcond_inner ← unexpandWpPostcondInner Φ - `(WP $wp_expr [{ $wp_postcond_inner }]) + let wpPostcondInner ← unexpandWpPostcondInner Φ + `(WP $wp_expr [{ $wpPostcondInner }]) | _ => throw () section testUnexpand @@ -298,3 +621,21 @@ variable (Φ : Val → PROP) #guard_msgs in #check WP e ? [{ Φ }] end testUnexpand + +/- +Notations used in Rocq +For Texan triples, more of the same + +{{{ P }}} e @ s ; E {{{ RET pat ; Q }}} +{{{ P ]}} e @ E {{{ RET pat ; Q }}} +{{{ P ]}} e @ E ? {{{ RET pat ; Q }}} +{{{ P ]}} e {{{ RET pat ; Q }}} +{{{ P ]}} e ? {{{ RET pat ; Q }}} + +{{{ P }}} e @ s ; E {{{ x .. y , RET pat ; Q }}} +{{{ P }}} e @ E {{{ x .. y , RET pat ; Q }}} +{{{ P }}} e @ E ? {{{ x .. y , RET pat ; Q }}} +{{{ P }}} e {{{ x .. y , RET pat ; Q }}} +{{{ P }}} e ? {{{ x .. y , RET pat ; Q }}} + +-/ diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean new file mode 100644 index 00000000..aee1aaf2 --- /dev/null +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -0,0 +1,356 @@ +module + +public import Iris.Algebra +public import Iris.Instances.Lib.FUpd +public import Iris.Instances.Lib.LaterCredits +public import Iris.BI +public import Iris.BI.WeakestPre +public import Iris.BI.BigOp.BigSepList +public import Iris.BI.DerivedLaws +public import Iris.BI.Updates +public import Iris.ProofMode +meta import Iris.BI.Updates +public import Iris.ProgramLogic.Language +public import Iris.Std.CoPset + +namespace Iris + +open ProgramLogic Language.Notation + +@[expose] public section + +class StateInterp + (State: Type s) + (Obs : outParam <| Type o) + (GF : BundledGFunctors) + where + stateInterp : State → Nat → List (Obs) → Nat → IProp GF + +export StateInterp (stateInterp) + +/- TODO: Should this be a class? Maybe we just need to be explicit about the + instance it belongs to. Otherwise, we could have some problems if somewhere + someone defines a NumLatersPerStep instance and that one gets taken by + everyone else. -/ +class NumLatersPerStep where + numLatersPerStep : Nat → Nat + +export NumLatersPerStep (numLatersPerStep) + +class IrisGS_gen (hlc : outParam <| Bool) + (Expr : Type e) + {Val : Type v} + {State : Type s} + {Obs : Type o} + [Λ : Language Expr State Obs Val] + (GF : BundledGFunctors) + extends + StateInterp State Obs GF, + InvGS_gen hlc GF, + NumLatersPerStep where + + forkPost : Val → IProp GF + + state_interp_mono σ ns obs nt : + iprop(stateInterp σ ns obs nt ⊢ |={∅}=> stateInterp σ (ns + 1) obs nt) + +variable {hlc : outParam Bool} +variable {Expr State Obs Val} +variable [Λ : Language Expr State Obs Val] +variable {GF : BundledGFunctors} +variable [ι : IrisGS_gen hlc Expr GF] + +instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ + +def wp.pre (s : Stuckness) + (wp : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF) : + CoPset -> Expr -> (Val -> IProp GF) -> IProp GF := fun E e₁ Φ => + match toVal e₁ with + | some v => iprop(|={E}=> Φ v) + | none => iprop(∀ (σ₁ : State) (ns : Nat) (obs obs' : List Obs) (nt : Nat), + stateInterp σ₁ ns (obs ++ obs') nt ={E,∅}=∗ + ⌜if s matches .NotStuck then PrimStep.Reducible (e₁, σ₁) else True⌝ ∗ + ∀ e₂ σ₂ eₜ, ⌜(e₁, σ₁) --> (e₂, σ₂, eₜ)⌝ -∗ + £ (numLatersPerStep ns + 1) + ={∅}▷=∗^[numLatersPerStep ns + 1] |={∅,E}=> + stateInterp σ₂ (ns + 1) obs' (eₜ.length + nt) ∗ + wp E e₂ Φ ∗ + [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) + + +instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where + distLater_dist := by + intros n wp wp' Hwp E e₁ Φ + dsimp only [pre] + cases toVal e₁ + case some _ => simp + dsimp + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne (.of_eq rfl) ?_ + induction numLatersPerStep ns + case zero => + refine step_fupdN_contractive.distLater_dist ?_ + intros i ih + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne ?_ ?_ + · apply Hwp i ih + refine BI.BigSepL.bigSepL_dist ?_ + intros k x h + · apply Hwp i ih + case succ n IH => + apply BIFUpdate.ne.ne + apply BI.later_ne.ne + apply BIFUpdate.ne.ne + assumption + +-- instance wp.pre.ne s : OFE.NonExpansive (wp.pre s (ι := ι)) +-- := OFE.ne_of_contractive (wp.pre s (ι := ι)) + +-- TODO: In this part of the Rocq code, a lot of juggling +-- is happening with `wp_def`, `wp_aux`, `wp'` and `wp_unseal`. +-- I wonder what is the purpose of all of these, and if +-- it's possible to achieve this differently in Lean. +@[implicit_reducible] +instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where + wp s := fixpoint (wp.pre s) + +section Wp + +-- TODO: Move out of here +def _root_.Function.toContractiveHom (f : α → β)[OFE α][OFE β][ι : OFE.Contractive f] : α -c> β where + f := f + contractive := ι + +@[rocq_alias wp_unfold] +theorem wp_unfold s E (e : Expr) (Φ : Val → IProp GF) : + WP e @ s ; E {{ Φ }} ⊣⊢ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ := + BI.equiv_iff.1 <| fixpoint_unfold (f := (wp.pre (ι := ι) s).toContractiveHom) E e Φ + +@[rocq_alias wp_ne] +instance wp_ne (s : Stuckness) E (e : Expr) : + OFE.NonExpansive (Wp.wp (PROP := IProp GF) s E e) where + ne {n Φ₁ Φ₂} HΦ := by + induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => + calc iprop(Wp.wp s E e Φ₁) + _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₁) n + _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by + dsimp [wp.pre] + cases toVal e + case some v => exact BIFUpdate.ne.ne <| HΦ v + dsimp + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne (.of_eq rfl) ?_ + induction numLatersPerStep ns + case zero => + refine step_fupdN_contractive.distLater_dist ?_ + intros i ih + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne ?_ (.of_eq rfl) + apply IH i ih _ _ <| OFE.dist_lt HΦ ih + case succ n IH => + apply BIFUpdate.ne.ne + apply BI.later_ne.ne + apply BIFUpdate.ne.ne + assumption + _ ≡{n}≡ Wp.wp s E e Φ₂ := + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₂) n |>.symm + +#rocq_ignore wp_proper "Derivable using NonExpansive.eqv" + +@[rocq_alias wp_contractive] +instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : + OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where + distLater_dist {n Φ₁ Φ₂} HΦ := by + calc iprop(Wp.wp s E e Φ₁) + _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₁) n + _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by + simp only [wp.pre, h] + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne (.of_eq rfl) ?_ + induction numLatersPerStep ns + case zero => + refine step_fupdN_contractive.distLater_dist ?_ + intros i ih + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne ?_ (.of_eq rfl) + apply OFE.NonExpansive.ne + apply HΦ i ih + case succ n IH => + apply BIFUpdate.ne.ne + apply BI.later_ne.ne + apply BIFUpdate.ne.ne + assumption + _ ≡{n}≡ Wp.wp s E e Φ₂ := + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₂) n |>.symm + +@[rocq_alias wp_value_fupd'] +theorem wp_value_fupd' (s : Stuckness) E (Φ : Val → IProp GF) (v : Val) : + WP (v : Expr) @ s ; E {{ Φ }} ⊣⊢ |={E}=> Φ v := + calc iprop(WP (v : Expr) @ s ; E {{ Φ }}) + _ ⊣⊢ wp.pre s (Wp.wp s) E (v : Expr) Φ := wp_unfold .. + _ ⊣⊢ |={E}=> Φ v := by + simp only [toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] + +#synth (BI.BILoeb (IProp GF)) + +@[rocq_alias wp_strong_mono] +theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} : + s₁ ≤ s₂ → E₁ ⊆ E₂ → + ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by + intros hs hE + istart + irevert %e %Φ %Ψ %E₁ %E₂ %hE + iapply BI.loeb_wand_intuitionistically $$ [] + imodintro + iintro #IH %e %Φ %Ψ %E₁ %E₂ %hE H + irevert IH + refine (wp_unfold (ι := ι) ..).1.trans ?_ + iintro H #IH HΦ + refine BI.Entails.trans (?_ : ProofMode.Entails' _ _) (wp_unfold s₂ E₂ e Ψ).2 + dsimp only [wp.pre] + match toVal e with + | none => + dsimp + iintro %σ₁ %ns %obs %obs' %nt Hσ + imod fupd_mask_intro_subseteq hE (P := iprop(emp)) $$ [] with Hclose -- TODO: Should we add rocq_alias `fupd_mask_subseteq` to this theorem? + · exact BI.intuitionistically_elim_emp + icases H $$ Hσ with >⟨%h, H⟩ + imodintro + isplit + · match s₁, s₂ with + | .MaybeStuck, .NotStuck => simp [LE.le] at hs + | .NotStuck, .NotStuck + | .MaybeStuck, .MaybeStuck + | .NotStuck, .MaybeStuck => + dsimp at h ⊢ + ipure_intro <;> simp only [*] + iintro %e₂ %σ₂ %eₜ #hstep «h£» + dsimp [Nat.repeat] + imod H $$ hstep «h£» with H + iintro !> !>; imod H; iintro !> + iapply step_fupdN_wand $$ H + iintro >⟨aux, H, Hefs⟩ + imod Hclose + imodintro + isplitl [aux] + · iassumption + isplitr [Hefs] + · iapply IH $$ %e₂ %Φ %Ψ %E₁ %E₂ %hE H HΦ + · iapply BI.BigSepL.bigSepL_impl $$ Hefs + iintro !> %k %e' %_ H + iapply IH $$ %e' %_ %_ %⊤ %_ %Std.LawfulSet.subset_refl H + iintro %v H + imodintro + iassumption + | some v => + dsimp + ihave h := fupd_mask_mono hE $$ H + imod h + iapply HΦ $$ h + + +/- +Lemma fupd_wp s E e Φ : (|={E}=> WP e @ s; E {{ Φ }}) ⊢ WP e @ s; E {{ Φ }}. +Proof. + rewrite wp_unfold /wp_pre. iIntros "H". destruct (to_val e) as [v|] eqn:?. + { by iMod "H". } + iIntros (σ1 ns κ κs nt) "Hσ1". iMod "H". by iApply "H". +Qed. +-/ +theorem fupd_wp (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : + (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by + refine (BIFUpdate.mono <| (wp_unfold ..).1).trans ?_ + refine BI.Entails.trans ?_ (wp_unfold ..).2 + iintro H + match h: toVal e with + | some v => + simp only [wp.pre, h] + imod H + iassumption + | none => + simp only [wp.pre, h] + iintro %σ₁ %ns %obs %obs' %nt + imod H with H + iassumption + +theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : + -- TODO: Fix `WP` syntax so this doesn't happen. + WP e @ s ; E {{v, iprop(|={E}=> Φ v) }} ⊢ WP e @ s ; E {{ Φ }} := by + iintro h + iapply wp_strong_mono (Std.IsPreorder.le_refl _) Std.LawfulSet.subset_refl $$ h + iintro %v h + iassumption + +theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} + [Language.Atomic ↑s e] : + (|={E1,E2}=> WP e @ s ; E2 {{v, iprop(|={E2,E1}=> Φ v) }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by + refine (BIFUpdate.mono <| (wp_unfold ..).1).trans ?_ + refine BI.Entails.trans ?_ (wp_unfold ..).2 + iintro H + cases h : toVal e + case some v => + simp only [wp.pre, h] + iapply BIFUpdate.trans (E2 := E2) + imod H + iassumption + case none => + simp only [wp.pre, h] + iintro %σ₁ %ns %obs %obs' %nt Hσ + imod H + imod H $$ Hσ with ⟨%h, H⟩ + imodintro + isplitl [] + · ipure_intro; assumption + iintro %e2 %σ2 %efs %Hstep Hcred + ihave aux := H $$ %e2 %σ2 %efs %Hstep Hcred + iapply step_fupdN_wand $$ aux + iintro >(⟨Hσ,H,Hefs⟩) + match s with + | .NotStuck => + simp only [↓reduceIte] at h + obtain ⟨obs, e', σ2, efs, hstep⟩ := h + sorry + | .MaybeStuck => + sorry + iapply H + sorry + +end Wp From 04858f5484b6aec42a86fcb621381bcc1a951a9c Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 13 May 2026 16:02:20 +0200 Subject: [PATCH 35/76] feat: preliminary Texan triple syntax --- Iris/Iris/BI/WeakestPre.lean | 51 ++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index e37cce09..d82cdc96 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -68,16 +68,6 @@ syntax (name := texanTriple) "{{{ " term " }}} " wp_expr " {{{ " texanPostcond " /- This section checks whether the syntax is recognized correctly for all combinations -/ section testNotation -set_option trace.Elab.info false - -/-- Suppress all `info` level messages from a command -/ -elab "#no_info" "in" cmd:command : command => do - Lean.Elab.Command.elabCommandTopLevel cmd - modify fun st => {st with - messages := {st.messages with - unreported := st.messages.unreported.filter (¬ ·.severity matches MessageSeverity.information) - } - } /-- info: elaboration function for `wp` has not been implemented @@ -427,8 +417,11 @@ meta def wpMacro : Lean.Macro := fun stx => do -- -- logInfo s!"Found! {s.getArgs.size} {repr s}" -- let ids ← collectUnresolvedIds s -- logInfo s!"Collected identifiers {ids}" --- let stx ← `(∀ $ids*, $t) --- Elab.Term.elabTerm stx ty? +-- if ids.isEmpty then +-- Elab.Term.elabTerm t ty? +-- else +-- let stx ← `(∀ $ids*, $t) +-- Elab.Term.elabTerm stx ty? -- else -- Elab.Term.elabTerm t ty? -- | _ => unreachable! @@ -440,15 +433,20 @@ meta def wpMacro : Lean.Macro := fun stx => do -- @[macro texanTriple] -- meta def wpTexanTriple : Lean.Macro -- | `({{{ $P:term }}} $expr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q }}}) => do --- let xs : TSyntaxArray `ident := xs.getD #[] --- `(iprop(∀ Φ, $P -∗ ▷ (∀ $xs*, underExtraBinders($Q → Φ sourceExtraBinders($pat))) -∗ (WP $expr {{ Φ }}))) +-- let k ← match xs with +-- | some xs => `(∀ $xs*, underExtraBinders($Q → Φ sourceExtraBinders($pat))) +-- | none => `($Q:term → Φ $pat) +-- `(iprop(∀ Φ, $P -∗ ▷ $k -∗ (WP $expr {{ Φ }}))) -- | _ => Lean.Macro.throwUnsupported @[macro texanTriple] meta def wpTexanTriple : Lean.Macro - | `({{{ $P:term }}} $expr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q }}}) => do - let xs : TSyntaxArray `ident := xs.getD #[] - `(iprop(∀ Φ, $P -∗ ▷ (∀ $xs*, $Q → Φ $pat) -∗ (WP $expr {{ Φ }}))) + | `({{{ $P:term }}} $wpExpr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q:term }}}) => do + -- It seems like `∀ $xs*, Ψ` does not translate to `Ψ`. + let k ← match xs with + | some xs => `(∀ $xs*, $Q:term → Φ $pat) + | none => `($Q:term → Φ $pat) + `(iprop(∀ Φ, $P -∗ ▷ $k -∗ (WP $wpExpr {{ Φ }}))) | _ => Lean.Macro.throwUnsupported section testElab @@ -502,7 +500,6 @@ variable (Φ : Val → PROP) /-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ #guard_msgs in #check WP e ? [{ Φ }] - variable (P : PROP) (Q : PROP) (v : Nat) (s : Stuckness) [Wp PROP Expr Nat Stuckness] -- Can we do away with the `x .. y` by obtaining the identifiers directly from `RET pat`? @@ -518,18 +515,16 @@ variable (P : PROP) (Q : PROP) (v : Nat) (s : Stuckness) [Wp PROP Expr Nat Stuck /-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ #guard_msgs in #check {{{ P }}} e ? {{{ x y , RET (x+1) ; Q }}} -#check iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp s E e Φ) -/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp s E e Φ) : PROP -/ +/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp s E e Φ) : PROP -/ #guard_msgs in #check {{{ P }}} e @ s ; E {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ +/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ #guard_msgs in #check {{{ P }}} e @ E {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ E ? {{{ RET pat ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e {{{ RET pat ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e ? {{{ RET pat ; Q }}} - +/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e @ E ? {{{ RET 0 ; Q }}} +/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e {{{ RET 0 ; Q }}} +/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ +#guard_msgs in #check {{{ P }}} e ? {{{ RET 0 ; Q }}} end testElab From 393e4bf8d53532fe311df689bf56c431493b4ab5 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:24:48 +0200 Subject: [PATCH 36/76] fix: move WeakestPre BI definitions under the Iris namespace --- Iris/Iris/BI/WeakestPre.lean | 106 ++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index d82cdc96..47cf46bb 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -19,6 +19,8 @@ public meta import Iris.Std.RocqPorting public section +namespace Iris + #check Lean.PrettyPrinter.Unexpander open Lean @@ -70,260 +72,260 @@ syntax (name := texanTriple) "{{{ " term " }}} " wp_expr " {{{ " texanPostcond " section testNotation /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E {{ Φ }} -/ #guard_msgs in #check_failure WP e @ s ; E {{ Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E {{ Φ }} -/ #guard_msgs in #check_failure WP e @ E {{ Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? {{ Φ }} -/ #guard_msgs in #check_failure WP e @ E ? {{ Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e {{ Φ }} -/ #guard_msgs in #check_failure WP e {{ Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? {{ Φ }} -/ #guard_msgs in #check_failure WP e ? {{ Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E {{ v, Φ }} -/ #guard_msgs in #check_failure WP e @ s ; E {{v, Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E {{ v, Φ }} -/ #guard_msgs in #check_failure WP e @ E {{v, Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? {{ v, Φ }} -/ #guard_msgs in #check_failure WP e @ E ? {{v, Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e {{ v, Φ }} -/ #guard_msgs in #check_failure WP e {{v, Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? {{ v, Φ }} -/ #guard_msgs in #check_failure WP e ? {{v, Φ }} /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E ⦃ Φ ⦄ -/ #guard_msgs in #check_failure WP e @ s ; E ⦃ Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ⦃ Φ ⦄ -/ #guard_msgs in #check_failure WP e @ E ⦃ Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? ⦃ Φ ⦄ -/ #guard_msgs in #check_failure WP e @ E ? ⦃ Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ⦃ Φ ⦄ -/ #guard_msgs in #check_failure WP e ⦃ Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? ⦃ Φ ⦄ -/ #guard_msgs in #check_failure WP e ? ⦃ Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E ⦃ v, Φ ⦄ -/ #guard_msgs in #check_failure WP e @ s ; E ⦃v, Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ⦃ v, Φ ⦄ -/ #guard_msgs in #check_failure WP e @ E ⦃v, Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? ⦃ v, Φ ⦄ -/ #guard_msgs in #check_failure WP e @ E ? ⦃v, Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ⦃ v, Φ ⦄ -/ #guard_msgs in #check_failure WP e ⦃v, Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? ⦃ v, Φ ⦄ -/ #guard_msgs in #check_failure WP e ? ⦃v, Φ ⦄ /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E [{ Φ }] -/ #guard_msgs in #check_failure WP e @ s ; E [{ Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E [{ Φ }] -/ #guard_msgs in #check_failure WP e @ E [{ Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? [{ Φ }] -/ #guard_msgs in #check_failure WP e @ E ? [{ Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e [{ Φ }] -/ #guard_msgs in #check_failure WP e [{ Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? [{ Φ }] -/ #guard_msgs in #check_failure WP e ? [{ Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E [{ v, Φ }] -/ #guard_msgs in #check_failure WP e @ s ; E [{v, Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E [{ v, Φ }] -/ #guard_msgs in #check_failure WP e @ E [{v, Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? [{ v, Φ }] -/ #guard_msgs in #check_failure WP e @ E ? [{v, Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e [{ v, Φ }] -/ #guard_msgs in #check_failure WP e [{v, Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? [{ v, Φ }] -/ #guard_msgs in #check_failure WP e ? [{v, Φ }] /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E 〖 Φ 〗 -/ #guard_msgs in #check_failure WP e @ s ; E 〖 Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E 〖 Φ 〗 -/ #guard_msgs in #check_failure WP e @ E 〖 Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? 〖 Φ 〗 -/ #guard_msgs in #check_failure WP e @ E ? 〖 Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e 〖 Φ 〗 -/ #guard_msgs in #check_failure WP e 〖 Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? 〖 Φ 〗 -/ #guard_msgs in #check_failure WP e ? 〖 Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ s ; E 〖 v, Φ 〗 -/ #guard_msgs in #check_failure WP e @ s ; E 〖v, Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E 〖 v, Φ 〗 -/ #guard_msgs in #check_failure WP e @ E 〖v, Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e @ E ? 〖 v, Φ 〗 -/ #guard_msgs in #check_failure WP e @ E ? 〖v, Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e 〖 v, Φ 〗 -/ #guard_msgs in #check_failure WP e 〖v, Φ 〗 /-- -info: elaboration function for `wp` has not been implemented +info: elaboration function for `Iris.wp` has not been implemented WP e ? 〖 v, Φ 〗 -/ #guard_msgs in #check_failure WP e ? 〖v, Φ 〗 /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ s ; E {{{ x y, RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ x y , RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ E {{{ x y, RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ E {{{ x y , RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ E ? {{{ x y, RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ x y , RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e {{{ x y, RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e {{{ x y , RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e ? {{{ x y, RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e ? {{{ x y , RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ s ; E {{{ RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ E {{{ RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ E {{{ RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e @ E ? {{{ RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e {{{ RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e {{{ RET pat ; Q }}} /-- -info: elaboration function for `texanTriple` has not been implemented +info: elaboration function for `Iris.texanTriple` has not been implemented {{{ P }}} e ? {{{ RET pat; Q }}} -/ #guard_msgs in #check_failure {{{ P }}} e ? {{{ RET pat ; Q }}} @@ -331,7 +333,7 @@ info: elaboration function for `texanTriple` has not been implemented end testNotation open Lean in -meta def parseWpExpr : Lean.TSyntax `wp_expr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun +meta def parseWpExpr : Lean.TSyntax ``wp_expr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun | `(wp_expr| $e @ $s ; $E) => return (e, s, E) | `(wp_expr| $e @ $E) => @@ -533,7 +535,7 @@ meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TS | `($Φ:term) => `(wpPostcondInner| $Φ:term) open Lean in -meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax `wp_expr) := do +meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax ``wp_expr) := do match s, E with | `(Stuckness.NotStuck), `(⊤) => `(wp_expr| $e:term) | `(Stuckness.NotStuck), E => `(wp_expr| $e:term @ $E:term) From 3f23865fc1e5e71b59d54237fd0ef1f181b28efe Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:26:00 +0200 Subject: [PATCH 37/76] refactor: small opinionated improvements --- Iris/Iris/Instances/Lib/LaterCredits.lean | 7 ++-- Iris/Iris/ProofMode/Patterns/SpecPattern.lean | 35 ++++++------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/Iris/Iris/Instances/Lib/LaterCredits.lean b/Iris/Iris/Instances/Lib/LaterCredits.lean index cf4edbbd..9f215074 100644 --- a/Iris/Iris/Instances/Lib/LaterCredits.lean +++ b/Iris/Iris/Instances/Lib/LaterCredits.lean @@ -68,10 +68,9 @@ section Operations variable {GF : BundledGFunctors} [LC : LcGS GF] -theorem lc_split {n m} : £ (n + m) ⊣⊢@{IProp GF} £ n ∗ £ m := by - -- FIXME: Timeout on iOwn_op. Why? - refine .trans ?_ iOwn_op - exact .rfl +theorem lc_split {n m} : £ (n + m) ⊣⊢@{IProp GF} £ n ∗ £ m := + -- -- FIXME: Timeout on iOwn_op. Why? + iOwn_op (E := LC.lc_elem) (a1 := ◯ n) (a2 := ◯ m) @[rocq_alias lc_zero] theorem lc_zero : ⊢@{IProp GF} |==> £ 0 := iOwn_unit (ε := UCMRA.unit) diff --git a/Iris/Iris/ProofMode/Patterns/SpecPattern.lean b/Iris/Iris/ProofMode/Patterns/SpecPattern.lean index f435d982..08524e96 100644 --- a/Iris/Iris/ProofMode/Patterns/SpecPattern.lean +++ b/Iris/Iris/ProofMode/Patterns/SpecPattern.lean @@ -91,36 +91,21 @@ where go : TSyntax `specPat → Option SpecPat | `(specPat| $name:ident) => some <| .ident name | `(specPat| % $term:term) => some <| .pure term - | `(specPat| [$[$names:frameIdent]*]) => + | `(specPat| [$[$names:frameIdent]*] $[as $goal:ident]?) => let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .spatial, negate := false, frame, hyps } .anonymous - | `(specPat| [$[$names:frameIdent]*] as $goal:ident) => + some <| .goal {kind := .spatial, negate := false, frame, hyps } <| (TSyntax.getId <*> goal).getD .anonymous + | `(specPat| [- $[$names:frameIdent]*] $[as $goal:ident]?) => let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .spatial, negate := false, frame, hyps } goal.getId - | `(specPat| [- $[$names:frameIdent]*]) => + some <| .goal {kind := .spatial, negate := true, frame, hyps } <| (TSyntax.getId <*> goal).getD .anonymous + | `(specPat| [> $[$names:frameIdent]*] $[as $goal:ident]?) => let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .spatial, negate := true, frame, hyps } .anonymous - | `(specPat| [- $[$names:frameIdent]*] as $goal:ident) => + some <| .goal {kind := .modal, negate := false, frame, hyps } <| (TSyntax.getId <*> goal).getD .anonymous + | `(specPat| [> - $[$names:frameIdent]*] $[as $goal:ident]?) => let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .spatial, negate := true, frame, hyps } goal.getId - | `(specPat| [> $[$names:frameIdent]*]) => + some <| .goal {kind := .modal, negate := true, frame, hyps } <| (TSyntax.getId <*> goal).getD .anonymous + | `(specPat| [# $[$names:frameIdent]*] $[as $goal:ident]?) => let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .modal, negate := false, frame, hyps } .anonymous - | `(specPat| [> $[$names:frameIdent]*] as $goal:ident) => - let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .modal, negate := false, frame, hyps } goal.getId - | `(specPat| [> - $[$names:frameIdent]*]) => - let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .modal, negate := true, frame, hyps } .anonymous - | `(specPat| [> - $[$names:frameIdent]*] as $goal:ident) => - let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .modal, negate := true, frame, hyps } goal.getId - | `(specPat| [# $[$names:frameIdent]*]) => - let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .intuitionistic, negate := false, frame, hyps } .anonymous - | `(specPat| [# $[$names:frameIdent]*] as $goal:ident) => - let (hyps, frame) := names.toList.partitionMap FrameIdent.parse; - some <| .goal {kind := .intuitionistic, negate := false, frame, hyps } goal.getId + some <| .goal {kind := .intuitionistic, negate := false, frame, hyps } <| (TSyntax.getId <*> goal).getD .anonymous | `(specPat| [$]) => some <| .autoframe .spatial | `(specPat| [# $]) => some <| .autoframe .intuitionistic | `(specPat| [> $]) => some <| .autoframe .modal From 3a2c7459af32a91aec9f6fe339900aa027260db1 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:28:03 +0200 Subject: [PATCH 38/76] refactor: qol improvements, use `iframe` and `iloeb` in some proofs --- Iris/Iris/ProgramLogic/Language.lean | 8 +-- Iris/Iris/ProgramLogic/WeakestPre.lean | 71 ++++++++++++++------------ 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Iris/Iris/ProgramLogic/Language.lean b/Iris/Iris/ProgramLogic/Language.lean index 9102bdc3..96fa40ed 100644 --- a/Iris/Iris/ProgramLogic/Language.lean +++ b/Iris/Iris/ProgramLogic/Language.lean @@ -26,7 +26,7 @@ class ToVal (Expr : Type _) (Val : outParam (Type _)) where toVal : Expr → Option Val ofVal : Val → Expr /-- If `toVal` is defined for an expression, `coe` is its inverse -/ - coe_of_toVal_eq_some (e : Expr) (v : Val) : toVal e = some v → ofVal v = e + coe_of_toVal_eq_some {e : Expr} {v : Val} : toVal e = some v → ofVal v = e /-- `toVal` is defined `coe`, and works as its inverse -/ toVal_coe (v : Val) : toVal (ofVal v) = some v export ToVal (toVal coe_of_toVal_eq_some toVal_coe) @@ -46,7 +46,7 @@ instance : Coe Val Expr where coe := ofVal @[grind! ., rocq_alias of_to_val_flip] theorem toVal_eq_iff_coe (e : Expr) (v : Val) : v = e ↔ toVal e = some v := - ⟨(· ▸ toVal_coe v), coe_of_toVal_eq_some e v⟩ + ⟨(· ▸ toVal_coe v), coe_of_toVal_eq_some⟩ @[rocq_alias of_val_inj] instance : ι.ofVal.Injective := by @@ -210,7 +210,7 @@ theorem toVal_none_of_reducible : Reducible (e, σ) → toVal e = none := by grind only [Reducible, val_stuck] @[rocq_alias val_irreducible] -theorem val_irreducible : (toVal e).isSome → Irreducible (e, σ) := by +theorem val_irreducible : (toVal e).isSome → ∀ σ, Irreducible (e, σ) := by grind only [Irreducible, val_stuck, = Option.isSome_none] end ReducibilityLemmas @@ -225,7 +225,7 @@ class Atomic (a : Atomicity) (e : Expr) : Prop where atomic (σ : State) obs e' σ' eₜ : (e, σ) --> (e', σ', eₜ) → match a with - | .WeaklyAtomic => ¬ Reducible (e', σ') + | .WeaklyAtomic => Irreducible (e', σ') | .StronglyAtomic => (toVal e').isSome @[rocq_alias strongly_atomic_atomic] diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index aee1aaf2..aeff7c5e 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -62,6 +62,17 @@ variable [ι : IrisGS_gen hlc Expr GF] instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ +/-- Reducibility condition depending on stuckness. +```lean4 +-- s.MaybeReducible (e, σ) equivalent to… +if s matches .NotStuck then Reducible (e, σ) else True +``` +-/ +@[simp] +abbrev Stuckness.MaybeReducible : Stuckness → Expr × State → Prop +| .NotStuck, (e₁, σ₁) => PrimStep.Reducible (e₁, σ₁) +| _, _ => True + def wp.pre (s : Stuckness) (wp : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF) : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF := fun E e₁ Φ => @@ -69,7 +80,7 @@ def wp.pre (s : Stuckness) | some v => iprop(|={E}=> Φ v) | none => iprop(∀ (σ₁ : State) (ns : Nat) (obs obs' : List Obs) (nt : Nat), stateInterp σ₁ ns (obs ++ obs') nt ={E,∅}=∗ - ⌜if s matches .NotStuck then PrimStep.Reducible (e₁, σ₁) else True⌝ ∗ + ⌜s.MaybeReducible (e₁, σ₁)⌝ ∗ ∀ e₂ σ₂ eₜ, ⌜(e₁, σ₁) --> (e₂, σ₂, eₜ)⌝ -∗ £ (numLatersPerStep ns + 1) ={∅}▷=∗^[numLatersPerStep ns + 1] |={∅,E}=> @@ -134,7 +145,7 @@ def _root_.Function.toContractiveHom (f : α → β)[OFE α][OFE β][ι : OFE.Co contractive := ι @[rocq_alias wp_unfold] -theorem wp_unfold s E (e : Expr) (Φ : Val → IProp GF) : +theorem wp_unfold {s E} {e : Expr} {Φ : Val → IProp GF} : WP e @ s ; E {{ Φ }} ⊣⊢ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ := BI.equiv_iff.1 <| fixpoint_unfold (f := (wp.pre (ι := ι) s).toContractiveHom) E e Φ @@ -145,7 +156,7 @@ instance wp_ne (s : Stuckness) E (e : Expr) : induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => calc iprop(Wp.wp s E e Φ₁) _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₁) n + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by dsimp [wp.pre] cases toVal e @@ -178,17 +189,18 @@ instance wp_ne (s : Stuckness) E (e : Expr) : apply BIFUpdate.ne.ne assumption _ ≡{n}≡ Wp.wp s E e Φ₂ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₂) n |>.symm + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n |>.symm #rocq_ignore wp_proper "Derivable using NonExpansive.eqv" +-- This definition comes after `wp_ne` because it depends on it. @[rocq_alias wp_contractive] instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where distLater_dist {n Φ₁ Φ₂} HΦ := by calc iprop(Wp.wp s E e Φ₁) _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₁) n + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by simp only [wp.pre, h] refine BI.forall_ne (fun σ₁ => ?_) @@ -219,32 +231,28 @@ instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : apply BIFUpdate.ne.ne assumption _ ≡{n}≡ Wp.wp s E e Φ₂ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold s E e Φ₂) n |>.symm + OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n |>.symm @[rocq_alias wp_value_fupd'] -theorem wp_value_fupd' (s : Stuckness) E (Φ : Val → IProp GF) (v : Val) : +theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : WP (v : Expr) @ s ; E {{ Φ }} ⊣⊢ |={E}=> Φ v := calc iprop(WP (v : Expr) @ s ; E {{ Φ }}) - _ ⊣⊢ wp.pre s (Wp.wp s) E (v : Expr) Φ := wp_unfold .. + _ ⊣⊢ wp.pre s (Wp.wp s) E (v : Expr) Φ := wp_unfold _ ⊣⊢ |={E}=> Φ v := by simp only [toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] -#synth (BI.BILoeb (IProp GF)) - @[rocq_alias wp_strong_mono] theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} : s₁ ≤ s₂ → E₁ ⊆ E₂ → ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by - intros hs hE + intro hs hE istart - irevert %e %Φ %Ψ %E₁ %E₂ %hE - iapply BI.loeb_wand_intuitionistically $$ [] - imodintro - iintro #IH %e %Φ %Ψ %E₁ %E₂ %hE H - irevert IH - refine (wp_unfold (ι := ι) ..).1.trans ?_ - iintro H #IH HΦ - refine BI.Entails.trans (?_ : ProofMode.Entails' _ _) (wp_unfold s₂ E₂ e Ψ).2 + iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE + iintro H + refine (BI.sep_mono .rfl wp_unfold.1).trans ?_ + iintro ⟨IH,H⟩ HΦ + refine BI.Entails.trans ?_ wp_unfold.2 + iintro ⟨⟨#IH,H⟩,HΦ⟩ dsimp only [wp.pre] match toVal e with | none => @@ -260,8 +268,7 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V | .NotStuck, .NotStuck | .MaybeStuck, .MaybeStuck | .NotStuck, .MaybeStuck => - dsimp at h ⊢ - ipure_intro <;> simp only [*] + ipure_intro; grind only iintro %e₂ %σ₂ %eₜ #hstep «h£» dsimp [Nat.repeat] imod H $$ hstep «h£» with H @@ -297,8 +304,8 @@ Qed. -/ theorem fupd_wp (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by - refine (BIFUpdate.mono <| (wp_unfold ..).1).trans ?_ - refine BI.Entails.trans ?_ (wp_unfold ..).2 + refine (BIFUpdate.mono <| wp_unfold.1).trans ?_ + refine BI.Entails.trans ?_ wp_unfold.2 iintro H match h: toVal e with | some v => @@ -322,23 +329,21 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} [Language.Atomic ↑s e] : (|={E1,E2}=> WP e @ s ; E2 {{v, iprop(|={E2,E1}=> Φ v) }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by - refine (BIFUpdate.mono <| (wp_unfold ..).1).trans ?_ - refine BI.Entails.trans ?_ (wp_unfold ..).2 + refine (BIFUpdate.mono <| wp_unfold.1).trans ?_ + refine BI.Entails.trans ?_ wp_unfold.2 iintro H - cases h : toVal e - case some v => - simp only [wp.pre, h] + match He : toVal e with + | some v => + simp only [wp.pre, He] iapply BIFUpdate.trans (E2 := E2) imod H iassumption - case none => - simp only [wp.pre, h] + | none => + simp only [wp.pre, He] iintro %σ₁ %ns %obs %obs' %nt Hσ imod H - imod H $$ Hσ with ⟨%h, H⟩ + imod H $$ Hσ with ⟨$, H⟩ imodintro - isplitl [] - · ipure_intro; assumption iintro %e2 %σ2 %efs %Hstep Hcred ihave aux := H $$ %e2 %σ2 %efs %Hstep Hcred iapply step_fupdN_wand $$ aux From 80a3dbe38676ddea74d26a6de4d1c1a4d56528b0 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:28:45 +0200 Subject: [PATCH 39/76] feat: add stuckness_to_atomicity --- Iris/Iris/ProgramLogic/Language.lean | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProgramLogic/Language.lean b/Iris/Iris/ProgramLogic/Language.lean index 96fa40ed..1274f540 100644 --- a/Iris/Iris/ProgramLogic/Language.lean +++ b/Iris/Iris/ProgramLogic/Language.lean @@ -8,6 +8,7 @@ meta import Iris.Std.RocqPorting public import Iris.Std.FromMathlib public import Iris.Std.Relation public import Iris.Std.List +public import Iris.BI.WeakestPre public meta import Lean.PrettyPrinter.Delaborator public import Batteries.Data.List.Basic @@ -217,8 +218,15 @@ end ReducibilityLemmas @[rocq_alias atomicity] inductive Atomicity where -| WeaklyAtomic -| StronglyAtomic + | WeaklyAtomic + | StronglyAtomic + +@[rocq_alias stuckness_to_atomicity, coe] +abbrev Atomicity.ofStuckness : Stuckness → Atomicity + | .MaybeStuck => .StronglyAtomic + | .NotStuck => .WeaklyAtomic + +instance : Coe Stuckness Atomicity where coe := Atomicity.ofStuckness @[rocq_alias Atomic] class Atomic (a : Atomicity) (e : Expr) : Prop where From 49230dc73f7b31d1ef6aa4b4c29a125f2473df87 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:29:29 +0200 Subject: [PATCH 40/76] feat: wp_atomic --- Iris/Iris/ProgramLogic/WeakestPre.lean | 33 ++++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index aeff7c5e..2a349375 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -327,7 +327,7 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : iassumption theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} - [Language.Atomic ↑s e] : + [ι : Language.Atomic ↑s e] : (|={E1,E2}=> WP e @ s ; E2 {{v, iprop(|={E2,E1}=> Φ v) }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by refine (BIFUpdate.mono <| wp_unfold.1).trans ?_ refine BI.Entails.trans ?_ wp_unfold.2 @@ -348,14 +348,27 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro ihave aux := H $$ %e2 %σ2 %efs %Hstep Hcred iapply step_fupdN_wand $$ aux iintro >(⟨Hσ,H,Hefs⟩) - match s with - | .NotStuck => - simp only [↓reduceIte] at h - obtain ⟨obs, e', σ2, efs, hstep⟩ := h - sorry - | .MaybeStuck => - sorry - iapply H - sorry + cases s with -- TODO: Example of place where `match` is worse than `cases` + | NotStuck => + -- TODO: replace this when `irw` is available. + istop; refine (BI.sep_mono (BI.sep_mono .rfl wp_unfold.1) (BI.BigSepL.bigSepL_mono λ_↦ wp_unfold.1)).trans ?_; refine BI.Entails.trans ?_ (BIFUpdate.mono <| (BI.sep_mono .rfl (BI.sep_mono wp_unfold.2 (BI.BigSepL.bigSepL_mono λ_↦ wp_unfold.2) ))); iintro ⟨⟨Hσ,H⟩,Hefs⟩ + simp [wp.pre] + have := (ι.atomic _ _ _ _ _ Hstep) + simp at this + match h₂ : toVal e2 with + | some v2 => + icases H with > > $ + iframe + | none => + simp + icases H $$ %σ2 %(ns +1) %([]) %_ %(efs.length +nt) [Hσ] with >⟨%h, _⟩ + · exact .rfl + nomatch (Language.not_reducible_iff_irreducible.mpr (ι.atomic _ _ _ _ _ Hstep)) h + | MaybeStuck => + have ⟨v, h⟩ := Option.isSome_iff_exists.mp (ι.atomic _ _ _ _ _ Hstep) + obtain ⟨rfl⟩ := (ToVal.coe_of_toVal_eq_some h) + istop; refine (BI.sep_mono (BI.sep_mono .rfl wp_value_fupd'.1) .rfl).trans ?_; refine BI.Entails.trans ?_ (BIFUpdate.mono <| (BI.sep_mono .rfl (BI.sep_mono wp_value_fupd'.2 .rfl ))); iintro ⟨⟨Hσ,H⟩,Hefs⟩ + imod H with > H + iframe end Wp From 727abd0f1c8df52acd09ed5f71bf1674ff7fc20c Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 11:29:44 +0200 Subject: [PATCH 41/76] feat: wp_credit_access --- Iris/Iris/BI/Updates.lean | 59 ++++++++++++++++++++++++ Iris/Iris/ProgramLogic/WeakestPre.lean | 63 ++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index 6963c8d8..8fc6376c 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -359,6 +359,65 @@ theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> ((P -∗ Q) ∗ _) := mono (later_mono (fupd_frame_l ..)) _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> _ := mono (later_mono (mono IH)) +theorem step_fupd_wand {Eo Ei : CoPset} {P Q : PROP} : + (|={Eo}[Ei]▷=> P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=> Q) := by + exact step_fupdN_wand (n := 1) + +#check emp_sep.2 +#check BIFUpdate.trans + +#check fupd_frame_r + +theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : + Ei₂ ⊆ Ei₁ → + Eo₁ ⊆ Eo₂ → + (|={Eo₁}[Ei₁]▷=> P) ⊢ |={Eo₂}[Ei₂]▷=> P := by + intro Ei₂_Ei₁ Eo₁_Eo₂ + + refine emp_sep.2.trans ?_ + refine (sep_mono (fupd_mask_intro_subseteq Eo₁_Eo₂) .rfl).trans ?_ + refine fupd_frame_r.trans ?_ + refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Eo₁)) + + refine fupd_frame_l.trans ?_ + refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Ei₁)) + + refine (sep_mono (fupd_mask_intro_subseteq Ei₂_Ei₁) .rfl).trans ?_ + refine fupd_frame_r.trans ?_ + apply mono + + refine (sep_mono later_intro .rfl).trans ?_ + refine later_sep.2.trans ?_ + apply later_mono + + refine fupd_frame_r.trans ?_ + refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Ei₁)) + + refine fupd_frame_l.trans ?_ + refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Eo₁)) + + refine fupd_frame_r.trans ?_ + refine mono emp_sep.1 + +theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} : + Ei ⊆ Eo → + ▷ P ⊢ |={Eo}[Ei]▷=> P := by + intro Ei_Eo + calc iprop(▷ P) + _ ⊢ |={Ei}=> ▷ P := fupd_intro + _ ⊢ |={Ei}[Ei]▷=> P := mono <| later_mono fupd_intro + _ ⊢ |={Eo}[Ei]▷=> P := step_fupd_mask_mono (subset_refl) Ei_Eo + +theorem step_fupdN_le {n m : Nat}{Eo Ei : CoPset}{P : PROP} : + n ≤ m → + Ei ⊆ Eo → + (|={Eo}[Ei]▷=>^[n] P) ⊢ |={Eo}[Ei]▷=>^[m] P + | .refl, _ => .rfl + | .step (m := m) n_m, Ei_Eo => by + refine step_fupdN_le n_m Ei_Eo |>.trans ?_ + refine later_intro.trans ?_ + apply step_fupd_intro Ei_Eo + @[rocq_alias step_fupd_fupd] theorem step_fupd_fupd {Eo Ei : CoPset} {P : PROP} : (|={Eo}[Ei]▷=> P) ⊣⊢ (|={Eo}[Ei]▷=> |={Eo}=> P) := ⟨mono <| later_mono <| mono fupd_intro, mono <| later_mono BIFUpdate.trans⟩ diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 2a349375..866000ee 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -371,4 +371,67 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro imod H with > H iframe +/-- (copy-pasted from Rocq formalization) + + This lemma gives us access to the later credits that are generated in each step, + asuming that we have instantiated `numLaterPerStep` with a non-trivial function + (for instance, a linear function). + + This lemma can be used to provide a "regeneration" mechanism for later credits. + `stateInter` will have to be defined in a way that involves the required + regeneration tokens. + + In detail, a client can use this lemma as follows: + + 1. Then client obtains the state interpreatation `stateInterp _ ns _ _` + + 2. It uses some ghost state wired up to the interpretation to know that + `ns = k + m`, and update the state interpretation to `stateInterp _ m _ _` + + 3. _After_ `e` has finally stepped, we get `numLatersPerStep k` later credits + that we can use to prove `P` in the postcondition, and we have to update the + state interpretation from `stateInterp _ (m+1) _ _` to + `stateInterp _ (ns+1) _ _` again + +-/ +theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} : + toVal e = none → + (∀ m k, numLatersPerStep m + numLatersPerStep k ≤ numLatersPerStep (m + k)) → + (∀ (σ₁ : State) ns obs nt, + stateInterp σ₁ ns obs nt ={E}=∗ + ∃ k m, stateInterp σ₁ m obs nt ∗ ⌜ns = m + k⌝ ∗ ( + ∀ nt (σ₂: State) obs, £ (numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ + stateInterp σ₂ (ns+1) obs nt ∗ P)) ⊢ + WP e @ s ; E {{ v, iprop(P ={E}=∗ Φ v) }} -∗ + WP e @ s ; E {{ Φ }} := by + intro h Htri + refine BI.Entails.trans ?_ (BI.wand_mono wp_unfold.1 wp_unfold.2) + iintro Hupd Hwp + simp [wp.pre, h] + iintro %σ₁ %ns %obs %obs' %nt Hσ₁ + imod Hupd $$ Hσ₁ with ⟨%k, %m, Hσ₁, %h, Hpost⟩; subst h + imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ + imodintro + iintro %e₂ %σ₂ %efs %Hstep Hc + istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨Hpost,Hwp⟩,Hc⟩ + icases Hc with ⟨Hc,Hone⟩ + ihave Hc := lc_weaken _ (Htri m k) $$ Hc + istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨⟨Hpost,Hwp⟩,Hone⟩,Hc⟩ + icases Hc with ⟨Hm, Hk⟩ + -- TODO: Redo with `icombine` when available + ihave Hm := lc_split.mpr $$ [Hm Hone] + · iframe + simp [Nat.repeat] + ihave Hwp := Hwp $$ [] [Hm] + · ipure_intro; assumption + · rw [Nat.add_comm]; exact .rfl + iapply step_fupd_wand $$ Hwp; iintro Hwp + iapply step_fupdN_le (n := numLatersPerStep m) (by grind only) (Std.LawfulSet.subset_refl) + iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ + icases Hpost $$ Hk SI with >⟨$, HP⟩ + imodintro + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ Hwp + iintro %v HΦ + iapply HΦ $$ HP + end Wp From c2c26620d2b1d286febba42c41c7d946c4952580 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 15:18:35 +0200 Subject: [PATCH 42/76] fix: IProp is Leibnitz! --- Iris/Iris/ProgramLogic/WeakestPre.lean | 139 ++++++++++++------------- 1 file changed, 64 insertions(+), 75 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 866000ee..77c34ff6 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -154,42 +154,36 @@ instance wp_ne (s : Stuckness) E (e : Expr) : OFE.NonExpansive (Wp.wp (PROP := IProp GF) s E e) where ne {n Φ₁ Φ₂} HΦ := by induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => - calc iprop(Wp.wp s E e Φ₁) - _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n - _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by - dsimp [wp.pre] - cases toVal e - case some v => exact BIFUpdate.ne.ne <| HΦ v - dsimp - refine BI.forall_ne (fun σ₁ => ?_) - refine BI.forall_ne (fun ns => ?_) - refine BI.forall_ne (fun obs => ?_) - refine BI.forall_ne (fun obs' => ?_) - refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.forall_ne (fun e₂ => ?_) - refine BI.forall_ne (fun σ₂ => ?_) - refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BI.wand_ne.ne (.of_eq rfl) ?_ - induction numLatersPerStep ns - case zero => - refine step_fupdN_contractive.distLater_dist ?_ - intros i ih - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.sep_ne.ne ?_ (.of_eq rfl) - apply IH i ih _ _ <| OFE.dist_lt HΦ ih - case succ n IH => - apply BIFUpdate.ne.ne - apply BI.later_ne.ne - apply BIFUpdate.ne.ne - assumption - _ ≡{n}≡ Wp.wp s E e Φ₂ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n |>.symm + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + dsimp only [wp.pre, Stuckness.MaybeReducible] + cases toVal e + case some v => exact BIFUpdate.ne.ne <| HΦ v + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne (.of_eq rfl) ?_ + induction numLatersPerStep ns with + | zero => + refine step_fupdN_contractive.distLater_dist ?_ + intros i ih + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne ?_ (.of_eq rfl) + apply IH i ih _ _ <| OFE.dist_lt HΦ ih + | succ n IH => + apply BIFUpdate.ne.ne + apply BI.later_ne.ne + apply BIFUpdate.ne.ne + assumption #rocq_ignore wp_proper "Derivable using NonExpansive.eqv" @@ -198,40 +192,35 @@ instance wp_ne (s : Stuckness) E (e : Expr) : instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where distLater_dist {n Φ₁ Φ₂} HΦ := by - calc iprop(Wp.wp s E e Φ₁) - _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₁ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n - _ ≡{n}≡ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ₂ := by - simp only [wp.pre, h] - refine BI.forall_ne (fun σ₁ => ?_) - refine BI.forall_ne (fun ns => ?_) - refine BI.forall_ne (fun obs => ?_) - refine BI.forall_ne (fun obs' => ?_) - refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.forall_ne (fun e₂ => ?_) - refine BI.forall_ne (fun σ₂ => ?_) - refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BI.wand_ne.ne (.of_eq rfl) ?_ - induction numLatersPerStep ns - case zero => - refine step_fupdN_contractive.distLater_dist ?_ - intros i ih - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.sep_ne.ne ?_ (.of_eq rfl) - apply OFE.NonExpansive.ne - apply HΦ i ih - case succ n IH => - apply BIFUpdate.ne.ne - apply BI.later_ne.ne - apply BIFUpdate.ne.ne - assumption - _ ≡{n}≡ Wp.wp s E e Φ₂ := - OFE.equiv_dist.1 (BI.equiv_iff.2 <| wp_unfold) n |>.symm + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [wp.pre, h] + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne (.of_eq rfl) ?_ + induction numLatersPerStep ns + case zero => + refine step_fupdN_contractive.distLater_dist ?_ + intros i ih + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne ?_ (.of_eq rfl) + apply OFE.NonExpansive.ne + apply HΦ i ih + case succ n IH => + apply BIFUpdate.ne.ne + apply BI.later_ne.ne + apply BIFUpdate.ne.ne + assumption @[rocq_alias wp_value_fupd'] theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : @@ -350,9 +339,8 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro iintro >(⟨Hσ,H,Hefs⟩) cases s with -- TODO: Example of place where `match` is worse than `cases` | NotStuck => - -- TODO: replace this when `irw` is available. - istop; refine (BI.sep_mono (BI.sep_mono .rfl wp_unfold.1) (BI.BigSepL.bigSepL_mono λ_↦ wp_unfold.1)).trans ?_; refine BI.Entails.trans ?_ (BIFUpdate.mono <| (BI.sep_mono .rfl (BI.sep_mono wp_unfold.2 (BI.BigSepL.bigSepL_mono λ_↦ wp_unfold.2) ))); iintro ⟨⟨Hσ,H⟩,Hefs⟩ - simp [wp.pre] + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [wp.pre] have := (ι.atomic _ _ _ _ _ Hstep) simp at this match h₂ : toVal e2 with @@ -405,7 +393,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} WP e @ s ; E {{ v, iprop(P ={E}=∗ Φ v) }} -∗ WP e @ s ; E {{ Φ }} := by intro h Htri - refine BI.Entails.trans ?_ (BI.wand_mono wp_unfold.1 wp_unfold.2) + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] iintro Hupd Hwp simp [wp.pre, h] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ @@ -424,7 +412,8 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} simp [Nat.repeat] ihave Hwp := Hwp $$ [] [Hm] · ipure_intro; assumption - · rw [Nat.add_comm]; exact .rfl + · simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] + exact .rfl iapply step_fupd_wand $$ Hwp; iintro Hwp iapply step_fupdN_le (n := numLatersPerStep m) (by grind only) (Std.LawfulSet.subset_refl) iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ From 5f406b044aa6266e9569935e360087153c6bfddc Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 15:18:49 +0200 Subject: [PATCH 43/76] fix: non-terminal simps only --- Iris/Iris/ProgramLogic/WeakestPre.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 77c34ff6..697f1e5d 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -259,7 +259,7 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V | .NotStuck, .MaybeStuck => ipure_intro; grind only iintro %e₂ %σ₂ %eₜ #hstep «h£» - dsimp [Nat.repeat] + dsimp only [Nat.repeat] imod H $$ hstep «h£» with H iintro !> !>; imod H; iintro !> iapply step_fupdN_wand $$ H @@ -395,13 +395,13 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} intro h Htri simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] iintro Hupd Hwp - simp [wp.pre, h] + simp only [wp.pre, h] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ imod Hupd $$ Hσ₁ with ⟨%k, %m, Hσ₁, %h, Hpost⟩; subst h imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ imodintro iintro %e₂ %σ₂ %efs %Hstep Hc - istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨Hpost,Hwp⟩,Hc⟩ + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split)] icases Hc with ⟨Hc,Hone⟩ ihave Hc := lc_weaken _ (Htri m k) $$ Hc istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨⟨Hpost,Hwp⟩,Hone⟩,Hc⟩ @@ -409,7 +409,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} -- TODO: Redo with `icombine` when available ihave Hm := lc_split.mpr $$ [Hm Hone] · iframe - simp [Nat.repeat] + simp only [Nat.repeat] ihave Hwp := Hwp $$ [] [Hm] · ipure_intro; assumption · simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] From 72c3df2bf124c31dfb2eb7dc82514723ecccd59a Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 15:29:46 +0200 Subject: [PATCH 44/76] feat: wp_step_fupdN_strong --- Iris/Iris/ProgramLogic/WeakestPre.lean | 61 ++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 697f1e5d..882b5aa8 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -423,4 +423,65 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} iintro %v HΦ iapply HΦ $$ HP +theorem wp_step_fupdN_strong {n}{s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} : + toVal e = none → + E2 ⊆ E1 → + -- TODO: This was written as an ∧ in Iris Rocq. I've separated it because it doesn't seem like + -- icases is able to handle ∧ expressions. + (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ numLatersPerStep ns + 1⌝) → + (|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ + WP e @ s ; E2 {{ v, iprop(P ={E1}=∗ Φ v)}} ⊢ + WP e @ s ; E1 {{ Φ }} := by + intro toVal_e E2_E1 interp + match n with + | 0 => + iintro ⟨Hp, Hwp⟩ + iapply wp_strong_mono (Std.IsPreorder.le_refl s) E2_E1 $$ Hwp + iintro %v H + refine (BI.sep_mono BIFUpdate.trans .rfl).trans ?_; iintro ⟨Hp,H⟩ + imod Hp + iapply H $$ Hp + | n+1 => + simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr (wp_unfold))] + iintro ⟨Hp,Hwp⟩ + simp only [wp.pre, toVal_e] + iintro %σ₁ %ns %obs %obs' %nt Hσ₁ + if Hn : n ≤ numLatersPerStep ns then + imod Hp + imod Hwp $$ Hσ₁ with ⟨$, H⟩ + -- #check BIFUpdate.subset + iintro !> %e₂ %σ₂ %efs %Hstep Hcred + icases H $$ %_ %_ %_ %Hstep Hcred with H + simp only [Nat.repeat] + imod H; imod Hp + iintro !> !> + imod H; imod Hp + imodintro + clear interp + generalize numLatersPerStep ns = n0 at * + induction n generalizing n0 with + | zero => + iapply step_fupdN_wand $$ H + iintro >⟨$, Hwp, $⟩ + simp only [Nat.repeat] + imod Hp + imodintro + iapply wp_strong_mono (Std.IsPreorder.le_refl s) E2_E1 $$ Hwp + iintro %v HΦ + iapply HΦ $$ Hp + | succ n IH => + obtain ⟨n0, rfl⟩ : ∃ n0', n0 = n0' + 1 := by cases n0 <;> grind only + simp only [Nat.repeat] + imod Hp + imod H + imodintro + imodintro + imod Hp + imod H + imodintro + iapply IH n0 (Nat.le_of_succ_le_succ Hn) $$ [$]; + else + imod interp $$ Hσ₁ with %h + grind only + end Wp From fa016f4e18b19c81ddbbaf20ed84408a320a8c33 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 16:39:15 +0200 Subject: [PATCH 45/76] feat: wp_bind --- Iris/Iris/ProgramLogic/WeakestPre.lean | 27 ++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 882b5aa8..d8ee8529 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -484,4 +484,31 @@ theorem wp_step_fupdN_strong {n}{s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : imod interp $$ Hσ₁ with %h grind only +theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : + -- TODO: Figure out how to make this work better. + -- 1. Get rid of parenthesis around the WP expression + -- 2. Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) + WP e @ s ; E {{v, iprop(WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }}) }} ⊢ WP (K e) @ s ; E {{ Φ }} := by + iintro H + iloeb as IH generalizing %E %e %Φ + rewrite (occs := [2]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + simp only [wp.pre] + match h: toVal e with + | some v => + simp only [ToVal.coe_of_toVal_eq_some h] + iapply fupd_wp $$ H + | none => + rw [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] + iintro %σ₁ %step %obs %obs' %n Hσ + imod H $$ [$] with ⟨%_, H⟩ + imodintro + isplit + · ipure_intro; grind only [cases Stuckness, Language.Context.reducible_fill] + iintro %e₂ %σ₂ %efs %HKstep Hcred + obtain ⟨e₂', rfl, Hstep⟩ := κ.primStep_fill_inv h HKstep + icases H $$ %e₂' %σ₂ %efs %Hstep Hcred with >H; imodintro; imodintro + imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H + imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H + end Wp From cd7697d0cd39137bee6f78b489a15ab2a3fc02e9 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 17:08:10 +0200 Subject: [PATCH 46/76] feat: wp_bind_inv --- Iris/Iris/ProgramLogic/WeakestPre.lean | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index d8ee8529..47533aaa 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -511,4 +511,28 @@ theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H +theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : + WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} := by + iintro H + iloeb as IH generalizing %E %e %Φ + rewrite (occs := [3]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + simp only [wp.pre] + match h: toVal e with + | some v => + simp only [ToVal.coe_of_toVal_eq_some h] + iapply fupd_wp $$ H + | none => + rewrite (occs := [2]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] + iintro %σ₁ %step %obs %obs' %n Hσ + imod H $$ [$] with ⟨%_, H⟩ + imodintro + isplit + · ipure_intro; grind only [cases Stuckness, Language.Context.reducible_fill_inv] + iintro %e₂ %σ₂ %efs %Hstep Hcred + have HKstep := κ.primStep_fill Hstep + icases H $$ %(K e₂) %σ₂ %efs %HKstep Hcred with >H; imodintro; imodintro + imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H + imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H + end Wp From 324c25261b5510b0370a5bb33d2e08044d3bb1ae Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 17:08:22 +0200 Subject: [PATCH 47/76] feat: wp_mono --- Iris/Iris/ProgramLogic/WeakestPre.lean | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 47533aaa..85ca5d8b 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -535,4 +535,38 @@ theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H +/-! ## Derived rules -/ + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in +theorem wp_mono : + (∀ v, Φ v ⊢ Ψ v) → WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := by + iintro %HΦ H + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H + iintro %v HΨ; + ihave aux := HΦ $$ HΨ + exact fupd_intro + +variable {s₁ s₂ : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +theorem wp_stuck_mono : + s₁ ≤ s₂ → WP e @ s₁; E {{ Φ }} ⊢ WP e @ s₂ ; E {{ Φ }} := by + iintro %s₁s₂ Hwp + iapply wp_strong_mono s₁s₂ (Std.LawfulSet.subset_refl) $$ Hwp + iintro %v HΦ + exact fupd_intro + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +theorem wp_stuck_weaken : + WP e @ s; E {{ Φ }} ⊢ WP e @ E ?{{ Φ }} := + wp_stuck_mono (Stuckness.le_MaybeStuck) + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} in +theorem wp_mask_mono : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by + iintro %E₁_E₂ Hwp + iapply wp_strong_mono (Std.IsPreorder.le_refl s) E₁_E₂ $$ Hwp + iintro %v HΦ + exact fupd_intro + +#rocq_ignore wp_mono' "No `Proper` typeclass in Lean" +#rocq_ignore wp_flip_mono' "No `Proper` typeclass in Lean" + end Wp From 9f0a63e6345090dceb981bc711708d212512a7d7 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 17:29:53 +0200 Subject: [PATCH 48/76] fix: simplify proofs, using UPred being Leibnitz --- Iris/Iris/ProgramLogic/WeakestPre.lean | 44 ++++++++++++-------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 85ca5d8b..098c1cf1 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -62,6 +62,10 @@ variable [ι : IrisGS_gen hlc Expr GF] instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ +@[simp] +-- TODO: Move to a better place, probably think of a better name +theorem rw_iProp{P Q : IProp GF} : P ⊣⊢ Q → P = Q := OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr + /-- Reducibility condition depending on stuckness. ```lean4 -- s.MaybeReducible (e, σ) equivalent to… @@ -154,7 +158,7 @@ instance wp_ne (s : Stuckness) E (e : Expr) : OFE.NonExpansive (Wp.wp (PROP := IProp GF) s E e) where ne {n Φ₁ Φ₂} HΦ := by induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [rw_iProp wp_unfold] dsimp only [wp.pre, Stuckness.MaybeReducible] cases toVal e case some v => exact BIFUpdate.ne.ne <| HΦ v @@ -192,7 +196,7 @@ instance wp_ne (s : Stuckness) E (e : Expr) : instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where distLater_dist {n Φ₁ Φ₂} HΦ := by - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [rw_iProp wp_unfold] simp only [wp.pre, h] refine BI.forall_ne (fun σ₁ => ?_) refine BI.forall_ne (fun ns => ?_) @@ -224,24 +228,18 @@ instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : @[rocq_alias wp_value_fupd'] theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : - WP (v : Expr) @ s ; E {{ Φ }} ⊣⊢ |={E}=> Φ v := - calc iprop(WP (v : Expr) @ s ; E {{ Φ }}) - _ ⊣⊢ wp.pre s (Wp.wp s) E (v : Expr) Φ := wp_unfold - _ ⊣⊢ |={E}=> Φ v := by - simp only [toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] + WP (v : Expr) @ s ; E {{ Φ }} ⊣⊢ |={E}=> Φ v := by + simp only [rw_iProp wp_unfold, toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] @[rocq_alias wp_strong_mono] theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} : s₁ ≤ s₂ → E₁ ⊆ E₂ → ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by intro hs hE - istart iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE - iintro H - refine (BI.sep_mono .rfl wp_unfold.1).trans ?_ - iintro ⟨IH,H⟩ HΦ - refine BI.Entails.trans ?_ wp_unfold.2 - iintro ⟨⟨#IH,H⟩,HΦ⟩ + rw (occs := [1]) [rw_iProp wp_unfold] + rw (occs := [1]) [rw_iProp wp_unfold] + iintro H HΦ dsimp only [wp.pre] match toVal e with | none => @@ -293,8 +291,7 @@ Qed. -/ theorem fupd_wp (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by - refine (BIFUpdate.mono <| wp_unfold.1).trans ?_ - refine BI.Entails.trans ?_ wp_unfold.2 + simp only [rw_iProp wp_unfold] iintro H match h: toVal e with | some v => @@ -318,8 +315,7 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} [ι : Language.Atomic ↑s e] : (|={E1,E2}=> WP e @ s ; E2 {{v, iprop(|={E2,E1}=> Φ v) }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by - refine (BIFUpdate.mono <| wp_unfold.1).trans ?_ - refine BI.Entails.trans ?_ wp_unfold.2 + simp only [rw_iProp wp_unfold] iintro H match He : toVal e with | some v => @@ -339,7 +335,7 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro iintro >(⟨Hσ,H,Hefs⟩) cases s with -- TODO: Example of place where `match` is worse than `cases` | NotStuck => - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [rw_iProp wp_unfold] simp only [wp.pre] have := (ι.atomic _ _ _ _ _ Hstep) simp at this @@ -393,7 +389,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} WP e @ s ; E {{ v, iprop(P ={E}=∗ Φ v) }} -∗ WP e @ s ; E {{ Φ }} := by intro h Htri - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr wp_unfold)] + simp only [rw_iProp wp_unfold] iintro Hupd Hwp simp only [wp.pre, h] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ @@ -442,7 +438,7 @@ theorem wp_step_fupdN_strong {n}{s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : imod Hp iapply H $$ Hp | n+1 => - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr (wp_unfold))] + simp only [rw_iProp wp_unfold] iintro ⟨Hp,Hwp⟩ simp only [wp.pre, toVal_e] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ @@ -491,14 +487,14 @@ theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E WP e @ s ; E {{v, iprop(WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }}) }} ⊢ WP (K e) @ s ; E {{ Φ }} := by iintro H iloeb as IH generalizing %E %e %Φ - rewrite (occs := [2]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + rewrite (occs := [2]) [rw_iProp wp_unfold] simp only [wp.pre] match h: toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H | none => - rw [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + rw [rw_iProp wp_unfold] simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] iintro %σ₁ %step %obs %obs' %n Hσ imod H $$ [$] with ⟨%_, H⟩ @@ -515,14 +511,14 @@ theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} := by iintro H iloeb as IH generalizing %E %e %Φ - rewrite (occs := [3]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + rewrite (occs := [3]) [rw_iProp wp_unfold] simp only [wp.pre] match h: toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H | none => - rewrite (occs := [2]) [(OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr) wp_unfold] + rewrite (occs := [2]) [rw_iProp wp_unfold] simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] iintro %σ₁ %step %obs %obs' %n Hσ imod H $$ [$] with ⟨%_, H⟩ From c1433aabdae2c750f9db99fffac5518c0c687ce2 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 17:30:26 +0200 Subject: [PATCH 49/76] feat: wp_value --- Iris/Iris/ProgramLogic/WeakestPre.lean | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 098c1cf1..8161385d 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -565,4 +565,16 @@ theorem wp_mask_mono : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E #rocq_ignore wp_mono' "No `Proper` typeclass in Lean" #rocq_ignore wp_flip_mono' "No `Proper` typeclass in Lean" +variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +theorem wp_value_fupd : Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v + | ⟨h⟩ => h ▸ wp_value_fupd' + +variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +theorem wp_value' : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := + fupd_intro.trans wp_value_fupd'.2 + +variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +theorem wp_value : Language.IntoVal e v → Φ e ⊢ WP e @ s; E {{ Φ }} + | ⟨h⟩ => h ▸ wp_value' + end Wp From e1f8c770da7cf5f9ea028f775e8f04e879c9497f Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 17:30:38 +0200 Subject: [PATCH 50/76] feat: wp_frame_{l,r} --- Iris/Iris/ProgramLogic/WeakestPre.lean | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 8161385d..8437030f 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -577,4 +577,16 @@ variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} theorem wp_value : Language.IntoVal e v → Φ e ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in +theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, iprop(R ∗ Φ v) }} := by + iintro ⟨_, H⟩ + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H + iframe + iintro %x + iapply fupd_intro + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in +theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, iprop(R ∗ Φ v) }} := + BI.sep_comm.1.trans wp_frame_l + end Wp From be16c9a5e46a637bdfa1152d39be8ed9997a8b27 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 20:18:08 +0200 Subject: [PATCH 51/76] feat: wp_step_fupdN --- Iris/Iris/ProgramLogic/WeakestPre.lean | 55 +++++++++++++++++++++++++- Iris/Iris/Std/GenSets.lean | 16 ++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 8437030f..9d8215ef 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -419,16 +419,17 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} iintro %v HΦ iapply HΦ $$ HP -theorem wp_step_fupdN_strong {n}{s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} : +theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} : toVal e = none → E2 ⊆ E1 → + ∀ {n}, -- TODO: This was written as an ∧ in Iris Rocq. I've separated it because it doesn't seem like -- icases is able to handle ∧ expressions. (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ numLatersPerStep ns + 1⌝) → (|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ WP e @ s ; E2 {{ v, iprop(P ={E1}=∗ Φ v)}} ⊢ WP e @ s ; E1 {{ Φ }} := by - intro toVal_e E2_E1 interp + intro toVal_e E2_E1 n interp match n with | 0 => iintro ⟨Hp, Hwp⟩ @@ -589,4 +590,54 @@ variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IPro theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, iprop(R ∗ Φ v) }} := BI.sep_comm.1.trans wp_frame_l + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in +/-- (copy-pasted from Rocq formalization) + + This lemma states that if we can prove that [n] laters are used in + the current physical step, then one can perform an n-steps fancy + update during that physical step. The resources needed to prove the + bound on [n] are not used up: they can be reused in the proof of + the WP or in the proof of the n-steps fancy update. In order to + describe this unusual resource flow, we use ordinary conjunction as + a premise. +-/ +theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → + (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (numLatersPerStep ns)+1⌝) → + ((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ + WP e @ s; E₂ {{ v, iprop(P ={E₁}=∗ Φ v) }}) -∗ + WP e @ s; E₁ {{ Φ }} := by + intro toVal_e E₂E₁ Hstate + iintro H + iapply wp_step_fupdN_strong (s := s) (P := P) toVal_e E₂E₁ Hstate $$ [H] + apply BI.sep_mono_l + iintro Hp + imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from Std.LawfulSet.diff_subset_left) with H + imod Hp + imod H with toClear; iclear toClear + simp [show E₁ \ (E₁ \ E₂) = E₂ from Std.LawfulSet.diff_self_diff_of_subset E₂E₁] + imodintro + iapply step_fupdN_wand $$ Hp; iintro H + iapply fupd_mask_frame (Std.LawfulSet.empty_subset) + imod H + imodintro + simp only [Std.LawfulSet.diff_empty, ←Std.LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in +theorem wp_step_fupd : + toVal e = none → E₂ ⊆ E₁ → + (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, iprop(P ={E₁}=∗ Φ v) }} -∗ WP e @ s; E₁ {{ Φ }} := + fun toVal_e E₂E₁=> by + iintro HR H + iapply wp_step_fupdN_strong (n := 1) toVal_e E₂E₁ (by + intros; iintro H + refine .trans ?_ <| fupd_mask_intro_discard (Std.LawfulSet.empty_subset) + simp only [Nat.le_add_left, BI.true_intro] + -- TODO: Maybe it's useful to have `_ ={E,E'}=∗ True ↔ True` in the simp set (Is this even true? maybe `E' ⊆ E`) + ) $$ [-] + iframe H + imod HR + simp only [Nat.repeat] + iframe + end Wp diff --git a/Iris/Iris/Std/GenSets.lean b/Iris/Iris/Std/GenSets.lean index 68027f79..3c40aa10 100644 --- a/Iris/Iris/Std/GenSets.lean +++ b/Iris/Iris/Std/GenSets.lean @@ -119,6 +119,9 @@ theorem eq_subset {X Y : S} : X ⊆ Y → Y ⊆ X → X = Y := by ext x exact ⟨H1 x, H2 x⟩ +instance : Std.Antisymm (fun x y : S => x ⊆ y) where + antisymm _ _ := eq_subset + /-- Proper subset is equivalent to subset plus inequality. -/ theorem ssubset_subset {X Y : S} : (X ⊂ Y) ↔ (X ⊆ Y ∧ X ≠ Y) := by simp [SSubset, Subset]; grind only @@ -353,10 +356,17 @@ theorem insert_subset_subset {s₁ s₂ : S} {x : A} (H : s₁ ⊆ s₂) : inser theorem subset_refl {s : S} : s ⊆ s := by intro x _; assumption +instance : Std.Refl (fun x y : S => x ⊆ y) where + refl _ := subset_refl + /-- Subset relation is transitive. -/ theorem subset_trans {s₁ s₂ s₃ : S} : s₁ ⊆ s₂ → s₂ ⊆ s₃ → s₁ ⊆ s₃ := by intro h1 h2 x hx; exact h2 _ (h1 _ hx) +-- ↓ It looks like a face 🥹 +instance : Trans (fun x y : S => x ⊆ y) (·⊆ ·) (·⊆ ·) where + trans := subset_trans + /-! ### Disjointness -/ /-- Disjoint sets have empty intersection and vice versa. -/ @@ -442,6 +452,12 @@ theorem diff_subset_left {s₁ s₂ : S} : s₁ \ s₂ ⊆ s₁ := by intro y G; rw [mem_diff] at G exact G.left +theorem diff_self_diff_of_subset {s u : S} : s ⊆ u → u \ (u \ s) = s := by + intro su + apply eq_subset + · intro x; grind [mem_diff] + · intro x xs; simp [mem_diff, su x xs, xs] + /-- A set is disjoint from the part removed by taking a difference. -/ theorem disjoint_diff_right {s₁ s₂ : S} : s₁ ## (s₂ \ s₁) := by intro x ⟨hx1, hx2⟩ From b4745d7f0449306b8104439c63a69de7caab5fcb Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 20:39:57 +0200 Subject: [PATCH 52/76] feat: wp_frame_step_{l,r} --- Iris/Iris/ProgramLogic/WeakestPre.lean | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 9d8215ef..a3eed3a1 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -640,4 +640,34 @@ theorem wp_step_fupd : simp only [Nat.repeat] iframe +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → + (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, iprop(R ∗ Φ v) }} := by + iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ + iapply wp_step_fupd toVal_e E₂E₁ $$ Hu + iapply wp_mono $$ Hwp + iintro %x $ $ + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +theorem wp_frame_step_r : toVal e = none → E₂ ⊆ E₁ → + WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := + (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → + (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, iprop(R ∗ Φ v) }} := by + iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ + iapply wp_frame_step_l toVal_e E₂E₁ + iframe + iapply fupd_mask_intro E₂E₁ + iintro _ + imodintro + apply BIFUpdate.mono + exact BI.true_intro + +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +theorem wp_frame_step_r' : toVal e = none → E₂ ⊆ E₁ → + WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := + (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) + end Wp From c76b3bfa693240768b85bd333b543b7f6ce660b1 Mon Sep 17 00:00:00 2001 From: ayhon Date: Fri, 15 May 2026 20:49:01 +0200 Subject: [PATCH 53/76] feat: wp_wand --- Iris/Iris/ProgramLogic/WeakestPre.lean | 31 ++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index a3eed3a1..5e6e31d8 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -653,7 +653,7 @@ theorem wp_frame_step_r : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, iprop(R ∗ Φ v) }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ @@ -665,9 +665,36 @@ theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → apply BIFUpdate.mono exact BI.true_intro -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_r' : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in +theorem wp_wand : + WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by + iintro Hwp H + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ Hwp + iintro %v HΦ + icases H $$ HΦ with H + exact fupd_intro + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +theorem wp_wand_l : + (∀ v, Φ v -∗ Ψ v) ∗ WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := + BI.wand_elim' wp_wand + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +theorem wp_wand_r : + WP e @ s ; E {{ Φ }} ∗ (∀ v, Φ v -∗ Ψ v) ⊢ WP e @ s ; E {{ Ψ }} := + BI.wand_elim wp_wand + +variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ :Val → IProp GF}{R : IProp GF} in +theorem wp_frame_wand : + R ⊢ WP e @ s; E {{ v, iprop(R -∗ Φ v) }} -∗ WP e @ s; E {{ Φ }} := by + iintro R Hwp + iapply wp_wand $$ Hwp + iintro %v H + iapply H $$ R + end Wp From e7efdcf4d54aec6a311149778066bdf446618710 Mon Sep 17 00:00:00 2001 From: ayhon Date: Sat, 16 May 2026 01:07:50 +0200 Subject: [PATCH 54/76] fix: add Stuckness order theorems --- Iris/Iris/BI/WeakestPre.lean | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 47cf46bb..1877aae2 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -36,10 +36,12 @@ instance instLE: LE Stuckness where le x y := ¬ (x = .MaybeStuck ∧ y = .NotStuck) instance : Std.IsPreorder Stuckness where - le_refl := by - simp only [LE.le, not_and, forall_eq, reduceCtorEq, not_false_eq_true] - le_trans := by - simp only [LE.le]; grind only [Stuckness] + le_refl := by grind only [Stuckness, LE.le, instLE] + le_trans := by grind only [Stuckness, LE.le, instLE] + +@[simp] theorem le_MaybeStuck {s : Stuckness} : s ≤ MaybeStuck := by cases s <;> grind only [Stuckness, LE.le, instLE] + +@[simp] theorem NotSuck_le {s : Stuckness} : NotStuck ≤ s := by cases s <;> grind only [Stuckness, LE.le, instLE] end Stuckness From 8f790c7f8b371ad75228b8aca1903aa16a5033ff Mon Sep 17 00:00:00 2001 From: ayhon Date: Sat, 16 May 2026 01:08:37 +0200 Subject: [PATCH 55/76] feat: add proof mode classes --- Iris/Iris/ProgramLogic/WeakestPre.lean | 92 +++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 5e6e31d8..f1a88572 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -289,7 +289,7 @@ Proof. iIntros (σ1 ns κ κs nt) "Hσ1". iMod "H". by iApply "H". Qed. -/ -theorem fupd_wp (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : +theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by simp only [rw_iProp wp_unfold] iintro H @@ -575,7 +575,7 @@ theorem wp_value' : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := fupd_intro.trans wp_value_fupd'.2 variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in -theorem wp_value : Language.IntoVal e v → Φ e ⊢ WP e @ s; E {{ Φ }} +theorem wp_value : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in @@ -698,3 +698,91 @@ theorem wp_frame_wand : iapply H $$ R end Wp + +section ProofModeClasses + +open ProofMode + +variable {hlc : outParam Bool} +variable {Expr State Obs Val : Type _} +variable [Λ : Language Expr State Obs Val] +variable {GF : BundledGFunctors} +variable [ι : IrisGS_gen hlc Expr GF] + +variable {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ Ψ : Val → IProp GF} {P Q R : IProp GF} + +-- TODO: Add priorities + +instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : + -- TODO: I didn't move over the `FrameInstantiateExistDisabled` constant. Ask if it's necessary. + Frame p R (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Ψ }}) where + frame := by + replace H v := (H v).frame + refine wp_frame_l.trans ?_ + apply wp_mono + apply H + +instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where + is_except0 := + calc iprop(◇ _) + _ ⊢ ◇ |={E}=> _ := BI.except0_mono fupd_intro + _ ⊢ |={E}=> _ := BIFUpdate.except0 + _ ⊢ WP e @ s ; E {{ Φ }} := fupd_wp + +instance elimModalFupdWp p : + ElimModal True p false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where + elim_modal := by + rintro ⟨⟩; iintro ⟨H, H⟩ + refine (BI.sep_mono BI.intuitionisticallyIf_elim .rfl).trans ?_ + refine fupd_frame_r.trans ?_ + refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ + exact fupd_wp + +/-- + Error message instance for non-mask-changing view shifts. Also uses a slightly + different error: we cannot apply `fupd_mask_subseteq` if `e` is not atomic, so + we tell the user to first add a leading `fupd` and then change the mask of that. +-/ +instance elimModalFupdWp_wrongMask : + ElimModal (PMError "Goal and eliminated modality must have the same mask. + Use `iapply fupd_wp; imod (fupd_mask_subseteq E₂)` to adjust the mask of your goal to `E₂`") + p false iprop(|={E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where + elim_modal := nofun + +instance elimModalFupdWpAtomic : + ElimModal (Language.Atomic ↑s e) p false iprop(|={E₁,E₂}=> P) P (WP e @ s ; E₁ {{ Φ }}) (WP e @ s ; E₂ {{ v, iprop(|={E₂,E₁}=> Φ v)}}) where + elim_modal := by + rintro atomic; iintro ⟨H, H⟩ + refine (BI.sep_mono BI.intuitionisticallyIf_elim .rfl).trans ?_ + refine fupd_frame_r.trans ?_ + refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ + exact wp_atomic + +instance elimModalFupdWpAtomic_wrongMask : + ElimModal (PMError "Goal and eliminated modality must have the same mask. + Use `iapply fupd_wp; imod (fupd_mask_subseteq E₂)` to adjust the mask of your goal to `E₂`") + p false iprop(|={E₁,E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where + elim_modal := nofun + +-- instance addModalFupdWp : +-- ProofMode.AddModal iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) where +-- add_modal := by +-- refine fupd_frame_r.trans ?_ +-- refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ +-- exact fupd_wp + +-- instance elimAccWpAtomic : +-- ElimAcc (X := X) (Atomic ↑s e) +-- (fupd E₁ E₂) (fupd E₂ E₁) +-- α β γ (WP e @ s ; E₁ {{ Φ}}) +-- iprop(λ x ↦ WP e @ s ; E₂ {{ v, iprop(|={E₂}=> β x ∗ (γ x -∗? Φ v)) }}) where +-- elim_acc := sorry + +-- instance elimAccWpNonAtomic : +-- ElimAcc (X := X) True +-- (fupd E E) (fupd E E) +-- α β γ (WP e @ s ; E {{ Φ}}) +-- iprop(λ x ↦ WP e @ s ; E {{ v, iprop(|={E}=> β x ∗ (γ x -∗? Φ v)) }}) where +-- elim_acc := sorry + +end ProofModeClasses From b6eee982bdc0845db52abe402314fe3be2e3efc0 Mon Sep 17 00:00:00 2001 From: ayhon Date: Sat, 16 May 2026 01:08:51 +0200 Subject: [PATCH 56/76] refactor: rename `wp_expr` to `wpExpr` --- Iris/Iris/BI/WeakestPre.lean | 41 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 1877aae2..20f6fcf9 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -51,8 +51,9 @@ class Wp (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) where class TotalWP (PROP Expr) (Val : outParam (Type _)) (A : Type _) where totalWp : A → CoPset → Expr → (Val → PROP) → PROP -syntax wp_expr := - term:max (" @ " term:max (" ; " term:max) <|> ((" ? ")? )) <|> (" ? ")? + +syntax wpExpr := + term:max (" @ " term:max (" ; " term:max) <|> ((" ? ")? )) <|> (" ? ")? declare_syntax_cat wpPostcondInner syntax ident ", " term : wpPostcondInner @@ -64,11 +65,11 @@ syntax " [{ " wpPostcondInner " }] " : wpPostcond syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond -- Are spaces outside of parens used in the pp? syntax " 〖 " wpPostcondInner " 〗 " : wpPostcond -syntax (name := wp) "WP " wp_expr wpPostcond : term +syntax (name := wp) "WP " wpExpr wpPostcond : term syntax texanPostcond := (ident+ ", ")? " RET " term:min "; " term -syntax (name := texanTriple) "{{{ " term " }}} " wp_expr " {{{ " texanPostcond " }}}" : term +syntax (name := texanTriple) "{{{ " term " }}} " wpExpr " {{{ " texanPostcond " }}}" : term /- This section checks whether the syntax is recognized correctly for all combinations -/ section testNotation @@ -335,16 +336,16 @@ info: elaboration function for `Iris.texanTriple` has not been implemented end testNotation open Lean in -meta def parseWpExpr : Lean.TSyntax ``wp_expr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun - | `(wp_expr| $e @ $s ; $E) => +meta def parseWpExpr : Lean.TSyntax ``wpExpr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun + | `(wpExpr| $e @ $s ; $E) => return (e, s, E) - | `(wp_expr| $e @ $E) => + | `(wpExpr| $e @ $E) => return (e, ←`(Stuckness.NotStuck), E) - | `(wp_expr| $e @ $E ?) => + | `(wpExpr| $e @ $E ?) => return (e, ←`(Stuckness.MaybeStuck), E) - | `(wp_expr| $e:term) => + | `(wpExpr| $e:term) => return (e, ←`(Stuckness.NotStuck), ←`(⊤)) - | `(wp_expr| $e:term ?) => + | `(wpExpr| $e:term ?) => return (e, ←`(Stuckness.MaybeStuck), ←`(⊤)) | _ => Lean.Macro.throwUnsupported @@ -537,28 +538,28 @@ meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TS | `($Φ:term) => `(wpPostcondInner| $Φ:term) open Lean in -meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax ``wp_expr) := do +meta def makeWpExpr (s E e : TSyntax `term) : PrettyPrinter.UnexpandM (TSyntax ``wpExpr) := do match s, E with - | `(Stuckness.NotStuck), `(⊤) => `(wp_expr| $e:term) - | `(Stuckness.NotStuck), E => `(wp_expr| $e:term @ $E:term) - | `(Stuckness.MaybeStuck), `(⊤) => `(wp_expr| $e:term ?) - | `(Stuckness.MaybeStuck), E => `(wp_expr| $e:term @ $E:term ?) - | s, E => `(wp_expr| $e:term @ $s:term ; $E:term) + | `(Stuckness.NotStuck), `(⊤) => `(wpExpr| $e:term) + | `(Stuckness.NotStuck), E => `(wpExpr| $e:term @ $E:term) + | `(Stuckness.MaybeStuck), `(⊤) => `(wpExpr| $e:term ?) + | `(Stuckness.MaybeStuck), E => `(wpExpr| $e:term @ $E:term ?) + | s, E => `(wpExpr| $e:term @ $s:term ; $E:term) @[app_unexpander Wp.wp] meta def unexpanderWp : PrettyPrinter.Unexpander | `($_wp $s $E $e $Φ) => do - let wp_expr ← makeWpExpr s E e + let wpExpr ← makeWpExpr s E e let wpPostcondInner ← unexpandWpPostcondInner Φ - `(WP $wp_expr {{ $wpPostcondInner }}) + `(WP $wpExpr {{ $wpPostcondInner }}) | _ => throw () @[app_unexpander TotalWP.totalWp] meta def unexpanderTotalWp : PrettyPrinter.Unexpander | `($_wp $s $E $e $Φ) => do - let wp_expr ← makeWpExpr s E e + let wpExpr ← makeWpExpr s E e let wpPostcondInner ← unexpandWpPostcondInner Φ - `(WP $wp_expr [{ $wpPostcondInner }]) + `(WP $wpExpr [{ $wpPostcondInner }]) | _ => throw () section testUnexpand From 000565d36166e6692fc5ffe178f53e09b1dfa640 Mon Sep 17 00:00:00 2001 From: ayhon Date: Sat, 16 May 2026 01:22:08 +0200 Subject: [PATCH 57/76] feat: add copyright header --- Iris/Iris/ProgramLogic/WeakestPre.lean | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index f1a88572..7c669d2d 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -1,3 +1,7 @@ +/- +Copyright (c) 2026 Fernando Leal. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +-/ module public import Iris.Algebra From d9774039298a4ef1691a202d15d7855cf9cdf942 Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 18 May 2026 11:26:39 +0200 Subject: [PATCH 58/76] fix: remove infos --- Iris/Iris/BI/Updates.lean | 5 ----- Iris/Iris/BI/WeakestPre.lean | 2 -- 2 files changed, 7 deletions(-) diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index 8fc6376c..ac27c72a 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -363,11 +363,6 @@ theorem step_fupd_wand {Eo Ei : CoPset} {P Q : PROP} : (|={Eo}[Ei]▷=> P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=> Q) := by exact step_fupdN_wand (n := 1) -#check emp_sep.2 -#check BIFUpdate.trans - -#check fupd_frame_r - theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : Ei₂ ⊆ Ei₁ → Eo₁ ⊆ Eo₂ → diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 20f6fcf9..98cd05ca 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -21,8 +21,6 @@ public section namespace Iris -#check Lean.PrettyPrinter.Unexpander - open Lean inductive Stuckness where From 415d0919f645141071269c2b5d3963a1f1dd005e Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 18 May 2026 13:01:34 +0200 Subject: [PATCH 59/76] chore: clean up file --- Iris/Iris/Algebra/OFE.lean | 7 +- Iris/Iris/ProgramLogic/Language.lean | 2 +- Iris/Iris/ProgramLogic/WeakestPre.lean | 262 ++++++++++++------------- 3 files changed, 128 insertions(+), 143 deletions(-) diff --git a/Iris/Iris/Algebra/OFE.lean b/Iris/Iris/Algebra/OFE.lean index d6224f10..3b86d527 100644 --- a/Iris/Iris/Algebra/OFE.lean +++ b/Iris/Iris/Algebra/OFE.lean @@ -275,6 +275,12 @@ infixr:25 " -c> " => ContractiveHom instance [OFE α] [OFE β] : CoeFun (α -c> β) (fun _ => α → β) := ⟨fun x => x.toHom.f⟩ instance [OFE α] [OFE β] (f : α -c> β) : Contractive f := f.contractive +-- TODO: I propose this definition, to allow doing `f.toContractiveHom` to infer the necessary +-- instances to make `f : α → β` into a `α -c> β`. +def _root_.Function.toContractiveHom (f : α → β)[OFE α][OFE β][ι : OFE.Contractive f] : α -c> β where + f := f + contractive := ι + theorem InvImage.equivalence {α : Sort u} {β : Sort v} {r : β → β → Prop} {f : α → β} (H : Equivalence r) : Equivalence (InvImage r f) where refl _ := H.refl _ @@ -1770,4 +1776,3 @@ theorem OFE.cast_dist [Iα : OFE α] [Iβ : OFE β] {x y : α} (Ht : α = β) (HIt : Iα = Ht ▸ Iβ) (H : x ≡{n}≡ y) : (Ht ▸ x) ≡{n}≡ (Ht ▸ y) := by subst Ht; subst HIt; exact H - diff --git a/Iris/Iris/ProgramLogic/Language.lean b/Iris/Iris/ProgramLogic/Language.lean index 1274f540..25f3b0c6 100644 --- a/Iris/Iris/ProgramLogic/Language.lean +++ b/Iris/Iris/ProgramLogic/Language.lean @@ -230,7 +230,7 @@ instance : Coe Stuckness Atomicity where coe := Atomicity.ofStuckness @[rocq_alias Atomic] class Atomic (a : Atomicity) (e : Expr) : Prop where - atomic (σ : State) obs e' σ' eₜ : + atomic {σ : State} {obs e' σ' eₜ} : (e, σ) --> (e', σ', eₜ) → match a with | .WeaklyAtomic => Irreducible (e', σ') diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 7c669d2d..1a97b22a 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -23,24 +23,35 @@ open ProgramLogic Language.Notation @[expose] public section +/-- + Carrier typeclass for the `stateInterp` operation. + + + This operation cannot be placed directly inside of `IrisGS_gen` + because Lean then wouldn't be able to derive from its arguments + the values of `Expr` and `Val`, and so they're asked explicitly. + This was not a problem in Iris Rocq becuse of canonical structures. + In Iris Lean, we instead fix our choice of `State` from the choice + of `Expr`, so `Expr` cannot be inferred from `State` instead. +-/ class StateInterp + -- TODO: This probably should be a `semiOutParam` (State: Type s) (Obs : outParam <| Type o) (GF : BundledGFunctors) where + /-- + Axiomatic interpretation of a state in a language model. + - `σ` is the state whose interpretation we take + - `ns` are the number of prior steps (TODO: Check whether true) + - `obs` are the observations prior to this state + - `nt` are the number of threads previously spawned + + -/ stateInterp : State → Nat → List (Obs) → Nat → IProp GF export StateInterp (stateInterp) -/- TODO: Should this be a class? Maybe we just need to be explicit about the - instance it belongs to. Otherwise, we could have some problems if somewhere - someone defines a NumLatersPerStep instance and that one gets taken by - everyone else. -/ -class NumLatersPerStep where - numLatersPerStep : Nat → Nat - -export NumLatersPerStep (numLatersPerStep) - class IrisGS_gen (hlc : outParam <| Bool) (Expr : Type e) {Val : Type v} @@ -50,14 +61,29 @@ class IrisGS_gen (hlc : outParam <| Bool) (GF : BundledGFunctors) extends StateInterp State Obs GF, - InvGS_gen hlc GF, - NumLatersPerStep where + InvGS_gen hlc GF where + + /-- + Number of later credits obtained from taking one step in the + operational semantics of our language. + -/ + -- TODO: Should we have a default of `1`? + numLatersPerStep : Nat → Nat + /-- + Postcondition of forked threads + -/ + -- TODO: Should we have a default of `True`? forkPost : Val → IProp GF + /-- + The number of steps in the state interpretation should only be + considered a lower bound. + -/ state_interp_mono σ ns obs nt : iprop(stateInterp σ ns obs nt ⊢ |={∅}=> stateInterp σ (ns + 1) obs nt) + variable {hlc : outParam Bool} variable {Expr State Obs Val} variable [Λ : Language Expr State Obs Val] @@ -66,7 +92,6 @@ variable [ι : IrisGS_gen hlc Expr GF] instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ -@[simp] -- TODO: Move to a better place, probably think of a better name theorem rw_iProp{P Q : IProp GF} : P ⊣⊢ Q → P = Q := OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr @@ -76,7 +101,6 @@ theorem rw_iProp{P Q : IProp GF} : P ⊣⊢ Q → P = Q := OFE.Leibniz.eq_of_eqv if s matches .NotStuck then Reducible (e, σ) else True ``` -/ -@[simp] abbrev Stuckness.MaybeReducible : Stuckness → Expr × State → Prop | .NotStuck, (e₁, σ₁) => PrimStep.Reducible (e₁, σ₁) | _, _ => True @@ -90,108 +114,87 @@ def wp.pre (s : Stuckness) stateInterp σ₁ ns (obs ++ obs') nt ={E,∅}=∗ ⌜s.MaybeReducible (e₁, σ₁)⌝ ∗ ∀ e₂ σ₂ eₜ, ⌜(e₁, σ₁) --> (e₂, σ₂, eₜ)⌝ -∗ - £ (numLatersPerStep ns + 1) - ={∅}▷=∗^[numLatersPerStep ns + 1] |={∅,E}=> + £ (ι.numLatersPerStep ns + 1) + ={∅}▷=∗^[ι.numLatersPerStep ns + 1] |={∅,E}=> stateInterp σ₂ (ns + 1) obs' (eₜ.length + nt) ∗ wp E e₂ Φ ∗ [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) - instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where distLater_dist := by intros n wp wp' Hwp E e₁ Φ dsimp only [pre] cases toVal e₁ - case some _ => simp - dsimp + case some _ => exact .rfl + dsimp only refine BI.forall_ne (fun σ₁ => ?_) refine BI.forall_ne (fun ns => ?_) refine BI.forall_ne (fun obs => ?_) refine BI.forall_ne (fun obs' => ?_) refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.wand_ne.ne .rfl ?_ refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ + refine BI.sep_ne.ne .rfl ?_ refine BI.forall_ne (fun e₂ => ?_) refine BI.forall_ne (fun σ₂ => ?_) refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BI.wand_ne.ne (.of_eq rfl) ?_ - induction numLatersPerStep ns - case zero => - refine step_fupdN_contractive.distLater_dist ?_ - intros i ih - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.sep_ne.ne ?_ ?_ - · apply Hwp i ih - refine BI.BigSepL.bigSepL_dist ?_ - intros k x h - · apply Hwp i ih - case succ n IH => - apply BIFUpdate.ne.ne - apply BI.later_ne.ne - apply BIFUpdate.ne.ne - assumption - --- instance wp.pre.ne s : OFE.NonExpansive (wp.pre s (ι := ι)) --- := OFE.ne_of_contractive (wp.pre s (ι := ι)) - --- TODO: In this part of the Rocq code, a lot of juggling --- is happening with `wp_def`, `wp_aux`, `wp'` and `wp_unseal`. --- I wonder what is the purpose of all of these, and if --- it's possible to achieve this differently in Lean. + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BIFUpdate.ne.ne ?_ + refine OFE.Contractive.distLater_dist fun m m_n => ?_ + refine BIFUpdate.ne.ne ?_ + refine step_fupdN_ne.ne ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + apply BI.sep_ne.ne + · apply Hwp m m_n + · refine BI.BigSepL.bigSepL_dist (fun _ => (Hwp m m_n _ _ _)) + @[implicit_reducible] instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where wp s := fixpoint (wp.pre s) section Wp --- TODO: Move out of here -def _root_.Function.toContractiveHom (f : α → β)[OFE α][OFE β][ι : OFE.Contractive f] : α -c> β where - f := f - contractive := ι - @[rocq_alias wp_unfold] theorem wp_unfold {s E} {e : Expr} {Φ : Val → IProp GF} : WP e @ s ; E {{ Φ }} ⊣⊢ wp.pre s (Wp.wp (PROP := IProp GF) s) E e Φ := - BI.equiv_iff.1 <| fixpoint_unfold (f := (wp.pre (ι := ι) s).toContractiveHom) E e Φ + BI.equiv_iff.1 <| fixpoint_unfold (f := (wp.pre s).toContractiveHom) E e Φ @[rocq_alias wp_ne] -instance wp_ne (s : Stuckness) E (e : Expr) : +instance wp_ne {s : Stuckness} {E} {e : Expr} : OFE.NonExpansive (Wp.wp (PROP := IProp GF) s E e) where ne {n Φ₁ Φ₂} HΦ := by induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => simp only [rw_iProp wp_unfold] - dsimp only [wp.pre, Stuckness.MaybeReducible] + dsimp only [wp.pre] cases toVal e case some v => exact BIFUpdate.ne.ne <| HΦ v - refine BI.forall_ne (fun σ₁ => ?_) - refine BI.forall_ne (fun ns => ?_) - refine BI.forall_ne (fun obs => ?_) - refine BI.forall_ne (fun obs' => ?_) - refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ + + -- Composing a bunch of nonexpansive operations… + refine BI.forall_ne fun σ₁ => ?_ + refine BI.forall_ne fun ns => ?_ + refine BI.forall_ne fun obs => ?_ + refine BI.forall_ne fun obs' => ?_ + refine BI.forall_ne fun nt => ?_ + refine BI.wand_ne.ne .rfl ?_ refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.forall_ne (fun e₂ => ?_) - refine BI.forall_ne (fun σ₂ => ?_) - refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BI.wand_ne.ne (.of_eq rfl) ?_ - induction numLatersPerStep ns with - | zero => - refine step_fupdN_contractive.distLater_dist ?_ - intros i ih - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.sep_ne.ne ?_ (.of_eq rfl) - apply IH i ih _ _ <| OFE.dist_lt HΦ ih - | succ n IH => - apply BIFUpdate.ne.ne - apply BI.later_ne.ne - apply BIFUpdate.ne.ne - assumption + refine BI.sep_ne.ne .rfl ?_ + refine BI.forall_ne fun e₂ => ?_ + refine BI.forall_ne fun σ₂ => ?_ + refine BI.forall_ne fun eₜ => ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + + -- The `step_fupdN` |={∅}▷=>^[n+1] is contractive + refine step_fupdN_contractive.distLater_dist fun m n_m => ?_ + + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.sep_ne.ne ?_ .rfl + + -- We can now apply the induction hypothesis + apply IH m n_m <| OFE.dist_lt HΦ n_m #rocq_ignore wp_proper "Derivable using NonExpansive.eqv" @@ -202,33 +205,25 @@ instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : distLater_dist {n Φ₁ Φ₂} HΦ := by simp only [rw_iProp wp_unfold] simp only [wp.pre, h] - refine BI.forall_ne (fun σ₁ => ?_) - refine BI.forall_ne (fun ns => ?_) - refine BI.forall_ne (fun obs => ?_) - refine BI.forall_ne (fun obs' => ?_) - refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ + refine BI.forall_ne fun σ₁ => ?_ + refine BI.forall_ne fun ns => ?_ + refine BI.forall_ne fun obs => ?_ + refine BI.forall_ne fun obs' => ?_ + refine BI.forall_ne fun nt => ?_ + refine BI.wand_ne.ne .rfl ?_ refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.forall_ne (fun e₂ => ?_) - refine BI.forall_ne (fun σ₂ => ?_) - refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne (.of_eq rfl) ?_ - refine BI.wand_ne.ne (.of_eq rfl) ?_ - induction numLatersPerStep ns - case zero => - refine step_fupdN_contractive.distLater_dist ?_ - intros i ih - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne (.of_eq rfl) ?_ - refine BI.sep_ne.ne ?_ (.of_eq rfl) - apply OFE.NonExpansive.ne - apply HΦ i ih - case succ n IH => - apply BIFUpdate.ne.ne - apply BI.later_ne.ne - apply BIFUpdate.ne.ne - assumption + refine BI.sep_ne.ne .rfl ?_ + refine BI.forall_ne fun e₂ => ?_ + refine BI.forall_ne fun σ₂ => ?_ + refine BI.forall_ne fun eₜ => ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + refine step_fupdN_contractive.distLater_dist fun m n_m => ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.sep_ne.ne ?_ .rfl + refine wp_ne.ne ?_ + apply HΦ m n_m @[rocq_alias wp_value_fupd'] theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : @@ -241,13 +236,12 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by intro hs hE iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE - rw (occs := [1]) [rw_iProp wp_unfold] - rw (occs := [1]) [rw_iProp wp_unfold] + rw [rw_iProp wp_unfold, rw_iProp wp_unfold] iintro H HΦ dsimp only [wp.pre] match toVal e with | none => - dsimp + dsimp only iintro %σ₁ %ns %obs %obs' %nt Hσ imod fupd_mask_intro_subseteq hE (P := iprop(emp)) $$ [] with Hclose -- TODO: Should we add rocq_alias `fupd_mask_subseteq` to this theorem? · exact BI.intuitionistically_elim_emp @@ -279,20 +273,11 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V imodintro iassumption | some v => - dsimp + dsimp only ihave h := fupd_mask_mono hE $$ H imod h iapply HΦ $$ h - -/- -Lemma fupd_wp s E e Φ : (|={E}=> WP e @ s; E {{ Φ }}) ⊢ WP e @ s; E {{ Φ }}. -Proof. - rewrite wp_unfold /wp_pre. iIntros "H". destruct (to_val e) as [v|] eqn:?. - { by iMod "H". } - iIntros (σ1 ns κ κs nt) "Hσ1". iMod "H". by iApply "H". -Qed. --/ theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by simp only [rw_iProp wp_unfold] @@ -337,25 +322,24 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro ihave aux := H $$ %e2 %σ2 %efs %Hstep Hcred iapply step_fupdN_wand $$ aux iintro >(⟨Hσ,H,Hefs⟩) + have Hatomic := ι.atomic Hstep cases s with -- TODO: Example of place where `match` is worse than `cases` | NotStuck => simp only [rw_iProp wp_unfold] - simp only [wp.pre] - have := (ι.atomic _ _ _ _ _ Hstep) - simp at this + dsimp only [wp.pre] at Hatomic ⊢ match h₂ : toVal e2 with | some v2 => icases H with > > $ iframe | none => - simp + simp only [Stuckness.MaybeReducible] icases H $$ %σ2 %(ns +1) %([]) %_ %(efs.length +nt) [Hσ] with >⟨%h, _⟩ · exact .rfl - nomatch (Language.not_reducible_iff_irreducible.mpr (ι.atomic _ _ _ _ _ Hstep)) h + nomatch (Language.not_reducible_iff_irreducible.mpr Hatomic) h | MaybeStuck => - have ⟨v, h⟩ := Option.isSome_iff_exists.mp (ι.atomic _ _ _ _ _ Hstep) + have ⟨v, h⟩ := Option.isSome_iff_exists.mp (ι.atomic Hstep) obtain ⟨rfl⟩ := (ToVal.coe_of_toVal_eq_some h) - istop; refine (BI.sep_mono (BI.sep_mono .rfl wp_value_fupd'.1) .rfl).trans ?_; refine BI.Entails.trans ?_ (BIFUpdate.mono <| (BI.sep_mono .rfl (BI.sep_mono wp_value_fupd'.2 .rfl ))); iintro ⟨⟨Hσ,H⟩,Hefs⟩ + simp only [rw_iProp wp_value_fupd'] imod H with > H iframe @@ -376,7 +360,7 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro 2. It uses some ghost state wired up to the interpretation to know that `ns = k + m`, and update the state interpretation to `stateInterp _ m _ _` - 3. _After_ `e` has finally stepped, we get `numLatersPerStep k` later credits + 3. _After_ `e` has finally stepped, we get `ι.numLatersPerStep k` later credits that we can use to prove `P` in the postcondition, and we have to update the state interpretation from `stateInterp _ (m+1) _ _` to `stateInterp _ (ns+1) _ _` again @@ -384,11 +368,11 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro -/ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} : toVal e = none → - (∀ m k, numLatersPerStep m + numLatersPerStep k ≤ numLatersPerStep (m + k)) → + (∀ m k, ι.numLatersPerStep m + ι.numLatersPerStep k ≤ ι.numLatersPerStep (m + k)) → (∀ (σ₁ : State) ns obs nt, stateInterp σ₁ ns obs nt ={E}=∗ ∃ k m, stateInterp σ₁ m obs nt ∗ ⌜ns = m + k⌝ ∗ ( - ∀ nt (σ₂: State) obs, £ (numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ + ∀ nt (σ₂: State) obs, £ (ι.numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ stateInterp σ₂ (ns+1) obs nt ∗ P)) ⊢ WP e @ s ; E {{ v, iprop(P ={E}=∗ Φ v) }} -∗ WP e @ s ; E {{ Φ }} := by @@ -401,7 +385,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ imodintro iintro %e₂ %σ₂ %efs %Hstep Hc - simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split)] + simp only [rw_iProp lc_split] icases Hc with ⟨Hc,Hone⟩ ihave Hc := lc_weaken _ (Htri m k) $$ Hc istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨⟨Hpost,Hwp⟩,Hone⟩,Hc⟩ @@ -415,7 +399,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} · simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] exact .rfl iapply step_fupd_wand $$ Hwp; iintro Hwp - iapply step_fupdN_le (n := numLatersPerStep m) (by grind only) (Std.LawfulSet.subset_refl) + iapply step_fupdN_le (n := ι.numLatersPerStep m) (by grind only) (Std.LawfulSet.subset_refl) iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ icases Hpost $$ Hk SI with >⟨$, HP⟩ imodintro @@ -429,7 +413,7 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr ∀ {n}, -- TODO: This was written as an ∧ in Iris Rocq. I've separated it because it doesn't seem like -- icases is able to handle ∧ expressions. - (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ numLatersPerStep ns + 1⌝) → + (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ ι.numLatersPerStep ns + 1⌝) → (|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ WP e @ s ; E2 {{ v, iprop(P ={E1}=∗ Φ v)}} ⊢ WP e @ s ; E1 {{ Φ }} := by @@ -447,10 +431,9 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr iintro ⟨Hp,Hwp⟩ simp only [wp.pre, toVal_e] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ - if Hn : n ≤ numLatersPerStep ns then + if Hn : n ≤ ι.numLatersPerStep ns then imod Hp imod Hwp $$ Hσ₁ with ⟨$, H⟩ - -- #check BIFUpdate.subset iintro !> %e₂ %σ₂ %efs %Hstep Hcred icases H $$ %_ %_ %_ %Hstep Hcred with H simp only [Nat.repeat] @@ -459,7 +442,7 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr imod H; imod Hp imodintro clear interp - generalize numLatersPerStep ns = n0 at * + generalize ι.numLatersPerStep ns = n0 at * induction n generalizing n0 with | zero => iapply step_fupdN_wand $$ H @@ -607,7 +590,7 @@ variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val a premise. -/ theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → - (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (numLatersPerStep ns)+1⌝) → + (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) → ((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ WP e @ s; E₂ {{ v, iprop(P ={E₁}=∗ Φ v) }}) -∗ WP e @ s; E₁ {{ Φ }} := by @@ -619,13 +602,13 @@ theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from Std.LawfulSet.diff_subset_left) with H imod Hp imod H with toClear; iclear toClear - simp [show E₁ \ (E₁ \ E₂) = E₂ from Std.LawfulSet.diff_self_diff_of_subset E₂E₁] + simp only [show E₁ \ (E₁ \ E₂) = E₂ from Std.LawfulSet.diff_self_diff_of_subset E₂E₁] imodintro iapply step_fupdN_wand $$ Hp; iintro H iapply fupd_mask_frame (Std.LawfulSet.empty_subset) imod H imodintro - simp only [Std.LawfulSet.diff_empty, ←Std.LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] + simp [Std.LawfulSet.diff_empty, ←Std.LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in theorem wp_step_fupd : @@ -636,8 +619,7 @@ theorem wp_step_fupd : iapply wp_step_fupdN_strong (n := 1) toVal_e E₂E₁ (by intros; iintro H refine .trans ?_ <| fupd_mask_intro_discard (Std.LawfulSet.empty_subset) - simp only [Nat.le_add_left, BI.true_intro] - -- TODO: Maybe it's useful to have `_ ={E,E'}=∗ True ↔ True` in the simp set (Is this even true? maybe `E' ⊆ E`) + simp [Nat.le_add_left, BI.true_intro] ) $$ [-] iframe H imod HR @@ -715,8 +697,6 @@ variable [ι : IrisGS_gen hlc Expr GF] variable {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ Ψ : Val → IProp GF} {P Q R : IProp GF} --- TODO: Add priorities - instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : -- TODO: I didn't move over the `FrameInstantiateExistDisabled` constant. Ask if it's necessary. Frame p R (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Ψ }}) where From 6fb81d32f8a149ff330a127eac9a8128ad0b8e2b Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Tue, 19 May 2026 18:01:10 +0200 Subject: [PATCH 60/76] pass over BI --- Iris/Iris/Algebra/OFE.lean | 7 +- Iris/Iris/BI/Updates.lean | 42 +- Iris/Iris/BI/WeakestPre.lean | 509 +--------------------- Iris/Iris/HeapLang/Notation.lean | 7 + Iris/Iris/Instances/Lib/LaterCredits.lean | 2 +- Iris/Iris/ProgramLogic/Language.lean | 76 ---- Iris/Iris/ProgramLogic/WeakestPre.lean | 35 +- Iris/Iris/Tests/Language.lean | 80 ++++ Iris/Iris/Tests/WP.lean | 206 +++++++++ 9 files changed, 334 insertions(+), 630 deletions(-) create mode 100644 Iris/Iris/Tests/Language.lean create mode 100644 Iris/Iris/Tests/WP.lean diff --git a/Iris/Iris/Algebra/OFE.lean b/Iris/Iris/Algebra/OFE.lean index 3b86d527..d1c5bbd2 100644 --- a/Iris/Iris/Algebra/OFE.lean +++ b/Iris/Iris/Algebra/OFE.lean @@ -275,12 +275,13 @@ infixr:25 " -c> " => ContractiveHom instance [OFE α] [OFE β] : CoeFun (α -c> β) (fun _ => α → β) := ⟨fun x => x.toHom.f⟩ instance [OFE α] [OFE β] (f : α -c> β) : Contractive f := f.contractive --- TODO: I propose this definition, to allow doing `f.toContractiveHom` to infer the necessary --- instances to make `f : α → β` into a `α -c> β`. -def _root_.Function.toContractiveHom (f : α → β)[OFE α][OFE β][ι : OFE.Contractive f] : α -c> β where +def _root_.Function.toContractiveHom (f : α → β) [OFE α] [OFE β] [ι : OFE.Contractive f] : α -c> β where f := f contractive := ι +@[simp] theorem _root_.Function.toContractiveHom_apply {f : α → β} [OFE α] [OFE β] [ι : OFE.Contractive f] {x} : + f.toContractiveHom x = f x := by rfl + theorem InvImage.equivalence {α : Sort u} {β : Sort v} {r : β → β → Prop} {f : α → β} (H : Equivalence r) : Equivalence (InvImage r f) where refl _ := H.refl _ diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index ac27c72a..19fb8c61 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -317,41 +317,27 @@ theorem step_fupdN_contractive {E1 E2 : CoPset} {n : Nat} [ι : BILaterContracti intro i x y xy_i induction n with | zero => - dsimp only [Nat.repeat] - apply BIFUpdate.ne.ne - apply ι.distLater_dist - intros j ji - apply BIFUpdate.ne.ne - apply xy_i j ji + exact BIFUpdate.ne.ne (ι.distLater_dist (fun j ji => BIFUpdate.ne.ne (xy_i j ji))) | succ n IH => - dsimp only [Nat.repeat] - apply BIFUpdate.ne.ne - apply later_ne.ne - apply BIFUpdate.ne.ne - assumption + exact BIFUpdate.ne.ne (later_ne.ne (BIFUpdate.ne.ne IH)) theorem step_fupdN_ne {E1 E2 : CoPset} {n : Nat} : OFE.NonExpansive (iprop(|={E1}[E2]▷=>^[n] · : PROP)) where ne := by intro i x y xy_i induction n with - | zero => simp only [Nat.repeat, xy_i] + | zero => simp [Nat.repeat, xy_i] | succ n IH => - dsimp only [Nat.repeat] - apply BIFUpdate.ne.ne - apply later_ne.ne - apply BIFUpdate.ne.ne - assumption + exact BIFUpdate.ne.ne (later_ne.ne (BIFUpdate.ne.ne IH)) +@[rocq_alias step_fupdN_wand] theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : (|={Eo}[Ei]▷=>^[n] P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=>^[n] Q) := by refine wand_intro' ?_ induction n with | zero => - dsimp [Nat.repeat] exact wand_elim_l | succ n IH => - dsimp [Nat.repeat] calc iprop((P -∗ Q) ∗ |={Eo,Ei}=> ▷ |={Ei,Eo}=> _) _ ⊢ |={Eo,Ei}=> (P -∗ Q) ∗ ▷ |={Ei,Eo}=> _ := (fupd_frame_l ..) _ ⊢ |={Eo,Ei}=> (▷ (P -∗ Q)) ∗ ▷ |={Ei,Eo}=> _ := mono (sep_mono (later_intro) .rfl) @@ -359,10 +345,12 @@ theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> ((P -∗ Q) ∗ _) := mono (later_mono (fupd_frame_l ..)) _ ⊢ |={Eo,Ei}=> ▷ |={Ei,Eo}=> _ := mono (later_mono (mono IH)) +@[rocq_alias step_fupd_wand] theorem step_fupd_wand {Eo Ei : CoPset} {P Q : PROP} : (|={Eo}[Ei]▷=> P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=> Q) := by exact step_fupdN_wand (n := 1) +@[rocq_alias step_fupd_mask_mono] theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : Ei₂ ⊆ Ei₁ → Eo₁ ⊆ Eo₂ → @@ -379,11 +367,11 @@ theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : refine (sep_mono (fupd_mask_intro_subseteq Ei₂_Ei₁) .rfl).trans ?_ refine fupd_frame_r.trans ?_ - apply mono + refine mono ?_ refine (sep_mono later_intro .rfl).trans ?_ refine later_sep.2.trans ?_ - apply later_mono + refine later_mono ?_ refine fupd_frame_r.trans ?_ refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Ei₁)) @@ -392,8 +380,9 @@ theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Eo₁)) refine fupd_frame_r.trans ?_ - refine mono emp_sep.1 + exact mono emp_sep.1 +@[rocq_alias step_fupd_intro] theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} : Ei ⊆ Eo → ▷ P ⊢ |={Eo}[Ei]▷=> P := by @@ -403,15 +392,14 @@ theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} : _ ⊢ |={Ei}[Ei]▷=> P := mono <| later_mono fupd_intro _ ⊢ |={Eo}[Ei]▷=> P := step_fupd_mask_mono (subset_refl) Ei_Eo -theorem step_fupdN_le {n m : Nat}{Eo Ei : CoPset}{P : PROP} : +@[rocq_alias step_fupdN_le] +theorem step_fupdN_le {n m : Nat} {Eo Ei : CoPset} {P : PROP} : n ≤ m → Ei ⊆ Eo → (|={Eo}[Ei]▷=>^[n] P) ⊢ |={Eo}[Ei]▷=>^[m] P | .refl, _ => .rfl - | .step (m := m) n_m, Ei_Eo => by - refine step_fupdN_le n_m Ei_Eo |>.trans ?_ - refine later_intro.trans ?_ - apply step_fupd_intro Ei_Eo + | .step (m := m) n_m, Ei_Eo => + step_fupdN_le n_m Ei_Eo |>.trans (later_intro.trans (step_fupd_intro Ei_Eo)) @[rocq_alias step_fupd_fupd] theorem step_fupd_fupd {Eo Ei : CoPset} {P : PROP} : (|={Eo}[Ei]▷=> P) ⊣⊢ (|={Eo}[Ei]▷=> |={Eo}=> P) := diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 98cd05ca..00645c0c 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -49,7 +49,6 @@ class Wp (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) where class TotalWP (PROP Expr) (Val : outParam (Type _)) (A : Type _) where totalWp : A → CoPset → Expr → (Val → PROP) → PROP - syntax wpExpr := term:max (" @ " term:max (" ; " term:max) <|> ((" ? ")? )) <|> (" ? ")? @@ -60,279 +59,11 @@ syntax term : wpPostcondInner declare_syntax_cat wpPostcond syntax " {{ " wpPostcondInner " }} " : wpPostcond syntax " [{ " wpPostcondInner " }] " : wpPostcond -syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond -- Are spaces outside of parens used in the pp? +syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond syntax " 〖 " wpPostcondInner " 〗 " : wpPostcond syntax (name := wp) "WP " wpExpr wpPostcond : term -syntax texanPostcond := (ident+ ", ")? " RET " term:min "; " term - -syntax (name := texanTriple) "{{{ " term " }}} " wpExpr " {{{ " texanPostcond " }}}" : term - -/- This section checks whether the syntax is recognized correctly for all combinations -/ -section testNotation - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E {{ Φ }} --/ -#guard_msgs in #check_failure WP e @ s ; E {{ Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E {{ Φ }} --/ -#guard_msgs in #check_failure WP e @ E {{ Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? {{ Φ }} --/ -#guard_msgs in #check_failure WP e @ E ? {{ Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e {{ Φ }} --/ -#guard_msgs in #check_failure WP e {{ Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? {{ Φ }} --/ -#guard_msgs in #check_failure WP e ? {{ Φ }} - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E {{ v, Φ }} --/ -#guard_msgs in #check_failure WP e @ s ; E {{v, Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E {{ v, Φ }} --/ -#guard_msgs in #check_failure WP e @ E {{v, Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? {{ v, Φ }} --/ -#guard_msgs in #check_failure WP e @ E ? {{v, Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e {{ v, Φ }} --/ -#guard_msgs in #check_failure WP e {{v, Φ }} -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? {{ v, Φ }} --/ -#guard_msgs in #check_failure WP e ? {{v, Φ }} - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E ⦃ Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ s ; E ⦃ Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ⦃ Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ E ⦃ Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? ⦃ Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ E ? ⦃ Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ⦃ Φ ⦄ --/ -#guard_msgs in #check_failure WP e ⦃ Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? ⦃ Φ ⦄ --/ -#guard_msgs in #check_failure WP e ? ⦃ Φ ⦄ - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E ⦃ v, Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ s ; E ⦃v, Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ⦃ v, Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ E ⦃v, Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? ⦃ v, Φ ⦄ --/ -#guard_msgs in #check_failure WP e @ E ? ⦃v, Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ⦃ v, Φ ⦄ --/ -#guard_msgs in #check_failure WP e ⦃v, Φ ⦄ -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? ⦃ v, Φ ⦄ --/ -#guard_msgs in #check_failure WP e ? ⦃v, Φ ⦄ - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E [{ Φ }] --/ -#guard_msgs in #check_failure WP e @ s ; E [{ Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E [{ Φ }] --/ -#guard_msgs in #check_failure WP e @ E [{ Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? [{ Φ }] --/ -#guard_msgs in #check_failure WP e @ E ? [{ Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e [{ Φ }] --/ -#guard_msgs in #check_failure WP e [{ Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? [{ Φ }] --/ -#guard_msgs in #check_failure WP e ? [{ Φ }] - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E [{ v, Φ }] --/ -#guard_msgs in #check_failure WP e @ s ; E [{v, Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E [{ v, Φ }] --/ -#guard_msgs in #check_failure WP e @ E [{v, Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? [{ v, Φ }] --/ -#guard_msgs in #check_failure WP e @ E ? [{v, Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e [{ v, Φ }] --/ -#guard_msgs in #check_failure WP e [{v, Φ }] -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? [{ v, Φ }] --/ -#guard_msgs in #check_failure WP e ? [{v, Φ }] - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E 〖 Φ 〗 --/ -#guard_msgs in #check_failure WP e @ s ; E 〖 Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E 〖 Φ 〗 --/ -#guard_msgs in #check_failure WP e @ E 〖 Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? 〖 Φ 〗 --/ -#guard_msgs in #check_failure WP e @ E ? 〖 Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e 〖 Φ 〗 --/ -#guard_msgs in #check_failure WP e 〖 Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? 〖 Φ 〗 --/ -#guard_msgs in #check_failure WP e ? 〖 Φ 〗 - -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ s ; E 〖 v, Φ 〗 --/ -#guard_msgs in #check_failure WP e @ s ; E 〖v, Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E 〖 v, Φ 〗 --/ -#guard_msgs in #check_failure WP e @ E 〖v, Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e @ E ? 〖 v, Φ 〗 --/ -#guard_msgs in #check_failure WP e @ E ? 〖v, Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e 〖 v, Φ 〗 --/ -#guard_msgs in #check_failure WP e 〖v, Φ 〗 -/-- -info: elaboration function for `Iris.wp` has not been implemented - WP e ? 〖 v, Φ 〗 --/ -#guard_msgs in #check_failure WP e ? 〖v, Φ 〗 - -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ s ; E {{{ x y, RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ x y , RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ E {{{ x y, RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ E {{{ x y , RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ E ? {{{ x y, RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ x y , RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e {{{ x y, RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e {{{ x y , RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e ? {{{ x y, RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e ? {{{ x y , RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ s ; E {{{ RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ s ; E {{{ RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ E {{{ RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ E {{{ RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e @ E ? {{{ RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e @ E ? {{{ RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e {{{ RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e {{{ RET pat ; Q }}} -/-- -info: elaboration function for `Iris.texanTriple` has not been implemented - {{{ P }}} e ? {{{ RET pat; Q }}} --/ -#guard_msgs in #check_failure {{{ P }}} e ? {{{ RET pat ; Q }}} - -end testNotation - open Lean in meta def parseWpExpr : Lean.TSyntax ``wpExpr → Lean.MacroM (TSyntax `term × TSyntax `term × TSyntax `term) := fun | `(wpExpr| $e @ $s ; $E) => @@ -350,8 +81,8 @@ meta def parseWpExpr : Lean.TSyntax ``wpExpr → Lean.MacroM (TSyntax `term × T open Lean in meta def parseWpPostcondInner (stx : TSyntax `wpPostcondInner) : MacroM (TSyntax `term) := do match stx with - | `(wpPostcondInner| $v:ident, $Φ:term) => `(fun $v => $Φ) - | `(wpPostcondInner| $Φ:term) => return Φ + | `(wpPostcondInner| $v:ident, $Φ:term) => `(fun $v => iprop($Φ)) + | `(wpPostcondInner| $Φ:term) => return iprop(Φ) | _ => Macro.throwUnsupported open Lean in @@ -377,161 +108,9 @@ meta def wpMacro : Lean.Macro := fun stx => do `(Wp.wp $s $E $e $Φ) | _ => Lean.Macro.throwUnsupported --- syntax (name := underExtraBinders) "underExtraBinders(" term:min ")" : term --- syntax (name := sourceExtraBinders) "sourceExtraBinders(" term ")" : term - --- private meta partial def findSource : Syntax → Option Syntax --- | .missing => none --- | .node _ ``sourceExtraBinders args => do --- let #[_, inner, _] := args | none --- return inner --- | .node _ _ args => do --- let #[res] := args.filterMap findSource | none --- return res --- | .ident _ _ _ _ => none --- | .atom _ _ => none - --- private meta partial def collectUnresolvedIds : Syntax → Elab.Term.TermElabM (Array Ident) --- | .missing => return {} --- | .node _ _ args => do --- args.flatMapM collectUnresolvedIds --- | id@(.ident ..) => do --- try --- let _ ← Elab.Term.elabIdent id .none --- return {} --- catch --- | .error _ md => --- let msg ← md.toString --- if msg.startsWith "Unknown identifier" then --- return #[⟨id⟩] --- else --- return {} --- | _ => return {} --- | .atom _ _ => --- return {} - --- elab "sourceExtraBinders(" t:term ")" : term => Elab.Term.elabTerm t .none - --- @[term_elab underExtraBinders] --- meta def elabUnderExtraBinders: Elab.Term.TermElab := fun stx ty? => do --- match stx with --- | `(underExtraBinders( $t:term ) ) => --- if let .some s := findSource t then --- -- logInfo s!"Found! {s.getArgs.size} {repr s}" --- let ids ← collectUnresolvedIds s --- logInfo s!"Collected identifiers {ids}" --- if ids.isEmpty then --- Elab.Term.elabTerm t ty? --- else --- let stx ← `(∀ $ids*, $t) --- Elab.Term.elabTerm stx ty? --- else --- Elab.Term.elabTerm t ty? --- | _ => unreachable! - --- macro_rules --- | `(iprop(underExtraBinders($t))) => `(underExtraBinders(iprop($t))) --- | `(iprop(sourceExtraBinders($t))) => `(sourceExtraBinders(iprop($t))) - --- @[macro texanTriple] --- meta def wpTexanTriple : Lean.Macro --- | `({{{ $P:term }}} $expr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q }}}) => do --- let k ← match xs with --- | some xs => `(∀ $xs*, underExtraBinders($Q → Φ sourceExtraBinders($pat))) --- | none => `($Q:term → Φ $pat) --- `(iprop(∀ Φ, $P -∗ ▷ $k -∗ (WP $expr {{ Φ }}))) --- | _ => Lean.Macro.throwUnsupported - -@[macro texanTriple] -meta def wpTexanTriple : Lean.Macro - | `({{{ $P:term }}} $wpExpr {{{ $[$[$xs:ident]* ,]? RET $pat ; $Q:term }}}) => do - -- It seems like `∀ $xs*, Ψ` does not translate to `Ψ`. - let k ← match xs with - | some xs => `(∀ $xs*, $Q:term → Φ $pat) - | none => `($Q:term → Φ $pat) - `(iprop(∀ Φ, $P -∗ ▷ $k -∗ (WP $wpExpr {{ Φ }}))) - | _ => Lean.Macro.throwUnsupported - -section testElab -set_option linter.unusedVariables false - -variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) -variable [ι : Iris.BI PROP] -variable [Wp PROP Expr Val A] -variable [Wp PROP Expr Val Stuckness] -variable [TotalWP PROP Expr Val A] -variable [TotalWP PROP Expr Val Stuckness] - -variable (e : Expr)(s : A)(E : CoPset) -variable (Φ : PROP) - -/-- info: Wp.wp s E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ s ; E {{v, Φ }} -/-- info: Wp.wp Stuckness.NotStuck E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ E {{ v, Φ }} -/-- info: Wp.wp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ -#guard_msgs in #check WP e {{v, Φ }} -/-- info: Wp.wp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ -#guard_msgs in #check WP e ? {{v, Φ }} - -/-- info: TotalWP.totalWp s E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ s ; E [{v, Φ }] -/-- info: TotalWP.totalWp Stuckness.NotStuck E e fun v => Φ : PROP -/ -#guard_msgs in #check WP e @ E [{ v, Φ }] -/-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e fun v => Φ : PROP -/ -#guard_msgs in #check WP e [{v, Φ }] -/-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e fun v => Φ : PROP -/ -#guard_msgs in #check WP e ? [{v, Φ }] - -variable (Φ : Val → PROP) - -/-- info: Wp.wp s E e Φ : PROP -/ -#guard_msgs in #check WP e @ s ; E {{ Φ }} -/-- info: Wp.wp Stuckness.NotStuck E e Φ : PROP -/ -#guard_msgs in #check WP e @ E {{ Φ }} -/-- info: Wp.wp Stuckness.NotStuck ⊤ e Φ : PROP -/ -#guard_msgs in #check WP e {{ Φ }} -/-- info: Wp.wp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ -#guard_msgs in #check WP e ? {{ Φ }} - -/-- info: TotalWP.totalWp s E e Φ : PROP -/ -#guard_msgs in #check WP e @ s ; E [{ Φ }] -/-- info: TotalWP.totalWp Stuckness.NotStuck E e Φ : PROP -/ -#guard_msgs in #check WP e @ E [{ Φ }] -/-- info: TotalWP.totalWp Stuckness.NotStuck ⊤ e Φ : PROP -/ -#guard_msgs in #check WP e [{ Φ }] -/-- info: TotalWP.totalWp Stuckness.MaybeStuck ⊤ e Φ : PROP -/ -#guard_msgs in #check WP e ? [{ Φ }] - -variable (P : PROP) (Q : PROP) (v : Nat) (s : Stuckness) [Wp PROP Expr Nat Stuckness] - --- Can we do away with the `x .. y` by obtaining the identifiers directly from `RET pat`? - -/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp s E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ s ; E {{{ x y , RET (x+1) ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ E {{{ x y , RET (x+1) ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ E ? {{{ x y , RET (x+1) ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e {{{ x y , RET (x+1) ; Q }}} -/-- info: iprop(∀ Φ, P -∗ (▷ ∀ x y, Q → Φ (x + 1)) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e ? {{{ x y , RET (x+1) ; Q }}} - -/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp s E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ s ; E {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ E {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck E e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e @ E ? {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.NotStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e {{{ RET 0 ; Q }}} -/-- info: iprop(∀ Φ, P -∗ ▷ (Q → Φ 0) -∗ Wp.wp Stuckness.MaybeStuck ⊤ e Φ) : PROP -/ -#guard_msgs in #check {{{ P }}} e ? {{{ RET 0 ; Q }}} - -end testElab - meta def unexpandWpPostcondInner : TSyntax `term → PrettyPrinter.UnexpandM (TSyntax `wpPostcondInner) + | `(fun $v:ident => iprop($Φ:term)) => `(wpPostcondInner|$v:ident, $Φ:term) + | `(iprop($Φ:term)) => `(wpPostcondInner| $Φ:term) | `(fun $v:ident => $Φ:term) => `(wpPostcondInner|$v:ident, $Φ:term) | `($Φ:term) => `(wpPostcondInner| $Φ:term) @@ -559,81 +138,3 @@ meta def unexpanderTotalWp : PrettyPrinter.Unexpander let wpPostcondInner ← unexpandWpPostcondInner Φ `(WP $wpExpr [{ $wpPostcondInner }]) | _ => throw () - -section testUnexpand -set_option linter.unusedVariables false - -variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) -variable [Wp PROP Expr Val A] -variable [Wp PROP Expr Val Stuckness] -variable [TotalWP PROP Expr Val A] -variable [TotalWP PROP Expr Val Stuckness] - -variable (e : Expr)(s : A)(E : CoPset) -variable (Φ : PROP) - -/-- info: WP e @ s ; E {{ v, Φ }} : PROP -/ -#guard_msgs in #check WP e @ s ; E {{v, Φ }} -/-- info: WP e @ E {{ v, Φ }} : PROP -/ -#guard_msgs in #check WP e @ E {{ v, Φ}} -/-- info: WP e @ E ? {{ v, Φ }} : PROP -/ -#guard_msgs in #check WP e @ E ? {{ v, Φ}} -/-- info: WP e {{ v, Φ }} : PROP -/ -#guard_msgs in #check WP e {{v, Φ }} -/-- info: WP e ? {{ v, Φ }} : PROP -/ -#guard_msgs in #check WP e ? {{v, Φ }} - -/-- info: WP e @ s ; E [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e @ s ; E [{v, Φ }] -/-- info: WP e @ E [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e @ E [{ v, Φ}] -/-- info: WP e @ E ? [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e @ E ? [{ v, Φ}] -/-- info: WP e [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e [{v, Φ }] -/-- info: WP e ? [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e ? [{v, Φ }] - -variable (Φ : Val → PROP) - -/-- info: WP e @ s ; E {{ Φ }} : PROP -/ -#guard_msgs in #check WP e @ s ; E {{ Φ }} -/-- info: WP e @ E {{ Φ }} : PROP -/ -#guard_msgs in #check WP e @ E {{ Φ}} -/-- info: WP e @ E ? {{ Φ }} : PROP -/ -#guard_msgs in #check WP e @ E ? {{ Φ}} -/-- info: WP e {{ Φ }} : PROP -/ -#guard_msgs in #check WP e {{ Φ }} -/-- info: WP e ? {{ Φ }} : PROP -/ -#guard_msgs in #check WP e ? {{ Φ }} - -/-- info: WP e @ s ; E [{ Φ }] : PROP -/ -#guard_msgs in #check WP e @ s ; E [{ Φ }] -/-- info: WP e @ E [{ Φ }] : PROP -/ -#guard_msgs in #check WP e @ E [{ Φ}] -/-- info: WP e @ E ? [{ Φ }] : PROP -/ -#guard_msgs in #check WP e @ E ? [{ Φ}] -/-- info: WP e [{ Φ }] : PROP -/ -#guard_msgs in #check WP e [{ Φ }] -/-- info: WP e ? [{ Φ }] : PROP -/ -#guard_msgs in #check WP e ? [{ Φ }] - -end testUnexpand - -/- -Notations used in Rocq -For Texan triples, more of the same - -{{{ P }}} e @ s ; E {{{ RET pat ; Q }}} -{{{ P ]}} e @ E {{{ RET pat ; Q }}} -{{{ P ]}} e @ E ? {{{ RET pat ; Q }}} -{{{ P ]}} e {{{ RET pat ; Q }}} -{{{ P ]}} e ? {{{ RET pat ; Q }}} - -{{{ P }}} e @ s ; E {{{ x .. y , RET pat ; Q }}} -{{{ P }}} e @ E {{{ x .. y , RET pat ; Q }}} -{{{ P }}} e @ E ? {{{ x .. y , RET pat ; Q }}} -{{{ P }}} e {{{ x .. y , RET pat ; Q }}} -{{{ P }}} e ? {{{ x .. y , RET pat ; Q }}} - --/ diff --git a/Iris/Iris/HeapLang/Notation.lean b/Iris/Iris/HeapLang/Notation.lean index 09625dd2..3eb35d99 100644 --- a/Iris/Iris/HeapLang/Notation.lean +++ b/Iris/Iris/HeapLang/Notation.lean @@ -6,6 +6,7 @@ Authors: Michael Sammler module public import Iris.HeapLang.Syntax +public meta import Lean.PrettyPrinter.Parenthesizer public meta section namespace Iris.HeapLang @@ -149,6 +150,12 @@ syntax:100 "fork(" hl_exp ")" : hl_exp /-- assert -/ syntax:100 "assert(" hl_exp ")" : hl_exp +open Lean.PrettyPrinter.Parenthesizer in +@[category_parenthesizer hl_exp] +def hl_exp.parenthesizer : CategoryParenthesizer := fun prec => do + maybeParenthesize `hl_exp false (fun stx => Unhygienic.run `(hl_exp|($(⟨stx⟩)))) prec <| + parenthesizeCategoryCore `hl_exp prec + partial def unpackHLExp [Monad m] [MonadRef m] [MonadQuotation m] : Term → m (TSyntax `hl_exp) | `(hl($e)) => `(hl_exp|$e) | `($t) => `(hl_exp|{$t}) diff --git a/Iris/Iris/Instances/Lib/LaterCredits.lean b/Iris/Iris/Instances/Lib/LaterCredits.lean index 9f215074..f9a0e1dc 100644 --- a/Iris/Iris/Instances/Lib/LaterCredits.lean +++ b/Iris/Iris/Instances/Lib/LaterCredits.lean @@ -69,7 +69,7 @@ section Operations variable {GF : BundledGFunctors} [LC : LcGS GF] theorem lc_split {n m} : £ (n + m) ⊣⊢@{IProp GF} £ n ∗ £ m := - -- -- FIXME: Timeout on iOwn_op. Why? + -- FIXME: Timeout on iOwn_op. Why? iOwn_op (E := LC.lc_elem) (a1 := ◯ n) (a2 := ◯ m) @[rocq_alias lc_zero] diff --git a/Iris/Iris/ProgramLogic/Language.lean b/Iris/Iris/ProgramLogic/Language.lean index 25f3b0c6..0d3ff427 100644 --- a/Iris/Iris/ProgramLogic/Language.lean +++ b/Iris/Iris/ProgramLogic/Language.lean @@ -5,12 +5,8 @@ Released under Apache 2.0 license as described in the file LICENSE. module meta import Iris.Std.RocqPorting -public import Iris.Std.FromMathlib public import Iris.Std.Relation -public import Iris.Std.List public import Iris.BI.WeakestPre -public meta import Lean.PrettyPrinter.Delaborator -public import Batteries.Data.List.Basic #rocq_ignore LanguageMixin "This feature was implemented differently using typeclasses" #rocq_ignore language "This feature was implemented differently using typeclasses" @@ -489,75 +485,3 @@ theorem erasedStep_pureSteps {t₁ t₂ t₃ : List Expr} {σ₁ σ₂ : State} exact Std.List.Forall₂.append ps_ps₃ <| .cons lastSteps ss_ss₃ end Language - -section test -open Language - -section notations - -/-- -info: (e, σ) --> (e, σ, []) : Prop --/ -#guard_msgs in -variable (e : Expr) (σ : State) (obs : Obs) [PrimStep Expr State Obs] in -#check (PrimStep.primStep (e, σ) obs (e,σ,[])) - -/-- -info: (t, σ) -->ₜₚ (t, σ) : Prop --/ -#guard_msgs in -variable (t : List Expr) (σ : State) (obs : List Obs) [Language Expr State Obs Val] in -#check (Language.Step (t, σ) obs (t,σ)) - -/-- -info: (t, σ) -->ₜₚ^[0] (t, σ) : Prop --/ -#guard_msgs in -variable (t : List Expr) (σ : State) (obs : List Obs) [Language Expr State Obs Val] in -#check (Language.NSteps 0 (t, σ) obs (t,σ)) - -/-- -info: (t, σ) -·->ₜₚ (t, σ) : Prop --/ -#guard_msgs in -variable (t : List Expr) (σ : State) [Language Expr State Obs Val] in -#check (ErasedStep (t, σ) (t,σ)) - -/-- -info: e -ᵖ-> e : Prop --/ -#guard_msgs in -variable (e : Expr) [Language Expr State Obs Val] in -#check (PurePrimStep e e) - -/-- -info: e -ᵖ->^[0] e : Prop --/ -#guard_msgs in -variable (e : Expr) [Language Expr State Obs Val] in -#check (Relation.Iterate PurePrimStep 0 e e) - -/-- -info: e -ᵖ->* e : Prop --/ -#guard_msgs in -variable (e : Expr) [Language Expr State Obs Val] in -#check (Relation.ReflTransGen PurePrimStep e e) - -/-- -info: e -ᵖ->* e : Prop --/ -#guard_msgs in -variable (e : Expr) [Language Expr State Obs Val] in -#check (Relation.ReflTransGen PurePrimStep e e) - -/-- -info: t -ᵖ->ₜₚ* t : Prop --/ -#guard_msgs in -variable (t : List Expr) [Language Expr State Obs Val] in -#check (PureSteps t t) - -end notations - -end test diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 1a97b22a..1a3cdbf1 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -294,8 +294,7 @@ theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : iassumption theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : - -- TODO: Fix `WP` syntax so this doesn't happen. - WP e @ s ; E {{v, iprop(|={E}=> Φ v) }} ⊢ WP e @ s ; E {{ Φ }} := by + WP e @ s ; E {{v, |={E}=> Φ v }} ⊢ WP e @ s ; E {{ Φ }} := by iintro h iapply wp_strong_mono (Std.IsPreorder.le_refl _) Std.LawfulSet.subset_refl $$ h iintro %v h @@ -303,7 +302,7 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} [ι : Language.Atomic ↑s e] : - (|={E1,E2}=> WP e @ s ; E2 {{v, iprop(|={E2,E1}=> Φ v) }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by + (|={E1,E2}=> WP e @ s ; E2 {{v, |={E2,E1}=> Φ v }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by simp only [rw_iProp wp_unfold] iintro H match He : toVal e with @@ -374,7 +373,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} ∃ k m, stateInterp σ₁ m obs nt ∗ ⌜ns = m + k⌝ ∗ ( ∀ nt (σ₂: State) obs, £ (ι.numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ stateInterp σ₂ (ns+1) obs nt ∗ P)) ⊢ - WP e @ s ; E {{ v, iprop(P ={E}=∗ Φ v) }} -∗ + WP e @ s ; E {{ v, P ={E}=∗ Φ v }} -∗ WP e @ s ; E {{ Φ }} := by intro h Htri simp only [rw_iProp wp_unfold] @@ -415,7 +414,7 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr -- icases is able to handle ∧ expressions. (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ ι.numLatersPerStep ns + 1⌝) → (|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ - WP e @ s ; E2 {{ v, iprop(P ={E1}=∗ Φ v)}} ⊢ + WP e @ s ; E2 {{ v, P ={E1}=∗ Φ v}} ⊢ WP e @ s ; E1 {{ Φ }} := by intro toVal_e E2_E1 n interp match n with @@ -469,10 +468,8 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr grind only theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : - -- TODO: Figure out how to make this work better. - -- 1. Get rid of parenthesis around the WP expression - -- 2. Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) - WP e @ s ; E {{v, iprop(WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }}) }} ⊢ WP (K e) @ s ; E {{ Φ }} := by + -- TODO: Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) + WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} ⊢ WP (K e) @ s ; E {{ Φ }} := by iintro H iloeb as IH generalizing %E %e %Φ rewrite (occs := [2]) [rw_iProp wp_unfold] @@ -566,7 +563,7 @@ theorem wp_value : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in -theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, iprop(R ∗ Φ v) }} := by +theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by iintro ⟨_, H⟩ iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H iframe @@ -574,7 +571,7 @@ theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, iprop(R iapply fupd_intro variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in -theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, iprop(R ∗ Φ v) }} := +theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := BI.sep_comm.1.trans wp_frame_l @@ -592,7 +589,7 @@ variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) → ((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ - WP e @ s; E₂ {{ v, iprop(P ={E₁}=∗ Φ v) }}) -∗ + WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }}) -∗ WP e @ s; E₁ {{ Φ }} := by intro toVal_e E₂E₁ Hstate iintro H @@ -613,7 +610,7 @@ theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in theorem wp_step_fupd : toVal e = none → E₂ ⊆ E₁ → - (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, iprop(P ={E₁}=∗ Φ v) }} -∗ WP e @ s; E₁ {{ Φ }} := + (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }} -∗ WP e @ s; E₁ {{ Φ }} := fun toVal_e E₂E₁=> by iintro HR H iapply wp_step_fupdN_strong (n := 1) toVal_e E₂E₁ (by @@ -628,7 +625,7 @@ theorem wp_step_fupd : variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → - (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, iprop(R ∗ Φ v) }} := by + (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ iapply wp_step_fupd toVal_e E₂E₁ $$ Hu iapply wp_mono $$ Hwp @@ -636,12 +633,12 @@ theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_r : toVal e = none → E₂ ⊆ E₁ → - WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := + WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → - (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, iprop(R ∗ Φ v) }} := by + (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ iapply wp_frame_step_l toVal_e E₂E₁ iframe @@ -653,7 +650,7 @@ theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in theorem wp_frame_step_r' : toVal e = none → E₂ ⊆ E₁ → - WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, iprop(Φ v ∗ R) }} := + WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in @@ -677,7 +674,7 @@ theorem wp_wand_r : variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ :Val → IProp GF}{R : IProp GF} in theorem wp_frame_wand : - R ⊢ WP e @ s; E {{ v, iprop(R -∗ Φ v) }} -∗ WP e @ s; E {{ Φ }} := by + R ⊢ WP e @ s; E {{ v, R -∗ Φ v }} -∗ WP e @ s; E {{ Φ }} := by iintro R Hwp iapply wp_wand $$ Hwp iintro %v H @@ -734,7 +731,7 @@ instance elimModalFupdWp_wrongMask : elim_modal := nofun instance elimModalFupdWpAtomic : - ElimModal (Language.Atomic ↑s e) p false iprop(|={E₁,E₂}=> P) P (WP e @ s ; E₁ {{ Φ }}) (WP e @ s ; E₂ {{ v, iprop(|={E₂,E₁}=> Φ v)}}) where + ElimModal (Language.Atomic ↑s e) p false iprop(|={E₁,E₂}=> P) P (WP e @ s ; E₁ {{ Φ }}) (WP e @ s ; E₂ {{ v, |={E₂,E₁}=> Φ v}}) where elim_modal := by rintro atomic; iintro ⟨H, H⟩ refine (BI.sep_mono BI.intuitionisticallyIf_elim .rfl).trans ?_ diff --git a/Iris/Iris/Tests/Language.lean b/Iris/Iris/Tests/Language.lean new file mode 100644 index 00000000..28afb5ec --- /dev/null +++ b/Iris/Iris/Tests/Language.lean @@ -0,0 +1,80 @@ +/- +Copyright (c) 2026 Fernando Leal. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +-/ +module + +public import Iris.ProgramLogic.Language + +@[expose] public section + +namespace Iris.Tests +open Iris ProgramLogic Language Notation + +/-! This section provides tests for notation used for the Language interface. -/ +section Notation + +/-- +info: (e, σ) --> (e, σ, []) : Prop +-/ +#guard_msgs in +variable (e : Expr) (σ : State) (obs : Obs) [PrimStep Expr State Obs] in +#check (PrimStep.primStep (e, σ) obs (e,σ,[])) + +/-- +info: (t, σ) -->ₜₚ (t, σ) : Prop +-/ +#guard_msgs in +variable (t : List Expr) (σ : State) (obs : List Obs) [Language Expr State Obs Val] in +#check (Language.Step (t, σ) obs (t,σ)) + +/-- +info: (t, σ) -->ₜₚ^[0] (t, σ) : Prop +-/ +#guard_msgs in +variable (t : List Expr) (σ : State) (obs : List Obs) [Language Expr State Obs Val] in +#check (Language.NSteps 0 (t, σ) obs (t,σ)) + +/-- +info: (t, σ) -·->ₜₚ (t, σ) : Prop +-/ +#guard_msgs in +variable (t : List Expr) (σ : State) [Language Expr State Obs Val] in +#check (ErasedStep (t, σ) (t,σ)) + +/-- +info: e -ᵖ-> e : Prop +-/ +#guard_msgs in +variable (e : Expr) [Language Expr State Obs Val] in +#check (PurePrimStep e e) + +/-- +info: e -ᵖ->^[0] e : Prop +-/ +#guard_msgs in +variable (e : Expr) [Language Expr State Obs Val] in +#check (Relation.Iterate PurePrimStep 0 e e) + +/-- +info: e -ᵖ->* e : Prop +-/ +#guard_msgs in +variable (e : Expr) [Language Expr State Obs Val] in +#check (FromMathlib.Relation.ReflTransGen PurePrimStep e e) + +/-- +info: e -ᵖ->* e : Prop +-/ +#guard_msgs in +variable (e : Expr) [Language Expr State Obs Val] in +#check (FromMathlib.Relation.ReflTransGen PurePrimStep e e) + +/-- +info: t -ᵖ->ₜₚ* t : Prop +-/ +#guard_msgs in +variable (t : List Expr) [Language Expr State Obs Val] in +#check (PureSteps t t) + +end Notation diff --git a/Iris/Iris/Tests/WP.lean b/Iris/Iris/Tests/WP.lean new file mode 100644 index 00000000..275e6f8d --- /dev/null +++ b/Iris/Iris/Tests/WP.lean @@ -0,0 +1,206 @@ +/- +Copyright (c) 2026 Fernando Leal. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +-/ +module + +public import Iris.ProgramLogic.WeakestPre +public import Iris.HeapLang + +@[expose] public section + +namespace Iris.Tests +open Iris + +/- This section checks whether the syntax is recognized correctly for all combinations -/ +section TestWP +set_option linter.unusedVariables false + +variable (PROP Expr : Type _) (Val : outParam (Type _)) (A : Type _) +variable [Wp PROP Expr Val A] +variable [Wp PROP Expr Val Stuckness] +variable [TotalWP PROP Expr Val A] +variable [TotalWP PROP Expr Val Stuckness] + +variable (e : Expr) (s : A) (E : CoPset) + +-- Base no-binder cases +variable (Φ : Val → PROP) + +/-- info: WP e @ s ; E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E {{ Φ }} +/-- info: WP e @ E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E {{ Φ}} +/-- info: WP e @ E ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? {{ Φ}} +/-- info: WP e {{ Φ }} : PROP -/ +#guard_msgs in #check WP e {{ Φ }} +/-- info: WP e ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{ Φ }} + +/-- info: WP e @ s ; E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E [{ Φ }] +/-- info: WP e @ E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E [{ Φ}] +/-- info: WP e @ E ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? [{ Φ}] +/-- info: WP e [{ Φ }] : PROP -/ +#guard_msgs in #check WP e [{ Φ }] +/-- info: WP e ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{ Φ }] + +/-- info: WP e @ s ; E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E ⦃ Φ ⦄ +/-- info: WP e @ E {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ⦃ Φ ⦄ +/-- info: WP e @ E ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? ⦃ Φ ⦄ +/-- info: WP e {{ Φ }} : PROP -/ +#guard_msgs in #check WP e ⦃ Φ ⦄ +/-- info: WP e ? {{ Φ }} : PROP -/ +#guard_msgs in #check WP e ? ⦃ Φ ⦄ + +/-- info: WP e @ s ; E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E 〖 Φ 〗 +/-- info: WP e @ E [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E 〖 Φ 〗 +/-- info: WP e @ E ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? 〖 Φ 〗 +/-- info: WP e [{ Φ }] : PROP -/ +#guard_msgs in #check WP e 〖 Φ 〗 +/-- info: WP e ? [{ Φ }] : PROP -/ +#guard_msgs in #check WP e ? 〖 Φ 〗 + +-- Base binder cases +variable (Φ : PROP) + +/-- info: WP e @ s ; E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E {{v, Φ }} +/-- info: WP e @ E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E {{ v, Φ}} +/-- info: WP e @ E ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? {{ v, Φ}} +/-- info: WP e {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e {{v, Φ }} +/-- info: WP e ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ }} + +/-- info: WP e @ s ; E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E [{v, Φ }] +/-- info: WP e @ E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E [{ v, Φ}] +/-- info: WP e @ E ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? [{ v, Φ}] +/-- info: WP e [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e [{v, Φ }] +/-- info: WP e ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ }] + +/-- info: WP e @ s ; E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ s ; E ⦃v, Φ⦄ +/-- info: WP e @ E {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ⦃v, Φ⦄ +/-- info: WP e @ E ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e @ E ? ⦃v, Φ⦄ +/-- info: WP e {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e ⦃v, Φ⦄ +/-- info: WP e ? {{ v, Φ }} : PROP -/ +#guard_msgs in #check WP e ? ⦃v, Φ⦄ + +/-- info: WP e @ s ; E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ s ; E 〖v, Φ〗 +/-- info: WP e @ E [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E 〖v, Φ〗 +/-- info: WP e @ E ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e @ E ? 〖v, Φ〗 +/-- info: WP e [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e 〖v, Φ〗 +/-- info: WP e ? [{ v, Φ }] : PROP -/ +#guard_msgs in #check WP e ? 〖v, Φ〗 + +-- BI binder cases +variable [BI PROP] + +/-- info: WP e ? {{ v, Φ ∗ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ ∗ Φ}} +/-- info: WP e ? {{ v, Φ ∧ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ ∧ Φ}} +/-- info: WP e ? {{ v, Φ ∨ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ ∨ Φ}} +/-- info: WP e ? {{ v, Φ -∗ Φ }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ -∗ Φ}} + +/-- info: WP e ? [{ v, Φ ∗ Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ ∗ Φ}] +/-- info: WP e ? [{ v, Φ -∗ Φ }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ -∗ Φ}] + +/-- info: WP e ? {{ v, Φ ∗ Φ }} : PROP -/ +#guard_msgs in #check WP e ? ⦃v, Φ ∗ Φ⦄ +/-- info: WP e ? [{ v, Φ ∗ Φ }] : PROP -/ +#guard_msgs in #check WP e ? 〖v, Φ ∗ Φ〗 + +/-- info: WP e @ E {{ v, Φ ∗ Φ }} : PROP -/ +#guard_msgs in #check WP e @ E {{v, Φ ∗ Φ}} +/-- info: WP e {{ v, Φ -∗ Φ }} : PROP -/ +#guard_msgs in #check WP e {{v, Φ -∗ Φ}} + +-- BI no-binder cases +variable (Φ : Val → PROP) + +/-- info: WP e ? {{ v, Φ v ∗ Φ v }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ v ∗ Φ v}} +/-- info: WP e ? {{ v, Φ v ∧ Φ v }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ v ∧ Φ v}} +/-- info: WP e ? {{ v, Φ v ∨ Φ v }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ v ∨ Φ v}} +/-- info: WP e ? {{ v, Φ v -∗ Φ v }} : PROP -/ +#guard_msgs in #check WP e ? {{v, Φ v -∗ Φ v}} +/-- info: WP e ? [{ v, Φ v ∗ Φ v }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ v ∗ Φ v}] +/-- info: WP e ? [{ v, Φ v -∗ Φ v }] : PROP -/ +#guard_msgs in #check WP e ? [{v, Φ v -∗ Φ v}] +/-- info: WP e ? {{ v, Φ v ∗ Φ v }} : PROP -/ +#guard_msgs in #check WP e ? ⦃v, Φ v ∗ Φ v⦄ +/-- info: WP e ? [{ v, Φ v ∗ Φ v }] : PROP -/ +#guard_msgs in #check WP e ? 〖v, Φ v ∗ Φ v〗 +/-- info: WP e @ E {{ v, Φ v ∗ Φ v }} : PROP -/ +#guard_msgs in #check WP e @ E {{v, Φ v ∗ Φ v}} +/-- info: WP e {{ v, Φ v -∗ Φ v }} : PROP -/ +#guard_msgs in #check WP e {{v, Φ v -∗ Φ v}} + +end TestWP + +section HeapLangTestWP +set_option linter.unusedVariables false + +open Iris.HeapLang + +variable (PROP : Type _) [BI PROP] +variable [Wp PROP Exp Val Stuckness] +variable (E : CoPset) (Φ : Val → PROP) (P : PROP) + +/-- info: WP hl(#1) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(#1) {{ Φ }} +/-- info: WP hl((#1 + #2)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(#1 + #2) {{ Φ }} +/-- info: WP hl((#1 < #2)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(#1 < #2) {{ Φ }} +/-- info: WP hl(if (#0 < #1) then #1 else #2) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(if #0 < #1 then #1 else #2) {{ Φ }} +/-- info: WP hl((λ x, x)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(λ x, x) {{ Φ }} +/-- info: WP hl((rec f x := f x)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(rec f x := f x) {{ Φ }} +/-- info: WP hl(#1; #2) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(#1; #2) {{ Φ }} +/-- info: WP hl((#1, #2)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl((#1, #2)) {{ Φ }} +/-- info: WP hl(ref(#0)) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(ref(#0)) {{ Φ }} +/-- info: WP hl(if (#1 < #2) then (#1 + #1) else #0) {{ Φ }} : PROP -/ +#guard_msgs in #check WP hl(if #1 < #2 then #1 + #1 else #0) {{ Φ }} +/-- info: WP hl(#1) {{ v, ⌜v = hl_val(#1)⌝ }} : PROP -/ +#guard_msgs in #check (WP hl(#1) {{ v, ⌜v = hl_val(#1)⌝ }} : PROP) + +end HeapLangTestWP From b6ac94525e8e78bba8bb14d1ea33236b41206ada Mon Sep 17 00:00:00 2001 From: ayhon Date: Mon, 18 May 2026 23:20:55 +0200 Subject: [PATCH 61/76] refactor: commute addition so it plays nicer with defeq --- Iris/Iris/ProgramLogic/WeakestPre.lean | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 1a3cdbf1..e09abe39 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -69,6 +69,8 @@ class IrisGS_gen (hlc : outParam <| Bool) -/ -- TODO: Should we have a default of `1`? numLatersPerStep : Nat → Nat + -- TODO: Even when referenced with the typeclass instance, the + -- display of `numLatersPerStep` is still kinda awful. /-- Postcondition of forked threads @@ -80,7 +82,7 @@ class IrisGS_gen (hlc : outParam <| Bool) The number of steps in the state interpretation should only be considered a lower bound. -/ - state_interp_mono σ ns obs nt : + stateInterp_mono σ ns obs nt : iprop(stateInterp σ ns obs nt ⊢ |={∅}=> stateInterp σ (ns + 1) obs nt) @@ -116,7 +118,9 @@ def wp.pre (s : Stuckness) ∀ e₂ σ₂ eₜ, ⌜(e₁, σ₁) --> (e₂, σ₂, eₜ)⌝ -∗ £ (ι.numLatersPerStep ns + 1) ={∅}▷=∗^[ι.numLatersPerStep ns + 1] |={∅,E}=> - stateInterp σ₂ (ns + 1) obs' (eₜ.length + nt) ∗ + -- NOTE: Changed the order of `nt` and `eₜ.length` since in Lean + -- we have `n + 0 = n` and not `0 + n = n` + stateInterp σ₂ (ns + 1) obs' (nt + eₜ.length) ∗ wp E e₂ Φ ∗ [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) @@ -293,6 +297,13 @@ theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : imod H with H iassumption +-- Easier to use when rewritting +theorem fupd_wp_iff {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : + WP e @ s ; E {{ Φ }} ⊣⊢ (|={E}=> WP e @ s ; E {{ Φ }}) := by + constructor + · exact fupd_mask_intro_discard Std.LawfulSet.subset_refl + · exact fupd_wp + theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : WP e @ s ; E {{v, |={E}=> Φ v }} ⊢ WP e @ s ; E {{ Φ }} := by iintro h @@ -332,7 +343,7 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro iframe | none => simp only [Stuckness.MaybeReducible] - icases H $$ %σ2 %(ns +1) %([]) %_ %(efs.length +nt) [Hσ] with >⟨%h, _⟩ + icases H $$ %σ2 %(ns +1) %([]) %_ %(nt + efs.length) [Hσ] with >⟨%h, _⟩ · exact .rfl nomatch (Language.not_reducible_iff_irreducible.mpr Hatomic) h | MaybeStuck => From 0d918de018acfa580e66a4985e846d721b0798eb Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 20 May 2026 11:17:36 +0200 Subject: [PATCH 62/76] fix: remove left over code from merge --- Iris/Iris/ProofMode/ProofModeM.lean | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index 3a631bff..eb63c853 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -70,17 +70,6 @@ def addBIGoalWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (hyps : Hyps bi e) (goal : Q($prop)) (toClear : Array FVarId) (name : Name := .anonymous) : ProofModeM Q($e ⊢ $goal) := do withoutFVars (u:=0) toClear (addBIGoal hyps goal name) -def runTacticWithoutFVars {prop : Q(Type u)} {bi : Q(BI $prop)} - {e} (hyps : Hyps bi e) (goal : Q($prop)) (toClear : Array FVarId) (name : Name := .anonymous) - (k : ∀ {e : Q($prop)}(_hyps : Hyps bi e)(goal: Q($prop)), ProofModeM Q($e ⊢ $goal)) : - ProofModeM Q($e ⊢ $goal) := do - let .mvar mvid ← addBIGoalWithoutFVars hyps goal toClear name - | unreachable! - let expr ← mvid.withContext do - k hyps goal - mvid.assign expr - return expr - /-- Add an existing metavariable as a goal to the proof mode state if it is not already assigned or present. -/ def addMVarGoal (m : MVarId) (name : Name := .anonymous) : ProofModeM Unit := do if ← m.isAssignedOrDelayedAssigned then From 3bbfe0633e712bf364e95a1b99d6058833ddfda9 Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Wed, 20 May 2026 13:26:26 +0200 Subject: [PATCH 63/76] smaller tokens for wp --- Iris/Iris/BI/WeakestPre.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 00645c0c..baad85fe 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -57,8 +57,8 @@ syntax ident ", " term : wpPostcondInner syntax term : wpPostcondInner declare_syntax_cat wpPostcond -syntax " {{ " wpPostcondInner " }} " : wpPostcond -syntax " [{ " wpPostcondInner " }] " : wpPostcond +syntax " {" "{ " wpPostcondInner " }" "} " : wpPostcond +syntax " [" "{ " wpPostcondInner " }" "] " : wpPostcond syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond syntax " 〖 " wpPostcondInner " 〗 " : wpPostcond From 15b1a2094d65d6ddaad2cda6e146ccb1a420ca05 Mon Sep 17 00:00:00 2001 From: ayhon Date: Wed, 20 May 2026 13:47:17 +0200 Subject: [PATCH 64/76] feat: add `rocq_alias` --- Iris/Iris/ProgramLogic/WeakestPre.lean | 54 +++++++++++++++++++++++++- Iris/Iris/Tests/WP.lean | 12 +++--- 2 files changed, 59 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index e09abe39..fb88aa28 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -52,6 +52,7 @@ class StateInterp export StateInterp (stateInterp) +@[rocq_alias irisGS_gen] class IrisGS_gen (hlc : outParam <| Bool) (Expr : Type e) {Val : Type v} @@ -107,6 +108,7 @@ abbrev Stuckness.MaybeReducible : Stuckness → Expr × State → Prop | .NotStuck, (e₁, σ₁) => PrimStep.Reducible (e₁, σ₁) | _, _ => True +@[rocq_alias wp_pre] def wp.pre (s : Stuckness) (wp : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF) : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF := fun E e₁ Φ => @@ -124,6 +126,7 @@ def wp.pre (s : Stuckness) wp E e₂ Φ ∗ [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) +@[rocq_alias wp_pre_contractive] instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where distLater_dist := by intros n wp wp' Hwp E e₁ Φ @@ -154,10 +157,14 @@ instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where · apply Hwp m m_n · refine BI.BigSepL.bigSepL_dist (fun _ => (Hwp m m_n _ _ _)) -@[implicit_reducible] +@[implicit_reducible, rocq_alias wp_def] instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where wp s := fixpoint (wp.pre s) +#rocq_ignore wp_aux "We do not use Iris' custom seal/unseal visibility control" +#rocq_ignore wp' "We do not use Iris' custom seal/unseal visibility control" +#rocq_ignore wp_unseal "We do not use Iris' custom seal/unseal visibility control" + section Wp @[rocq_alias wp_unfold] @@ -282,6 +289,7 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V imod h iapply HΦ $$ h +@[rocq_alias fupd_wp] theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by simp only [rw_iProp wp_unfold] @@ -304,6 +312,7 @@ theorem fupd_wp_iff {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : · exact fupd_mask_intro_discard Std.LawfulSet.subset_refl · exact fupd_wp +@[rocq_alias wp_fupd] theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : WP e @ s ; E {{v, |={E}=> Φ v }} ⊢ WP e @ s ; E {{ Φ }} := by iintro h @@ -311,6 +320,7 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : iintro %v h iassumption +@[rocq_alias wp_atomic] theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} [ι : Language.Atomic ↑s e] : (|={E1,E2}=> WP e @ s ; E2 {{v, |={E2,E1}=> Φ v }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by @@ -376,6 +386,7 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro `stateInterp _ (ns+1) _ _` again -/ +@[rocq_alias wp_credit_access] theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} : toVal e = none → (∀ m k, ι.numLatersPerStep m + ι.numLatersPerStep k ≤ ι.numLatersPerStep (m + k)) → @@ -417,6 +428,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} iintro %v HΦ iapply HΦ $$ HP +@[rocq_alias wp_step_fupdN_strong] theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} : toVal e = none → E2 ⊆ E1 → @@ -478,6 +490,7 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr imod interp $$ Hσ₁ with %h grind only +@[rocq_alias wp_bind] theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : -- TODO: Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} ⊢ WP (K e) @ s ; E {{ Φ }} := by @@ -503,6 +516,7 @@ theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H +@[rocq_alias wp_bind_inv] theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} := by iintro H @@ -530,6 +544,7 @@ theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness /-! ## Derived rules -/ variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in +@[rocq_alias wp_mono] theorem wp_mono : (∀ v, Φ v ⊢ Ψ v) → WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := by iintro %HΦ H @@ -539,6 +554,7 @@ theorem wp_mono : exact fupd_intro variable {s₁ s₂ : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +@[rocq_alias wp_stuck_mono] theorem wp_stuck_mono : s₁ ≤ s₂ → WP e @ s₁; E {{ Φ }} ⊢ WP e @ s₂ ; E {{ Φ }} := by iintro %s₁s₂ Hwp @@ -547,11 +563,13 @@ theorem wp_stuck_mono : exact fupd_intro variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +@[rocq_alias wp_stuck_weaken] theorem wp_stuck_weaken : WP e @ s; E {{ Φ }} ⊢ WP e @ E ?{{ Φ }} := wp_stuck_mono (Stuckness.le_MaybeStuck) variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} in +@[rocq_alias wp_mask_mono] theorem wp_mask_mono : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by iintro %E₁_E₂ Hwp iapply wp_strong_mono (Std.IsPreorder.le_refl s) E₁_E₂ $$ Hwp @@ -562,18 +580,22 @@ theorem wp_mask_mono : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E #rocq_ignore wp_flip_mono' "No `Proper` typeclass in Lean" variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +@[rocq_alias wp_value_fupd] theorem wp_value_fupd : Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v | ⟨h⟩ => h ▸ wp_value_fupd' variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +@[rocq_alias wp_value'] theorem wp_value' : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := fupd_intro.trans wp_value_fupd'.2 variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in +@[rocq_alias wp_value] theorem wp_value : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in +@[rocq_alias wp_frame_l] theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by iintro ⟨_, H⟩ iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H @@ -582,6 +604,7 @@ theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v iapply fupd_intro variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in +@[rocq_alias wp_frame_r] theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := BI.sep_comm.1.trans wp_frame_l @@ -597,6 +620,7 @@ variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val describe this unusual resource flow, we use ordinary conjunction as a premise. -/ +@[rocq_alias wp_step_fupdN] theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) → ((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ @@ -619,6 +643,7 @@ theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → simp [Std.LawfulSet.diff_empty, ←Std.LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in +@[rocq_alias wp_step_fupd] theorem wp_step_fupd : toVal e = none → E₂ ⊆ E₁ → (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }} -∗ WP e @ s; E₁ {{ Φ }} := @@ -635,6 +660,7 @@ theorem wp_step_fupd : iframe variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +@[rocq_alias wp_frame_step_l] theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ @@ -643,11 +669,13 @@ theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → iintro %x $ $ variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in +@[rocq_alias wp_frame_step_r] theorem wp_frame_step_r : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in +@[rocq_alias wp_frame_step_l'] theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ @@ -660,11 +688,13 @@ theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → exact BI.true_intro variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in +@[rocq_alias wp_frame_step_r'] theorem wp_frame_step_r' : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in +@[rocq_alias wp_wand] theorem wp_wand : WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by iintro Hwp H @@ -674,16 +704,19 @@ theorem wp_wand : exact fupd_intro variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +@[rocq_alias wp_wand_l] theorem wp_wand_l : (∀ v, Φ v -∗ Ψ v) ∗ WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := BI.wand_elim' wp_wand variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in +@[rocq_alias wp_wand_r] theorem wp_wand_r : WP e @ s ; E {{ Φ }} ∗ (∀ v, Φ v -∗ Ψ v) ⊢ WP e @ s ; E {{ Ψ }} := BI.wand_elim wp_wand variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ :Val → IProp GF}{R : IProp GF} in +@[rocq_alias wp_frame_wand] theorem wp_frame_wand : R ⊢ WP e @ s; E {{ v, R -∗ Φ v }} -∗ WP e @ s; E {{ Φ }} := by iintro R Hwp @@ -705,6 +738,7 @@ variable [ι : IrisGS_gen hlc Expr GF] variable {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ Ψ : Val → IProp GF} {P Q R : IProp GF} +@[rocq_alias frame_wp] instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : -- TODO: I didn't move over the `FrameInstantiateExistDisabled` constant. Ask if it's necessary. Frame p R (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Ψ }}) where @@ -714,6 +748,7 @@ instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : apply wp_mono apply H +@[rocq_alias is_except_0_wp] instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where is_except0 := calc iprop(◇ _) @@ -721,6 +756,7 @@ instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where _ ⊢ |={E}=> _ := BIFUpdate.except0 _ ⊢ WP e @ s ; E {{ Φ }} := fupd_wp +@[rocq_alias elim_modal_fupd_wp] instance elimModalFupdWp p : ElimModal True p false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where elim_modal := by @@ -730,17 +766,27 @@ instance elimModalFupdWp p : refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ exact fupd_wp +@[rocq_alias elim_modal_bupd_wp] +instance elimModalBupdWp p : + ElimModal True p false iprop(|==> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where + elim_modal := by + rintro ⟨⟩ + refine BI.sep_mono (BI.intuitionisticallyIf_mono (BIUpdateFUpdate.fupd_of_bupd (E := E))) .rfl |>.trans ?_ + apply elimModalFupdWp _ |>.elim_modal ⟨⟩ + /-- Error message instance for non-mask-changing view shifts. Also uses a slightly different error: we cannot apply `fupd_mask_subseteq` if `e` is not atomic, so we tell the user to first add a leading `fupd` and then change the mask of that. -/ +@[rocq_alias elim_modal_fupd_wp_wrong_mask] instance elimModalFupdWp_wrongMask : ElimModal (PMError "Goal and eliminated modality must have the same mask. Use `iapply fupd_wp; imod (fupd_mask_subseteq E₂)` to adjust the mask of your goal to `E₂`") p false iprop(|={E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where elim_modal := nofun +@[rocq_alias elim_modal_fupd_wp_atomic] instance elimModalFupdWpAtomic : ElimModal (Language.Atomic ↑s e) p false iprop(|={E₁,E₂}=> P) P (WP e @ s ; E₁ {{ Φ }}) (WP e @ s ; E₂ {{ v, |={E₂,E₁}=> Φ v}}) where elim_modal := by @@ -750,12 +796,16 @@ instance elimModalFupdWpAtomic : refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ exact wp_atomic +@[rocq_alias elim_modal_fupd_wp_atomic_wrong_mask] instance elimModalFupdWpAtomic_wrongMask : ElimModal (PMError "Goal and eliminated modality must have the same mask. Use `iapply fupd_wp; imod (fupd_mask_subseteq E₂)` to adjust the mask of your goal to `E₂`") p false iprop(|={E₁,E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where elim_modal := nofun +-- TODO: Implement these when `AddModal` and `ElimAcc` are added. + +-- @[rocq_alias add_modal_fupd_wp] -- instance addModalFupdWp : -- ProofMode.AddModal iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) where -- add_modal := by @@ -763,6 +813,7 @@ instance elimModalFupdWpAtomic_wrongMask : -- refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ -- exact fupd_wp +-- @[rocq_alias elim_acc_wp_atomic] -- instance elimAccWpAtomic : -- ElimAcc (X := X) (Atomic ↑s e) -- (fupd E₁ E₂) (fupd E₂ E₁) @@ -770,6 +821,7 @@ instance elimModalFupdWpAtomic_wrongMask : -- iprop(λ x ↦ WP e @ s ; E₂ {{ v, iprop(|={E₂}=> β x ∗ (γ x -∗? Φ v)) }}) where -- elim_acc := sorry +-- @[rocq_alias elim_acc_wp_nonatomic] -- instance elimAccWpNonAtomic : -- ElimAcc (X := X) True -- (fupd E E) (fupd E E) diff --git a/Iris/Iris/Tests/WP.lean b/Iris/Iris/Tests/WP.lean index 275e6f8d..5b09f796 100644 --- a/Iris/Iris/Tests/WP.lean +++ b/Iris/Iris/Tests/WP.lean @@ -30,9 +30,9 @@ variable (Φ : Val → PROP) /-- info: WP e @ s ; E {{ Φ }} : PROP -/ #guard_msgs in #check WP e @ s ; E {{ Φ }} /-- info: WP e @ E {{ Φ }} : PROP -/ -#guard_msgs in #check WP e @ E {{ Φ}} +#guard_msgs in #check WP e @ E {{ Φ }} /-- info: WP e @ E ? {{ Φ }} : PROP -/ -#guard_msgs in #check WP e @ E ? {{ Φ}} +#guard_msgs in #check WP e @ E ? {{ Φ }} /-- info: WP e {{ Φ }} : PROP -/ #guard_msgs in #check WP e {{ Φ }} /-- info: WP e ? {{ Φ }} : PROP -/ @@ -41,9 +41,9 @@ variable (Φ : Val → PROP) /-- info: WP e @ s ; E [{ Φ }] : PROP -/ #guard_msgs in #check WP e @ s ; E [{ Φ }] /-- info: WP e @ E [{ Φ }] : PROP -/ -#guard_msgs in #check WP e @ E [{ Φ}] +#guard_msgs in #check WP e @ E [{ Φ }] /-- info: WP e @ E ? [{ Φ }] : PROP -/ -#guard_msgs in #check WP e @ E ? [{ Φ}] +#guard_msgs in #check WP e @ E ? [{ Φ }] /-- info: WP e [{ Φ }] : PROP -/ #guard_msgs in #check WP e [{ Φ }] /-- info: WP e ? [{ Φ }] : PROP -/ @@ -88,9 +88,9 @@ variable (Φ : PROP) /-- info: WP e @ s ; E [{ v, Φ }] : PROP -/ #guard_msgs in #check WP e @ s ; E [{v, Φ }] /-- info: WP e @ E [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e @ E [{ v, Φ}] +#guard_msgs in #check WP e @ E [{ v, Φ }] /-- info: WP e @ E ? [{ v, Φ }] : PROP -/ -#guard_msgs in #check WP e @ E ? [{ v, Φ}] +#guard_msgs in #check WP e @ E ? [{ v, Φ }] /-- info: WP e [{ v, Φ }] : PROP -/ #guard_msgs in #check WP e [{v, Φ }] /-- info: WP e ? [{ v, Φ }] : PROP -/ From ca7d84ccf15dd101866b3099916c01b4d905249e Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Wed, 20 May 2026 17:12:42 +0200 Subject: [PATCH 65/76] pass over program logic --- Iris/Iris/BI/Updates.lean | 4 + Iris/Iris/BI/WeakestPre.lean | 4 + Iris/Iris/Instances/IProp/Instance.lean | 3 + Iris/Iris/ProgramLogic/WeakestPre.lean | 358 +++++++++++------------- 4 files changed, 168 insertions(+), 201 deletions(-) diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index 19fb8c61..b138e8e6 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -204,6 +204,10 @@ theorem fupd_mask_intro_subseteq {E1 E2 : CoPset} {P : PROP} : E2 ⊆ E1 → P λ h => (emp_sep.2.trans <| sep_mono_l <| subset h).trans <| frame_r.trans <| mono <| frame_r.trans <| mono emp_sep.1 +@[rocq_alias fupd_mask_subseteq] +theorem fupd_mask_subseteq {E1 E2 : CoPset} : E2 ⊆ E1 → ⊢@{PROP} |={E1,E2}=> |={E2,E1}=> emp := + λ Hsub => fupd_mask_intro_subseteq Hsub (P := iprop(emp)) + theorem fupd_intro {E : CoPset} {P : PROP} : P ⊢ |={E}=> P := (fupd_mask_intro_subseteq λ _ => id).trans trans diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index baad85fe..1f87ae53 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -57,6 +57,10 @@ syntax ident ", " term : wpPostcondInner syntax term : wpPostcondInner declare_syntax_cat wpPostcond +-- Avoids conflicts with +-- example {a : PUnit.{i}} : PUnit.{i} := a +-- ^^ +-- see: https://github.com/leanprover-community/iris-lean/pull/393 syntax " {" "{ " wpPostcondInner " }" "} " : wpPostcond syntax " [" "{ " wpPostcondInner " }" "] " : wpPostcond syntax " ⦃ " wpPostcondInner " ⦄ " : wpPostcond diff --git a/Iris/Iris/Instances/IProp/Instance.lean b/Iris/Iris/Instances/IProp/Instance.lean index 18729626..5b6615ff 100644 --- a/Iris/Iris/Instances/IProp/Instance.lean +++ b/Iris/Iris/Instances/IProp/Instance.lean @@ -16,6 +16,9 @@ namespace Iris open COFE +@[ext] +theorem IProp.ext {P Q : IProp GF} : P ⊣⊢ Q → P = Q := OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr + /-- Apply an OFunctor at a fixed type -/ abbrev COFE.OFunctorPre.ap (F : OFunctorPre) (T : Type _) [OFE T] := F T T diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index fb88aa28..0f7e6649 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -6,23 +6,22 @@ module public import Iris.Algebra public import Iris.Instances.Lib.FUpd -public import Iris.Instances.Lib.LaterCredits public import Iris.BI public import Iris.BI.WeakestPre -public import Iris.BI.BigOp.BigSepList -public import Iris.BI.DerivedLaws -public import Iris.BI.Updates public import Iris.ProofMode -meta import Iris.BI.Updates public import Iris.ProgramLogic.Language public import Iris.Std.CoPset namespace Iris -open ProgramLogic Language.Notation +open ProgramLogic Language.Notation Std @[expose] public section +/-! +TODO: AddModal, ElimAcc instances +-/ + /-- Carrier typeclass for the `stateInterp` operation. @@ -35,15 +34,14 @@ open ProgramLogic Language.Notation of `Expr`, so `Expr` cannot be inferred from `State` instead. -/ class StateInterp - -- TODO: This probably should be a `semiOutParam` - (State: Type s) + (State: semiOutParam <| Type s) (Obs : outParam <| Type o) (GF : BundledGFunctors) where /-- Axiomatic interpretation of a state in a language model. - `σ` is the state whose interpretation we take - - `ns` are the number of prior steps (TODO: Check whether true) + - `ns` are the number of prior steps - `obs` are the observations prior to this state - `nt` are the number of threads previously spawned @@ -68,7 +66,6 @@ class IrisGS_gen (hlc : outParam <| Bool) Number of later credits obtained from taking one step in the operational semantics of our language. -/ - -- TODO: Should we have a default of `1`? numLatersPerStep : Nat → Nat -- TODO: Even when referenced with the typeclass instance, the -- display of `numLatersPerStep` is still kinda awful. @@ -76,7 +73,6 @@ class IrisGS_gen (hlc : outParam <| Bool) /-- Postcondition of forked threads -/ - -- TODO: Should we have a default of `True`? forkPost : Val → IProp GF /-- @@ -95,9 +91,6 @@ variable [ι : IrisGS_gen hlc Expr GF] instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ --- TODO: Move to a better place, probably think of a better name -theorem rw_iProp{P Q : IProp GF} : P ⊣⊢ Q → P = Q := OFE.Leibniz.eq_of_eqv ∘ BI.equiv_iff.mpr - /-- Reducibility condition depending on stuckness. ```lean4 -- s.MaybeReducible (e, σ) equivalent to… @@ -153,9 +146,9 @@ instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where refine step_fupdN_ne.ne ?_ refine BIFUpdate.ne.ne ?_ refine BI.sep_ne.ne .rfl ?_ - apply BI.sep_ne.ne - · apply Hwp m m_n - · refine BI.BigSepL.bigSepL_dist (fun _ => (Hwp m m_n _ _ _)) + refine BI.sep_ne.ne ?_ ?_ + · exact Hwp m m_n _ _ _ + · exact BI.BigSepL.bigSepL_dist (fun _ => (Hwp m m_n _ _ _)) @[implicit_reducible, rocq_alias wp_def] instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where @@ -177,35 +170,33 @@ instance wp_ne {s : Stuckness} {E} {e : Expr} : OFE.NonExpansive (Wp.wp (PROP := IProp GF) s E e) where ne {n Φ₁ Φ₂} HΦ := by induction n using Nat.strongRecOn generalizing e E Φ₁ Φ₂ with | ind n IH => - simp only [rw_iProp wp_unfold] + simp only [IProp.ext wp_unfold] dsimp only [wp.pre] cases toVal e case some v => exact BIFUpdate.ne.ne <| HΦ v - -- Composing a bunch of nonexpansive operations… refine BI.forall_ne fun σ₁ => ?_ refine BI.forall_ne fun ns => ?_ refine BI.forall_ne fun obs => ?_ refine BI.forall_ne fun obs' => ?_ refine BI.forall_ne fun nt => ?_ - refine BI.wand_ne.ne .rfl ?_ - refine BIFUpdate.ne.ne ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BIFUpdate.ne.ne ?_ refine BI.sep_ne.ne .rfl ?_ refine BI.forall_ne fun e₂ => ?_ refine BI.forall_ne fun σ₂ => ?_ refine BI.forall_ne fun eₜ => ?_ - refine BI.wand_ne.ne .rfl ?_ - refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ -- The `step_fupdN` |={∅}▷=>^[n+1] is contractive refine step_fupdN_contractive.distLater_dist fun m n_m => ?_ - refine BIFUpdate.ne.ne ?_ + refine BIFUpdate.ne.ne ?_ refine BI.sep_ne.ne .rfl ?_ refine BI.sep_ne.ne ?_ .rfl - -- We can now apply the induction hypothesis - apply IH m n_m <| OFE.dist_lt HΦ n_m + exact IH m n_m <| OFE.dist_lt HΦ n_m #rocq_ignore wp_proper "Derivable using NonExpansive.eqv" @@ -214,7 +205,7 @@ instance wp_ne {s : Stuckness} {E} {e : Expr} : instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where distLater_dist {n Φ₁ Φ₂} HΦ := by - simp only [rw_iProp wp_unfold] + simp only [IProp.ext wp_unfold] simp only [wp.pre, h] refine BI.forall_ne fun σ₁ => ?_ refine BI.forall_ne fun ns => ?_ @@ -234,12 +225,12 @@ instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : refine BI.sep_ne.ne .rfl ?_ refine BI.sep_ne.ne ?_ .rfl refine wp_ne.ne ?_ - apply HΦ m n_m + exact HΦ m n_m @[rocq_alias wp_value_fupd'] theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : WP (v : Expr) @ s ; E {{ Φ }} ⊣⊢ |={E}=> Φ v := by - simp only [rw_iProp wp_unfold, toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] + simp [IProp.ext wp_unfold, toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] @[rocq_alias wp_strong_mono] theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} : @@ -247,15 +238,14 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by intro hs hE iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE - rw [rw_iProp wp_unfold, rw_iProp wp_unfold] + rw [IProp.ext wp_unfold, IProp.ext wp_unfold] iintro H HΦ dsimp only [wp.pre] match toVal e with | none => dsimp only iintro %σ₁ %ns %obs %obs' %nt Hσ - imod fupd_mask_intro_subseteq hE (P := iprop(emp)) $$ [] with Hclose -- TODO: Should we add rocq_alias `fupd_mask_subseteq` to this theorem? - · exact BI.intuitionistically_elim_emp + imod fupd_mask_subseteq hE with Hclose icases H $$ Hσ with >⟨%h, H⟩ imodintro isplit @@ -265,34 +255,32 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V | .MaybeStuck, .MaybeStuck | .NotStuck, .MaybeStuck => ipure_intro; grind only - iintro %e₂ %σ₂ %eₜ #hstep «h£» + iintro %e₂ %σ₂ %eₜ #hstep hc dsimp only [Nat.repeat] - imod H $$ hstep «h£» with H + imod H $$ hstep hc with H iintro !> !>; imod H; iintro !> iapply step_fupdN_wand $$ H iintro >⟨aux, H, Hefs⟩ imod Hclose imodintro - isplitl [aux] - · iassumption + iframe aux isplitr [Hefs] · iapply IH $$ %e₂ %Φ %Ψ %E₁ %E₂ %hE H HΦ · iapply BI.BigSepL.bigSepL_impl $$ Hefs iintro !> %k %e' %_ H - iapply IH $$ %e' %_ %_ %⊤ %_ %Std.LawfulSet.subset_refl H + iapply IH $$ %e' %_ %_ %⊤ %_ %LawfulSet.subset_refl H iintro %v H imodintro iassumption | some v => dsimp only - ihave h := fupd_mask_mono hE $$ H - imod h + imod fupd_mask_mono hE $$ H with h iapply HΦ $$ h @[rocq_alias fupd_wp] theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : (|={E}=> WP e @ s ; E {{ Φ }}) ⊢ WP e @ s ; E {{ Φ }} := by - simp only [rw_iProp wp_unfold] + simp only [IProp.ext wp_unfold] iintro H match h: toVal e with | some v => @@ -305,18 +293,17 @@ theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : imod H with H iassumption --- Easier to use when rewritting theorem fupd_wp_iff {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : - WP e @ s ; E {{ Φ }} ⊣⊢ (|={E}=> WP e @ s ; E {{ Φ }}) := by + WP e @ s ; E {{ Φ }} ⊣⊢ (|={E}=> WP e @ s ; E {{ Φ }}) := by constructor - · exact fupd_mask_intro_discard Std.LawfulSet.subset_refl + · exact fupd_mask_intro_discard LawfulSet.subset_refl · exact fupd_wp @[rocq_alias wp_fupd] theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : WP e @ s ; E {{v, |={E}=> Φ v }} ⊢ WP e @ s ; E {{ Φ }} := by iintro h - iapply wp_strong_mono (Std.IsPreorder.le_refl _) Std.LawfulSet.subset_refl $$ h + iapply wp_strong_mono (Std.IsPreorder.le_refl _) LawfulSet.subset_refl $$ h iintro %v h iassumption @@ -324,7 +311,7 @@ theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IProp GF} [ι : Language.Atomic ↑s e] : (|={E1,E2}=> WP e @ s ; E2 {{v, |={E2,E1}=> Φ v }}) ⊢ (WP e @ s ; E1 {{ Φ }}) := by - simp only [rw_iProp wp_unfold] + simp only [IProp.ext wp_unfold] iintro H match He : toVal e with | some v => @@ -342,24 +329,26 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro ihave aux := H $$ %e2 %σ2 %efs %Hstep Hcred iapply step_fupdN_wand $$ aux iintro >(⟨Hσ,H,Hefs⟩) - have Hatomic := ι.atomic Hstep - cases s with -- TODO: Example of place where `match` is worse than `cases` - | NotStuck => - simp only [rw_iProp wp_unfold] - dsimp only [wp.pre] at Hatomic ⊢ + irevert %ι + match s with + | .NotStuck => + simp only [IProp.ext wp_unfold] + dsimp only [wp.pre] match h₂ : toVal e2 with | some v2 => + iintro %ι icases H with > > $ iframe | none => - simp only [Stuckness.MaybeReducible] + iintro %ι icases H $$ %σ2 %(ns +1) %([]) %_ %(nt + efs.length) [Hσ] with >⟨%h, _⟩ · exact .rfl - nomatch (Language.not_reducible_iff_irreducible.mpr Hatomic) h - | MaybeStuck => + exact ((Language.not_reducible_iff_irreducible.mpr (ι.atomic Hstep)) h).elim + | .MaybeStuck => + iintro %ι have ⟨v, h⟩ := Option.isSome_iff_exists.mp (ι.atomic Hstep) obtain ⟨rfl⟩ := (ToVal.coe_of_toVal_eq_some h) - simp only [rw_iProp wp_value_fupd'] + simp only [IProp.ext wp_value_fupd'] imod H with > H iframe @@ -398,7 +387,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} WP e @ s ; E {{ v, P ={E}=∗ Φ v }} -∗ WP e @ s ; E {{ Φ }} := by intro h Htri - simp only [rw_iProp wp_unfold] + simp only [IProp.ext wp_unfold] iintro Hupd Hwp simp only [wp.pre, h] iintro %σ₁ %ns %obs %obs' %nt Hσ₁ @@ -406,70 +395,68 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ imodintro iintro %e₂ %σ₂ %efs %Hstep Hc - simp only [rw_iProp lc_split] + simp only [IProp.ext lc_split] icases Hc with ⟨Hc,Hone⟩ ihave Hc := lc_weaken _ (Htri m k) $$ Hc - istop; refine (BI.sep_mono .rfl (lc_split.1)).trans ?_; iintro ⟨⟨⟨Hpost,Hwp⟩,Hone⟩,Hc⟩ - icases Hc with ⟨Hm, Hk⟩ + icases lc_split $$ Hc with ⟨Hm, Hk⟩ -- TODO: Redo with `icombine` when available - ihave Hm := lc_split.mpr $$ [Hm Hone] - · iframe - simp only [Nat.repeat] + ihave Hm := lc_split.mpr $$ [$Hm $Hone] + dsimp only [Nat.repeat] ihave Hwp := Hwp $$ [] [Hm] · ipure_intro; assumption - · simp only [OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.Leibniz.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] + · simp only [OFE.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] exact .rfl iapply step_fupd_wand $$ Hwp; iintro Hwp - iapply step_fupdN_le (n := ι.numLatersPerStep m) (by grind only) (Std.LawfulSet.subset_refl) + iapply step_fupdN_le (n := ι.numLatersPerStep m) (by grind only) (LawfulSet.subset_refl) iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ icases Hpost $$ Hk SI with >⟨$, HP⟩ imodintro - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ Hwp + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ Hwp iintro %v HΦ iapply HΦ $$ HP +-- TODO: icases support for ⟨_, H⟩ and ⟨H, _⟩ patterns for hypothesis of the form A ∧ B @[rocq_alias wp_step_fupdN_strong] -theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} : +theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} {n} : toVal e = none → E2 ⊆ E1 → - ∀ {n}, - -- TODO: This was written as an ∧ in Iris Rocq. I've separated it because it doesn't seem like - -- icases is able to handle ∧ expressions. - (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ ι.numLatersPerStep ns + 1⌝) → - (|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ - WP e @ s ; E2 {{ v, P ={E1}=∗ Φ v}} ⊢ + (∀ (σ : State) ns obs nt, stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ ι.numLatersPerStep ns + 1⌝) + ∧ ((|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) + ∗ WP e @ s ; E2 {{ v, P ={E1}=∗ Φ v}}) ⊢ WP e @ s ; E1 {{ Φ }} := by - intro toVal_e E2_E1 n interp + intro toVal_e E2_E1 match n with | 0 => - iintro ⟨Hp, Hwp⟩ + iintro H + icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ iapply wp_strong_mono (Std.IsPreorder.le_refl s) E2_E1 $$ Hwp iintro %v H - refine (BI.sep_mono BIFUpdate.trans .rfl).trans ?_; iintro ⟨Hp,H⟩ + dsimp only [Nat.repeat] + imod Hp imod Hp iapply H $$ Hp | n+1 => - simp only [rw_iProp wp_unfold] - iintro ⟨Hp,Hwp⟩ + simp only [IProp.ext wp_unfold] simp only [wp.pre, toVal_e] - iintro %σ₁ %ns %obs %obs' %nt Hσ₁ + iintro H %σ₁ %ns %obs %obs' %nt Hσ₁ if Hn : n ≤ ι.numLatersPerStep ns then + icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ imod Hp + dsimp only [Nat.repeat] imod Hwp $$ Hσ₁ with ⟨$, H⟩ iintro !> %e₂ %σ₂ %efs %Hstep Hcred icases H $$ %_ %_ %_ %Hstep Hcred with H - simp only [Nat.repeat] + dsimp only [Nat.repeat] imod H; imod Hp iintro !> !> imod H; imod Hp imodintro - clear interp generalize ι.numLatersPerStep ns = n0 at * induction n generalizing n0 with | zero => iapply step_fupdN_wand $$ H iintro >⟨$, Hwp, $⟩ - simp only [Nat.repeat] + dsimp only [Nat.repeat] imod Hp imodintro iapply wp_strong_mono (Std.IsPreorder.le_refl s) E2_E1 $$ Hwp @@ -477,7 +464,7 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr iapply HΦ $$ Hp | succ n IH => obtain ⟨n0, rfl⟩ : ∃ n0', n0 = n0' + 1 := by cases n0 <;> grind only - simp only [Nat.repeat] + dsimp only [Nat.repeat] imod Hp imod H imodintro @@ -487,23 +474,24 @@ theorem wp_step_fupdN_strong {s : Stuckness}{E1 E2 : CoPset} {e : Expr} {P : IPr imodintro iapply IH n0 (Nat.le_of_succ_le_succ Hn) $$ [$]; else + icases BI.and_elim_l $$ H with interp imod interp $$ Hσ₁ with %h grind only @[rocq_alias wp_bind] theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : -- TODO: Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) - WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} ⊢ WP (K e) @ s ; E {{ Φ }} := by + WP e @ s ; E {{v, WP (K (↑v : Val)) @ s ; E {{ Φ }} }} ⊢ WP (K e) @ s ; E {{ Φ }} := by iintro H iloeb as IH generalizing %E %e %Φ - rewrite (occs := [2]) [rw_iProp wp_unfold] + rewrite (occs := [2]) [IProp.ext wp_unfold] simp only [wp.pre] match h: toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H | none => - rw [rw_iProp wp_unfold] + rw [IProp.ext wp_unfold] simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] iintro %σ₁ %step %obs %obs' %n Hσ imod H $$ [$] with ⟨%_, H⟩ @@ -518,17 +506,17 @@ theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E @[rocq_alias wp_bind_inv] theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : - WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K ((v : Val) : Expr)) @ s ; E {{ Φ }} }} := by + WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K (↑v : Val)) @ s ; E {{ Φ }} }} := by iintro H iloeb as IH generalizing %E %e %Φ - rewrite (occs := [3]) [rw_iProp wp_unfold] + rewrite (occs := [3]) [IProp.ext wp_unfold] simp only [wp.pre] match h: toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H | none => - rewrite (occs := [2]) [rw_iProp wp_unfold] + rewrite (occs := [2]) [IProp.ext wp_unfold] simp only [wp.pre, κ.toVal_eq_none_fill h, Nat.repeat] iintro %σ₁ %step %obs %obs' %n Hσ imod H $$ [$] with ⟨%_, H⟩ @@ -536,80 +524,74 @@ theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness isplit · ipure_intro; grind only [cases Stuckness, Language.Context.reducible_fill_inv] iintro %e₂ %σ₂ %efs %Hstep Hcred - have HKstep := κ.primStep_fill Hstep - icases H $$ %(K e₂) %σ₂ %efs %HKstep Hcred with >H; imodintro; imodintro + icases H $$ %(K e₂) %σ₂ %efs %(κ.primStep_fill Hstep) Hcred with >H; imodintro; imodintro imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H /-! ## Derived rules -/ -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in @[rocq_alias wp_mono] -theorem wp_mono : +theorem wp_mono {s : Stuckness} {E : CoPset} {e : Expr} {Φ Ψ : Val → IProp GF} : (∀ v, Φ v ⊢ Ψ v) → WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := by iintro %HΦ H - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H - iintro %v HΨ; - ihave aux := HΦ $$ HΨ - exact fupd_intro + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ H + iintro %v HΨ + imodintro + iapply HΦ $$ HΨ -variable {s₁ s₂ : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in @[rocq_alias wp_stuck_mono] -theorem wp_stuck_mono : +theorem wp_stuck_mono {s₁ s₂ : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : s₁ ≤ s₂ → WP e @ s₁; E {{ Φ }} ⊢ WP e @ s₂ ; E {{ Φ }} := by iintro %s₁s₂ Hwp - iapply wp_strong_mono s₁s₂ (Std.LawfulSet.subset_refl) $$ Hwp + iapply wp_strong_mono s₁s₂ (LawfulSet.subset_refl) $$ Hwp iintro %v HΦ - exact fupd_intro + iframe HΦ -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in @[rocq_alias wp_stuck_weaken] -theorem wp_stuck_weaken : +theorem wp_stuck_weaken {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : WP e @ s; E {{ Φ }} ⊢ WP e @ E ?{{ Φ }} := wp_stuck_mono (Stuckness.le_MaybeStuck) -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} in @[rocq_alias wp_mask_mono] -theorem wp_mask_mono : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by +theorem wp_mask_mono {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} + : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by iintro %E₁_E₂ Hwp iapply wp_strong_mono (Std.IsPreorder.le_refl s) E₁_E₂ $$ Hwp iintro %v HΦ - exact fupd_intro + iframe HΦ #rocq_ignore wp_mono' "No `Proper` typeclass in Lean" #rocq_ignore wp_flip_mono' "No `Proper` typeclass in Lean" -variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in @[rocq_alias wp_value_fupd] -theorem wp_value_fupd : Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v +theorem wp_value_fupd {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} + : Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v | ⟨h⟩ => h ▸ wp_value_fupd' -variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in @[rocq_alias wp_value'] -theorem wp_value' : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := +theorem wp_value' {s : Stuckness} {E : CoPset} {v : Val} {Φ : Val → IProp GF} + : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := fupd_intro.trans wp_value_fupd'.2 -variable {s : Stuckness} {E : CoPset} {e : Expr}{v : Val}{Φ : Val → IProp GF} in @[rocq_alias wp_value] -theorem wp_value : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} +theorem wp_value {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} + : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in @[rocq_alias wp_frame_l] -theorem wp_frame_l : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by +theorem wp_frame_l {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} + : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by iintro ⟨_, H⟩ - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ H + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ H iframe iintro %x iapply fupd_intro -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF}{R : IProp GF} in @[rocq_alias wp_frame_r] -theorem wp_frame_r : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := +theorem wp_frame_r {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} + : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := BI.sep_comm.1.trans wp_frame_l - -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in /-- (copy-pasted from Rocq formalization) This lemma states that if we can prove that [n] laters are used in @@ -621,103 +603,101 @@ variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val a premise. -/ @[rocq_alias wp_step_fupdN] -theorem wp_step_fupdN {n : Nat} : toVal e = none → E₂ ⊆ E₁ → - (∀ (σ : State) ns obs nt, ⊢@{IProp GF} stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) → - ((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ - WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }}) -∗ +theorem wp_step_fupdN {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp GF} {Φ : Val → IProp GF} {n : Nat} + : toVal e = none → E₂ ⊆ E₁ → + (∀ (σ : State) ns obs nt, stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) ∧ + (((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ + WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }})) -∗ WP e @ s; E₁ {{ Φ }} := by - intro toVal_e E₂E₁ Hstate + intro toVal_e E₂E₁ iintro H - iapply wp_step_fupdN_strong (s := s) (P := P) toVal_e E₂E₁ Hstate $$ [H] + iapply wp_step_fupdN_strong (s := s) (P := P) (n := n) toVal_e E₂E₁ $$ [H] + iapply BI.and_mono_r $$ H apply BI.sep_mono_l - iintro Hp - imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from Std.LawfulSet.diff_subset_left) with H - imod Hp - imod H with toClear; iclear toClear - simp only [show E₁ \ (E₁ \ E₂) = E₂ from Std.LawfulSet.diff_self_diff_of_subset E₂E₁] + iintro HP + imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from LawfulSet.diff_subset_left) with G + imod HP + imod G with toClear; iclear toClear + rw [show E₁ \ (E₁ \ E₂) = E₂ from LawfulSet.diff_self_diff_of_subset E₂E₁] imodintro - iapply step_fupdN_wand $$ Hp; iintro H - iapply fupd_mask_frame (Std.LawfulSet.empty_subset) + iapply step_fupdN_wand $$ HP; iintro H + iapply fupd_mask_frame (LawfulSet.empty_subset) imod H imodintro - simp [Std.LawfulSet.diff_empty, ←Std.LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] + simp [LawfulSet.diff_empty, ←LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} in @[rocq_alias wp_step_fupd] -theorem wp_step_fupd : - toVal e = none → E₂ ⊆ E₁ → +theorem wp_step_fupd {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp GF} {Φ : Val → IProp GF} + : toVal e = none → E₂ ⊆ E₁ → (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }} -∗ WP e @ s; E₁ {{ Φ }} := fun toVal_e E₂E₁=> by iintro HR H - iapply wp_step_fupdN_strong (n := 1) toVal_e E₂E₁ (by - intros; iintro H - refine .trans ?_ <| fupd_mask_intro_discard (Std.LawfulSet.empty_subset) - simp [Nat.le_add_left, BI.true_intro] - ) $$ [-] + iapply wp_step_fupdN_strong (n := 1) (P := P) toVal_e E₂E₁ $$ [-] iframe H - imod HR - simp only [Nat.repeat] - iframe + isplit + · iintro %σ %ns %obj %nt interp + iapply fupd_mask_intro_discard LawfulSet.empty_subset $$ [HR] + simp [Nat.le_add_left, BI.true_intro] + · imod HR + dsimp only [Nat.repeat] + iframe -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in @[rocq_alias wp_frame_step_l] -theorem wp_frame_step_l : toVal e = none → E₂ ⊆ E₁ → +theorem wp_frame_step_l {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} + : toVal e = none → E₂ ⊆ E₁ → (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ iapply wp_step_fupd toVal_e E₂E₁ $$ Hu iapply wp_mono $$ Hwp iintro %x $ $ -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{P : IProp GF}{Φ : Val → IProp GF} {R : IProp GF} in @[rocq_alias wp_frame_step_r] -theorem wp_frame_step_r : toVal e = none → E₂ ⊆ E₁ → +theorem wp_frame_step_r {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} + : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in @[rocq_alias wp_frame_step_l'] -theorem wp_frame_step_l' : toVal e = none → E₂ ⊆ E₁ → +theorem wp_frame_step_l' {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} + : toVal e = none → E₂ ⊆ E₁ → (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ iapply wp_frame_step_l toVal_e E₂E₁ iframe iapply fupd_mask_intro E₂E₁ - iintro _ + iintro H + imodintro + imod H imodintro - apply BIFUpdate.mono - exact BI.true_intro + iapply BI.true_intro $$ H -variable {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} in @[rocq_alias wp_frame_step_r'] -theorem wp_frame_step_r' : toVal e = none → E₂ ⊆ E₁ → +theorem wp_frame_step_r' {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} + : toVal e = none → E₂ ⊆ E₁ → WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ Ψ : Val → IProp GF} in @[rocq_alias wp_wand] -theorem wp_wand : - WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by +theorem wp_wand {s : Stuckness} {E : CoPset} {e : Expr} {Φ Ψ : Val → IProp GF} + : WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by iintro Hwp H - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (Std.LawfulSet.subset_refl) $$ Hwp + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ Hwp iintro %v HΦ icases H $$ HΦ with H - exact fupd_intro + imodintro; iframe -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in @[rocq_alias wp_wand_l] -theorem wp_wand_l : +theorem wp_wand_l {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : (∀ v, Φ v -∗ Ψ v) ∗ WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := BI.wand_elim' wp_wand -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ : Val → IProp GF} in @[rocq_alias wp_wand_r] -theorem wp_wand_r : +theorem wp_wand_r {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : WP e @ s ; E {{ Φ }} ∗ (∀ v, Φ v -∗ Ψ v) ⊢ WP e @ s ; E {{ Ψ }} := BI.wand_elim wp_wand -variable {s : Stuckness} {E : CoPset} {e : Expr}{Φ :Val → IProp GF}{R : IProp GF} in @[rocq_alias wp_frame_wand] -theorem wp_frame_wand : +theorem wp_frame_wand {s : Stuckness} {E : CoPset} {e : Expr} {Φ :Val → IProp GF} {R : IProp GF} : R ⊢ WP e @ s; E {{ v, R -∗ Φ v }} -∗ WP e @ s; E {{ Φ }} := by iintro R Hwp iapply wp_wand $$ Hwp @@ -740,7 +720,9 @@ variable {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ Ψ : Val → IPro @[rocq_alias frame_wp] instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : - -- TODO: I didn't move over the `FrameInstantiateExistDisabled` constant. Ask if it's necessary. + -- TODO: move FrameInstantiateExistDisabled over the `FrameInstantiateExistDisabled` constant + -- Blocked by #390 + -- see: https://github.com/leanprover-community/iris-lean/pull/393 Frame p R (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Ψ }}) where frame := by replace H v := (H v).frame @@ -760,11 +742,11 @@ instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where instance elimModalFupdWp p : ElimModal True p false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where elim_modal := by - rintro ⟨⟩; iintro ⟨H, H⟩ - refine (BI.sep_mono BI.intuitionisticallyIf_elim .rfl).trans ?_ - refine fupd_frame_r.trans ?_ - refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ - exact fupd_wp + intro ⟨⟩; iintro ⟨H, G⟩ + icases BI.intuitionisticallyIf_elim $$ H with H + iapply fupd_wp + imod H; imodintro + iapply G $$ H @[rocq_alias elim_modal_bupd_wp] instance elimModalBupdWp p : @@ -790,11 +772,11 @@ instance elimModalFupdWp_wrongMask : instance elimModalFupdWpAtomic : ElimModal (Language.Atomic ↑s e) p false iprop(|={E₁,E₂}=> P) P (WP e @ s ; E₁ {{ Φ }}) (WP e @ s ; E₂ {{ v, |={E₂,E₁}=> Φ v}}) where elim_modal := by - rintro atomic; iintro ⟨H, H⟩ - refine (BI.sep_mono BI.intuitionisticallyIf_elim .rfl).trans ?_ - refine fupd_frame_r.trans ?_ - refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ - exact wp_atomic + rintro atomic; iintro ⟨H, G⟩ + icases BI.intuitionisticallyIf_elim $$ H with H + iapply wp_atomic + imod H; imodintro + iapply G $$ H @[rocq_alias elim_modal_fupd_wp_atomic_wrong_mask] instance elimModalFupdWpAtomic_wrongMask : @@ -803,30 +785,4 @@ instance elimModalFupdWpAtomic_wrongMask : p false iprop(|={E₁,E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where elim_modal := nofun --- TODO: Implement these when `AddModal` and `ElimAcc` are added. - --- @[rocq_alias add_modal_fupd_wp] --- instance addModalFupdWp : --- ProofMode.AddModal iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) where --- add_modal := by --- refine fupd_frame_r.trans ?_ --- refine BIFUpdate.mono BI.wand_elim_r |>.trans ?_ --- exact fupd_wp - --- @[rocq_alias elim_acc_wp_atomic] --- instance elimAccWpAtomic : --- ElimAcc (X := X) (Atomic ↑s e) --- (fupd E₁ E₂) (fupd E₂ E₁) --- α β γ (WP e @ s ; E₁ {{ Φ}}) --- iprop(λ x ↦ WP e @ s ; E₂ {{ v, iprop(|={E₂}=> β x ∗ (γ x -∗? Φ v)) }}) where --- elim_acc := sorry - --- @[rocq_alias elim_acc_wp_nonatomic] --- instance elimAccWpNonAtomic : --- ElimAcc (X := X) True --- (fupd E E) (fupd E E) --- α β γ (WP e @ s ; E {{ Φ}}) --- iprop(λ x ↦ WP e @ s ; E {{ v, iprop(|={E}=> β x ∗ (γ x -∗? Φ v)) }}) where --- elim_acc := sorry - end ProofModeClasses From f059f0b7ee66613ade8d7fab37dfa9872125ba99 Mon Sep 17 00:00:00 2001 From: Markus de Medeiros Date: Wed, 20 May 2026 15:11:14 -0400 Subject: [PATCH 66/76] chore: cleanup updates --- Iris/Iris/BI/Updates.lean | 73 ++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/Iris/Iris/BI/Updates.lean b/Iris/Iris/BI/Updates.lean index b138e8e6..2187c2bc 100644 --- a/Iris/Iris/BI/Updates.lean +++ b/Iris/Iris/BI/Updates.lean @@ -200,29 +200,32 @@ variable [BI PROP] [BIFUpdate PROP] open BIFUpdate LawfulSet -theorem fupd_mask_intro_subseteq {E1 E2 : CoPset} {P : PROP} : E2 ⊆ E1 → P ⊢ |={E1,E2}=> |={E2,E1}=> P := - λ h => (emp_sep.2.trans <| sep_mono_l <| subset h).trans <| +theorem fupd_mask_intro_subseteq {E1 E2 : CoPset} {P : PROP} (h : E2 ⊆ E1) : + P ⊢ |={E1,E2}=> |={E2,E1}=> P := + (emp_sep.2.trans <| sep_mono_l <| subset h).trans <| frame_r.trans <| mono <| frame_r.trans <| mono emp_sep.1 @[rocq_alias fupd_mask_subseteq] -theorem fupd_mask_subseteq {E1 E2 : CoPset} : E2 ⊆ E1 → ⊢@{PROP} |={E1,E2}=> |={E2,E1}=> emp := - λ Hsub => fupd_mask_intro_subseteq Hsub (P := iprop(emp)) +theorem fupd_mask_subseteq {E1 E2 : CoPset} (h : E2 ⊆ E1) : ⊢@{PROP} |={E1,E2}=> |={E2,E1}=> emp := + fupd_mask_intro_subseteq h theorem fupd_intro {E : CoPset} {P : PROP} : P ⊢ |={E}=> P := (fupd_mask_intro_subseteq λ _ => id).trans trans -- Introduction lemma for a mask-chaging fupd -theorem fupd_mask_intro {E1 E2 : CoPset} {P : PROP} : - E2 ⊆ E1 → ((|={E2,E1}=> emp) -∗ P) ⊢ |={E1,E2}=> P := - λ h => (wand_mono_r fupd_intro).trans <| +theorem fupd_mask_intro {E1 E2 : CoPset} {P : PROP} (h : E2 ⊆ E1) : + ((|={E2,E1}=> emp) -∗ P) ⊢ |={E1,E2}=> P := + (wand_mono_r fupd_intro).trans <| (emp_sep.2.trans <| sep_mono_l <| subset h).trans <| frame_r.trans <| (mono wand_elim_r).trans trans -theorem fupd_mask_intro_discard {E1 E2 : CoPset} {P : PROP} [Absorbing P] : E2 ⊆ E1 → P ⊢ |={E1,E2}=> P := - λ h => (wand_intro' sep_elim_r).trans <| fupd_mask_intro h +theorem fupd_mask_intro_discard {E1 E2 : CoPset} {P : PROP} [Absorbing P] (h : E2 ⊆ E1) : + P ⊢ |={E1,E2}=> P := + (wand_intro' sep_elim_r).trans <| fupd_mask_intro h -theorem fupd_elim {E1 E2 E3 : CoPset} {P Q : PROP} : (Q ⊢ |={E2,E3}=> P) → (|={E1,E2}=> Q) ⊢ |={E1,E3}=> P := - λ h => (mono h).trans trans +theorem fupd_elim {E1 E2 E3 : CoPset} {P Q : PROP} (h : Q ⊢ |={E2,E3}=> P) : + (|={E1,E2}=> Q) ⊢ |={E1,E3}=> P := + (mono h).trans trans theorem fupd_frame_l {E1 E2 : CoPset} {P Q : PROP} : P ∗ (|={E1,E2}=> Q) ⊢ |={E1,E2}=> P ∗ Q := sep_symm.trans <| frame_r.trans <| mono sep_symm @@ -317,22 +320,17 @@ open BIFUpdate LawfulSet theorem step_fupdN_contractive {E1 E2 : CoPset} {n : Nat} [ι : BILaterContractive PROP] : OFE.Contractive (iprop(|={E1}[E2]▷=>^[n + 1] · : PROP)) where - distLater_dist := by - intro i x y xy_i + distLater_dist {i x y} xy_i := by induction n with - | zero => - exact BIFUpdate.ne.ne (ι.distLater_dist (fun j ji => BIFUpdate.ne.ne (xy_i j ji))) - | succ n IH => - exact BIFUpdate.ne.ne (later_ne.ne (BIFUpdate.ne.ne IH)) + | zero => exact ne.ne (ι.distLater_dist (ne.ne <| xy_i · ·)) + | succ n IH => exact ne.ne (later_ne.ne (ne.ne IH)) theorem step_fupdN_ne {E1 E2 : CoPset} {n : Nat} : OFE.NonExpansive (iprop(|={E1}[E2]▷=>^[n] · : PROP)) where - ne := by - intro i x y xy_i + ne {i x y} xy_i := by induction n with | zero => simp [Nat.repeat, xy_i] - | succ n IH => - exact BIFUpdate.ne.ne (later_ne.ne (BIFUpdate.ne.ne IH)) + | succ n IH => exact ne.ne (later_ne.ne (ne.ne IH)) @[rocq_alias step_fupdN_wand] theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : @@ -351,46 +349,35 @@ theorem step_fupdN_wand {Eo Ei : CoPset} {n : Nat} {P Q : PROP} : @[rocq_alias step_fupd_wand] theorem step_fupd_wand {Eo Ei : CoPset} {P Q : PROP} : - (|={Eo}[Ei]▷=> P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=> Q) := by - exact step_fupdN_wand (n := 1) + (|={Eo}[Ei]▷=> P) ⊢ (P -∗ Q) -∗ (|={Eo}[Ei]▷=> Q) := + step_fupdN_wand (n := 1) @[rocq_alias step_fupd_mask_mono] -theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} : - Ei₂ ⊆ Ei₁ → - Eo₁ ⊆ Eo₂ → +theorem step_fupd_mask_mono {Eo₁ Eo₂ Ei₁ Ei₂ : CoPset} {P : PROP} + (Ei₂_Ei₁ : Ei₂ ⊆ Ei₁) (Eo₁_Eo₂ : Eo₁ ⊆ Eo₂) : (|={Eo₁}[Ei₁]▷=> P) ⊢ |={Eo₂}[Ei₂]▷=> P := by - intro Ei₂_Ei₁ Eo₁_Eo₂ - refine emp_sep.2.trans ?_ refine (sep_mono (fupd_mask_intro_subseteq Eo₁_Eo₂) .rfl).trans ?_ refine fupd_frame_r.trans ?_ - refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Eo₁)) - + refine BI.Entails.trans (mono ?_) (trans (E2 := Eo₁)) refine fupd_frame_l.trans ?_ - refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Ei₁)) - + refine BI.Entails.trans (mono ?_) (trans (E2 := Ei₁)) refine (sep_mono (fupd_mask_intro_subseteq Ei₂_Ei₁) .rfl).trans ?_ refine fupd_frame_r.trans ?_ refine mono ?_ - refine (sep_mono later_intro .rfl).trans ?_ refine later_sep.2.trans ?_ refine later_mono ?_ - refine fupd_frame_r.trans ?_ - refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Ei₁)) - + refine BI.Entails.trans (mono ?_) (trans (E2 := Ei₁)) refine fupd_frame_l.trans ?_ - refine BI.Entails.trans (mono ?_) (BIFUpdate.trans (E2 := Eo₁)) - + refine BI.Entails.trans (mono ?_) (trans (E2 := Eo₁)) refine fupd_frame_r.trans ?_ exact mono emp_sep.1 @[rocq_alias step_fupd_intro] -theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} : - Ei ⊆ Eo → +theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} (Ei_Eo : Ei ⊆ Eo) : ▷ P ⊢ |={Eo}[Ei]▷=> P := by - intro Ei_Eo calc iprop(▷ P) _ ⊢ |={Ei}=> ▷ P := fupd_intro _ ⊢ |={Ei}[Ei]▷=> P := mono <| later_mono fupd_intro @@ -398,9 +385,7 @@ theorem step_fupd_intro {Ei Eo : CoPset} {P : PROP} : @[rocq_alias step_fupdN_le] theorem step_fupdN_le {n m : Nat} {Eo Ei : CoPset} {P : PROP} : - n ≤ m → - Ei ⊆ Eo → - (|={Eo}[Ei]▷=>^[n] P) ⊢ |={Eo}[Ei]▷=>^[m] P + n ≤ m → Ei ⊆ Eo → (|={Eo}[Ei]▷=>^[n] P) ⊢ |={Eo}[Ei]▷=>^[m] P | .refl, _ => .rfl | .step (m := m) n_m, Ei_Eo => step_fupdN_le n_m Ei_Eo |>.trans (later_intro.trans (step_fupd_intro Ei_Eo)) From 8a760761737310a05fd9459d251d56e00ece6693 Mon Sep 17 00:00:00 2001 From: Markus de Medeiros Date: Wed, 20 May 2026 15:16:21 -0400 Subject: [PATCH 67/76] minor: wp line length --- Iris/Iris/BI/WeakestPre.lean | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/BI/WeakestPre.lean b/Iris/Iris/BI/WeakestPre.lean index 1f87ae53..1b7a1fd6 100644 --- a/Iris/Iris/BI/WeakestPre.lean +++ b/Iris/Iris/BI/WeakestPre.lean @@ -37,9 +37,11 @@ instance : Std.IsPreorder Stuckness where le_refl := by grind only [Stuckness, LE.le, instLE] le_trans := by grind only [Stuckness, LE.le, instLE] -@[simp] theorem le_MaybeStuck {s : Stuckness} : s ≤ MaybeStuck := by cases s <;> grind only [Stuckness, LE.le, instLE] +@[simp] theorem le_MaybeStuck {s : Stuckness} : s ≤ MaybeStuck := by + cases s <;> grind only [Stuckness, LE.le, instLE] -@[simp] theorem NotSuck_le {s : Stuckness} : NotStuck ≤ s := by cases s <;> grind only [Stuckness, LE.le, instLE] +@[simp] theorem NotSuck_le {s : Stuckness} : NotStuck ≤ s := by + cases s <;> grind only [Stuckness, LE.le, instLE] end Stuckness From f10221a1688057c0326a2c54a0a049df342e5787 Mon Sep 17 00:00:00 2001 From: Markus de Medeiros Date: Wed, 20 May 2026 16:41:48 -0400 Subject: [PATCH 68/76] chore: cleanup in WeakestPre --- Iris/Iris/ProgramLogic/WeakestPre.lean | 483 ++++++++++--------------- 1 file changed, 192 insertions(+), 291 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 0f7e6649..93154cf9 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -22,133 +22,93 @@ open ProgramLogic Language.Notation Std TODO: AddModal, ElimAcc instances -/ -/-- - Carrier typeclass for the `stateInterp` operation. - - - This operation cannot be placed directly inside of `IrisGS_gen` - because Lean then wouldn't be able to derive from its arguments - the values of `Expr` and `Val`, and so they're asked explicitly. - This was not a problem in Iris Rocq becuse of canonical structures. - In Iris Lean, we instead fix our choice of `State` from the choice - of `Expr`, so `Expr` cannot be inferred from `State` instead. +/-- Carrier typeclass for the `stateInterp` operation. + +This operation cannot be placed directly inside of `IrisGS_gen` +because Lean then wouldn't be able to derive from its arguments +the values of `Expr` and `Val`, and so they're asked explicitly. +This was not a problem in Iris Rocq becuse of canonical structures. +In Iris Lean, we instead fix our choice of `State` from the choice +of `Expr`, so `Expr` cannot be inferred from `State` instead. -/ -class StateInterp - (State: semiOutParam <| Type s) - (Obs : outParam <| Type o) - (GF : BundledGFunctors) +class StateInterp (State : semiOutParam $ Type _) (Obs : outParam $ Type _) (GF : BundledGFunctors) where - /-- - Axiomatic interpretation of a state in a language model. - - `σ` is the state whose interpretation we take - - `ns` are the number of prior steps - - `obs` are the observations prior to this state - - `nt` are the number of threads previously spawned - - -/ - stateInterp : State → Nat → List (Obs) → Nat → IProp GF + /-- Interpretation of a state in a language model. Takes a state, + number of steps, list of observations prior to the state, and number of + threads previously spawned. -/ + stateInterp : State → Nat → List Obs → Nat → IProp GF export StateInterp (stateInterp) @[rocq_alias irisGS_gen] -class IrisGS_gen (hlc : outParam <| Bool) - (Expr : Type e) - {Val : Type v} - {State : Type s} - {Obs : Type o} - [Λ : Language Expr State Obs Val] - (GF : BundledGFunctors) - extends - StateInterp State Obs GF, - InvGS_gen hlc GF where - - /-- - Number of later credits obtained from taking one step in the - operational semantics of our language. - -/ +class IrisGS_gen (hlc : outParam Bool) (Expr : Type _) {Val : Type _} {State : Type _} + {Obs : Type _} [Λ : Language Expr State Obs Val] (GF : BundledGFunctors) extends + StateInterp State Obs GF, InvGS_gen hlc GF where + /-- Number of later credits obtained from taking one step in the + operational semantics of our language. -/ numLatersPerStep : Nat → Nat -- TODO: Even when referenced with the typeclass instance, the -- display of `numLatersPerStep` is still kinda awful. - - /-- - Postcondition of forked threads - -/ + /-- Postcondition of forked threads -/ forkPost : Val → IProp GF - - /-- - The number of steps in the state interpretation should only be - considered a lower bound. - -/ + /-- The number of steps in the state interpretation should only be + considered a lower bound. -/ stateInterp_mono σ ns obs nt : iprop(stateInterp σ ns obs nt ⊢ |={∅}=> stateInterp σ (ns + 1) obs nt) - -variable {hlc : outParam Bool} -variable {Expr State Obs Val} +variable {hlc : outParam Bool} {Expr State Obs Val} variable [Λ : Language Expr State Obs Val] -variable {GF : BundledGFunctors} -variable [ι : IrisGS_gen hlc Expr GF] - -instance : IrisGS_gen hlc Expr GF → Language Expr State Obs Val := fun _ => Λ +variable {GF : BundledGFunctors} [ι : IrisGS_gen hlc Expr GF] -/-- Reducibility condition depending on stuckness. -```lean4 --- s.MaybeReducible (e, σ) equivalent to… -if s matches .NotStuck then Reducible (e, σ) else True -``` --/ +/-- Reducibility condition depending on stuckness. -/ abbrev Stuckness.MaybeReducible : Stuckness → Expr × State → Prop | .NotStuck, (e₁, σ₁) => PrimStep.Reducible (e₁, σ₁) | _, _ => True @[rocq_alias wp_pre] -def wp.pre (s : Stuckness) - (wp : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF) : - CoPset -> Expr -> (Val -> IProp GF) -> IProp GF := fun E e₁ Φ => +def wp.pre (s : Stuckness) (wp : CoPset -> Expr -> (Val -> IProp GF) -> IProp GF) (E : CoPset) + (e₁ : Expr) (Φ : Val -> IProp GF) : IProp GF := match toVal e₁ with | some v => iprop(|={E}=> Φ v) | none => iprop(∀ (σ₁ : State) (ns : Nat) (obs obs' : List Obs) (nt : Nat), stateInterp σ₁ ns (obs ++ obs') nt ={E,∅}=∗ ⌜s.MaybeReducible (e₁, σ₁)⌝ ∗ ∀ e₂ σ₂ eₜ, ⌜(e₁, σ₁) --> (e₂, σ₂, eₜ)⌝ -∗ - £ (ι.numLatersPerStep ns + 1) - ={∅}▷=∗^[ι.numLatersPerStep ns + 1] |={∅,E}=> - -- NOTE: Changed the order of `nt` and `eₜ.length` since in Lean - -- we have `n + 0 = n` and not `0 + n = n` + £ (ι.numLatersPerStep ns + 1) ={∅}▷=∗^[ι.numLatersPerStep ns + 1] |={∅,E}=> stateInterp σ₂ (ns + 1) obs' (nt + eₜ.length) ∗ - wp E e₂ Φ ∗ - [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) + wp E e₂ Φ ∗ [∗list] e' ∈ eₜ, wp ⊤ e' ι.forkPost) @[rocq_alias wp_pre_contractive] instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where distLater_dist := by intros n wp wp' Hwp E e₁ Φ - dsimp only [pre] + unfold pre cases toVal e₁ - case some _ => exact .rfl - dsimp only - refine BI.forall_ne (fun σ₁ => ?_) - refine BI.forall_ne (fun ns => ?_) - refine BI.forall_ne (fun obs => ?_) - refine BI.forall_ne (fun obs' => ?_) - refine BI.forall_ne (fun nt => ?_) - refine BI.wand_ne.ne .rfl ?_ - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne .rfl ?_ - refine BI.forall_ne (fun e₂ => ?_) - refine BI.forall_ne (fun σ₂ => ?_) - refine BI.forall_ne (fun eₜ => ?_) - refine BI.wand_ne.ne .rfl ?_ - refine BI.wand_ne.ne .rfl ?_ - refine BIFUpdate.ne.ne ?_ - refine OFE.Contractive.distLater_dist fun m m_n => ?_ - refine BIFUpdate.ne.ne ?_ - refine step_fupdN_ne.ne ?_ - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne .rfl ?_ - refine BI.sep_ne.ne ?_ ?_ - · exact Hwp m m_n _ _ _ - · exact BI.BigSepL.bigSepL_dist (fun _ => (Hwp m m_n _ _ _)) + case some _ => + exact .rfl + case none => + refine BI.forall_ne (fun σ₁ => ?_) + refine BI.forall_ne (fun ns => ?_) + refine BI.forall_ne (fun obs => ?_) + refine BI.forall_ne (fun obs' => ?_) + refine BI.forall_ne (fun nt => ?_) + refine BI.wand_ne.ne .rfl ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.forall_ne (fun e₂ => ?_) + refine BI.forall_ne (fun σ₂ => ?_) + refine BI.forall_ne (fun eₜ => ?_) + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BIFUpdate.ne.ne ?_ + refine OFE.Contractive.distLater_dist fun m m_n => ?_ + refine BIFUpdate.ne.ne ?_ + refine step_fupdN_ne.ne ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.sep_ne.ne ?_ ?_ + · exact Hwp m m_n _ _ _ + · exact BI.BigSepL.bigSepL_dist <| fun _ => Hwp m m_n _ _ _ @[implicit_reducible, rocq_alias wp_def] instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where @@ -173,34 +133,30 @@ instance wp_ne {s : Stuckness} {E} {e : Expr} : simp only [IProp.ext wp_unfold] dsimp only [wp.pre] cases toVal e - case some v => exact BIFUpdate.ne.ne <| HΦ v - - refine BI.forall_ne fun σ₁ => ?_ - refine BI.forall_ne fun ns => ?_ - refine BI.forall_ne fun obs => ?_ - refine BI.forall_ne fun obs' => ?_ - refine BI.forall_ne fun nt => ?_ - refine BI.wand_ne.ne .rfl ?_ - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne .rfl ?_ - refine BI.forall_ne fun e₂ => ?_ - refine BI.forall_ne fun σ₂ => ?_ - refine BI.forall_ne fun eₜ => ?_ - refine BI.wand_ne.ne .rfl ?_ - refine BI.wand_ne.ne .rfl ?_ - - -- The `step_fupdN` |={∅}▷=>^[n+1] is contractive - refine step_fupdN_contractive.distLater_dist fun m n_m => ?_ - - refine BIFUpdate.ne.ne ?_ - refine BI.sep_ne.ne .rfl ?_ - refine BI.sep_ne.ne ?_ .rfl - - exact IH m n_m <| OFE.dist_lt HΦ n_m + case some v => + exact BIFUpdate.ne.ne <| HΦ v + case none => + refine BI.forall_ne fun σ₁ => ?_ + refine BI.forall_ne fun ns => ?_ + refine BI.forall_ne fun obs => ?_ + refine BI.forall_ne fun obs' => ?_ + refine BI.forall_ne fun nt => ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.forall_ne fun e₂ => ?_ + refine BI.forall_ne fun σ₂ => ?_ + refine BI.forall_ne fun eₜ => ?_ + refine BI.wand_ne.ne .rfl ?_ + refine BI.wand_ne.ne .rfl ?_ + refine step_fupdN_contractive.distLater_dist fun m n_m => ?_ + refine BIFUpdate.ne.ne ?_ + refine BI.sep_ne.ne .rfl ?_ + refine BI.sep_ne.ne ?_ .rfl + exact IH m n_m <| OFE.dist_lt HΦ n_m #rocq_ignore wp_proper "Derivable using NonExpansive.eqv" --- This definition comes after `wp_ne` because it depends on it. @[rocq_alias wp_contractive] instance wp_contractive (s : Stuckness) E (e : Expr) (h : toVal e = none) : OFE.Contractive (Wp.wp (PROP := IProp GF) s E e) where @@ -233,11 +189,11 @@ theorem wp_value_fupd' {s : Stuckness} {E} {Φ : Val → IProp GF} {v : Val} : simp [IProp.ext wp_unfold, toVal_coe, BI.BIBase.BiEntails.rfl, wp.pre] @[rocq_alias wp_strong_mono] -theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} : - s₁ ≤ s₂ → E₁ ⊆ E₂ → +theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : Val → IProp GF} + (hs : s₁ ≤ s₂) (hE : E₁ ⊆ E₂) : ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by - intro hs hE iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE + -- FIXME: Delaboration error rw [IProp.ext wp_unfold, IProp.ext wp_unfold] iintro H HΦ dsimp only [wp.pre] @@ -249,29 +205,26 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V icases H $$ Hσ with >⟨%h, H⟩ imodintro isplit - · match s₁, s₂ with - | .MaybeStuck, .NotStuck => simp [LE.le] at hs - | .NotStuck, .NotStuck - | .MaybeStuck, .MaybeStuck - | .NotStuck, .MaybeStuck => - ipure_intro; grind only - iintro %e₂ %σ₂ %eₜ #hstep hc - dsimp only [Nat.repeat] - imod H $$ hstep hc with H - iintro !> !>; imod H; iintro !> - iapply step_fupdN_wand $$ H - iintro >⟨aux, H, Hefs⟩ - imod Hclose - imodintro - iframe aux - isplitr [Hefs] - · iapply IH $$ %e₂ %Φ %Ψ %E₁ %E₂ %hE H HΦ - · iapply BI.BigSepL.bigSepL_impl $$ Hefs - iintro !> %k %e' %_ H - iapply IH $$ %e' %_ %_ %⊤ %_ %LawfulSet.subset_refl H - iintro %v H + · simp only [LE.le] at hs + ipure_intro + grind [cases Stuckness] + · iintro %e₂ %σ₂ %eₜ #hstep hc + dsimp only [Nat.repeat] + imod H $$ hstep hc with H + iintro !> !>; imod H; iintro !> + iapply step_fupdN_wand $$ H + iintro >⟨aux, H, Hefs⟩ + imod Hclose imodintro - iassumption + iframe aux + isplitr [Hefs] + · iapply IH $$ %e₂ %Φ %Ψ %E₁ %E₂ %hE H HΦ + · iapply BI.BigSepL.bigSepL_impl $$ Hefs + iintro !> %k %e' %_ H + iapply IH $$ %e' %_ %_ %⊤ %_ %LawfulSet.subset_refl H + iintro %v H + imodintro + iassumption | some v => dsimp only imod fupd_mask_mono hE $$ H with h @@ -294,10 +247,8 @@ theorem fupd_wp {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : iassumption theorem fupd_wp_iff {s : Stuckness}{E}{e : Expr} {Φ : Val → IProp GF} : - WP e @ s ; E {{ Φ }} ⊣⊢ (|={E}=> WP e @ s ; E {{ Φ }}) := by - constructor - · exact fupd_mask_intro_discard LawfulSet.subset_refl - · exact fupd_wp + WP e @ s ; E {{ Φ }} ⊣⊢ (|={E}=> WP e @ s ; E {{ Φ }}) := + ⟨fupd_mask_intro_discard LawfulSet.subset_refl, fupd_wp⟩ @[rocq_alias wp_fupd] theorem wp_fupd (s : Stuckness) E (e : Expr) (Φ : Val → IProp GF) : @@ -352,79 +303,51 @@ theorem wp_atomic {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {Φ : Val → IPro imod H with > H iframe -/-- (copy-pasted from Rocq formalization) - - This lemma gives us access to the later credits that are generated in each step, - asuming that we have instantiated `numLaterPerStep` with a non-trivial function - (for instance, a linear function). - - This lemma can be used to provide a "regeneration" mechanism for later credits. - `stateInter` will have to be defined in a way that involves the required - regeneration tokens. - - In detail, a client can use this lemma as follows: - - 1. Then client obtains the state interpreatation `stateInterp _ ns _ _` - - 2. It uses some ghost state wired up to the interpretation to know that - `ns = k + m`, and update the state interpretation to `stateInterp _ m _ _` - - 3. _After_ `e` has finally stepped, we get `ι.numLatersPerStep k` later credits - that we can use to prove `P` in the postcondition, and we have to update the - state interpretation from `stateInterp _ (m+1) _ _` to - `stateInterp _ (ns+1) _ _` again - --/ @[rocq_alias wp_credit_access] -theorem wp_credit_access {s : Stuckness} {E : CoPset}{e : Expr}{Φ}{P: IProp GF} : - toVal e = none → - (∀ m k, ι.numLatersPerStep m + ι.numLatersPerStep k ≤ ι.numLatersPerStep (m + k)) → - (∀ (σ₁ : State) ns obs nt, - stateInterp σ₁ ns obs nt ={E}=∗ - ∃ k m, stateInterp σ₁ m obs nt ∗ ⌜ns = m + k⌝ ∗ ( - ∀ nt (σ₂: State) obs, £ (ι.numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ - stateInterp σ₂ (ns+1) obs nt ∗ P)) ⊢ - WP e @ s ; E {{ v, P ={E}=∗ Φ v }} -∗ - WP e @ s ; E {{ Φ }} := by - intro h Htri - simp only [IProp.ext wp_unfold] - iintro Hupd Hwp - simp only [wp.pre, h] - iintro %σ₁ %ns %obs %obs' %nt Hσ₁ - imod Hupd $$ Hσ₁ with ⟨%k, %m, Hσ₁, %h, Hpost⟩; subst h - imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ - imodintro - iintro %e₂ %σ₂ %efs %Hstep Hc - simp only [IProp.ext lc_split] - icases Hc with ⟨Hc,Hone⟩ - ihave Hc := lc_weaken _ (Htri m k) $$ Hc - icases lc_split $$ Hc with ⟨Hm, Hk⟩ - -- TODO: Redo with `icombine` when available - ihave Hm := lc_split.mpr $$ [$Hm $Hone] - dsimp only [Nat.repeat] - ihave Hwp := Hwp $$ [] [Hm] - · ipure_intro; assumption - · simp only [OFE.eq_of_eqv (BI.equiv_iff.mpr lc_split), OFE.eq_of_eqv (BI.equiv_iff.mpr BI.sep_comm)] - exact .rfl - iapply step_fupd_wand $$ Hwp; iintro Hwp - iapply step_fupdN_le (n := ι.numLatersPerStep m) (by grind only) (LawfulSet.subset_refl) - iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ - icases Hpost $$ Hk SI with >⟨$, HP⟩ - imodintro - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ Hwp - iintro %v HΦ - iapply HΦ $$ HP +theorem wp_credit_access {s : Stuckness} {E : CoPset} {e : Expr} {Φ} {P: IProp GF} (h : toVal e = none) + (Htri : ∀ m k, ι.numLatersPerStep m + ι.numLatersPerStep k ≤ ι.numLatersPerStep (m + k)) : + (∀ (σ₁ : State) ns obs nt, + stateInterp σ₁ ns obs nt ={E}=∗ + ∃ k m, stateInterp σ₁ m obs nt ∗ ⌜ns = m + k⌝ ∗ ( + ∀ nt (σ₂: State) obs, £ (ι.numLatersPerStep k) -∗ stateInterp σ₂ (m+1) obs nt ={E}=∗ + stateInterp σ₂ (ns+1) obs nt ∗ P)) ⊢ + WP e @ s ; E {{ v, P ={E}=∗ Φ v }} -∗ + WP e @ s ; E {{ Φ }} := by + simp only [IProp.ext wp_unfold] + iintro Hupd Hwp + simp only [wp.pre, h] + iintro %σ₁ %ns %obs %obs' %nt Hσ₁ + imod Hupd $$ Hσ₁ with ⟨%k, %m, Hσ₁, %h, Hpost⟩; subst h + imod Hwp $$ Hσ₁ with ⟨$,Hwp⟩ + imodintro + iintro %e₂ %σ₂ %efs %Hstep Hc + simp only [IProp.ext lc_split] + icases Hc with ⟨Hc,Hone⟩ + ihave Hc := lc_weaken _ (Htri m k) $$ Hc + icases lc_split $$ Hc with ⟨Hm, Hk⟩ + -- TODO: Redo with `icombine` when available + ihave Hm := lc_split.mpr $$ [$Hm $Hone] + dsimp only [Nat.repeat] + ihave Hwp := Hwp $$ [] [Hm] + · ipure_intro; assumption + · simp [OFE.eq_of_eqv (BI.equiv_iff.mpr lc_split)] + iapply step_fupd_wand $$ Hwp; iintro Hwp + iapply step_fupdN_le (n := ι.numLatersPerStep m) (by grind only) LawfulSet.subset_refl + iapply step_fupdN_wand $$ Hwp; iintro >⟨SI, Hwp, $⟩ + icases Hpost $$ Hk SI with >⟨$, HP⟩ + imodintro + iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ Hwp + iintro %v HΦ + iapply HΦ $$ HP -- TODO: icases support for ⟨_, H⟩ and ⟨H, _⟩ patterns for hypothesis of the form A ∧ B @[rocq_alias wp_step_fupdN_strong] -theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} {n} : - toVal e = none → - E2 ⊆ E1 → +theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} {n} + (toVal_e : toVal e = none) (E2_E1 : E2 ⊆ E1) : (∀ (σ : State) ns obs nt, stateInterp σ ns obs nt ={E1, ∅}=∗ ⌜n ≤ ι.numLatersPerStep ns + 1⌝) ∧ ((|={E1,E2}=> |={∅}▷=>^[n] |={E2,E1}=> P) ∗ WP e @ s ; E2 {{ v, P ={E1}=∗ Φ v}}) ⊢ WP e @ s ; E1 {{ Φ }} := by - intro toVal_e E2_E1 match n with | 0 => iintro H @@ -439,8 +362,8 @@ theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IP simp only [IProp.ext wp_unfold] simp only [wp.pre, toVal_e] iintro H %σ₁ %ns %obs %obs' %nt Hσ₁ - if Hn : n ≤ ι.numLatersPerStep ns then - icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ + by_cases Hn : n ≤ ι.numLatersPerStep ns + · icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ imod Hp dsimp only [Nat.repeat] imod Hwp $$ Hσ₁ with ⟨$, H⟩ @@ -463,30 +386,24 @@ theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IP iintro %v HΦ iapply HΦ $$ Hp | succ n IH => - obtain ⟨n0, rfl⟩ : ∃ n0', n0 = n0' + 1 := by cases n0 <;> grind only + obtain ⟨n0, rfl⟩ : ∃ n0', n0 = n0' + 1 := by cases n0 <;> grind dsimp only [Nat.repeat] - imod Hp - imod H - imodintro - imodintro - imod Hp - imod H - imodintro + imod Hp; imod H; imodintro; imodintro; imod Hp; imod H; imodintro iapply IH n0 (Nat.le_of_succ_le_succ Hn) $$ [$]; - else - icases BI.and_elim_l $$ H with interp + · icases BI.and_elim_l $$ H with interp imod interp $$ Hσ₁ with %h grind only @[rocq_alias wp_bind] -theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : +theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} + {Φ : Val → IProp GF} : -- TODO: Have `WP` use the correct `Val` type from the `Wp` instance (it should anyways, it's an outParam, no?) WP e @ s ; E {{v, WP (K (↑v : Val)) @ s ; E {{ Φ }} }} ⊢ WP (K e) @ s ; E {{ Φ }} := by iintro H iloeb as IH generalizing %E %e %Φ rewrite (occs := [2]) [IProp.ext wp_unfold] simp only [wp.pre] - match h: toVal e with + match h : toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H @@ -498,20 +415,21 @@ theorem wp_bind (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E imodintro isplit · ipure_intro; grind only [cases Stuckness, Language.Context.reducible_fill] - iintro %e₂ %σ₂ %efs %HKstep Hcred - obtain ⟨e₂', rfl, Hstep⟩ := κ.primStep_fill_inv h HKstep - icases H $$ %e₂' %σ₂ %efs %Hstep Hcred with >H; imodintro; imodintro - imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H - imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H + · iintro %e₂ %σ₂ %efs %HKstep Hcred + obtain ⟨e₂', rfl, Hstep⟩ := κ.primStep_fill_inv h HKstep + icases H $$ %e₂' %σ₂ %efs %Hstep Hcred with >H; imodintro; imodintro + imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H + imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H @[rocq_alias wp_bind_inv] -theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : +theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness} {E : CoPset} {e : Expr} + {Φ : Val → IProp GF} : WP (K e) @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{v, WP (K (↑v : Val)) @ s ; E {{ Φ }} }} := by iintro H iloeb as IH generalizing %E %e %Φ rewrite (occs := [3]) [IProp.ext wp_unfold] simp only [wp.pre] - match h: toVal e with + match h : toVal e with | some v => simp only [ToVal.coe_of_toVal_eq_some h] iapply fupd_wp $$ H @@ -523,10 +441,10 @@ theorem wp_bind_inv (K : Expr → Expr) [κ : Language.Context K] {s : Stuckness imodintro isplit · ipure_intro; grind only [cases Stuckness, Language.Context.reducible_fill_inv] - iintro %e₂ %σ₂ %efs %Hstep Hcred - icases H $$ %(K e₂) %σ₂ %efs %(κ.primStep_fill Hstep) Hcred with >H; imodintro; imodintro - imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H - imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H + · iintro %e₂ %σ₂ %efs %Hstep Hcred + icases H $$ %(K e₂) %σ₂ %efs %(κ.primStep_fill Hstep) Hcred with >H; imodintro; imodintro + imod H; imodintro; iapply step_fupdN_wand $$ H; iintro H + imod H with ⟨$, H, $⟩; imodintro; iapply IH $$ H /-! ## Derived rules -/ @@ -535,8 +453,7 @@ theorem wp_mono {s : Stuckness} {E : CoPset} {e : Expr} {Φ Ψ : Val → IProp G (∀ v, Φ v ⊢ Ψ v) → WP e @ s ; E {{ Φ }} ⊢ WP e @ s ; E {{ Ψ }} := by iintro %HΦ H iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ H - iintro %v HΨ - imodintro + iintro %v HΨ !> iapply HΦ $$ HΨ @[rocq_alias wp_stuck_mono] @@ -550,12 +467,12 @@ theorem wp_stuck_mono {s₁ s₂ : Stuckness} {E : CoPset} {e : Expr} {Φ : Val @[rocq_alias wp_stuck_weaken] theorem wp_stuck_weaken {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} : WP e @ s; E {{ Φ }} ⊢ WP e @ E ?{{ Φ }} := - wp_stuck_mono (Stuckness.le_MaybeStuck) + wp_stuck_mono (Stuckness.le_MaybeStuck) @[rocq_alias wp_mask_mono] theorem wp_mask_mono {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} - : E₁ ⊆ E₂ → WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by - iintro %E₁_E₂ Hwp + (E₁_E₂ : E₁ ⊆ E₂) : WP e @ s; E₁ {{ Φ }} ⊢ WP e @ s; E₂ {{ Φ }} := by + iintro Hwp iapply wp_strong_mono (Std.IsPreorder.le_refl s) E₁_E₂ $$ Hwp iintro %v HΦ iframe HΦ @@ -564,23 +481,23 @@ theorem wp_mask_mono {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val #rocq_ignore wp_flip_mono' "No `Proper` typeclass in Lean" @[rocq_alias wp_value_fupd] -theorem wp_value_fupd {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} - : Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v +theorem wp_value_fupd {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} : + Language.IntoVal e v → WP e @ s; E {{ Φ }} ⊣⊢ |={E}=> Φ v | ⟨h⟩ => h ▸ wp_value_fupd' @[rocq_alias wp_value'] -theorem wp_value' {s : Stuckness} {E : CoPset} {v : Val} {Φ : Val → IProp GF} - : Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := +theorem wp_value' {s : Stuckness} {E : CoPset} {v : Val} {Φ : Val → IProp GF} : + Φ v ⊢ WP (v : Expr) @ s; E {{ Φ }} := fupd_intro.trans wp_value_fupd'.2 @[rocq_alias wp_value] -theorem wp_value {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} - : Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} +theorem wp_value {s : Stuckness} {E : CoPset} {e : Expr} {v : Val} {Φ : Val → IProp GF} : + Language.IntoVal e v → Φ v ⊢ WP e @ s; E {{ Φ }} | ⟨h⟩ => h ▸ wp_value' @[rocq_alias wp_frame_l] -theorem wp_frame_l {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} - : R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by +theorem wp_frame_l {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} : + R ∗ WP e @ s; E {{ Φ }} ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := by iintro ⟨_, H⟩ iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ H iframe @@ -588,28 +505,17 @@ theorem wp_frame_l {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp G iapply fupd_intro @[rocq_alias wp_frame_r] -theorem wp_frame_r {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} - : WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := +theorem wp_frame_r {s : Stuckness} {E : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} : + WP e @ s; E {{ Φ }} ∗ R ⊢ WP e @ s; E {{ v, R ∗ Φ v }} := BI.sep_comm.1.trans wp_frame_l -/-- (copy-pasted from Rocq formalization) - - This lemma states that if we can prove that [n] laters are used in - the current physical step, then one can perform an n-steps fancy - update during that physical step. The resources needed to prove the - bound on [n] are not used up: they can be reused in the proof of - the WP or in the proof of the n-steps fancy update. In order to - describe this unusual resource flow, we use ordinary conjunction as - a premise. --/ @[rocq_alias wp_step_fupdN] theorem wp_step_fupdN {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp GF} {Φ : Val → IProp GF} {n : Nat} - : toVal e = none → E₂ ⊆ E₁ → + (toVal_e : toVal e = none) (E₂E₁ : E₂ ⊆ E₁) : (∀ (σ : State) ns obs nt, stateInterp σ ns obs nt ={E₁,∅}=∗ ⌜n ≤ (ι.numLatersPerStep ns)+1⌝) ∧ (((|={E₁\E₂,∅}=> |={∅}▷=>^[n] |={∅,E₁\E₂}=> P) ∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }})) -∗ WP e @ s; E₁ {{ Φ }} := by - intro toVal_e E₂E₁ iintro H iapply wp_step_fupdN_strong (s := s) (P := P) (n := n) toVal_e E₂E₁ $$ [H] iapply BI.and_mono_r $$ H @@ -617,51 +523,50 @@ theorem wp_step_fupdN {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp iintro HP imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from LawfulSet.diff_subset_left) with G imod HP - imod G with toClear; iclear toClear + imod G with - rw [show E₁ \ (E₁ \ E₂) = E₂ from LawfulSet.diff_self_diff_of_subset E₂E₁] imodintro iapply step_fupdN_wand $$ HP; iintro H - iapply fupd_mask_frame (LawfulSet.empty_subset) + iapply fupd_mask_frame LawfulSet.empty_subset imod H imodintro simp [LawfulSet.diff_empty, ←LawfulSet.diff_subset_decomp E₂E₁, fupd_intro] @[rocq_alias wp_step_fupd] theorem wp_step_fupd {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp GF} {Φ : Val → IProp GF} - : toVal e = none → E₂ ⊆ E₁ → - (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }} -∗ WP e @ s; E₁ {{ Φ }} := - fun toVal_e E₂E₁=> by + (toVal_e : toVal e = none) (E₂E₁ : E₂ ⊆ E₁) : + (|={E₁}[E₂]▷=> P) -∗ WP e @ s; E₂ {{ v, P ={E₁}=∗ Φ v }} -∗ WP e @ s; E₁ {{ Φ }} := by iintro HR H iapply wp_step_fupdN_strong (n := 1) (P := P) toVal_e E₂E₁ $$ [-] iframe H isplit · iintro %σ %ns %obj %nt interp iapply fupd_mask_intro_discard LawfulSet.empty_subset $$ [HR] - simp [Nat.le_add_left, BI.true_intro] + simp [BI.true_intro] · imod HR dsimp only [Nat.repeat] iframe @[rocq_alias wp_frame_step_l] theorem wp_frame_step_l {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} - : toVal e = none → E₂ ⊆ E₁ → + (toVal_e : toVal e = none) (E₂E₁ : E₂ ⊆ E₁) : (|={E₁}[E₂]▷=> R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by - iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ + iintro ⟨Hu, Hwp⟩ iapply wp_step_fupd toVal_e E₂E₁ $$ Hu iapply wp_mono $$ Hwp iintro %x $ $ @[rocq_alias wp_frame_step_r] theorem wp_frame_step_r {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} - : toVal e = none → E₂ ⊆ E₁ → + (h1 : toVal e = none) (h2 : E₂ ⊆ E₁) : WP e @ s; E₂ {{ Φ }} ∗ (|={E₁}[E₂]▷=> R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := - (BI.sep_comm.1.trans <| wp_frame_step_l · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) + (BI.sep_comm.1.trans <| wp_frame_step_l h1 h2 |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) @[rocq_alias wp_frame_step_l'] theorem wp_frame_step_l' {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Val → IProp GF} {R : IProp GF} - : toVal e = none → E₂ ⊆ E₁ → + (toVal_e : toVal e = none) (E₂E₁ : E₂ ⊆ E₁) : (▷ R) ∗ WP e @ s; E₂ {{ Φ }} ⊢ WP e @ s; E₁ {{ v, R ∗ Φ v }} := by - iintro %toVal_e %E₂E₁ ⟨Hu, Hwp⟩ + iintro ⟨Hu, Hwp⟩ iapply wp_frame_step_l toVal_e E₂E₁ iframe iapply fupd_mask_intro E₂E₁ @@ -673,15 +578,14 @@ theorem wp_frame_step_l' {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr}{Φ : Va @[rocq_alias wp_frame_step_r'] theorem wp_frame_step_r' {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {Φ : Val → IProp GF} {R : IProp GF} - : toVal e = none → E₂ ⊆ E₁ → - WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := - (BI.sep_comm.1.trans <| wp_frame_step_l' · · |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) + (h1 : toVal e = none) (h2 : E₂ ⊆ E₁) : WP e @ s; E₂ {{ Φ }} ∗ (▷ R) ⊢ WP e @ s; E₁ {{ v, Φ v ∗ R }} := + (BI.sep_comm.1.trans <| wp_frame_step_l' h1 h2 |>.trans <| wp_mono (fun _ => BI.sep_comm.1)) @[rocq_alias wp_wand] -theorem wp_wand {s : Stuckness} {E : CoPset} {e : Expr} {Φ Ψ : Val → IProp GF} - : WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by +theorem wp_wand {s : Stuckness} {E : CoPset} {e : Expr} {Φ Ψ : Val → IProp GF} : + WP e @ s ; E {{ Φ }} ⊢ (∀ v, Φ v -∗ Ψ v) -∗ WP e @ s ; E {{ Ψ }} := by iintro Hwp H - iapply wp_strong_mono (Std.IsPreorder.le_refl s) (LawfulSet.subset_refl) $$ Hwp + iapply wp_strong_mono (Std.IsPreorder.le_refl s) LawfulSet.subset_refl $$ Hwp iintro %v HΦ icases H $$ HΦ with H imodintro; iframe @@ -725,10 +629,9 @@ instance frameWp {p : Bool} [H : ∀ v, Frame p R (Φ v) (Ψ v)] : -- see: https://github.com/leanprover-community/iris-lean/pull/393 Frame p R (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Ψ }}) where frame := by - replace H v := (H v).frame refine wp_frame_l.trans ?_ apply wp_mono - apply H + exact fun v => frame @[rocq_alias is_except_0_wp] instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where @@ -742,7 +645,7 @@ instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where instance elimModalFupdWp p : ElimModal True p false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where elim_modal := by - intro ⟨⟩; iintro ⟨H, G⟩ + iintro %_ ⟨H, G⟩ icases BI.intuitionisticallyIf_elim $$ H with H iapply fupd_wp imod H; imodintro @@ -756,11 +659,9 @@ instance elimModalBupdWp p : refine BI.sep_mono (BI.intuitionisticallyIf_mono (BIUpdateFUpdate.fupd_of_bupd (E := E))) .rfl |>.trans ?_ apply elimModalFupdWp _ |>.elim_modal ⟨⟩ -/-- - Error message instance for non-mask-changing view shifts. Also uses a slightly - different error: we cannot apply `fupd_mask_subseteq` if `e` is not atomic, so - we tell the user to first add a leading `fupd` and then change the mask of that. --/ +/-- Error message instance for non-mask-changing view shifts. Also uses a slightly +different error: we cannot apply `fupd_mask_subseteq` if `e` is not atomic, so +we tell the user to first add a leading `fupd` and then change the mask of that. -/ @[rocq_alias elim_modal_fupd_wp_wrong_mask] instance elimModalFupdWp_wrongMask : ElimModal (PMError "Goal and eliminated modality must have the same mask. From 22fe3e62d0292bc30e74d1d917f06ebd7d873068 Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Thu, 21 May 2026 10:45:11 +0200 Subject: [PATCH 69/76] fix delab bug, minor nits --- Iris/Iris/ProgramLogic/WeakestPre.lean | 12 ++++-------- Iris/Iris/ProofMode/Tactics/Revert.lean | 5 ++++- Iris/Iris/Tests.lean | 2 ++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 93154cf9..895c7864 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -193,7 +193,6 @@ theorem wp_strong_mono {s₁ s₂ : Stuckness} {E₁ E₂} {e : Expr} {Φ Ψ : V (hs : s₁ ≤ s₂) (hE : E₁ ⊆ E₂) : ⊢ WP e @ s₁ ; E₁ {{ Φ }} -∗ (∀ v, Φ v ={E₂}=∗ Ψ v) -∗ WP e @ s₂ ; E₂ {{ Ψ }} := by iloeb as IH generalizing %e %Φ %Ψ %E₁ %E₂ %hE - -- FIXME: Delaboration error rw [IProp.ext wp_unfold, IProp.ext wp_unfold] iintro H HΦ dsimp only [wp.pre] @@ -340,7 +339,6 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset} {e : Expr} {Φ} {P: IProp iintro %v HΦ iapply HΦ $$ HP --- TODO: icases support for ⟨_, H⟩ and ⟨H, _⟩ patterns for hypothesis of the form A ∧ B @[rocq_alias wp_step_fupdN_strong] theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IProp GF} {Φ} {n} (toVal_e : toVal e = none) (E2_E1 : E2 ⊆ E1) : @@ -350,8 +348,7 @@ theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IP WP e @ s ; E1 {{ Φ }} := by match n with | 0 => - iintro H - icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ + iintro ⟨-, ⟨Hp, Hwp⟩⟩ iapply wp_strong_mono (Std.IsPreorder.le_refl s) E2_E1 $$ Hwp iintro %v H dsimp only [Nat.repeat] @@ -363,7 +360,7 @@ theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IP simp only [wp.pre, toVal_e] iintro H %σ₁ %ns %obs %obs' %nt Hσ₁ by_cases Hn : n ≤ ι.numLatersPerStep ns - · icases BI.and_elim_r $$ H with ⟨Hp, Hwp⟩ + · icases H with ⟨-, ⟨Hp, Hwp⟩⟩ imod Hp dsimp only [Nat.repeat] imod Hwp $$ Hσ₁ with ⟨$, H⟩ @@ -390,7 +387,7 @@ theorem wp_step_fupdN_strong {s : Stuckness} {E1 E2 : CoPset} {e : Expr} {P : IP dsimp only [Nat.repeat] imod Hp; imod H; imodintro; imodintro; imod Hp; imod H; imodintro iapply IH n0 (Nat.le_of_succ_le_succ Hn) $$ [$]; - · icases BI.and_elim_l $$ H with interp + · icases H with ⟨interp, -⟩ imod interp $$ Hσ₁ with %h grind only @@ -519,8 +516,7 @@ theorem wp_step_fupdN {s : Stuckness} {E₁ E₂ : CoPset} {e : Expr} {P : IProp iintro H iapply wp_step_fupdN_strong (s := s) (P := P) (n := n) toVal_e E₂E₁ $$ [H] iapply BI.and_mono_r $$ H - apply BI.sep_mono_l - iintro HP + iintro ⟨HP, $⟩ imod fupd_mask_subseteq_emptyset_difference (show E₁\ E₂ ⊆ E₁ from LawfulSet.diff_subset_left) with G imod HP imod G with - diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index cb9e4b98..905367f3 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -69,7 +69,10 @@ private def RevertState.revertLeanForallHyp ProofModeM (@RevertState u prop bi origE origGoal) := do let { e, hyps, goal, reverted, pf } := st let x : Q($α) := mkFVar f - have Φ : Q($α → $prop) := ← mkLambdaFVars #[x] goal + have Φ : Q($α → $prop) := ← do + return match ← mkLambdaFVars #[x] goal with + | .lam n t b _ => .lam n t b .default + | e => e let goal' : Q($prop) := q(BI.forall $Φ) have pf' : Q(($e ⊢ $goal') → ($origE ⊢ $origGoal)) := ← withLocalDeclDQ `h q($e ⊢ BI.forall $Φ) fun h => do diff --git a/Iris/Iris/Tests.lean b/Iris/Iris/Tests.lean index ccd8f5cd..5ff15ac5 100644 --- a/Iris/Iris/Tests.lean +++ b/Iris/Iris/Tests.lean @@ -5,3 +5,5 @@ public import Iris.Tests.InstancesImport public import Iris.Tests.Notation public import Iris.Tests.Tactics public import Iris.Tests.HeapLang +public import Iris.Tests.Language +public import Iris.Tests.WP From f54a7c6272c582b7a3157ac3896a21e2e5a1f392 Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Thu, 21 May 2026 10:49:01 +0200 Subject: [PATCH 70/76] todo icombine --- Iris/Iris/ProgramLogic/WeakestPre.lean | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 895c7864..b4f33df8 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -324,8 +324,7 @@ theorem wp_credit_access {s : Stuckness} {E : CoPset} {e : Expr} {Φ} {P: IProp icases Hc with ⟨Hc,Hone⟩ ihave Hc := lc_weaken _ (Htri m k) $$ Hc icases lc_split $$ Hc with ⟨Hm, Hk⟩ - -- TODO: Redo with `icombine` when available - ihave Hm := lc_split.mpr $$ [$Hm $Hone] + icombine Hm Hone as Hm dsimp only [Nat.repeat] ihave Hwp := Hwp $$ [] [Hm] · ipure_intro; assumption From bddadc59e223f4a7d65b51dd1181bee613457f78 Mon Sep 17 00:00:00 2001 From: ayhon Date: Thu, 21 May 2026 10:59:07 +0200 Subject: [PATCH 71/76] fix: allow `State` to not be `semiOutParam` --- Iris/Iris/NotationTest.lean.ignore | 118 +++++++++++++++++++++++++ Iris/Iris/ProgramLogic/WeakestPre.lean | 2 +- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Iris/Iris/NotationTest.lean.ignore diff --git a/Iris/Iris/NotationTest.lean.ignore b/Iris/Iris/NotationTest.lean.ignore new file mode 100644 index 00000000..d0c8b12f --- /dev/null +++ b/Iris/Iris/NotationTest.lean.ignore @@ -0,0 +1,118 @@ +import Lean +-- import Lean.NotationExtra + +class BIBase (PROP : Type u) where + Entails : PROP → PROP → Prop + emp : PROP + pure : Prop → PROP + and : PROP → PROP → PROP + or : PROP → PROP → PROP + imp : PROP → PROP → PROP + sForall : (PROP → Prop) → PROP + sExists : (PROP → Prop) → PROP + sep : PROP → PROP → PROP + wand : PROP → PROP → PROP + persistently : PROP → PROP + later : PROP → PROP + +namespace BIBase + +def «forall» [BIBase PROP] {α : Sort _} (P : α → PROP) : PROP := sForall fun p => ∃ a, P a = p +def «exists» [BIBase PROP] {α : Sort _} (P : α → PROP) : PROP := sExists fun p => ∃ a, P a = p + +end BIBase + +-- We introduce a new category merely so we can write `iprop => term` macros; as we want +-- iprop syntax to be embeddable arbitrarily into `term`, all actual syntax must be defined in the +-- latter category as before. +declare_syntax_cat iprop + +syntax term : iprop +syntax "iprop(" iprop:min ")" : term + +macro_rules + | `(iprop| $P:ident) => `($P) +macro_rules + | `(iprop| ($P:term)) => `(iprop($P:term)) + +/-- Embedding of pure Lean proposition as separation logic proposition. -/ +syntax "⌜" term "⌝" : iprop +/-- Separating conjunction. -/ +syntax:35 iprop:36 " ∗ " iprop:35 : iprop +/-- Separating implication. -/ +syntax:25 iprop:26 " -∗ " iprop:25 : iprop +/-- Persistency modality. `persistently` is a primitive of BI. -/ +syntax:max " " iprop:40 : iprop +/-- Later modality. `later` is a primitive of BI. -/ +syntax:max "▷ " iprop:40 : iprop + +/-- Bidirectional implication on separation logic propositions. -/ +syntax:27 iprop:28 " ↔ " iprop:28 : iprop +/-- Bidrectional separating implication on separation logic propositions. -/ +syntax:27 iprop:28 " ∗-∗ " iprop:28 : iprop + +open Lean in +/-- Existential quantification on separation logic propositions. -/ +macro (name := ipropExists) "∃" xs:explicitBinders ", " b:iprop : iprop => do + return ⟨← expandExplicitBinders ``BIBase.exists xs b⟩ + +-- `iprop` syntax interpretation +/-- The empty predicate of a BI logic -/ +macro_rules + | `(iprop| emp) => ``(BIBase.emp) +/-- A pure predicate of a BI logic -/ +macro_rules + | `(iprop| ⌜$φ⌝) => ``(BIBase.pure $φ) +/-- A conjunction of a BI logic -/ +macro_rules + | `(iprop| $P:term ∧ $Q) => ``(BIBase.and iprop($P:term) iprop($Q:term)) +/-- A disjunction of a BI logic -/ +macro_rules + | `(iprop| $P:term ∨ $Q) => ``(BIBase.or iprop($P:term) iprop($Q:term)) +/-- An implication of a BI logic -/ +macro_rules + | `(iprop| $P:term → $Q) => ``(BIBase.imp iprop($P:term) iprop($Q:term)) +/-- An existential quantifier over a predicate of a BI logic -/ +macro_rules + | `(ipropExists| ∃ $xs, $Ψ) => do Lean.expandExplicitBinders ``BIBase.exists xs (← ``(iprop($Ψ))) +/-- A separating conjunction of a BI logic -/ +macro_rules + | `(iprop| $P:iprop ∗ $Q:iprop) => ``(BIBase.sep iprop($P) iprop($Q)) +/-- A separating implication of a BI logic -/ +macro_rules + | `(iprop| $P -∗ $Q) => ``(0) +/-- The persistant modality of a BI logic -/ +macro_rules + | `(iprop| $P) => ``(BIBase.persistently iprop($P)) +/-- The later modality of a BI logic -/ +macro_rules + | `(iprop| ▷ $P) => ``(BIBase.later iprop($P)) + +elab_rules : term + | `(iprop($P:term)) => + Lean.Elab.Term.elabTerm P .none + +-- open Lean Elab in +-- elab_rules : term +-- | `(iprop($P)) => do +-- let env ← getEnv +-- -- Drive step-wise macro elaboration ourselves so we can get access to the name of the unfolded +-- -- macro and create info trees per macro; like `elabTermAux` except for `PTerm` below. +-- let result ← match (← liftMacroM (expandMacroImpl? env P)) with +-- | some (decl, stxNew?) => +-- let P' ← liftMacroM <| liftExcept stxNew? +-- -- NOTE: `P` itself will have synthetic source info after the first expansion because it is +-- -- a syntax node representing the `term : iprop` embedding created by the RHS of each macro +-- -- rule. Its only child, the `term` node, is the one transferred from the LHS, so we should +-- -- use it in the info tree nodes to get the correct connection to the original source code. +-- let PTerm := P.raw.getArg 0 +-- Term.withTermInfoContext' decl PTerm do +-- withMacroExpansionInfo PTerm P' do +-- Term.elabTerm P' none +-- | none => throwUnsupportedSyntax + +-- Go-to-definition will now correctly jump to each of the three macro rules +variable [BIBase Nat](A B C : Nat) +set_option trace.Elab.info true in +#check iprop(A -∗ B) +#check iprop((A -∗ B) → C) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index b4f33df8..889a8b93 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -31,7 +31,7 @@ This was not a problem in Iris Rocq becuse of canonical structures. In Iris Lean, we instead fix our choice of `State` from the choice of `Expr`, so `Expr` cannot be inferred from `State` instead. -/ -class StateInterp (State : semiOutParam $ Type _) (Obs : outParam $ Type _) (GF : BundledGFunctors) +class StateInterp (State : Type _) (Obs : outParam $ Type _) (GF : BundledGFunctors) where /-- Interpretation of a state in a language model. Takes a state, number of steps, list of observations prior to the state, and number of From 6df094f9f68602c18568ea9c3309c4ebbc102bd4 Mon Sep 17 00:00:00 2001 From: ayhon Date: Thu, 21 May 2026 11:24:49 +0200 Subject: [PATCH 72/76] fix: remove redundant implicit_reducible --- Iris/Iris/ProgramLogic/WeakestPre.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 889a8b93..8cbd4df4 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -110,7 +110,7 @@ instance wp.pre.contractive s : OFE.Contractive (wp.pre s (ι := ι)) where · exact Hwp m m_n _ _ _ · exact BI.BigSepL.bigSepL_dist <| fun _ => Hwp m m_n _ _ _ -@[implicit_reducible, rocq_alias wp_def] +@[rocq_alias wp_def] instance wp.def : Wp (IProp GF) (Expr) (Val) Stuckness where wp s := fixpoint (wp.pre s) From 92bb4bfc3eac7642cf37a1c536c10abeb218f30b Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Thu, 21 May 2026 11:32:56 +0200 Subject: [PATCH 73/76] porting --- Iris/PORTING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Iris/PORTING.md b/Iris/PORTING.md index d3c20031..deb1b7be 100644 --- a/Iris/PORTING.md +++ b/Iris/PORTING.md @@ -87,14 +87,14 @@ Some porting tasks will require other tasks as dependencies, the GitHub issues p - [x] Unit - [x] Emtpy - [x] Product - - [ ] Sum + - [x] Sum - [x] Discrete - [x] Leibniz - [x] Option - [x] Later - [x] Discrete functions - [x] Isomorphisms - - [ ] Sigma + - [x] Sigma - [ ] `proofmode_classes.v` - [ ] IsOp - [ ] `reservation_map.v` @@ -259,7 +259,7 @@ Some porting tasks will require other tasks as dependencies, the GitHub issues p - [ ] Fupd mask change laws - [ ] Fupd step derived rules - [ ] Fupd plainly general laws -- [ ] `weakestpre.v` +- [x] `weakestpre.v` - [ ] `lib/atomic.v` - [ ] `lib/core.v` - [ ] `lib/counterexamples.v` @@ -289,3 +289,4 @@ See proofmode at https://leanprover-community.github.io/iris-lean/ - [x] `language.v` - [x] `ectx_language.v` - [x] `ectxi_language.v` + - [x] `weakestpre.v` From cea10ed0e89e11343ad43df6a86b2505fd50a813 Mon Sep 17 00:00:00 2001 From: Michael Sammler Date: Thu, 21 May 2026 20:47:55 +0200 Subject: [PATCH 74/76] add test for irevert change --- Iris/Iris/ProofMode/Tactics/Revert.lean | 10 ++++++---- Iris/Iris/Tests/Tactics.lean | 12 ++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Revert.lean b/Iris/Iris/ProofMode/Tactics/Revert.lean index 905367f3..8027b4ba 100644 --- a/Iris/Iris/ProofMode/Tactics/Revert.lean +++ b/Iris/Iris/ProofMode/Tactics/Revert.lean @@ -69,10 +69,12 @@ private def RevertState.revertLeanForallHyp ProofModeM (@RevertState u prop bi origE origGoal) := do let { e, hyps, goal, reverted, pf } := st let x : Q($α) := mkFVar f - have Φ : Q($α → $prop) := ← do - return match ← mkLambdaFVars #[x] goal with - | .lam n t b _ => .lam n t b .default - | e => e + -- abstract over x in the goal + let Φ ← mkLambdaFVars #[x] goal + -- make sure that the binder info is set to default, otherwise the + -- printing of BI.forall breaks + have Φ : Q($α → $prop) := + match Φ with | .lam n t b _ => .lam n t b .default | e => e let goal' : Q($prop) := q(BI.forall $Φ) have pf' : Q(($e ⊢ $goal') → ($origE ⊢ $origGoal)) := ← withLocalDeclDQ `h q($e ⊢ BI.forall $Φ) fun h => do diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 71bbf0ff..c5bc30be 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -362,6 +362,18 @@ example [BI PROP] (Φ : Bool → PROP) : ⊢ ∀ x, ⌜x = true⌝ -∗ iintro %x %hp H iexact H +/- Tests that `irevert` clears binder info (see https://github.com/leanprover-community/iris-lean/pull/393#issuecomment-4506443579) -/ +/-- +error: unsolved goals +PROP : Type u_1 +inst✝ : BI PROP +P : PROP +⊢ ⏎ + ⊢ ∀ x, P +-/ +#guard_msgs in +example [BI PROP] (P : PROP) {x : Nat} : ⊢ P := by + /- Tests `irevert` failing with dependency -/ /-- error: irevert: proofmode hypothesis H depends on x -/ #guard_msgs in From 3a8adb1d1bfc7d58309aa5ad63cf97d8abb1d35e Mon Sep 17 00:00:00 2001 From: Michael Sammler Date: Thu, 21 May 2026 20:49:51 +0200 Subject: [PATCH 75/76] add missing line --- Iris/Iris/Tests/Tactics.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index c5bc30be..edfb8d86 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -373,6 +373,7 @@ P : PROP -/ #guard_msgs in example [BI PROP] (P : PROP) {x : Nat} : ⊢ P := by + irevert %x /- Tests `irevert` failing with dependency -/ /-- error: irevert: proofmode hypothesis H depends on x -/ From a2875b78aa9ce36d70ad44e94d954fe202810e59 Mon Sep 17 00:00:00 2001 From: Sergei Stepanenko Date: Thu, 21 May 2026 21:20:14 +0200 Subject: [PATCH 76/76] rm missed file --- Iris/Iris/NotationTest.lean.ignore | 118 ----------------------------- 1 file changed, 118 deletions(-) delete mode 100644 Iris/Iris/NotationTest.lean.ignore diff --git a/Iris/Iris/NotationTest.lean.ignore b/Iris/Iris/NotationTest.lean.ignore deleted file mode 100644 index d0c8b12f..00000000 --- a/Iris/Iris/NotationTest.lean.ignore +++ /dev/null @@ -1,118 +0,0 @@ -import Lean --- import Lean.NotationExtra - -class BIBase (PROP : Type u) where - Entails : PROP → PROP → Prop - emp : PROP - pure : Prop → PROP - and : PROP → PROP → PROP - or : PROP → PROP → PROP - imp : PROP → PROP → PROP - sForall : (PROP → Prop) → PROP - sExists : (PROP → Prop) → PROP - sep : PROP → PROP → PROP - wand : PROP → PROP → PROP - persistently : PROP → PROP - later : PROP → PROP - -namespace BIBase - -def «forall» [BIBase PROP] {α : Sort _} (P : α → PROP) : PROP := sForall fun p => ∃ a, P a = p -def «exists» [BIBase PROP] {α : Sort _} (P : α → PROP) : PROP := sExists fun p => ∃ a, P a = p - -end BIBase - --- We introduce a new category merely so we can write `iprop => term` macros; as we want --- iprop syntax to be embeddable arbitrarily into `term`, all actual syntax must be defined in the --- latter category as before. -declare_syntax_cat iprop - -syntax term : iprop -syntax "iprop(" iprop:min ")" : term - -macro_rules - | `(iprop| $P:ident) => `($P) -macro_rules - | `(iprop| ($P:term)) => `(iprop($P:term)) - -/-- Embedding of pure Lean proposition as separation logic proposition. -/ -syntax "⌜" term "⌝" : iprop -/-- Separating conjunction. -/ -syntax:35 iprop:36 " ∗ " iprop:35 : iprop -/-- Separating implication. -/ -syntax:25 iprop:26 " -∗ " iprop:25 : iprop -/-- Persistency modality. `persistently` is a primitive of BI. -/ -syntax:max " " iprop:40 : iprop -/-- Later modality. `later` is a primitive of BI. -/ -syntax:max "▷ " iprop:40 : iprop - -/-- Bidirectional implication on separation logic propositions. -/ -syntax:27 iprop:28 " ↔ " iprop:28 : iprop -/-- Bidrectional separating implication on separation logic propositions. -/ -syntax:27 iprop:28 " ∗-∗ " iprop:28 : iprop - -open Lean in -/-- Existential quantification on separation logic propositions. -/ -macro (name := ipropExists) "∃" xs:explicitBinders ", " b:iprop : iprop => do - return ⟨← expandExplicitBinders ``BIBase.exists xs b⟩ - --- `iprop` syntax interpretation -/-- The empty predicate of a BI logic -/ -macro_rules - | `(iprop| emp) => ``(BIBase.emp) -/-- A pure predicate of a BI logic -/ -macro_rules - | `(iprop| ⌜$φ⌝) => ``(BIBase.pure $φ) -/-- A conjunction of a BI logic -/ -macro_rules - | `(iprop| $P:term ∧ $Q) => ``(BIBase.and iprop($P:term) iprop($Q:term)) -/-- A disjunction of a BI logic -/ -macro_rules - | `(iprop| $P:term ∨ $Q) => ``(BIBase.or iprop($P:term) iprop($Q:term)) -/-- An implication of a BI logic -/ -macro_rules - | `(iprop| $P:term → $Q) => ``(BIBase.imp iprop($P:term) iprop($Q:term)) -/-- An existential quantifier over a predicate of a BI logic -/ -macro_rules - | `(ipropExists| ∃ $xs, $Ψ) => do Lean.expandExplicitBinders ``BIBase.exists xs (← ``(iprop($Ψ))) -/-- A separating conjunction of a BI logic -/ -macro_rules - | `(iprop| $P:iprop ∗ $Q:iprop) => ``(BIBase.sep iprop($P) iprop($Q)) -/-- A separating implication of a BI logic -/ -macro_rules - | `(iprop| $P -∗ $Q) => ``(0) -/-- The persistant modality of a BI logic -/ -macro_rules - | `(iprop| $P) => ``(BIBase.persistently iprop($P)) -/-- The later modality of a BI logic -/ -macro_rules - | `(iprop| ▷ $P) => ``(BIBase.later iprop($P)) - -elab_rules : term - | `(iprop($P:term)) => - Lean.Elab.Term.elabTerm P .none - --- open Lean Elab in --- elab_rules : term --- | `(iprop($P)) => do --- let env ← getEnv --- -- Drive step-wise macro elaboration ourselves so we can get access to the name of the unfolded --- -- macro and create info trees per macro; like `elabTermAux` except for `PTerm` below. --- let result ← match (← liftMacroM (expandMacroImpl? env P)) with --- | some (decl, stxNew?) => --- let P' ← liftMacroM <| liftExcept stxNew? --- -- NOTE: `P` itself will have synthetic source info after the first expansion because it is --- -- a syntax node representing the `term : iprop` embedding created by the RHS of each macro --- -- rule. Its only child, the `term` node, is the one transferred from the LHS, so we should --- -- use it in the info tree nodes to get the correct connection to the original source code. --- let PTerm := P.raw.getArg 0 --- Term.withTermInfoContext' decl PTerm do --- withMacroExpansionInfo PTerm P' do --- Term.elabTerm P' none --- | none => throwUnsupportedSyntax - --- Go-to-definition will now correctly jump to each of the three macro rules -variable [BIBase Nat](A B C : Nat) -set_option trace.Elab.info true in -#check iprop(A -∗ B) -#check iprop((A -∗ B) → C)