From 77cc99b26a71184f3d43048a73a48bbe8c02a865 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 14:24:35 +0200 Subject: [PATCH 001/108] Start implementing iinv: tactic for opening invariants --- Iris/Iris/ProofMode/Tactics.lean | 1 + Iris/Iris/ProofMode/Tactics/Inv.lean | 38 ++++++++++++++++++++++++++++ Iris/Iris/Tests/Tactics.lean | 4 +++ 3 files changed, 43 insertions(+) create mode 100644 Iris/Iris/ProofMode/Tactics/Inv.lean diff --git a/Iris/Iris/ProofMode/Tactics.lean b/Iris/Iris/ProofMode/Tactics.lean index 361255881..20ecb0267 100644 --- a/Iris/Iris/ProofMode/Tactics.lean +++ b/Iris/Iris/ProofMode/Tactics.lean @@ -13,6 +13,7 @@ public meta import Iris.ProofMode.Tactics.Exists public meta import Iris.ProofMode.Tactics.Frame public meta import Iris.ProofMode.Tactics.Have public meta import Iris.ProofMode.Tactics.Intro +public meta import Iris.ProofMode.Tactics.Inv public meta import Iris.ProofMode.Tactics.LeftRight public meta import Iris.ProofMode.Tactics.Loeb public meta import Iris.ProofMode.Tactics.Mod diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean new file mode 100644 index 000000000..f65e740f7 --- /dev/null +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Alvin Tang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Michael Sammler, Alvin Tang +-/ +module + +public meta import Iris.ProofMode.Tactics.Assumption +public meta import Iris.ProofMode.Tactics.Cases +public meta import Iris.ProofMode.Patterns.CasesPattern +public meta import Iris.ProofMode.Patterns.IntroPattern +public meta import Iris.ProofMode.Patterns.SelPattern +public meta import Iris.ProofMode.ClassesMake + +namespace Iris.ProofMode + +public meta section +open Lean Elab Tactic Meta Qq BI Std + +private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) + (h : TSyntax `ident) (selPats : Option <| List SelPat) + (introPat : Option <| Syntax × IntroPat) + (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := + sorry + +/-- `iinv` opens an invariant in the proof state. -/ +syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? + " as " colGt introPat (ident)? : tactic + +elab_rules : tactic + | `(tactic| iinv $h:ident $[with $spats:selPat*]? as $ipat:introPat $[$hclose:ident]?) => do + let selPats ← spats.mapM <| fun pats => do + liftMacroM <| SelPat.parse pats + let introPat ← liftMacroM <| IntroPat.parse ipat + + ProofModeM.runTactic λ mvar { hyps, goal, .. } => do + let pf ← iInvCore hyps goal h selPats introPat hclose + mvar.assign pf diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 94c087900..8fa7f79a8 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -11,6 +11,7 @@ public import Iris.Instances.IProp public import Iris.Instances.Lib.LaterCredits public import Iris.Instances.Lib.Token public import Iris.Algebra.CMRA +public import Iris.Instances.Lib.Invariants @[expose] public section @@ -2776,3 +2777,6 @@ example (P Q : PROP) : iloeb as IH end iloeb + +example [BI PROP] : ⊢@{PROP} True := by + iinv From 3f62da23816cc70216636f09e651a1d20289772d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 15:02:41 +0200 Subject: [PATCH 002/108] Introduce type class `ElimInv` --- Iris/Iris/ProofMode/Classes.lean | 8 ++++++++ Iris/Iris/ProofMode/Tactics/Inv.lean | 18 ++++++++++++++---- Iris/Iris/Tests/Tactics.lean | 6 ++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index fdbe326cb..e5abf51d3 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -220,6 +220,14 @@ class CombineSepGives [BI PROP] (P Q : PROP) (R : outParam PROP) where combine_sep_gives : P ∗ Q ⊢ R export CombineSepGives (combine_sep_gives) +/-- The type class used for the `iinv` tactic. -/ +@[ipm_class, rocq_alias ElimInv] +class ElimInv [BI PROP] (φ : outParam Prop) {X : outParam Type} + (Pinv Pin : PROP) (Pout : outParam <| X → PROP) + (mPclose : outParam <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ mPclose x -∗ Q' x) ⊢ Q +export ElimInv (elim_inv) + #rocq_ignore elim_inv_tc_opaque "No tc_opaque in Lean" #rocq_ignore elim_modal_tc_opaque "No tc_opaque in Lean" #rocq_ignore from_and_tc_opaque "No tc_opaque in Lean" diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index f65e740f7..1d750a37f 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -18,9 +18,14 @@ public meta section open Lean Elab Tactic Meta Qq BI Std private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) - (h : TSyntax `ident) (selPats : Option <| List SelPat) - (introPat : Option <| Syntax × IntroPat) - (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := + (ivar : IVarId) (selPats : Option <| List SelPat) + (introPat : Syntax × IntroPat) (hclose : Option <| TSyntax `ident) : + ProofModeM Q($e ⊢ $goal) := do + + let ⟨_, _, _, out, p, eq, pf⟩ := hyps.remove false ivar + + let ϕ ← mkFreshExprMVarQ q($prop) + sorry /-- `iinv` opens an invariant in the proof state. -/ @@ -29,10 +34,15 @@ syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? elab_rules : tactic | `(tactic| iinv $h:ident $[with $spats:selPat*]? as $ipat:introPat $[$hclose:ident]?) => do + -- Parse the optional selection pattern used for auxiliary assertions needed to open the invariant let selPats ← spats.mapM <| fun pats => do liftMacroM <| SelPat.parse pats + -- Parse the introduction pattern used for destructing the result let introPat ← liftMacroM <| IntroPat.parse ipat ProofModeM.runTactic λ mvar { hyps, goal, .. } => do - let pf ← iInvCore hyps goal h selPats introPat hclose + -- Find the hypothesis in which the invariant is opened + let ivar ← hyps.findWithInfo h + + let pf ← iInvCore hyps goal ivar selPats introPat hclose mvar.assign pf diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 8fa7f79a8..2443515e0 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2778,5 +2778,7 @@ example (P Q : PROP) : end iloeb -example [BI PROP] : ⊢@{PROP} True := by - iinv +example [BI PROP] {P : PROP} : ⊢ P -∗ P := by + iintro HP + iinv P as ∗ + itrivial From f70f9876a944533b302dd5ce7b38e530bcf9223f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 15:33:34 +0200 Subject: [PATCH 003/108] `iInvCore`: `ElimInv` type class synthesis --- Iris/Iris/ProofMode/Tactics/Inv.lean | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 1d750a37f..75e7e6e78 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -21,10 +21,19 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( (ivar : IVarId) (selPats : Option <| List SelPat) (introPat : Syntax × IntroPat) (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do - - let ⟨_, _, _, out, p, eq, pf⟩ := hyps.remove false ivar - - let ϕ ← mkFreshExprMVarQ q($prop) + -- Find the hypothesis from the context + let some ⟨_, _, p, ty⟩ := hyps.getDecl? ivar + | throwError m!"iinv: unable to find {ivar.name}" + + let ϕ ← mkFreshExprMVarQ q(Prop) + let Pin ← mkFreshExprMVarQ q($prop) + let X ← mkFreshExprMVarQ q(Type) + let Pout ← mkFreshExprMVarQ q($X → $prop) + let Pclose ← mkFreshExprMVarQ q($X → $prop) + let Q' ← mkFreshExprMVarQ q($X → $prop) + + let some elimInv ← ProofModeM.trySynthInstanceQ q(@ElimInv $prop $bi $ϕ $X $ty $Pin $Pout $Pclose $goal $Q') + | throwError "iinv: ElimInv type class synthesis error" sorry From 7eeb1f2195880b88593c4bf248fe73f784d58c68 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 16:09:57 +0200 Subject: [PATCH 004/108] Some progress with iInvCore --- Iris/Iris/ProofMode/Tactics/Inv.lean | 15 ++++++++++++++- Iris/Iris/Tests/Tactics.lean | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 75e7e6e78..dcd3da858 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -17,6 +17,9 @@ namespace Iris.ProofMode public meta section open Lean Elab Tactic Meta Qq BI Std +@[rocq_alias tac_inv_elim] +theorem tac_inv_elim [BI PROP] {e goal : PROP} : e ⊢ goal := sorry + private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (selPats : Option <| List SelPat) (introPat : Syntax × IntroPat) (hclose : Option <| TSyntax `ident) : @@ -35,7 +38,17 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let some elimInv ← ProofModeM.trySynthInstanceQ q(@ElimInv $prop $bi $ϕ $X $ty $Pin $Pout $Pclose $goal $Q') | throwError "iinv: ElimInv type class synthesis error" - sorry + let hϕ ← iSolveSidecondition q($ϕ) + + let ⟨_, hyps', _, _, _, _, pf⟩ := hyps.remove false ivar + + let jvar ← mkFreshIVarId false + let name ← mkFreshUserName .anonymous + + let hyps'' := hyps'.add bi name jvar q(false) ty + + let pf' ← addBIGoal hyps'' goal + return q(tac_inv_elim) /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 2443515e0..a0d3b0f94 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2780,5 +2780,5 @@ end iloeb example [BI PROP] {P : PROP} : ⊢ P -∗ P := by iintro HP - iinv P as ∗ + iinv HP as ∗ itrivial From cebd144cefd037b0ecc40576c9f793f44bd9ac3a Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 16:20:18 +0200 Subject: [PATCH 005/108] Add accessor and type class IntoAcc --- Iris/Iris/ProofMode/Classes.lean | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index e5abf51d3..86db7a9a9 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -220,9 +220,21 @@ class CombineSepGives [BI PROP] (P Q : PROP) (R : outParam PROP) where combine_sep_gives : P ∗ Q ⊢ R export CombineSepGives (combine_sep_gives) +@[rocq_alias accessor] +def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) + (mγ : X → PROP) : PROP := + M1 iprop(∃ x, α x ∗ (β x -∗ M2 (mγ x))) + +@[ipm_class, rocq_alias IntoAcc] +class IntoAcc [BI PROP] (X : outParam Type) (Pacc : PROP) + (ϕ : outParam Prop) (Pin : outParam <| PROP) + (M1 M2 : outParam <| PROP → PROP) (α β : outParam <| X → PROP) + (mγ : outParam <| X → PROP) where + into_acc : ϕ → Pacc -∗ Pin -∗ accessor M1 M2 α β mγ + /-- The type class used for the `iinv` tactic. -/ @[ipm_class, rocq_alias ElimInv] -class ElimInv [BI PROP] (φ : outParam Prop) {X : outParam Type} +class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (Pinv Pin : PROP) (Pout : outParam <| X → PROP) (mPclose : outParam <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ mPclose x -∗ Q' x) ⊢ Q From dc31ce91767d181ecd96474587a7861dc0ea9eed Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 16:22:51 +0200 Subject: [PATCH 006/108] Add type class ElimAcc --- Iris/Iris/ProofMode/Classes.lean | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 86db7a9a9..dcb6ab102 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -225,6 +225,11 @@ def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) (mγ : X → PROP) : PROP := M1 iprop(∃ x, α x ∗ (β x -∗ M2 (mγ x))) +@[ipm_class, rocq_alias ElimAcc] +class ElimAcc [BI PROP] {X : Type} (ϕ : outParam Prop) (M1 M2 : PROP → PROP) + (α β : X → PROP) (mγ : X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + elim_acc : ϕ → ((∀ x, α x -∗ Q' x) -∗ accessor M1 M2 α β mγ -∗ Q) + @[ipm_class, rocq_alias IntoAcc] class IntoAcc [BI PROP] (X : outParam Type) (Pacc : PROP) (ϕ : outParam Prop) (Pin : outParam <| PROP) From d246841e5c93cefff50612914571ea987c4b3f4a Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 16:58:58 +0200 Subject: [PATCH 007/108] =?UTF-8?q?Fix=20type=20class=20definitions:=20`m?= =?UTF-8?q?=CE=B3`=20and=20`mPclose`=20involve=20`Option`=20with=20`emp`?= =?UTF-8?q?=20as=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Iris/Iris/ProofMode/Classes.lean | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index dcb6ab102..c507515c3 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -222,27 +222,27 @@ export CombineSepGives (combine_sep_gives) @[rocq_alias accessor] def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) - (mγ : X → PROP) : PROP := - M1 iprop(∃ x, α x ∗ (β x -∗ M2 (mγ x))) + (mγ : X → Option PROP) : PROP := + M1 iprop(∃ x, α x ∗ (β x -∗ M2 ((mγ x).getD emp))) @[ipm_class, rocq_alias ElimAcc] class ElimAcc [BI PROP] {X : Type} (ϕ : outParam Prop) (M1 M2 : PROP → PROP) - (α β : X → PROP) (mγ : X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + (α β : X → PROP) (mγ : X → Option PROP) (Q : PROP) (Q' : outParam <| X → PROP) where elim_acc : ϕ → ((∀ x, α x -∗ Q' x) -∗ accessor M1 M2 α β mγ -∗ Q) @[ipm_class, rocq_alias IntoAcc] class IntoAcc [BI PROP] (X : outParam Type) (Pacc : PROP) (ϕ : outParam Prop) (Pin : outParam <| PROP) (M1 M2 : outParam <| PROP → PROP) (α β : outParam <| X → PROP) - (mγ : outParam <| X → PROP) where + (mγ : outParam <| X → Option PROP) where into_acc : ϕ → Pacc -∗ Pin -∗ accessor M1 M2 α β mγ /-- The type class used for the `iinv` tactic. -/ @[ipm_class, rocq_alias ElimInv] class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (Pinv Pin : PROP) (Pout : outParam <| X → PROP) - (mPclose : outParam <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where - elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ mPclose x -∗ Q' x) ⊢ Q + (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ ((mPclose.map (· x)).getD emp) -∗ Q' x) ⊢ Q export ElimInv (elim_inv) #rocq_ignore elim_inv_tc_opaque "No tc_opaque in Lean" From 648635f3a52ef87608e65f1341531c3190380fe6 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 17:29:35 +0200 Subject: [PATCH 008/108] Add two ElimInv type class instances: `elim_inv_acc_without_close`, `elim_inv_acc_with_close` --- Iris/Iris/ProofMode/Classes.lean | 2 +- Iris/Iris/ProofMode/Instances.lean | 19 +++++++++++++++++++ Iris/Iris/ProofMode/Tactics/Inv.lean | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index c507515c3..b45375707 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -231,7 +231,7 @@ class ElimAcc [BI PROP] {X : Type} (ϕ : outParam Prop) (M1 M2 : PROP → PROP) elim_acc : ϕ → ((∀ x, α x -∗ Q' x) -∗ accessor M1 M2 α β mγ -∗ Q) @[ipm_class, rocq_alias IntoAcc] -class IntoAcc [BI PROP] (X : outParam Type) (Pacc : PROP) +class IntoAcc [BI PROP] {X : outParam Type} (Pacc : PROP) (ϕ : outParam Prop) (Pin : outParam <| PROP) (M1 M2 : outParam <| PROP → PROP) (α β : outParam <| X → PROP) (mγ : outParam <| X → Option PROP) where diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 8ebc6aa25..d9d5289d5 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -918,3 +918,22 @@ instance combineSepGives_persistently [BI PROP] (Q1 Q2 P : PROP) [h : CombineSepGives Q1 Q2 P] : CombineSepGives iprop( Q1) iprop( Q2) iprop( P) where combine_sep_gives := persistently_sep_mpr.trans (persistently_mono h.combine_sep_gives) + +@[rocq_alias elim_inv_acc_without_close] +instance elimInv_acc_without_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α none Q Q' where + elim_inv := sorry + +@[rocq_alias elim_inv_acc_with_close] +instance elimInv_acc_with_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin + α + (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) + Q (fun _ => Q') where + elim_inv := sorry diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index dcd3da858..2ac32fde3 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -35,7 +35,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pclose ← mkFreshExprMVarQ q($X → $prop) let Q' ← mkFreshExprMVarQ q($X → $prop) - let some elimInv ← ProofModeM.trySynthInstanceQ q(@ElimInv $prop $bi $ϕ $X $ty $Pin $Pout $Pclose $goal $Q') + let some elimInv ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $Pclose $goal $Q') | throwError "iinv: ElimInv type class synthesis error" let hϕ ← iSolveSidecondition q($ϕ) From e0ebd561ac97d29e59b3be4c8d2cff323da31db0 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 17:38:30 +0200 Subject: [PATCH 009/108] Adjust `outParam` config --- Iris/Iris/ProofMode/Classes.lean | 4 ++-- Iris/Iris/ProofMode/Instances.lean | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index b45375707..fec995c1a 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -240,8 +240,8 @@ class IntoAcc [BI PROP] {X : outParam Type} (Pacc : PROP) /-- The type class used for the `iinv` tactic. -/ @[ipm_class, rocq_alias ElimInv] class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) - (Pinv Pin : PROP) (Pout : outParam <| X → PROP) - (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + (Pinv : PROP) (Pin : outParam PROP) (Pout : outParam <| X → PROP) + (mPclose : outParam <| Option <| X → PROP) (Q : outParam PROP) (Q' : outParam <| X → PROP) where elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ ((mPclose.map (· x)).getD emp) -∗ Q' x) ⊢ Q export ElimInv (elim_inv) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index d9d5289d5..d31c0bb15 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -919,6 +919,7 @@ instance combineSepGives_persistently [BI PROP] (Q1 Q2 P : PROP) CombineSepGives iprop( Q1) iprop( Q2) iprop( P) where combine_sep_gives := persistently_sep_mpr.trans (persistently_mono h.combine_sep_gives) +set_option synthInstance.checkSynthOrder false in @[rocq_alias elim_inv_acc_without_close] instance elimInv_acc_without_close [BI PROP] {X : Type} ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) @@ -927,6 +928,7 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α none Q Q' where elim_inv := sorry +set_option synthInstance.checkSynthOrder false in @[rocq_alias elim_inv_acc_with_close] instance elimInv_acc_with_close [BI PROP] {X : Type} ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) From 3d39bac7e6cd9e9d1f36a0689a7b2aa480d01827 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 16 Jun 2026 18:16:28 +0200 Subject: [PATCH 010/108] Fix `outParam` config and types of mvars --- Iris/Iris/ProofMode/Classes.lean | 2 +- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index fec995c1a..30e0123b7 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -241,7 +241,7 @@ class IntoAcc [BI PROP] {X : outParam Type} (Pacc : PROP) @[ipm_class, rocq_alias ElimInv] class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (Pinv : PROP) (Pin : outParam PROP) (Pout : outParam <| X → PROP) - (mPclose : outParam <| Option <| X → PROP) (Q : outParam PROP) (Q' : outParam <| X → PROP) where + (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ ((mPclose.map (· x)).getD emp) -∗ Q' x) ⊢ Q export ElimInv (elim_inv) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 2ac32fde3..7035ea809 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -32,11 +32,11 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pin ← mkFreshExprMVarQ q($prop) let X ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) - let Pclose ← mkFreshExprMVarQ q($X → $prop) + let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) - let some elimInv ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $Pclose $goal $Q') - | throwError "iinv: ElimInv type class synthesis error" + let some elimInv ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $mPclose $goal $Q') + | throwError "iinv: ElimInv type class synthesis error with goal {goal}" let hϕ ← iSolveSidecondition q($ϕ) From 81cfee87b0a2bbdbc4651ec24be4abdc8a1bd2b2 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 10:19:25 +0200 Subject: [PATCH 011/108] Introduce type class `IntoInv` --- Iris/Iris/ProofMode/Classes.lean | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 30e0123b7..db5cdc19e 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -8,6 +8,7 @@ module public import Iris.BI public meta import Iris.ProofMode.SynthInstance public import Iris.ProofMode.Modalities +public import Iris.Std.Namespaces @[expose] public section @@ -220,6 +221,9 @@ class CombineSepGives [BI PROP] (P Q : PROP) (R : outParam PROP) where combine_sep_gives : P ∗ Q ⊢ R export CombineSepGives (combine_sep_gives) +@[ipm_class, rocq_alias IntoInv] +class IntoInv [BI PROP] (P : PROP) (N : Namespace) + @[rocq_alias accessor] def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) (mγ : X → Option PROP) : PROP := From a25e4b595e9f47d63f341d946cacdb4d6f38cce1 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 10:31:42 +0200 Subject: [PATCH 012/108] Port `into_inv_inv` and `into_acc_inv` definitions in Invariants.lean --- Iris/Iris/Instances/Lib/Invariants.lean | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/Instances/Lib/Invariants.lean b/Iris/Iris/Instances/Lib/Invariants.lean index 9be9d1fbc..705a07870 100644 --- a/Iris/Iris/Instances/Lib/Invariants.lean +++ b/Iris/Iris/Instances/Lib/Invariants.lean @@ -87,7 +87,17 @@ theorem except_0_inv (N : Namespace) (P : IProp GF) : ⊢ ◇ inv N P -∗ inv N instance is_except_0_inv (N : Namespace) (P : IProp GF) : IsExcept0 (inv N P) where is_except0 := by iintro H; iapply except_0_inv $$ H --- TODO: into_inv_inv, into_acc_inv +@[rocq_alias into_inv_inv] +instance into_inv_inv (N : Namespace) (P : IProp GF) : IntoInv (inv N P) N := {} + +set_option synthInstance.checkSynthOrder false in +@[rocq_alias into_acc_inv] +instance into_acc_inv (N : Namespace) (P : IProp GF) E : + IntoAcc (X := Unit) (inv N P) (↑N ⊆ E) iprop(True) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) + (λ _ => iprop(▷ P)) (λ _ => iprop(▷ P)) (λ _ => none) where + into_acc := by + iintro %x #Hinv #Htrue + sorry end Instances From bdbe46bf9ce9f3d12bf0b246163c442578b7eb76 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 11:06:59 +0200 Subject: [PATCH 013/108] Port `into_inv_na` and `into_acc_na` definitions in NaInvariants.lean --- Iris/Iris/Instances/Lib/NaInvariants.lean | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index 3819958ae..b9568a670 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -17,7 +17,7 @@ public import Iris.Std.CoPset namespace Iris -open BI CMRA OFE Iris Std LawfulSet DisjointLeibnizSet COFE +open BI CMRA OFE Iris Std LawfulSet DisjointLeibnizSet COFE ProofMode abbrev NaInvF : OFunctorPre := ProdOF (constOF (DisjointLeibnizSet CoPset)) (constOF (DisjointLeibnizSet PosSet)) @@ -222,6 +222,18 @@ nonrec theorem inv_acc {p : NaInvPoolName} {E F : CoPset} {N : Namespace} {P : I icases Hbad with %Hbad exact Hbad i ⟨mem_singleton.mpr rfl, mem_singleton.mpr rfl⟩ |>.elim +@[rocq_alias into_inv_na] +instance into_inv_na (N : Namespace) (P : IProp GF) : + IntoInv (inv p N P) N := {} + +set_option synthInstance.checkSynthOrder false in +@[rocq_alias into_acc_na] +instance into_acc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : + IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) + (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) + (λ _ => some (own p F)) where + into_acc := sorry + end NonAtomicInvariant end Iris end From c756df0ff1a5e666ffa90d0f0e1a3ca6f21f558f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 11:14:25 +0200 Subject: [PATCH 014/108] Port `into_inv_cinv` and `into_acc_cinv` definitions in CInvariants.lean --- Iris/Iris/Instances/Lib/CInvariants.lean | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index ae70b3056..fdd80f91f 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -20,7 +20,7 @@ public import Iris.Std.List namespace Iris -open BI CMRA OFE Iris Std LawfulSet Excl COFE +open BI CMRA OFE Iris Std LawfulSet Excl COFE ProofMode /-! # Cancelable Invariants -/ @@ -280,6 +280,17 @@ theorem cancel (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (Hsub : imodintro iexact HP +@[rocq_alias into_inv_cinv] +instance into_inv_cinv (N : Namespace) (γ : GName) (P : IProp GF) : + IntoInv (cinv N γ P) N := {} + +set_option synthInstance.checkSynthOrder false in +@[rocq_alias into_acc_cinv] +instance into_acc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : + IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) + (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where + into_acc := sorry + end CancelableInvariant end Iris end From 1554adcd0794d63e0677b96a7800cdb163e46abb Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 11:50:27 +0200 Subject: [PATCH 015/108] =?UTF-8?q?Introduce=20`-=E2=88=97=3F`=20in=20BIBa?= =?UTF-8?q?se.lean,=20optional=20wand=20premise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Iris/Iris/BI/BIBase.lean | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Iris/Iris/BI/BIBase.lean b/Iris/Iris/BI/BIBase.lean index ae7c216dd..e30e8ad75 100644 --- a/Iris/Iris/BI/BIBase.lean +++ b/Iris/Iris/BI/BIBase.lean @@ -54,6 +54,8 @@ syntax "⌜" term "⌝" : term syntax:35 term:36 " ∗ " term:35 : term /-- Separating implication. -/ syntax:25 term:26 " -∗ " term:25 : term +/-- Separating implication with an optional wand premise. -/ +syntax:25 term:26 " -∗? " term:25 : term /-- Persistency modality. `persistently` is a primitive of BI. -/ syntax:max " " term:40 : term /-- Later modality. `later` is a primitive of BI. -/ @@ -163,6 +165,18 @@ delab_rule iff delab_rule wandIff | `($_ $P $Q) => do ``(iprop($(← unpackIprop P) ∗-∗ $(← unpackIprop Q))) +@[rocq_alias bi_wandM] +def wandM [BIBase PROP] (mP : Option PROP) (Q : PROP) : PROP := + match mP with + | none => Q + | some P => iprop(P -∗ Q) + +macro_rules + | `(iprop($mP -∗? $Q)) => ``(wandM $mP iprop($Q)) + +delab_rule wandM + | `($_ $mP $Q) => do ``(iprop($mP -∗? $(← unpackIprop Q))) + /-- Affine modality. ``` def affinely (P) := emp ∧ P From d52087cdf79bb22cb06741bb879b376f491f1af8 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 11:50:45 +0200 Subject: [PATCH 016/108] Port `elim_acc_bupd` and `elim_acc_fupd` definitions in InstancesUpdates.lean --- Iris/Iris/ProofMode/InstancesUpdates.lean | 16 +++++++++++++++- Iris/Iris/Tests/Tactics.lean | 5 ----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index 30edff808..88234dc13 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 Michael Sammler. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Michael Sammler, Yunsong Yang +Authors: Michael Sammler, Yunsong Yang, Alvin Tang -/ module @@ -200,6 +200,20 @@ instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PR p false iprop(|={E1,E2}=> P) iprop(False) iprop(|={E0,E3}=> Q) iprop(False) where elim_modal h := by cases h +@[rocq_alias elim_acc_bupd] +instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : + ElimAcc True bupd bupd α β mγ + iprop(|==> Q) + (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where + elim_acc := sorry + +@[rocq_alias elim_acc_fupd] +instance elimAcc_fupd [FUpd PROP] {X} E1 E2 (α β : X → PROP) mγ (Q : PROP) : + ElimAcc True (fupd E1 E2) (fupd E2 E1) α β mγ + iprop(|={E1,E}=> Q) + (fun x => iprop(|={E2}=> β x ∗ (mγ x -∗? |={E1,E}=> Q))) where + elim_acc := sorry + end BIFancyUpdate section SBIFancyUpdate diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index a0d3b0f94..7d80b0fb6 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2777,8 +2777,3 @@ example (P Q : PROP) : iloeb as IH end iloeb - -example [BI PROP] {P : PROP} : ⊢ P -∗ P := by - iintro HP - iinv HP as ∗ - itrivial From f4ab6f90446fbaa721936e7a8262dcb1fc82948b Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 18 Jun 2026 11:54:21 +0200 Subject: [PATCH 017/108] Follow naming conventions for type class instances --- Iris/Iris/Instances/Lib/CInvariants.lean | 4 ++-- Iris/Iris/Instances/Lib/Invariants.lean | 4 ++-- Iris/Iris/Instances/Lib/NaInvariants.lean | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index fdd80f91f..fe9400f74 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -281,12 +281,12 @@ theorem cancel (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (Hsub : iexact HP @[rocq_alias into_inv_cinv] -instance into_inv_cinv (N : Namespace) (γ : GName) (P : IProp GF) : +instance intoInv_cinv (N : Namespace) (γ : GName) (P : IProp GF) : IntoInv (cinv N γ P) N := {} set_option synthInstance.checkSynthOrder false in @[rocq_alias into_acc_cinv] -instance into_acc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : +instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where into_acc := sorry diff --git a/Iris/Iris/Instances/Lib/Invariants.lean b/Iris/Iris/Instances/Lib/Invariants.lean index 705a07870..1be8cb6d6 100644 --- a/Iris/Iris/Instances/Lib/Invariants.lean +++ b/Iris/Iris/Instances/Lib/Invariants.lean @@ -88,11 +88,11 @@ instance is_except_0_inv (N : Namespace) (P : IProp GF) : IsExcept0 (inv N P) wh is_except0 := by iintro H; iapply except_0_inv $$ H @[rocq_alias into_inv_inv] -instance into_inv_inv (N : Namespace) (P : IProp GF) : IntoInv (inv N P) N := {} +instance intoInv_inv (N : Namespace) (P : IProp GF) : IntoInv (inv N P) N := {} set_option synthInstance.checkSynthOrder false in @[rocq_alias into_acc_inv] -instance into_acc_inv (N : Namespace) (P : IProp GF) E : +instance intoAcc_inv (N : Namespace) (P : IProp GF) E : IntoAcc (X := Unit) (inv N P) (↑N ⊆ E) iprop(True) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (λ _ => iprop(▷ P)) (λ _ => iprop(▷ P)) (λ _ => none) where into_acc := by diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index b9568a670..26968c402 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -223,12 +223,12 @@ nonrec theorem inv_acc {p : NaInvPoolName} {E F : CoPset} {N : Namespace} {P : I exact Hbad i ⟨mem_singleton.mpr rfl, mem_singleton.mpr rfl⟩ |>.elim @[rocq_alias into_inv_na] -instance into_inv_na (N : Namespace) (P : IProp GF) : +instance intoInv_na (N : Namespace) (P : IProp GF) : IntoInv (inv p N P) N := {} set_option synthInstance.checkSynthOrder false in @[rocq_alias into_acc_na] -instance into_acc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : +instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (λ _ => some (own p F)) where From 4fe12c8c6db6dfb8ca740b9248bc17e4cbd5b393 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sun, 21 Jun 2026 15:25:00 +0200 Subject: [PATCH 018/108] Comment out some type class instances for the moment --- Iris/Iris/Instances/Lib/CInvariants.lean | 12 ++++++------ Iris/Iris/Instances/Lib/NaInvariants.lean | 14 +++++++------- Iris/Iris/ProofMode/Instances.lean | 22 +++++++++++----------- Iris/Iris/ProofMode/InstancesUpdates.lean | 12 ++++++------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index fe9400f74..2cc9faba3 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -284,12 +284,12 @@ theorem cancel (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (Hsub : instance intoInv_cinv (N : Namespace) (γ : GName) (P : IProp GF) : IntoInv (cinv N γ P) N := {} -set_option synthInstance.checkSynthOrder false in -@[rocq_alias into_acc_cinv] -instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : - IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) - (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where - into_acc := sorry +-- set_option synthInstance.checkSynthOrder false in +-- @[rocq_alias into_acc_cinv] +-- instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : +-- IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) +-- (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where +-- into_acc := sorry end CancelableInvariant end Iris diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index be3187088..88e83b744 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -225,13 +225,13 @@ nonrec theorem inv_acc {p : NaInvPoolName} {E F : CoPset} {N : Namespace} {P : I instance intoInv_na (N : Namespace) (P : IProp GF) : IntoInv (inv p N P) N := {} -set_option synthInstance.checkSynthOrder false in -@[rocq_alias into_acc_na] -instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : - IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) - (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) - (λ _ => some (own p F)) where - into_acc := sorry +-- set_option synthInstance.checkSynthOrder false in +-- @[rocq_alias into_acc_na] +-- instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : +-- IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) +-- (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) +-- (λ _ => some (own p F)) where +-- into_acc := sorry end NonAtomicInvariant end Iris diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index d31c0bb15..855fd8dd4 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -928,14 +928,14 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α none Q Q' where elim_inv := sorry -set_option synthInstance.checkSynthOrder false in -@[rocq_alias elim_inv_acc_with_close] -instance elimInv_acc_with_close [BI PROP] {X : Type} - ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) - [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin - α - (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) - Q (fun _ => Q') where - elim_inv := sorry +-- set_option synthInstance.checkSynthOrder false in +-- @[rocq_alias elim_inv_acc_with_close] +-- instance elimInv_acc_with_close [BI PROP] {X : Type} +-- ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) +-- [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] +-- [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : +-- ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin +-- α +-- (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) +-- Q (fun _ => Q') where +-- elim_inv := sorry diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index 88234dc13..d43506b11 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -200,12 +200,12 @@ instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PR p false iprop(|={E1,E2}=> P) iprop(False) iprop(|={E0,E3}=> Q) iprop(False) where elim_modal h := by cases h -@[rocq_alias elim_acc_bupd] -instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : - ElimAcc True bupd bupd α β mγ - iprop(|==> Q) - (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where - elim_acc := sorry +-- @[rocq_alias elim_acc_bupd] +-- instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : +-- ElimAcc True bupd bupd α β mγ +-- iprop(|==> Q) +-- (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where +-- elim_acc := sorry @[rocq_alias elim_acc_fupd] instance elimAcc_fupd [FUpd PROP] {X} E1 E2 (α β : X → PROP) mγ (Q : PROP) : From 6932d0053778ed91078b69c522a454bb139f4696 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sun, 21 Jun 2026 16:50:14 +0200 Subject: [PATCH 019/108] Towards basic tactic `iinv ... as ...` with `intoAcc_inv` and `elimAcc_fupd` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 22 ++++++++++++---------- Iris/Iris/Tests/Tactics.lean | 12 ++++++++++++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 7035ea809..ddfdae883 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -7,6 +7,7 @@ module public meta import Iris.ProofMode.Tactics.Assumption public meta import Iris.ProofMode.Tactics.Cases +public meta import Iris.ProofMode.Tactics.Intro public meta import Iris.ProofMode.Patterns.CasesPattern public meta import Iris.ProofMode.Patterns.IntroPattern public meta import Iris.ProofMode.Patterns.SelPattern @@ -25,8 +26,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( (introPat : Syntax × IntroPat) (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let some ⟨_, _, p, ty⟩ := hyps.getDecl? ivar - | throwError m!"iinv: unable to find {ivar.name}" + let ⟨e', hyps', _, ty, p, eq, pf⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin ← mkFreshExprMVarQ q($prop) @@ -35,19 +35,21 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) - let some elimInv ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $mPclose $goal $Q') + let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $mPclose $goal $Q') | throwError "iinv: ElimInv type class synthesis error with goal {goal}" - let hϕ ← iSolveSidecondition q($ϕ) + let X : Q(Type) ← instantiateMVars X + let Pin : Q($prop) ← instantiateMVars Pin + let Pout : Q($X → $prop) ← instantiateMVars Pout + let Q' : Q($X → $prop) ← instantiateMVars Q' - let ⟨_, hyps', _, _, _, _, pf⟩ := hyps.remove false ivar + -- let hϕ ← iSolveSidecondition q($ϕ) - let jvar ← mkFreshIVarId false - let name ← mkFreshUserName .anonymous + let hAcc : Q(∀ x, $e' ⊢ $Pout x -∗ $Q' x) ← + withLocalDeclDQ (← mkFreshUserName `x) X fun x => do + let body ← iIntroCore hyps' q(iprop($Pout $x -∗ $Q' $x)) [introPat] + mkLambdaFVars #[x] body - let hyps'' := hyps'.add bi name jvar q(false) ty - - let pf' ← addBIGoal hyps'' goal return q(tac_inv_elim) /-- `iinv` opens an invariant in the proof state. -/ diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 34ad552d8..a0292c5a9 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2777,3 +2777,15 @@ example (P Q : PROP) : iloeb as IH end iloeb + +section iinv + +variable {GF : BundledGFunctors} [InvGS_gen hlc GF] +variable [BI PROP] [BIUpdate PROP] [BIFUpdate PROP] [BIUpdateFUpdate PROP] + +example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by + iintro #Hinv + iinv Hinv as #H + sorry + +end iinv From 313566797a9ccd0a7f738f2bd92bae470da5c168 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sun, 21 Jun 2026 17:06:37 +0200 Subject: [PATCH 020/108] Focus on the basic functionalities of `iInvCore` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 40 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index ddfdae883..0f2abbc68 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -22,11 +22,13 @@ open Lean Elab Tactic Meta Qq BI Std theorem tac_inv_elim [BI PROP] {e goal : PROP} : e ⊢ goal := sorry private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) - (ivar : IVarId) (selPats : Option <| List SelPat) - (introPat : Syntax × IntroPat) (hclose : Option <| TSyntax `ident) : + (ivar : IVarId) + -- (selPats : Option <| List SelPat) + (introPat : Syntax × IntroPat) : + -- (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let ⟨e', hyps', _, ty, p, eq, pf⟩ := hyps.remove false ivar + let ⟨e', hyps', _, ty, _, _ , _⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin ← mkFreshExprMVarQ q($prop) @@ -39,7 +41,6 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | throwError "iinv: ElimInv type class synthesis error with goal {goal}" let X : Q(Type) ← instantiateMVars X - let Pin : Q($prop) ← instantiateMVars Pin let Pout : Q($X → $prop) ← instantiateMVars Pout let Q' : Q($X → $prop) ← instantiateMVars Q' @@ -52,15 +53,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( return q(tac_inv_elim) -/-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? - " as " colGt introPat (ident)? : tactic - -elab_rules : tactic - | `(tactic| iinv $h:ident $[with $spats:selPat*]? as $ipat:introPat $[$hclose:ident]?) => do - -- Parse the optional selection pattern used for auxiliary assertions needed to open the invariant - let selPats ← spats.mapM <| fun pats => do - liftMacroM <| SelPat.parse pats +elab "iinv " h:ident " as " ipat:introPat : tactic => do -- Parse the introduction pattern used for destructing the result let introPat ← liftMacroM <| IntroPat.parse ipat @@ -68,5 +61,24 @@ elab_rules : tactic -- Find the hypothesis in which the invariant is opened let ivar ← hyps.findWithInfo h - let pf ← iInvCore hyps goal ivar selPats introPat hclose + let pf ← iInvCore hyps goal ivar introPat mvar.assign pf + +-- /-- `iinv` opens an invariant in the proof state. -/ +-- syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? +-- " as " colGt introPat (ident)? : tactic + +-- elab_rules : tactic +-- | `(tactic| iinv $h:ident $[with $spats:selPat*]? as $ipat:introPat $[$hclose:ident]?) => do +-- -- Parse the optional selection pattern used for auxiliary assertions needed to open the invariant +-- let selPats ← spats.mapM <| fun pats => do +-- liftMacroM <| SelPat.parse pats +-- -- Parse the introduction pattern used for destructing the result +-- let introPat ← liftMacroM <| IntroPat.parse ipat + +-- ProofModeM.runTactic λ mvar { hyps, goal, .. } => do +-- -- Find the hypothesis in which the invariant is opened +-- let ivar ← hyps.findWithInfo h + +-- let pf ← iInvCore hyps goal ivar selPats introPat hclose +-- mvar.assign pf From a3879b1ccedab715c4bf7ef5c518e73637150fae Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sun, 21 Jun 2026 17:51:26 +0200 Subject: [PATCH 021/108] Add test proofs for `iinv` --- Iris/Iris/Tests/Tactics.lean | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index a0292c5a9..5b553daa4 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2783,9 +2783,33 @@ section iinv variable {GF : BundledGFunctors} [InvGS_gen hlc GF] variable [BI PROP] [BIUpdate PROP] [BIFUpdate PROP] [BIUpdateFUpdate PROP] -example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by +example {N : Namespace} {P : IProp GF} : + inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H - sorry + -- Side condition + · simp + -- Main proof goal + · imodintro + isplit + · iexact H + · simp [BIBase.wandM] + imodintro + inext + iexact H + +example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : + inv N iprop( P) ={E}=∗ ▷ P := by + iintro #Hinv + iinv Hinv as #H + · simp + sorry + · imodintro + isplit + · iexact H + · simp [BIBase.wandM] + imodintro + inext + iexact H end iinv From 412fd21264fdcd838cf968a0ec0a2b0cf03abffa Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sun, 21 Jun 2026 17:51:46 +0200 Subject: [PATCH 022/108] Modify `iSolveSideCondition` to use `try` to avoid failure --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index ce79fb78d..d3a7ce860 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -29,7 +29,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM | .app (.const ``PMError _) (.lit (.strVal msg)) => throwError "{msg}" | _ => - let gs ← evalTacticAt (← `(tactic | trivial)) mvar.mvarId! + let gs ← evalTacticAt (← `(tactic | try trivial)) mvar.mvarId! if !gs.isEmpty then if failOnUnsolved then throwError "isolvesidecondition: failed to solve sidecondition {target}" diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 0f2abbc68..863d83bcf 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -44,11 +44,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pout : Q($X → $prop) ← instantiateMVars Pout let Q' : Q($X → $prop) ← instantiateMVars Q' - -- let hϕ ← iSolveSidecondition q($ϕ) + let hϕ ← iSolveSidecondition q($ϕ) false let hAcc : Q(∀ x, $e' ⊢ $Pout x -∗ $Q' x) ← withLocalDeclDQ (← mkFreshUserName `x) X fun x => do - let body ← iIntroCore hyps' q(iprop($Pout $x -∗ $Q' $x)) [introPat] + let poutX : Q($prop) := Expr.headBeta q($Pout $x) + let qX : Q($prop) := Expr.headBeta q($Q' $x) + let body ← iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] mkLambdaFVars #[x] body return q(tac_inv_elim) From 52a0c4930d54ab2a6540fad676e17ce2e9e11a02 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 09:57:55 +0200 Subject: [PATCH 023/108] `iSolveSideCondition`: use `observing?` instead of `try` --- Iris/Iris/ProofMode/Tactics/Basic.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index d3a7ce860..2606470a3 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -29,10 +29,10 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM | .app (.const ``PMError _) (.lit (.strVal msg)) => throwError "{msg}" | _ => - let gs ← evalTacticAt (← `(tactic | try trivial)) mvar.mvarId! + let gs ← (observing? <| evalTacticAt (← `(tactic | trivial)) mvar.mvarId!) <&> (·.getD []) if !gs.isEmpty then if failOnUnsolved then - throwError "isolvesidecondition: failed to solve sidecondition {target}" + throwError "iSolveSideCondition: failed to solve side condition {target}" else for g in gs do addMVarGoal g return mvar From ac9f04098ae430178fc3f95703a1ec8a336b5441 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 10:24:23 +0200 Subject: [PATCH 024/108] Fix `iSolveSideCondition`: return singleton containing original goal if `trivial` fails --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 2606470a3..df65b571d 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -29,7 +29,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM | .app (.const ``PMError _) (.lit (.strVal msg)) => throwError "{msg}" | _ => - let gs ← (observing? <| evalTacticAt (← `(tactic | trivial)) mvar.mvarId!) <&> (·.getD []) + let gs ← (observing? <| evalTacticAt (← `(tactic | trivial)) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then throwError "iSolveSideCondition: failed to solve side condition {target}" From fb1213c1c91ee687de3ff68e364903ac93d6e470 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 11:16:52 +0200 Subject: [PATCH 025/108] Port `wandM_sound` --- Iris/Iris/BI/DerivedLaws.lean | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Iris/Iris/BI/DerivedLaws.lean b/Iris/Iris/BI/DerivedLaws.lean index 599beaf62..a00ba157d 100644 --- a/Iris/Iris/BI/DerivedLaws.lean +++ b/Iris/Iris/BI/DerivedLaws.lean @@ -1036,6 +1036,12 @@ theorem emp_or [BI PROP] {P : PROP} [Affine P] : emp ∨ P ⊣⊢ emp := ⟨or_e theorem emp_wand [BI PROP] {P : PROP} : (emp -∗ P) ⊣⊢ P := ⟨emp_sep.mpr.trans wand_elim_right, wand_intro_left emp_sep.mp⟩ +@[rocq_alias wandM_sound] +theorem wandM_sound [BI PROP] {mP : Option PROP} {Q : PROP} : + (mP -∗? Q) ⊣⊢ (mP.getD emp -∗ Q) := by + cases mP <;> simp [BIBase.wandM] + exact emp_wand.symm + @[rocq_alias bi.or_emp] theorem or_emp [BI PROP] {P : PROP} [Affine P] : P ∨ emp ⊣⊢ emp := or_comm.trans emp_or From 35407d538172edc82b76fa7c4c77b6a3a82f3b81 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 11:17:38 +0200 Subject: [PATCH 026/108] Introduce assumptions into `tac_inv_elim` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 863d83bcf..cb0268dd4 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -19,7 +19,19 @@ public meta section open Lean Elab Tactic Meta Qq BI Std @[rocq_alias tac_inv_elim] -theorem tac_inv_elim [BI PROP] {e goal : PROP} : e ⊢ goal := sorry +theorem tac_inv_elim [BI PROP] {e e' goal : PROP} {ϕ : Prop} {X : Type} {p : Bool} + {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} + (inst : ElimInv ϕ X Pinv Pin Pout mPclose goal Q') + (hϕ : ϕ) + (hAcc : ∀ x, e' ⊢ Pout x -∗ Q' x) + (pf : e ⊣⊢ e' ∗ □?p Pinv) : + e ⊢ goal := by + apply pf.mp.trans + have h := inst.elim_inv hϕ + clear inst hϕ pf + cases mPclose with simp_all + | none => sorry + | some mPclose => sorry private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) @@ -28,7 +40,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let ⟨e', hyps', _, ty, _, _ , _⟩ := hyps.remove false ivar + let ⟨e', hyps', _, Pinv, _, _ , pf⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin ← mkFreshExprMVarQ q($prop) @@ -37,23 +49,24 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) - let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $ty $Pin $Pout $mPclose $goal $Q') - | throwError "iinv: ElimInv type class synthesis error with goal {goal}" - let X : Q(Type) ← instantiateMVars X let Pout : Q($X → $prop) ← instantiateMVars Pout let Q' : Q($X → $prop) ← instantiateMVars Q' + let mPclose : Q(Option ($X → $prop)) ← instantiateMVars mPclose + + let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $mPclose $goal $Q') + | throwError "iinv: ElimInv type class synthesis error with goal {goal}" let hϕ ← iSolveSidecondition q($ϕ) false let hAcc : Q(∀ x, $e' ⊢ $Pout x -∗ $Q' x) ← withLocalDeclDQ (← mkFreshUserName `x) X fun x => do let poutX : Q($prop) := Expr.headBeta q($Pout $x) - let qX : Q($prop) := Expr.headBeta q($Q' $x) + let qX : Q($prop) := Expr.headBeta q($Q' $x) let body ← iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] mkLambdaFVars #[x] body - return q(tac_inv_elim) + return q(tac_inv_elim $inst $hϕ $hAcc $pf) elab "iinv " h:ident " as " ipat:introPat : tactic => do -- Parse the introduction pattern used for destructing the result From 2c889e6bafc84a8aaf5585898475cd24a2adc931 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 11:46:05 +0200 Subject: [PATCH 027/108] Apply `simp` for `wandM` --- Iris/Iris/BI/BIBase.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/BI/BIBase.lean b/Iris/Iris/BI/BIBase.lean index e30e8ad75..91a9753cd 100644 --- a/Iris/Iris/BI/BIBase.lean +++ b/Iris/Iris/BI/BIBase.lean @@ -165,7 +165,7 @@ delab_rule iff delab_rule wandIff | `($_ $P $Q) => do ``(iprop($(← unpackIprop P) ∗-∗ $(← unpackIprop Q))) -@[rocq_alias bi_wandM] +@[simp, rocq_alias bi_wandM] def wandM [BIBase PROP] (mP : Option PROP) (Q : PROP) : PROP := match mP with | none => Q From 6a87583691f706d2b6da027a3925de991baba5d5 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 11:46:34 +0200 Subject: [PATCH 028/108] Test `iinv` with invalid invariant: `ElimInv` synthesis error --- Iris/Iris/ProofMode/Tactics/Inv.lean | 14 +++++--------- Iris/Iris/Tests/Tactics.lean | 20 +++++++++++++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index cb0268dd4..a0ca586e0 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -40,7 +40,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let ⟨e', hyps', _, Pinv, _, _ , pf⟩ := hyps.remove false ivar + let ⟨e', hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin ← mkFreshExprMVarQ q($prop) @@ -48,17 +48,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pout ← mkFreshExprMVarQ q($X → $prop) let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) - - let X : Q(Type) ← instantiateMVars X - let Pout : Q($X → $prop) ← instantiateMVars Pout - let Q' : Q($X → $prop) ← instantiateMVars Q' - let mPclose : Q(Option ($X → $prop)) ← instantiateMVars mPclose - let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $mPclose $goal $Q') - | throwError "iinv: ElimInv type class synthesis error with goal {goal}" + | throwError "iinv: invalid invariant {Pinv}" + -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + -- Create the wand proposition and apply the introduction pattern to destruct the premise let hAcc : Q(∀ x, $e' ⊢ $Pout x -∗ $Q' x) ← withLocalDeclDQ (← mkFreshUserName `x) X fun x => do let poutX : Q($prop) := Expr.headBeta q($Pout $x) @@ -66,7 +62,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let body ← iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] mkLambdaFVars #[x] body - return q(tac_inv_elim $inst $hϕ $hAcc $pf) + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq) elab "iinv " h:ident " as " ipat:introPat : tactic => do -- Parse the introduction pattern used for destructing the result diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 5b553daa4..42345bf31 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2783,6 +2783,10 @@ section iinv variable {GF : BundledGFunctors} [InvGS_gen hlc GF] variable [BI PROP] [BIUpdate PROP] [BIFUpdate PROP] [BIUpdateFUpdate PROP] +/-- + Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and + `intoAcc_inv` where the side condition is trivial. +-/ example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv @@ -2798,18 +2802,32 @@ example {N : Namespace} {P : IProp GF} : inext iexact H +/-- + Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and + `intoAcc_inv`, relying on the side condition `↑N ⊆ E`. +-/ example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H + -- Side condition · simp sorry + -- Main proof goal · imodintro isplit · iexact H - · simp [BIBase.wandM] + · simp imodintro inext iexact H +/- Tests `iinv` with an invalid invariant. -/ +/-- error: iinv: invalid invariant P -/ +#guard_msgs in +example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : + □ P ={E}=∗ ▷ P := by + iintro #Hinv + iinv Hinv as #H + end iinv From 56b8071ba9974c825a23a57ae75ff1c44b68bba2 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 12:44:30 +0200 Subject: [PATCH 029/108] `iinv`: starting implementing `as` clause (`hclose`) --- Iris/Iris/ProofMode/Instances.lean | 22 +++++++-------- Iris/Iris/ProofMode/Tactics/Inv.lean | 42 ++++++++++------------------ 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 855fd8dd4..d31c0bb15 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -928,14 +928,14 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α none Q Q' where elim_inv := sorry --- set_option synthInstance.checkSynthOrder false in --- @[rocq_alias elim_inv_acc_with_close] --- instance elimInv_acc_with_close [BI PROP] {X : Type} --- ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) --- [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] --- [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : --- ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin --- α --- (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) --- Q (fun _ => Q') where --- elim_inv := sorry +set_option synthInstance.checkSynthOrder false in +@[rocq_alias elim_inv_acc_with_close] +instance elimInv_acc_with_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin + α + (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) + Q (fun _ => Q') where + elim_inv := sorry diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index a0ca586e0..f744f1114 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -36,8 +36,8 @@ theorem tac_inv_elim [BI PROP] {e e' goal : PROP} {ϕ : Prop} {X : Type} {p : Bo private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) -- (selPats : Option <| List SelPat) - (introPat : Syntax × IntroPat) : - -- (hclose : Option <| TSyntax `ident) : + (introPat : Syntax × IntroPat) + (hclose : Option <| TSyntax `ident) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context let ⟨e', hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar @@ -64,32 +64,18 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( return q(tac_inv_elim $inst $hϕ $hAcc $pfEq) -elab "iinv " h:ident " as " ipat:introPat : tactic => do - -- Parse the introduction pattern used for destructing the result - let introPat ← liftMacroM <| IntroPat.parse ipat +/-- `iinv` opens an invariant in the proof state. -/ +syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? + " as " colGt introPat (ident)? : tactic - ProofModeM.runTactic λ mvar { hyps, goal, .. } => do - -- Find the hypothesis in which the invariant is opened - let ivar ← hyps.findWithInfo h +elab_rules : tactic + | `(tactic| iinv $h:ident as $ipat:introPat $[$hclose:ident]?) => do + -- Parse the introduction pattern used for destructing the result + let introPat ← liftMacroM <| IntroPat.parse ipat - let pf ← iInvCore hyps goal ivar introPat - mvar.assign pf + ProofModeM.runTactic λ mvar { hyps, goal, .. } => do + -- Find the hypothesis in which the invariant is opened + let ivar ← hyps.findWithInfo h --- /-- `iinv` opens an invariant in the proof state. -/ --- syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? --- " as " colGt introPat (ident)? : tactic - --- elab_rules : tactic --- | `(tactic| iinv $h:ident $[with $spats:selPat*]? as $ipat:introPat $[$hclose:ident]?) => do --- -- Parse the optional selection pattern used for auxiliary assertions needed to open the invariant --- let selPats ← spats.mapM <| fun pats => do --- liftMacroM <| SelPat.parse pats --- -- Parse the introduction pattern used for destructing the result --- let introPat ← liftMacroM <| IntroPat.parse ipat - --- ProofModeM.runTactic λ mvar { hyps, goal, .. } => do --- -- Find the hypothesis in which the invariant is opened --- let ivar ← hyps.findWithInfo h - --- let pf ← iInvCore hyps goal ivar selPats introPat hclose --- mvar.assign pf + let pf ← iInvCore hyps goal ivar introPat hclose + mvar.assign pf From bb64fd0d05ddbab6689f0509bebdba98b6f75c7f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Mon, 22 Jun 2026 18:23:17 +0200 Subject: [PATCH 030/108] Towards implement `hclose` in `iInvCore` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 25 +++++++++++++++++++++---- Iris/Iris/Tests/Tactics.lean | 15 +++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index f744f1114..5ad66e739 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -18,12 +18,15 @@ namespace Iris.ProofMode public meta section open Lean Elab Tactic Meta Qq BI Std +private def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Option PROP := + mP.map (· x) + @[rocq_alias tac_inv_elim] theorem tac_inv_elim [BI PROP] {e e' goal : PROP} {ϕ : Prop} {X : Type} {p : Bool} {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout mPclose goal Q') (hϕ : ϕ) - (hAcc : ∀ x, e' ⊢ Pout x -∗ Q' x) + (hAcc : ∀ x, e' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) (pf : e ⊣⊢ e' ∗ □?p Pinv) : e ⊢ goal := by apply pf.mp.trans @@ -54,12 +57,26 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + -- Create an introduction pattern for `hclose` + let hClosePat ← match hclose with + | none => pure none + | some hclose => pure <| some (hclose.raw, IntroPat.intro (.one (← `(binderIdent| $hclose:ident)))) + -- Create the wand proposition and apply the introduction pattern to destruct the premise - let hAcc : Q(∀ x, $e' ⊢ $Pout x -∗ $Q' x) ← - withLocalDeclDQ (← mkFreshUserName `x) X fun x => do + let hAcc : Q(∀ x : $X, $e' ⊢ $Pout x -∗ BIBase.wandM (optionMap $mPclose x) ($Q' x)) ← + withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do let poutX : Q($prop) := Expr.headBeta q($Pout $x) let qX : Q($prop) := Expr.headBeta q($Q' $x) - let body ← iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] + let body ← match mPclose with + | ~q(none) => + iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] + | ~q(some $f) => + match hClosePat with + | some closePat => + let closeX : Q($prop) := Expr.headBeta q(optionMap $mPclose $x) + iIntroCore hyps' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] + -- Throw an error if `hclose` is not given, but `mPclose` is not `none` + | none => throwError "iinv: error" mkLambdaFVars #[x] body return q(tac_inv_elim $inst $hϕ $hAcc $pfEq) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 42345bf31..4aa927395 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2802,6 +2802,21 @@ example {N : Namespace} {P : IProp GF} : inext iexact H +example {N : Namespace} {P : IProp GF} : + inv N iprop( P) ={⊤}=∗ ▷ P := by + iintro #Hinv + iinv Hinv as #H Hclose + -- Side condition + · simp + -- Main proof goal + · imodintro + isplit + · iexact H + · simp [BIBase.wandM] + imodintro + inext + iexact H + /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv`, relying on the side condition `↑N ⊆ E`. From 44d98499006c7d0934515b1764a2a880999db7b5 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 10:06:46 +0200 Subject: [PATCH 031/108] Add a separate Boolean argument `close` to `ElimInv` so that the tactic user can decide where to supply `hclose` --- Iris/Iris/ProofMode/Classes.lean | 3 ++- Iris/Iris/ProofMode/Instances.lean | 4 ++-- Iris/Iris/ProofMode/Tactics/Inv.lean | 10 ++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index e3948b6a6..153de28b1 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -245,11 +245,12 @@ class IntoAcc [BI PROP] {X : outParam Type} (Pacc : PROP) (mγ : outParam <| X → Option PROP) where into_acc : ϕ → Pacc -∗ Pin -∗ accessor M1 M2 α β mγ +set_option synthInstance.checkSynthOrder false in /-- The type class used for the `iinv` tactic. -/ @[ipm_class, rocq_alias ElimInv] class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (Pinv : PROP) (Pin : outParam PROP) (Pout : outParam <| X → PROP) - (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where + (close : Bool) (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ ((mPclose.map (· x)).getD emp) -∗ Q' x) ⊢ Q export ElimInv (elim_inv) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index d31c0bb15..866281ece 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -925,7 +925,7 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α none Q Q' where + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where elim_inv := sorry set_option synthInstance.checkSynthOrder false in @@ -936,6 +936,6 @@ instance elimInv_acc_with_close [BI PROP] {X : Type} [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α - (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) + true (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) Q (fun _ => Q') where elim_inv := sorry diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 5ad66e739..436ee0e90 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -22,9 +22,10 @@ private def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : mP.map (· x) @[rocq_alias tac_inv_elim] -theorem tac_inv_elim [BI PROP] {e e' goal : PROP} {ϕ : Prop} {X : Type} {p : Bool} +theorem tac_inv_elim [BI PROP] + {e e' goal : PROP} {ϕ : Prop} {X : Type} {p close : Bool} {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} - (inst : ElimInv ϕ X Pinv Pin Pout mPclose goal Q') + (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) (hAcc : ∀ x, e' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) (pf : e ⊣⊢ e' ∗ □?p Pinv) : @@ -49,9 +50,10 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pin ← mkFreshExprMVarQ q($prop) let X ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) + let close := if hclose.isSome then q(true) else q(false) let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) - let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $mPclose $goal $Q') + let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') | throwError "iinv: invalid invariant {Pinv}" -- Solve side conditions automatically if possible, otherwise add them into the proof state @@ -63,7 +65,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | some hclose => pure <| some (hclose.raw, IntroPat.intro (.one (← `(binderIdent| $hclose:ident)))) -- Create the wand proposition and apply the introduction pattern to destruct the premise - let hAcc : Q(∀ x : $X, $e' ⊢ $Pout x -∗ BIBase.wandM (optionMap $mPclose x) ($Q' x)) ← + let hAcc : Q(∀ x : $X, $e' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do let poutX : Q($prop) := Expr.headBeta q($Pout $x) let qX : Q($prop) := Expr.headBeta q($Q' $x) From ce49b38a8981ef0245725a595afd9089f9e8f962 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 10:16:16 +0200 Subject: [PATCH 032/108] Towards proving `tac_inv_elim` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 436ee0e90..7b2b5aa07 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -30,12 +30,20 @@ theorem tac_inv_elim [BI PROP] (hAcc : ∀ x, e' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) (pf : e ⊣⊢ e' ∗ □?p Pinv) : e ⊢ goal := by - apply pf.mp.trans have h := inst.elim_inv hϕ - clear inst hϕ pf cases mPclose with simp_all - | none => sorry - | some mPclose => sorry + | none => calc + e ⊢ e' ∗ □?p Pinv := pf.mp + _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sorry + _ ⊢ goal := h + | some mPclose => calc + e ⊢ e' ∗ □?p Pinv := pf.mp + _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sorry + _ ⊢ goal := h private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) From 3bd08459008a242a8041e3b6dba47bd24657f7bc Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 11:11:01 +0200 Subject: [PATCH 033/108] Further towards proving `tac_inv_elim` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 7b2b5aa07..2689056b9 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -36,13 +36,19 @@ theorem tac_inv_elim [BI PROP] e ⊢ e' ∗ □?p Pinv := pf.mp _ ⊢ □?p Pinv ∗ e' := sep_comm.mp _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sorry + _ ⊢ Pinv ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right emp_sep.mpr + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| sep_mono_left sorry + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_mono (fun _ => wand_mono_left sep_emp.mp) _ ⊢ goal := h | some mPclose => calc e ⊢ e' ∗ □?p Pinv := pf.mp _ ⊢ □?p Pinv ∗ e' := sep_comm.mp _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sorry + _ ⊢ Pinv ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| forall_mono <| fun _ => wand_curry.mp + _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right emp_sep.mpr + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| sep_mono_left sorry _ ⊢ goal := h private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) From d12405c6730c859664dd132f6e7a3c10853fb442 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 11:46:16 +0200 Subject: [PATCH 034/108] Implement the metaprogramming part for `hclose` - `ElimModal` requires `uncheckedInParam` in arguments to stop complaints - `optionMap` must be public - Add new test --- Iris/Iris/ProofMode/Classes.lean | 5 +++-- Iris/Iris/ProofMode/Tactics/Inv.lean | 5 +++-- Iris/Iris/Tests/Tactics.lean | 17 +++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 153de28b1..e6857b3a9 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -181,8 +181,9 @@ export FromModal (from_modal) /-- `ElimModal` turns `□?p P` into `□?p' P'` and `Q` into `Q'` under condition `φ`. -/ @[ipm_class, rocq_alias ElimModal] -class ElimModal {PROP} [BI PROP] (φ : outParam $ Prop) (p : Bool) (p' : outParam $ Bool) (P : PROP) - (P' : outParam $ PROP) (Q : PROP) (Q' : outParam $ PROP) where +class ElimModal {PROP} [BI PROP] (φ : outParam $ Prop) (p : Bool) + (p' : outParam $ uncheckedInParam Bool) (P : PROP) + (P' : outParam $ uncheckedInParam PROP) (Q : PROP) (Q' : outParam $ PROP) where elim_modal : φ → □?p P ∗ (□?p' P' -∗ Q') ⊢ Q export ElimModal (elim_modal) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 2689056b9..0e94828e5 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -18,7 +18,7 @@ namespace Iris.ProofMode public meta section open Lean Elab Tactic Meta Qq BI Std -private def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Option PROP := +def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Option PROP := mP.map (· x) @[rocq_alias tac_inv_elim] @@ -87,9 +87,10 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | ~q(none) => iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] | ~q(some $f) => + let f : Q($X → $prop) := f match hClosePat with | some closePat => - let closeX : Q($prop) := Expr.headBeta q(optionMap $mPclose $x) + let closeX : Q($prop) := Expr.headBeta q($f $x) iIntroCore hyps' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: error" diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 4aa927395..c88886b68 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2801,7 +2801,10 @@ example {N : Namespace} {P : IProp GF} : imodintro inext iexact H - +/-- + Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and + `intoAcc_inv` where the side condition is trivial. +-/ example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv @@ -2809,13 +2812,11 @@ example {N : Namespace} {P : IProp GF} : -- Side condition · simp -- Main proof goal - · imodintro - isplit - · iexact H - · simp [BIBase.wandM] - imodintro - inext - iexact H + · simp + imod Hclose $$ H + imodintro + inext + iexact H /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and From bac61328a3c6049307124101df384cbad16e9b5d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 12:52:20 +0200 Subject: [PATCH 035/108] Generalise `hclose` to be any intro patterns instead of an ident --- Iris/Iris/ProofMode/Tactics/Inv.lean | 18 +++++++----------- Iris/Iris/Tests/Tactics.lean | 15 +++++++++------ 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 0e94828e5..3018775c9 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -55,7 +55,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( (ivar : IVarId) -- (selPats : Option <| List SelPat) (introPat : Syntax × IntroPat) - (hclose : Option <| TSyntax `ident) : + (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context let ⟨e', hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar @@ -64,7 +64,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pin ← mkFreshExprMVarQ q($prop) let X ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) - let close := if hclose.isSome then q(true) else q(false) + let close := if closePat.isSome then q(true) else q(false) let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') @@ -73,11 +73,6 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false - -- Create an introduction pattern for `hclose` - let hClosePat ← match hclose with - | none => pure none - | some hclose => pure <| some (hclose.raw, IntroPat.intro (.one (← `(binderIdent| $hclose:ident)))) - -- Create the wand proposition and apply the introduction pattern to destruct the premise let hAcc : Q(∀ x : $X, $e' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do @@ -88,7 +83,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] | ~q(some $f) => let f : Q($X → $prop) := f - match hClosePat with + match closePat with | some closePat => let closeX : Q($prop) := Expr.headBeta q($f $x) iIntroCore hyps' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] @@ -100,16 +95,17 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? - " as " colGt introPat (ident)? : tactic + " as " colGt introPat (introPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident as $ipat:introPat $[$hclose:ident]?) => do + | `(tactic| iinv $h:ident as $ipat:introPat $[$cpat:introPat]?) => do -- Parse the introduction pattern used for destructing the result let introPat ← liftMacroM <| IntroPat.parse ipat + let closePat ← liftMacroM <| cpat.mapM IntroPat.parse ProofModeM.runTactic λ mvar { hyps, goal, .. } => do -- Find the hypothesis in which the invariant is opened let ivar ← hyps.findWithInfo h - let pf ← iInvCore hyps goal ivar introPat hclose + let pf ← iInvCore hyps goal ivar introPat closePat mvar.assign pf diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index c88886b68..cb956bd31 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -12,6 +12,8 @@ public import Iris.Instances.Lib.LaterCredits public import Iris.Instances.Lib.Token public import Iris.Algebra.CMRA public import Iris.Instances.Lib.Invariants +public import Iris.Instances.Lib.CInvariants +public import Iris.Instances.Lib.NaInvariants @[expose] public section @@ -2780,14 +2782,14 @@ end iloeb section iinv -variable {GF : BundledGFunctors} [InvGS_gen hlc GF] -variable [BI PROP] [BIUpdate PROP] [BIFUpdate PROP] [BIUpdateFUpdate PROP] +-- variable [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] +-- variable /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example {N : Namespace} {P : IProp GF} : +example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H @@ -2805,7 +2807,7 @@ example {N : Namespace} {P : IProp GF} : Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example {N : Namespace} {P : IProp GF} : +example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H Hclose @@ -2822,7 +2824,7 @@ example {N : Namespace} {P : IProp GF} : Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv`, relying on the side condition `↑N ⊆ E`. -/ -example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : +example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H @@ -2841,7 +2843,8 @@ example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : /- Tests `iinv` with an invalid invariant. -/ /-- error: iinv: invalid invariant P -/ #guard_msgs in -example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : +example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] + {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : □ P ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H From 6a0d7e79b05e667bae1cf5b229d7e2d39ba3d1aa Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 14:14:39 +0200 Subject: [PATCH 036/108] Start implementing selection pattern handling --- Iris/Iris/ProofMode/Tactics/Inv.lean | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 3018775c9..fc263ed8d 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -53,7 +53,7 @@ theorem tac_inv_elim [BI PROP] private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) - -- (selPats : Option <| List SelPat) + (selPats : Option <| List SelPat) (introPat : Syntax × IntroPat) (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do @@ -98,8 +98,9 @@ syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? " as " colGt introPat (introPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident as $ipat:introPat $[$cpat:introPat]?) => do - -- Parse the introduction pattern used for destructing the result + | `(tactic| iinv $h:ident $[with $spat:selPat*]? as $ipat:introPat $[$cpat:introPat]?) => do + -- Parse the introduction and selection patterns + let selPats ← liftMacroM <| spat.mapM SelPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat let closePat ← liftMacroM <| cpat.mapM IntroPat.parse @@ -107,5 +108,5 @@ elab_rules : tactic -- Find the hypothesis in which the invariant is opened let ivar ← hyps.findWithInfo h - let pf ← iInvCore hyps goal ivar introPat closePat + let pf ← iInvCore hyps goal ivar selPats introPat closePat mvar.assign pf From f93612e8d7304f9fd652920379f5b3450563b3c3 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 15:27:44 +0200 Subject: [PATCH 037/108] Fix: specialisation pattern rather than selection pattern --- Iris/Iris/ProofMode/Tactics/Inv.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index fc263ed8d..2716ed3c0 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -53,7 +53,7 @@ theorem tac_inv_elim [BI PROP] private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) - (selPats : Option <| List SelPat) + (selPats : Option SpecPat) (introPat : Syntax × IntroPat) (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do @@ -94,13 +94,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( return q(tac_inv_elim $inst $hϕ $hAcc $pfEq) /-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace selPat)*)? +syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace specPat)*)? " as " colGt introPat (introPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident $[with $spat:selPat*]? as $ipat:introPat $[$cpat:introPat]?) => do + | `(tactic| iinv $h:ident $[with $spat:specPat]? as $ipat:introPat $[$cpat:introPat]?) => do -- Parse the introduction and selection patterns - let selPats ← liftMacroM <| spat.mapM SelPat.parse + let selPats ← liftMacroM <| spat.mapM SpecPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat let closePat ← liftMacroM <| cpat.mapM IntroPat.parse From 5e6f8ad0b56baed53cc9ee01a4193313a5fb2d49 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 23 Jun 2026 17:58:01 +0200 Subject: [PATCH 038/108] Parse `specPats` as a list of specialisation patterns --- Iris/Iris/ProofMode/Tactics/Inv.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 2716ed3c0..b09baabf6 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -53,7 +53,7 @@ theorem tac_inv_elim [BI PROP] private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) - (selPats : Option SpecPat) + (specPats : List SpecPat) (introPat : Syntax × IntroPat) (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do @@ -98,9 +98,9 @@ syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace specPat)*)? " as " colGt introPat (introPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident $[with $spat:specPat]? as $ipat:introPat $[$cpat:introPat]?) => do + | `(tactic| iinv $h:ident $[with $spats:specPat*]? as $ipat:introPat $[$cpat:introPat]?) => do -- Parse the introduction and selection patterns - let selPats ← liftMacroM <| spat.mapM SpecPat.parse + let specPats ← spats.mapM (liftMacroM <| ·.mapM SpecPat.parse) <&> (·.getD #[] |>.toList) let introPat ← liftMacroM <| IntroPat.parse ipat let closePat ← liftMacroM <| cpat.mapM IntroPat.parse @@ -108,5 +108,5 @@ elab_rules : tactic -- Find the hypothesis in which the invariant is opened let ivar ← hyps.findWithInfo h - let pf ← iInvCore hyps goal ivar selPats introPat closePat + let pf ← iInvCore hyps goal ivar specPats introPat closePat mvar.assign pf From 332c25b69176047b5fccfa6eb8a7bf9736049984 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 10:54:03 +0200 Subject: [PATCH 039/108] Use `iSpecializeCore` for handling `with ...` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index b09baabf6..5043d2543 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -27,8 +27,9 @@ theorem tac_inv_elim [BI PROP] {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) - (hAcc : ∀ x, e' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) - (pf : e ⊣⊢ e' ∗ □?p Pinv) : + (hAcc : ∀ x, e'' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) + (pf : e ⊣⊢ e' ∗ □?p Pinv) + (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ out'') : e ⊢ goal := by have h := inst.elim_inv hϕ cases mPclose with simp_all @@ -36,7 +37,7 @@ theorem tac_inv_elim [BI PROP] e ⊢ e' ∗ □?p Pinv := pf.mp _ ⊢ □?p Pinv ∗ e' := sep_comm.mp _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ ∀ x, Pout x -∗ Q' x := sorry _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right emp_sep.mpr _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| sep_mono_left sorry _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_mono (fun _ => wand_mono_left sep_emp.mp) @@ -45,7 +46,7 @@ theorem tac_inv_elim [BI PROP] e ⊢ e' ∗ □?p Pinv := pf.mp _ ⊢ □?p Pinv ∗ e' := sep_comm.mp _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := sorry _ ⊢ Pinv ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| forall_mono <| fun _ => wand_curry.mp _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right emp_sep.mpr _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| sep_mono_left sorry @@ -58,7 +59,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let ⟨e', hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar + let ⟨_, hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin ← mkFreshExprMVarQ q($prop) @@ -73,8 +74,10 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + let ⟨e'', _, _, _, pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) specPats + -- Create the wand proposition and apply the introduction pattern to destruct the premise - let hAcc : Q(∀ x : $X, $e' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← + let hAcc : Q(∀ x : $X, $e'' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do let poutX : Q($prop) := Expr.headBeta q($Pout $x) let qX : Q($prop) := Expr.headBeta q($Q' $x) @@ -91,10 +94,10 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | none => throwError "iinv: error" mkLambdaFVars #[x] body - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq) + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) /-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace specPat)*)? +syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace specPat)+)? " as " colGt introPat (introPat)? : tactic elab_rules : tactic From 5f7806763c8673494b42aecd5dae1721db550c95 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 10:59:47 +0200 Subject: [PATCH 040/108] Uncomment `intoAcc_cinv`, add two relevant tests --- Iris/Iris/Instances/Lib/CInvariants.lean | 12 +++--- Iris/Iris/Tests/Tactics.lean | 50 +++++++++++++++++++----- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index 2cc9faba3..fe9400f74 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -284,12 +284,12 @@ theorem cancel (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (Hsub : instance intoInv_cinv (N : Namespace) (γ : GName) (P : IProp GF) : IntoInv (cinv N γ P) N := {} --- set_option synthInstance.checkSynthOrder false in --- @[rocq_alias into_acc_cinv] --- instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : --- IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) --- (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where --- into_acc := sorry +set_option synthInstance.checkSynthOrder false in +@[rocq_alias into_acc_cinv] +instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : + IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) + (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where + into_acc := sorry end CancelableInvariant end Iris diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index cb956bd31..5daf054ae 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -18,7 +18,7 @@ public import Iris.Instances.Lib.NaInvariants @[expose] public section namespace Iris.Tests -open BI CMRA DFrac +open BI CMRA DFrac CancelableInvariant NonAtomicInvariant /- This file contains tests with various scenarios for all available tactics. -/ @@ -2782,15 +2782,13 @@ end iloeb section iinv --- variable [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] --- variable +variable {hlc : HasLC} {GF : BundledGFunctors} [InvGS_gen hlc GF] /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P : IProp GF} : - inv N iprop( P) ={⊤}=∗ ▷ P := by +example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H -- Side condition @@ -2807,8 +2805,7 @@ example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P : IProp GF} : - inv N iprop( P) ={⊤}=∗ ▷ P := by +example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H Hclose -- Side condition @@ -2824,7 +2821,7 @@ example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} {P Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv`, relying on the side condition `↑N ⊆ E`. -/ -example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : +example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H @@ -2843,10 +2840,43 @@ example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] {N E : Namespace} { /- Tests `iinv` with an invalid invariant. -/ /-- error: iinv: invalid invariant P -/ #guard_msgs in -example [BI PROP] {GF : BundledGFunctors} [InvGS_gen hlc GF] - {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : +example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : □ P ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H +/-- Tests `iinv` with `elimInv_acc_with_close`, `elimAcc_fupd` and `intoAcc_cinv`. -/ +example [CInvG GF] {γ : GName} {p : Qp} : + cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by + iintro ⟨#Hinv, H⟩ + iinv Hinv as ⟨#HP, Hown⟩ + -- Side condition + · simp + -- Main proof goal + · simp + imodintro + isplit + iexact HP + iframe + imodintro + inext + iexact HP + · exact p + +/-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_cinv`. -/ +example [CInvG GF] {γ : GName} {p : Qp} : + cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by + iintro ⟨#Hinv, H⟩ + iinv Hinv as ⟨#HP, Hown⟩ Hclose + -- Side condition + · simp + -- Main proof goal + · simp + imod Hclose $$ HP + imodintro + iframe + inext + iexact HP + · exact p + end iinv From 58f83ecd4c47c3607f91d2fee10aae77ab2b90c5 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 11:17:20 +0200 Subject: [PATCH 041/108] Uncomment `intoAcc_na`, introduce relevant tests --- Iris/Iris/Instances/Lib/NaInvariants.lean | 14 ++++----- Iris/Iris/Tests/Tactics.lean | 38 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index 88e83b744..be3187088 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -225,13 +225,13 @@ nonrec theorem inv_acc {p : NaInvPoolName} {E F : CoPset} {N : Namespace} {P : I instance intoInv_na (N : Namespace) (P : IProp GF) : IntoInv (inv p N P) N := {} --- set_option synthInstance.checkSynthOrder false in --- @[rocq_alias into_acc_na] --- instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : --- IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) --- (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) --- (λ _ => some (own p F)) where --- into_acc := sorry +set_option synthInstance.checkSynthOrder false in +@[rocq_alias into_acc_na] +instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IProp GF) : + IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) + (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) + (λ _ => some (own p F)) where + into_acc := sorry end NonAtomicInvariant end Iris diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 5daf054ae..3b4b3be4d 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2845,7 +2845,7 @@ example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : iintro #Hinv iinv Hinv as #H -/-- Tests `iinv` with `elimInv_acc_with_close`, `elimAcc_fupd` and `intoAcc_cinv`. -/ +/-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_cinv`. -/ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ @@ -2879,4 +2879,40 @@ example [CInvG GF] {γ : GName} {p : Qp} : iexact HP · exact p +example [CInvG GF] {γ : GName} {p1 p2 : Qp} {N : Namespace} {P : IProp GF} : + cinv N γ iprop( P) ∗ own γ p1 ∗ own γ p2 + ⊢@{IProp GF} |={⊤}=> own γ p1 ∗ own γ p2 ∗ ▷ P := by + iintro ⟨#Hinv, Hown1, Hown2⟩ + sorry + +/-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_na`. -/ +example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} + {P : IProp GF} (h : ↑N ⊆ E2) : + NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 + ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#Hinv, Hown1, Hown2⟩ + iinv Hinv as ⟨#HP, Hown2⟩ + · simp + exact h + · imodintro + isplitl [Hown2] + · iframe HP Hown2 + · simp + iframe Hown1 Hown2 + iintro - + imodintro + inext + iexact HP + +/-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_na`. -/ +example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} + {P : IProp GF} (h : ↑N ⊆ E2) : + NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 + ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#Hinv, Hown1, Hown2⟩ + iinv Hinv as ⟨#HP, Hown2⟩ Hclose + sorry + sorry + sorry + end iinv From 5760e93d94b72f3dac5d4835919ba887a4736bdd Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 11:58:55 +0200 Subject: [PATCH 042/108] Bug fix: `hyps'` -> `hyps''` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 5043d2543..2bd20290e 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -54,7 +54,7 @@ theorem tac_inv_elim [BI PROP] private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) - (specPats : List SpecPat) + (specPat : Option SpecPat) (introPat : Syntax × IntroPat) (closePat : Option <| Syntax × IntroPat) : ProofModeM Q($e ⊢ $goal) := do @@ -74,7 +74,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false - let ⟨e'', _, _, _, pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) specPats + let ⟨e'', hyps'', _, _, pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) specPat.toList -- Create the wand proposition and apply the introduction pattern to destruct the premise let hAcc : Q(∀ x : $X, $e'' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← @@ -83,13 +83,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let qX : Q($prop) := Expr.headBeta q($Q' $x) let body ← match mPclose with | ~q(none) => - iIntroCore hyps' q(iprop($poutX -∗ $qX)) [introPat] + iIntroCore hyps'' q(iprop($poutX -∗ $qX)) [introPat] | ~q(some $f) => let f : Q($X → $prop) := f match closePat with | some closePat => let closeX : Q($prop) := Expr.headBeta q($f $x) - iIntroCore hyps' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] + iIntroCore hyps'' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: error" mkLambdaFVars #[x] body @@ -97,13 +97,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) /-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt ident (" with " (colGt ppSpace specPat)+)? - " as " colGt introPat (introPat)? : tactic +syntax (name := iinv) "iinv " colGt ident " as " colGt introPat (introPat)? + (" with " colGt ppSpace specPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident $[with $spats:specPat*]? as $ipat:introPat $[$cpat:introPat]?) => do + | `(tactic| iinv $h:ident as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do -- Parse the introduction and selection patterns - let specPats ← spats.mapM (liftMacroM <| ·.mapM SpecPat.parse) <&> (·.getD #[] |>.toList) + let specPat ← liftMacroM <| spat.mapM SpecPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat let closePat ← liftMacroM <| cpat.mapM IntroPat.parse @@ -111,5 +111,5 @@ elab_rules : tactic -- Find the hypothesis in which the invariant is opened let ivar ← hyps.findWithInfo h - let pf ← iInvCore hyps goal ivar specPats introPat closePat + let pf ← iInvCore hyps goal ivar specPat introPat closePat mvar.assign pf From 93d6547bf6cf637c130bca59ba4d7b279000eff2 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 11:59:10 +0200 Subject: [PATCH 043/108] More tests --- Iris/Iris/Tests/Tactics.lean | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 3b4b3be4d..a1ecbdbec 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2801,6 +2801,7 @@ example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P imodintro inext iexact H + /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_inv` where the side condition is trivial. @@ -2861,6 +2862,7 @@ example [CInvG GF] {γ : GName} {p : Qp} : imodintro inext iexact HP + -- To be eliminated with a default specialisation pattern · exact p /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_cinv`. -/ @@ -2877,13 +2879,26 @@ example [CInvG GF] {γ : GName} {p : Qp} : iframe inext iexact HP + -- To be eliminated with a default specialisation pattern · exact p +/-- + Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd`, + `intoAcc_cinv` and a specialisation pattern. -/ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {N : Namespace} {P : IProp GF} : cinv N γ iprop( P) ∗ own γ p1 ∗ own γ p2 ⊢@{IProp GF} |={⊤}=> own γ p1 ∗ own γ p2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - sorry + iinv Hinv as ⟨#HP, Hown2⟩ with [Hown2 //] + -- Side condition + · simp + -- Main proof goal + · imodintro + simp + iframe HP Hown1 Hown2 + imodintro + inext + iexact HP /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_na`. -/ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} @@ -2892,8 +2907,10 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv as ⟨#HP, Hown2⟩ + -- Side condition · simp exact h + -- Main proof goal · imodintro isplitl [Hown2] · iframe HP Hown2 @@ -2911,8 +2928,16 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv as ⟨#HP, Hown2⟩ Hclose - sorry - sorry - sorry + -- Side condition + · simp + exact h + -- Main proof goal + · imod Hclose $$ [HP Hown2] + · iframe + iexact HP + · iframe Hown1 Hown2 + imodintro + inext + iexact HP end iinv From 02222dde159667877abae5b5c1a9e11bbfa3fbee Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 13:33:24 +0200 Subject: [PATCH 044/108] Another test with NaInvariants --- Iris/Iris/Tests/Tactics.lean | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index a1ecbdbec..bb2606a3e 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2940,4 +2940,22 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} inext iexact HP +/-- Tests the robustness of `iinv` in presence of other invariants -/ +example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} + {P : IProp GF} (h : ↑N3 ⊆ E1) : + inv N1 P ∗ NonAtomicInvariant.inv t N3 iprop( P) ∗ inv N2 P ∗ own t E1 ∗ own t E2 + ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#_, #Hinv, #_, Hown1, Hown2⟩ + iinv Hinv as ⟨#HP, Hown1⟩ with Hown1 + · simp_all + · imodintro + isplitl [Hown1] + · iframe Hown1 HP + · simp + iintro Hown1 + iframe + imodintro + inext + iexact HP + end iinv From 6330fc49077ac8a74cc2e8f68ca00b87421d331a Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 14:53:23 +0200 Subject: [PATCH 045/108] Prove `tac_inv_elim` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 36 +++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 2bd20290e..65897a2ae 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -29,28 +29,30 @@ theorem tac_inv_elim [BI PROP] (hϕ : ϕ) (hAcc : ∀ x, e'' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) (pf : e ⊣⊢ e' ∗ □?p Pinv) - (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ out'') : + (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ Pin) : e ⊢ goal := by - have h := inst.elim_inv hϕ - cases mPclose with simp_all - | none => calc + have h0 := inst.elim_inv hϕ + have h1 : e ⊢ Pinv ∗ Pin ∗ e'' := calc e ⊢ e' ∗ □?p Pinv := pf.mp _ ⊢ □?p Pinv ∗ e' := sep_comm.mp _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ ∀ x, Pout x -∗ Q' x := sorry - _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right emp_sep.mpr - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| sep_mono_left sorry + _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right <| sep_emp.mpr + _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right <| wand_rfl + _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin + _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp + cases mPclose with simp_all + | none => calc + e ⊢ Pinv ∗ Pin ∗ e'' := h1 + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_intro hAcc _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_mono (fun _ => wand_mono_left sep_emp.mp) - _ ⊢ goal := h + _ ⊢ goal := h0 | some mPclose => calc - e ⊢ e' ∗ □?p Pinv := pf.mp - _ ⊢ □?p Pinv ∗ e' := sep_comm.mp - _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := sorry - _ ⊢ Pinv ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| forall_mono <| fun _ => wand_curry.mp - _ ⊢ Pinv ∗ emp ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right emp_sep.mpr - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := sep_mono_right <| sep_mono_left sorry - _ ⊢ goal := h + e ⊢ Pinv ∗ Pin ∗ e'' := h1 + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := + sep_mono_right <| sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := + sep_mono_right <| sep_mono_right <| forall_intro <| fun x => forall_elim x |>.trans wand_curry.mp + _ ⊢ goal := h0 private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) @@ -94,7 +96,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | none => throwError "iinv: error" mkLambdaFVars #[x] body - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq sorry) /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt ident " as " colGt introPat (introPat)? From 1b6e2377e94c4c7105ca046181fec0cb80af807b Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 15:44:43 +0200 Subject: [PATCH 046/108] Towards implementing default handling of specialisation pattern --- Iris/Iris/ProofMode/Tactics/Inv.lean | 11 ++++++++--- Iris/Iris/Tests/Tactics.lean | 4 ---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 65897a2ae..5626b54be 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -64,7 +64,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let ⟨_, hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) - let Pin ← mkFreshExprMVarQ q($prop) + let Pin : Q($prop) ← mkFreshExprMVarQ q($prop) let X ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) let close := if closePat.isSome then q(true) else q(false) @@ -76,7 +76,12 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false - let ⟨e'', hyps'', _, _, pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) specPat.toList + let defaultSpat : SpecPat := + .goal { kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] } .anonymous + let ⟨e'', hyps'', p'', out'', pfPin⟩ ← + iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD defaultSpat] + have : $out'' =Q $Pin := ⟨⟩ + have : $p'' =Q false := ⟨⟩ -- Create the wand proposition and apply the introduction pattern to destruct the premise let hAcc : Q(∀ x : $X, $e'' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← @@ -96,7 +101,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | none => throwError "iinv: error" mkLambdaFVars #[x] body - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq sorry) + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt ident " as " colGt introPat (introPat)? diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index bb2606a3e..14ef929ca 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2862,8 +2862,6 @@ example [CInvG GF] {γ : GName} {p : Qp} : imodintro inext iexact HP - -- To be eliminated with a default specialisation pattern - · exact p /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_cinv`. -/ example [CInvG GF] {γ : GName} {p : Qp} : @@ -2879,8 +2877,6 @@ example [CInvG GF] {γ : GName} {p : Qp} : iframe inext iexact HP - -- To be eliminated with a default specialisation pattern - · exact p /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd`, From f3a62642adaf9b2951d57b5a520b14fcf987c274 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 15:54:55 +0200 Subject: [PATCH 047/108] Fix for consistency --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index df65b571d..157a56f0c 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -32,7 +32,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM let gs ← (observing? <| evalTacticAt (← `(tactic | trivial)) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then - throwError "iSolveSideCondition: failed to solve side condition {target}" + throwError "iSolveSidecondition: failed to solve side condition {target}" else for g in gs do addMVarGoal g return mvar From 16a07706e4d298c4267cd86b694bdca006fba412 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 17:46:23 +0200 Subject: [PATCH 048/108] Add tests, including those showing an issue of wrong hypothesis choice being chosen --- Iris/Iris/ProofMode/Tactics/Inv.lean | 15 ++++---- Iris/Iris/Tests/Tactics.lean | 52 ++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 5626b54be..ec476c0d8 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -67,17 +67,19 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let Pin : Q($prop) ← mkFreshExprMVarQ q($prop) let X ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) + -- Decide whether to use `elimInv_acc_with_close` or `elimInv_acc_without_close` let close := if closePat.isSome then q(true) else q(false) let mPclose ← mkFreshExprMVarQ q(Option ($X → $prop)) let Q' ← mkFreshExprMVarQ q($X → $prop) let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') - | throwError "iinv: invalid invariant {Pinv}" + | throwError "iinv: invalid invariant {Pinv} (ElimInv type class synthesis failed)" -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + -- Using this instead of `.autoframe .spatial` in order to handle `Pin = True` let defaultSpat : SpecPat := - .goal { kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] } .anonymous + .goal { kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] } .anonymous let ⟨e'', hyps'', p'', out'', pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD defaultSpat] have : $out'' =Q $Pin := ⟨⟩ @@ -86,17 +88,14 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Create the wand proposition and apply the introduction pattern to destruct the premise let hAcc : Q(∀ x : $X, $e'' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do - let poutX : Q($prop) := Expr.headBeta q($Pout $x) - let qX : Q($prop) := Expr.headBeta q($Q' $x) let body ← match mPclose with | ~q(none) => - iIntroCore hyps'' q(iprop($poutX -∗ $qX)) [introPat] + iIntroCore hyps'' q(iprop($Pout $x -∗ $Q' $x)) [introPat] | ~q(some $f) => - let f : Q($X → $prop) := f match closePat with | some closePat => - let closeX : Q($prop) := Expr.headBeta q($f $x) - iIntroCore hyps'' q(iprop($poutX -∗ $closeX -∗ $qX)) [introPat, closePat] + let f : Q($X → $prop) := f + iIntroCore hyps'' q(iprop($Pout $x -∗ $f $x -∗ $Q' $x)) [introPat, closePat] -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: error" mkLambdaFVars #[x] body diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 14ef929ca..dbfa47899 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2839,7 +2839,7 @@ example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : iexact H /- Tests `iinv` with an invalid invariant. -/ -/-- error: iinv: invalid invariant P -/ +/-- error: iinv: invalid invariant P (ElimInv type class synthesis failed) -/ #guard_msgs in example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : □ P ={E}=∗ ▷ P := by @@ -2891,47 +2891,71 @@ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {N : Namespace} {P : IProp GF} : -- Main proof goal · imodintro simp - iframe HP Hown1 Hown2 + iframe HP ∗ imodintro inext iexact HP -/-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_na`. -/ +/-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_na`. -/ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} - {P : IProp GF} (h : ↑N ⊆ E2) : + {P : IProp GF} (h : ↑N ⊆ E1) : NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ + iinv Hinv as ⟨#HP, Hown2⟩ Hclose with Hown1 -- Side condition · simp exact h -- Main proof goal - · imodintro - isplitl [Hown2] - · iframe HP Hown2 + · imod Hclose $$ [HP Hown2] + · iframe + iexact HP · simp - iframe Hown1 Hown2 - iintro - + iframe imodintro inext iexact HP -/-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_na`. -/ +/-- TODO: wrong hypothesis being chosen. -/ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} - {P : IProp GF} (h : ↑N ⊆ E2) : + {P : IProp GF} (h : ↑N ⊆ E1) : NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv as ⟨#HP, Hown2⟩ Hclose -- Side condition + · simp + sorry + -- Main proof goal + · imod Hclose $$ [HP Hown2] + · iframe + iexact HP + · simp + iframe + imodintro + sorry + +/-- + Same test as above, with `[Hown2]` in the specialisation pattern. + Unlike using `[Hown2]`, +-/ +example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} + {P : IProp GF} (h : ↑N ⊆ E2) : + NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 + ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#Hinv, Hown1, Hown2⟩ + iinv Hinv as ⟨#HP, Hown2⟩ Hclose with [Hown2] + -- Side condition · simp exact h + -- Additional goal as framing is not automatically applied + · iexact Hown2 -- Main proof goal · imod Hclose $$ [HP Hown2] · iframe iexact HP - · iframe Hown1 Hown2 + · simp + iframe imodintro inext iexact HP @@ -2946,7 +2970,7 @@ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} · simp_all · imodintro isplitl [Hown1] - · iframe Hown1 HP + · iframe HP ∗ · simp iintro Hown1 iframe From 8b2c39479d8b639488ed3a11bd508c2c8929510f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 18:07:01 +0200 Subject: [PATCH 049/108] Special case to handle `Pin` being `True` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index ec476c0d8..03415e541 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -78,10 +78,23 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let hϕ ← iSolveSidecondition q($ϕ) false -- Using this instead of `.autoframe .spatial` in order to handle `Pin = True` - let defaultSpat : SpecPat := - .goal { kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] } .anonymous let ⟨e'', hyps'', p'', out'', pfPin⟩ ← - iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD defaultSpat] + match specPat with + | some pat => iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [pat] + | none => + -- Special case: `Pin = True`, not solved by `.autoframe .spatial` + -- Ideally `.autoframe .spatial` applies `iItrivial` + /- + Alternatively use: + `{ kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] }`, + but it does not always choose the correct hypotheses and thus produce unprovable goals + -/ + match Pin with + | ~q(iprop(True)) => + pure ⟨_, hyps', q(false), Pin, q(sep_mono_right true_intro)⟩ + | _=> + iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [.autoframe .spatial] + have : $out'' =Q $Pin := ⟨⟩ have : $p'' =Q false := ⟨⟩ From f2a6e3b03694425cee2ca0152b3ea220c91e8bfb Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 21:26:42 +0200 Subject: [PATCH 050/108] Reorganise `iinv` tests and minor fixes --- Iris/Iris/Tests/Tactics.lean | 72 ++++++------------------------------ 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index dbfa47899..177a09a35 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2782,13 +2782,13 @@ end iloeb section iinv -variable {hlc : HasLC} {GF : BundledGFunctors} [InvGS_gen hlc GF] +variable {hlc : HasLC} {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace} /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by +example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H -- Side condition @@ -2806,7 +2806,7 @@ example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_inv` where the side condition is trivial. -/ -example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by +example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H Hclose -- Side condition @@ -2822,14 +2822,9 @@ example {N : Namespace} {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_inv`, relying on the side condition `↑N ⊆ E`. -/ -example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : - inv N iprop( P) ={E}=∗ ▷ P := by +example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H - -- Side condition - · simp - sorry - -- Main proof goal · imodintro isplit · iexact H @@ -2841,8 +2836,7 @@ example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : /- Tests `iinv` with an invalid invariant. -/ /-- error: iinv: invalid invariant P (ElimInv type class synthesis failed) -/ #guard_msgs in -example {N E : Namespace} {P : IProp GF} {h : ↑N ⊆ E} : - □ P ={E}=∗ ▷ P := by +example {E : CoPset} {P : IProp GF} : □ P ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv as #H @@ -2881,7 +2875,7 @@ example [CInvG GF] {γ : GName} {p : Qp} : /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd`, `intoAcc_cinv` and a specialisation pattern. -/ -example [CInvG GF] {γ : GName} {p1 p2 : Qp} {N : Namespace} {P : IProp GF} : +example [CInvG GF] {γ : GName} {p1 p2 : Qp} {P : IProp GF} : cinv N γ iprop( P) ∗ own γ p1 ∗ own γ p2 ⊢@{IProp GF} |={⊤}=> own γ p1 ∗ own γ p2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ @@ -2897,59 +2891,13 @@ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {N : Namespace} {P : IProp GF} : iexact HP /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_na`. -/ -example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} - {P : IProp GF} (h : ↑N ⊆ E1) : +example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑N ⊆ E1) : NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ Hclose with Hown1 + iinv Hinv as ⟨#HP, Hown2⟩ Hclose with [Hown1 //] -- Side condition - · simp - exact h - -- Main proof goal - · imod Hclose $$ [HP Hown2] - · iframe - iexact HP - · simp - iframe - imodintro - inext - iexact HP - -/-- TODO: wrong hypothesis being chosen. -/ -example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} - {P : IProp GF} (h : ↑N ⊆ E1) : - NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 - ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by - iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ Hclose - -- Side condition - · simp - sorry - -- Main proof goal - · imod Hclose $$ [HP Hown2] - · iframe - iexact HP - · simp - iframe - imodintro - sorry - -/-- - Same test as above, with `[Hown2]` in the specialisation pattern. - Unlike using `[Hown2]`, --/ -example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} - {P : IProp GF} (h : ↑N ⊆ E2) : - NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 - ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by - iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ Hclose with [Hown2] - -- Side condition - · simp - exact h - -- Additional goal as framing is not automatically applied - · iexact Hown2 + · simp_all -- Main proof goal · imod Hclose $$ [HP Hown2] · iframe @@ -2967,7 +2915,9 @@ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#_, #Hinv, #_, Hown1, Hown2⟩ iinv Hinv as ⟨#HP, Hown1⟩ with Hown1 + -- Side condition · simp_all + -- Main proof goal · imodintro isplitl [Hown1] · iframe HP ∗ From f242b4957009ced0f7eba409c54ae46ea7943a3b Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 24 Jun 2026 21:39:50 +0200 Subject: [PATCH 051/108] Minor fix: `E` in `elimAcc_fupd` --- Iris/Iris/ProofMode/InstancesUpdates.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index d43506b11..a602d1728 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -208,7 +208,7 @@ instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PR -- elim_acc := sorry @[rocq_alias elim_acc_fupd] -instance elimAcc_fupd [FUpd PROP] {X} E1 E2 (α β : X → PROP) mγ (Q : PROP) : +instance elimAcc_fupd [FUpd PROP] {X} E1 E2 E (α β : X → PROP) mγ (Q : PROP) : ElimAcc True (fupd E1 E2) (fupd E2 E1) α β mγ iprop(|={E1,E}=> Q) (fun x => iprop(|={E2}=> β x ∗ (mγ x -∗? |={E1,E}=> Q))) where From 692920b3eabf10d1482453af8beb94bd3b0dbbb7 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 10:04:44 +0200 Subject: [PATCH 052/108] `tac_inv_elim`: formatting --- Iris/Iris/ProofMode/Tactics/Inv.lean | 32 +++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 03415e541..5a03003ed 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -23,7 +23,7 @@ def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Op @[rocq_alias tac_inv_elim] theorem tac_inv_elim [BI PROP] - {e e' goal : PROP} {ϕ : Prop} {X : Type} {p close : Bool} + {e e' e'' goal : PROP} {ϕ : Prop} {X : Type} {p close : Bool} {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) @@ -33,26 +33,28 @@ theorem tac_inv_elim [BI PROP] e ⊢ goal := by have h0 := inst.elim_inv hϕ have h1 : e ⊢ Pinv ∗ Pin ∗ e'' := calc - e ⊢ e' ∗ □?p Pinv := pf.mp - _ ⊢ □?p Pinv ∗ e' := sep_comm.mp - _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right <| sep_emp.mpr - _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right <| wand_rfl - _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin - _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp + e ⊢ e' ∗ □?p Pinv := pf.mp + _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr + _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl + _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin + _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp cases mPclose with simp_all | none => calc e ⊢ Pinv ∗ Pin ∗ e'' := h1 - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_intro hAcc - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := sep_mono_right <| sep_mono_right <| forall_mono (fun _ => wand_mono_left sep_emp.mp) + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := + sep_mono_right <| sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := + sep_mono_right <| sep_mono_right <| forall_mono <| fun _ => wand_mono_left sep_emp.mp _ ⊢ goal := h0 | some mPclose => calc - e ⊢ Pinv ∗ Pin ∗ e'' := h1 + e ⊢ Pinv ∗ Pin ∗ e'' := h1 _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_intro hAcc - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_intro <| fun x => forall_elim x |>.trans wand_curry.mp - _ ⊢ goal := h0 + sep_mono_right <| sep_mono_right <| forall_intro hAcc + _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := + sep_mono_right <| sep_mono_right <| forall_intro (forall_elim · |>.trans wand_curry.mp) + _ ⊢ goal := h0 private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) From 0ac6f0dc4224561b995f433d20cec9d6624cf055 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 10:11:17 +0200 Subject: [PATCH 053/108] Update comment --- Iris/Iris/ProofMode/Tactics/Inv.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 5a03003ed..3cbd80f2d 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -79,7 +79,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false - -- Using this instead of `.autoframe .spatial` in order to handle `Pin = True` + -- Obtain `e' ⊢ e'' ∗ Pin` let ⟨e'', hyps'', p'', out'', pfPin⟩ ← match specPat with | some pat => iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [pat] From c23adeb1725c864a256947d8101d0b62addc8930 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 10:36:00 +0200 Subject: [PATCH 054/108] New function `findInvariantWithNamespace` to find invariant using namespace --- Iris/Iris/ProofMode/Tactics/Inv.lean | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 3cbd80f2d..b261be0a4 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -117,6 +117,20 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) +/-- Find an invariant hypothesis with a given `Namespace` value. -/ +private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} {e} + (N : Q(Namespace)) (hyps : Hyps bi e) : ProofModeM <| Option IVarId := do + match hyps with + | .emp _ => return none + | .hyp _ _ ivar _ ty _ => + let inst ← ProofModeM.trySynthInstanceQ q(IntoInv $ty $N) + return if inst.isSome then some ivar else none + | .sep _ _ _ _ lhs rhs => + let rhsInvariant ← findInvariantWithNamespace N rhs + match rhsInvariant with + | some ivar => return some ivar + | none => return ← findInvariantWithNamespace N lhs + /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt ident " as " colGt introPat (introPat)? (" with " colGt ppSpace specPat)? : tactic From 4ad4d8a2b1c01e01a5a52b34308a10a7d3932b6d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 10:52:46 +0200 Subject: [PATCH 055/108] Implement `iinv N ...` where `N` is a `Namespace` value --- Iris/Iris/ProofMode/Tactics/Inv.lean | 16 ++++++++++++---- Iris/Iris/Tests/Tactics.lean | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index b261be0a4..5953d810e 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -132,19 +132,27 @@ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} | none => return ← findInvariantWithNamespace N lhs /-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt ident " as " colGt introPat (introPat)? +syntax (name := iinv) "iinv " colGt term " as " colGt introPat (introPat)? (" with " colGt ppSpace specPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:ident as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do + | `(tactic| iinv $h:term as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do -- Parse the introduction and selection patterns let specPat ← liftMacroM <| spat.mapM SpecPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat let closePat ← liftMacroM <| cpat.mapM IntroPat.parse ProofModeM.runTactic λ mvar { hyps, goal, .. } => do - -- Find the hypothesis in which the invariant is opened - let ivar ← hyps.findWithInfo h + -- Find the invariant hypothesis + let ivar ← do match ← try? <| hyps.findWithInfo ⟨h⟩ with + -- Hypothesis supplied by the user: return the `IVarId` value of the invariant directly + | some ivar => pure ivar + -- Namespace supplied by the user: use `IntoInv` to find the corresponding hypothesis + | none => + let N ← elabTermEnsuringTypeQ h q(Namespace) + match ← findInvariantWithNamespace N hyps with + | some ivar => pure ivar + | none => throwError m!"iinv: invariant {N} not found" let pf ← iInvCore hyps goal ivar specPat introPat closePat mvar.assign pf diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 177a09a35..85444b3ad 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2928,4 +2928,28 @@ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} inext iexact HP +/-- + Tests `iinv` with two invariant hypotheses using the same `Namespace` value. + The last hypothesis in the context with this `Namespace` value gets chosen. +-/ +example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} + {P Q : IProp GF} (h : ↑N ⊆ E1) : + NonAtomicInvariant.inv t N iprop( Q) ∗ + NonAtomicInvariant.inv t N iprop( P) ∗ + own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#_, #_, Hown1, Hown2⟩ + iinv N as ⟨#HP, Hown1⟩ with Hown1 + -- Side condition + · simp_all + -- Main proof goal + · imodintro + isplitl [Hown1] + · iframe HP ∗ + · simp + iintro Hown1 + iframe + imodintro + inext + iexact HP + end iinv From d4c76a1fd5c2818a816551b048802380139bb2aa Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 11:21:40 +0200 Subject: [PATCH 056/108] Minor formatting --- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 +++--- Iris/Iris/Tests/Tactics.lean | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 5953d810e..71cf85a2e 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -136,7 +136,7 @@ syntax (name := iinv) "iinv " colGt term " as " colGt introPat (introPat)? (" with " colGt ppSpace specPat)? : tactic elab_rules : tactic - | `(tactic| iinv $h:term as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do + | `(tactic| iinv $t:term as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do -- Parse the introduction and selection patterns let specPat ← liftMacroM <| spat.mapM SpecPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat @@ -144,12 +144,12 @@ elab_rules : tactic ProofModeM.runTactic λ mvar { hyps, goal, .. } => do -- Find the invariant hypothesis - let ivar ← do match ← try? <| hyps.findWithInfo ⟨h⟩ with + let ivar ← do match ← try? <| hyps.findWithInfo ⟨t⟩ with -- Hypothesis supplied by the user: return the `IVarId` value of the invariant directly | some ivar => pure ivar -- Namespace supplied by the user: use `IntoInv` to find the corresponding hypothesis | none => - let N ← elabTermEnsuringTypeQ h q(Namespace) + let N ← elabTermEnsuringTypeQ t q(Namespace) match ← findInvariantWithNamespace N hyps with | some ivar => pure ivar | none => throwError m!"iinv: invariant {N} not found" diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 85444b3ad..bbd85e85c 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2837,8 +2837,8 @@ example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ /-- error: iinv: invalid invariant P (ElimInv type class synthesis failed) -/ #guard_msgs in example {E : CoPset} {P : IProp GF} : □ P ={E}=∗ ▷ P := by - iintro #Hinv - iinv Hinv as #H + iintro #HP + iinv HP as #H /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_cinv`. -/ example [CInvG GF] {γ : GName} {p : Qp} : From 3bc25475c814657e36365df2e9c1719ebdc637e8 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 11:59:26 +0200 Subject: [PATCH 057/108] Add `elimAcc_wp_atomic` and `elimAcc_wp_nonatomic` --- Iris/Iris/ProgramLogic/WeakestPre.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 146984b1b..4cde28962 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -690,4 +690,17 @@ instance elimModalFupdWpAtomic_wrongMask : p false iprop(|={E₁,E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where elim_modal := nofun +@[rocq_alias elim_acc_wp_atomic] +instance (priority := low) elimAcc_wp_atomic {X} (E₁ E₂ : CoPset) α β (γ : X → Option (IProp GF)) : + ElimAcc (Language.Atomic ↑s e) (fupd E₁ E₂) (fupd E₂ E₁) α β γ + (WP e @ s ; E₁ {{ Φ }}) + (fun x => WP e @ s ; E₂ {{ v, |={E₂}=> β x ∗ (γ x -∗? Φ v) }}) where + elim_acc := sorry + +@[rocq_alias elim_acc_wp_nonatomic] +instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option (IProp GF)) : + ElimAcc True (fupd E E) (fupd E E) α β γ (WP e @ s ; E {{ Φ }}) + (fun x => WP e @ s ; E {{ v, |={E}=> β x ∗ (γ x -∗? Φ v) }}) where + elim_acc := sorry + end ProofModeClasses From 5840ecad49143d57c5da3bb090519edffb4d1be7 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 14:28:03 +0200 Subject: [PATCH 058/108] Adjust `iinv` syntax for consistency with `imod` and `ihave` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 +++--- Iris/Iris/Tests/Tactics.lean | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 71cf85a2e..da1117b52 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -132,11 +132,11 @@ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} | none => return ← findInvariantWithNamespace N lhs /-- `iinv` opens an invariant in the proof state. -/ -syntax (name := iinv) "iinv " colGt term " as " colGt introPat (introPat)? - (" with " colGt ppSpace specPat)? : tactic +syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? + " with " colGt introPat (introPat)? : tactic elab_rules : tactic - | `(tactic| iinv $t:term as $ipat:introPat $[$cpat:introPat]? $[with $spat:specPat]?) => do + | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $ipat:introPat $[$cpat:introPat]?) => do -- Parse the introduction and selection patterns let specPat ← liftMacroM <| spat.mapM SpecPat.parse let introPat ← liftMacroM <| IntroPat.parse ipat diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index bbd85e85c..ed6f2d73b 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2790,7 +2790,7 @@ variable {hlc : HasLC} {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace -/ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv - iinv Hinv as #H + iinv Hinv with #H -- Side condition · simp -- Main proof goal @@ -2808,7 +2808,7 @@ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by -/ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv - iinv Hinv as #H Hclose + iinv Hinv with #H Hclose -- Side condition · simp -- Main proof goal @@ -2824,7 +2824,7 @@ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by -/ example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv - iinv Hinv as #H + iinv Hinv with #H · imodintro isplit · iexact H @@ -2838,13 +2838,13 @@ example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ #guard_msgs in example {E : CoPset} {P : IProp GF} : □ P ={E}=∗ ▷ P := by iintro #HP - iinv HP as #H + iinv HP with #H /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and `intoAcc_cinv`. -/ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ - iinv Hinv as ⟨#HP, Hown⟩ + iinv Hinv with ⟨#HP, Hown⟩ -- Side condition · simp -- Main proof goal @@ -2861,7 +2861,7 @@ example [CInvG GF] {γ : GName} {p : Qp} : example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ - iinv Hinv as ⟨#HP, Hown⟩ Hclose + iinv Hinv with ⟨#HP, Hown⟩ Hclose -- Side condition · simp -- Main proof goal @@ -2879,7 +2879,7 @@ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {P : IProp GF} : cinv N γ iprop( P) ∗ own γ p1 ∗ own γ p2 ⊢@{IProp GF} |={⊤}=> own γ p1 ∗ own γ p2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ with [Hown2 //] + iinv Hinv $$ [Hown2 //] with ⟨#HP, Hown2⟩ -- Side condition · simp -- Main proof goal @@ -2895,7 +2895,7 @@ example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑ NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown2⟩ Hclose with [Hown1 //] + iinv Hinv $$ [Hown1 //] with ⟨#HP, Hown2⟩ Hclose -- Side condition · simp_all -- Main proof goal @@ -2914,7 +2914,7 @@ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} inv N1 P ∗ NonAtomicInvariant.inv t N3 iprop( P) ∗ inv N2 P ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#_, #Hinv, #_, Hown1, Hown2⟩ - iinv Hinv as ⟨#HP, Hown1⟩ with Hown1 + iinv Hinv $$ Hown1 with ⟨#HP, Hown1⟩ -- Side condition · simp_all -- Main proof goal @@ -2938,7 +2938,7 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#_, #_, Hown1, Hown2⟩ - iinv N as ⟨#HP, Hown1⟩ with Hown1 + iinv N $$ Hown1 with ⟨#HP, Hown1⟩ -- Side condition · simp_all -- Main proof goal From 5f3f0c490dee09943c4b08fe49e8299f25e1198c Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 14:53:12 +0200 Subject: [PATCH 059/108] Add missing `cotGt` in `iinv` syntax --- Iris/Iris/ProofMode/Tactics/Inv.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index da1117b52..0c58d459d 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -133,7 +133,7 @@ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? - " with " colGt introPat (introPat)? : tactic + " with " colGt introPat (colGt introPat)? : tactic elab_rules : tactic | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $ipat:introPat $[$cpat:introPat]?) => do From 499886a612be473c4311b75217b541231b2068ca Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 15:27:30 +0200 Subject: [PATCH 060/108] Modify `iSolveSidecondition` to apply `and_intros` and then to try `trivial` and `simp_all` one after another --- Iris/Iris/ProofMode/Tactics/Basic.lean | 4 +- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 +- Iris/Iris/Tests/Tactics.lean | 157 +++++++++++-------------- 3 files changed, 72 insertions(+), 95 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 157a56f0c..3e0403459 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -29,7 +29,9 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM | .app (.const ``PMError _) (.lit (.strVal msg)) => throwError "{msg}" | _ => - let gs ← (observing? <| evalTacticAt (← `(tactic | trivial)) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) + let gs ← (observing? <| + evalTacticAt (← `(tactic | and_intros <;> first | trivial | simp_all)) mvar.mvarId!) <&> + (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then throwError "iSolveSidecondition: failed to solve side condition {target}" diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 0c58d459d..702841aab 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -76,9 +76,6 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') | throwError "iinv: invalid invariant {Pinv} (ElimInv type class synthesis failed)" - -- Solve side conditions automatically if possible, otherwise add them into the proof state - let hϕ ← iSolveSidecondition q($ϕ) false - -- Obtain `e' ⊢ e'' ∗ Pin` let ⟨e'', hyps'', p'', out'', pfPin⟩ ← match specPat with @@ -115,6 +112,9 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( | none => throwError "iinv: error" mkLambdaFVars #[x] body + -- Solve side conditions automatically if possible, otherwise add them into the proof state + let hϕ ← iSolveSidecondition q($ϕ) false + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) /-- Find an invariant hypothesis with a given `Namespace` value. -/ diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index ed6f2d73b..fcc2a01a7 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2791,16 +2791,13 @@ variable {hlc : HasLC} {GF : BundledGFunctors} [InvGS_gen hlc GF] {N : Namespace example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv with #H - -- Side condition - · simp - -- Main proof goal - · imodintro - isplit - · iexact H - · simp [BIBase.wandM] - imodintro - inext - iexact H + imodintro + isplit + · iexact H + · simp [BIBase.wandM] + imodintro + inext + iexact H /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and @@ -2809,14 +2806,10 @@ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by iintro #Hinv iinv Hinv with #H Hclose - -- Side condition - · simp - -- Main proof goal - · simp - imod Hclose $$ H - imodintro - inext - iexact H + imod Hclose $$ H + imodintro + inext + iexact H /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd` and @@ -2825,13 +2818,13 @@ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ P := by iintro #Hinv iinv Hinv with #H - · imodintro - isplit - · iexact H - · simp - imodintro - inext - iexact H + imodintro + isplit + · iexact H + · simp + imodintro + inext + iexact H /- Tests `iinv` with an invalid invariant. -/ /-- error: iinv: invalid invariant P (ElimInv type class synthesis failed) -/ @@ -2845,32 +2838,26 @@ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ iinv Hinv with ⟨#HP, Hown⟩ - -- Side condition - · simp - -- Main proof goal - · simp - imodintro - isplit - iexact HP - iframe - imodintro - inext - iexact HP + simp + imodintro + isplit + iexact HP + iframe + imodintro + inext + iexact HP /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_cinv`. -/ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ iinv Hinv with ⟨#HP, Hown⟩ Hclose - -- Side condition - · simp - -- Main proof goal - · simp - imod Hclose $$ HP - imodintro - iframe - inext - iexact HP + simp + imod Hclose $$ HP + imodintro + iframe + inext + iexact HP /-- Tests `iinv` with `elimInv_acc_without_close`, `elimAcc_fupd`, @@ -2880,15 +2867,12 @@ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {P : IProp GF} : ⊢@{IProp GF} |={⊤}=> own γ p1 ∗ own γ p2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv $$ [Hown2 //] with ⟨#HP, Hown2⟩ - -- Side condition - · simp - -- Main proof goal - · imodintro - simp - iframe HP ∗ - imodintro - inext - iexact HP + imodintro + simp + iframe HP ∗ + imodintro + inext + iexact HP /-- Tests `iinv` with `elimInv_acc_with_close`, `elimModal_fupd_fupd` and `intoAcc_na`. -/ example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑N ⊆ E1) : @@ -2896,37 +2880,31 @@ example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑ ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv $$ [Hown1 //] with ⟨#HP, Hown2⟩ Hclose - -- Side condition - · simp_all - -- Main proof goal - · imod Hclose $$ [HP Hown2] - · iframe - iexact HP - · simp - iframe - imodintro - inext - iexact HP - -/-- Tests the robustness of `iinv` in presence of other invariants -/ + imod Hclose $$ [HP Hown2] + · iframe + iexact HP + · simp + iframe + imodintro + inext + iexact HP + +/-- Tests the robustness of `iinv` in presence of other invariants. -/ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} {P : IProp GF} (h : ↑N3 ⊆ E1) : inv N1 P ∗ NonAtomicInvariant.inv t N3 iprop( P) ∗ inv N2 P ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#_, #Hinv, #_, Hown1, Hown2⟩ iinv Hinv $$ Hown1 with ⟨#HP, Hown1⟩ - -- Side condition - · simp_all - -- Main proof goal - · imodintro - isplitl [Hown1] - · iframe HP ∗ - · simp - iintro Hown1 - iframe - imodintro - inext - iexact HP + imodintro + isplitl [Hown1] + · iframe HP ∗ + · simp + iintro Hown1 + iframe + imodintro + inext + iexact HP /-- Tests `iinv` with two invariant hypotheses using the same `Namespace` value. @@ -2939,17 +2917,14 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#_, #_, Hown1, Hown2⟩ iinv N $$ Hown1 with ⟨#HP, Hown1⟩ - -- Side condition - · simp_all - -- Main proof goal - · imodintro - isplitl [Hown1] - · iframe HP ∗ - · simp - iintro Hown1 - iframe - imodintro - inext - iexact HP + imodintro + isplitl [Hown1] + · iframe HP ∗ + · simp + iintro Hown1 + iframe + imodintro + inext + iexact HP end iinv From a25c57fc80fdf6d429fc754c45d50c4b8c709324 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 16:04:58 +0200 Subject: [PATCH 061/108] Uncomment `elim_acc_bupd`, test `iinv` with `elimAcc_wp_atomic` --- Iris/Iris/ProofMode/InstancesUpdates.lean | 12 ++++++------ Iris/Iris/Tests/Tactics.lean | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index a602d1728..c739b57f2 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -200,12 +200,12 @@ instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PR p false iprop(|={E1,E2}=> P) iprop(False) iprop(|={E0,E3}=> Q) iprop(False) where elim_modal h := by cases h --- @[rocq_alias elim_acc_bupd] --- instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : --- ElimAcc True bupd bupd α β mγ --- iprop(|==> Q) --- (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where --- elim_acc := sorry +@[rocq_alias elim_acc_bupd] +instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : + ElimAcc True bupd bupd α β mγ + iprop(|==> Q) + (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where + elim_acc := sorry @[rocq_alias elim_acc_fupd] instance elimAcc_fupd [FUpd PROP] {X} E1 E2 E (α β : X → PROP) mγ (Q : PROP) : diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index fcc2a01a7..e626f5bbb 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -14,11 +14,13 @@ public import Iris.Algebra.CMRA public import Iris.Instances.Lib.Invariants public import Iris.Instances.Lib.CInvariants public import Iris.Instances.Lib.NaInvariants +public import Iris.ProgramLogic.Language +public import Iris.ProgramLogic.WeakestPre @[expose] public section namespace Iris.Tests -open BI CMRA DFrac CancelableInvariant NonAtomicInvariant +open BI CMRA DFrac CancelableInvariant NonAtomicInvariant ProgramLogic /- This file contains tests with various scenarios for all available tactics. -/ @@ -2927,4 +2929,19 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} inext iexact HP +/- Variables to test `iinv` with `WP` -/ +variable {hlc : outParam HasLC} {Expr State Obs Val : Type _} [Λ : 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 : IProp GF} + +/-- Tests `iinv` with `elimInv_acc_without_close`, `intoAcc_inv` and `elimAcc_wp_atomic`. -/ +example [Language.Atomic ↑s e] (h : ↑N ⊆ E) : + ⊢ inv N P -∗ (▷ P -∗ WP e @ s ; (E \ ↑N) {{ v, |={E \ ↑N}=> ▷ P ∗ Φ v }}) -∗ WP e @ s ; E {{ Φ }} := by + iintro #Hinv Hwp + iinv Hinv with H + simp + iapply Hwp + iexact H + end iinv From e70d793fb142585fb11c18f0014ee844ae502ee4 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 16:16:40 +0200 Subject: [PATCH 062/108] Remove the requirement in `FrameResult.finishClose` that progress has to be made in order to handle `emp`/`True` --- Iris/Iris/ProofMode/Tactics/Frame.lean | 4 +--- Iris/Iris/ProofMode/Tactics/Inv.lean | 19 +------------------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Frame.lean b/Iris/Iris/ProofMode/Tactics/Frame.lean index af5be77c3..8204cfe0f 100644 --- a/Iris/Iris/ProofMode/Tactics/Frame.lean +++ b/Iris/Iris/ProofMode/Tactics/Frame.lean @@ -117,9 +117,7 @@ def FrameResult.finish {u prop bi origE origGoal} (res : @FrameResult u prop bi back with the remaining hypotheses. -/ def FrameResult.finishClose {u prop bi origE origGoal} (res : @FrameResult u prop bi origE origGoal) : ProofModeM ((e : Q($prop)) × (_ : Hyps bi e) × Q($origE ⊢ $e ∗ $origGoal)) := do - let {progress, e, hyps, goal, pf} := res - if !progress then - throwError "iframe: cannot solve {origGoal} by framing" + let {e, hyps, goal, pf, ..} := res -- try closing the goal for emp or True without calling k match goal with | ~q(iprop(emp)) => diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 702841aab..95ddd4317 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -76,24 +76,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') | throwError "iinv: invalid invariant {Pinv} (ElimInv type class synthesis failed)" - -- Obtain `e' ⊢ e'' ∗ Pin` - let ⟨e'', hyps'', p'', out'', pfPin⟩ ← - match specPat with - | some pat => iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [pat] - | none => - -- Special case: `Pin = True`, not solved by `.autoframe .spatial` - -- Ideally `.autoframe .spatial` applies `iItrivial` - /- - Alternatively use: - `{ kind := .spatial, negate := true, trivial := true, frame := [], hyps := [] }`, - but it does not always choose the correct hypotheses and thus produce unprovable goals - -/ - match Pin with - | ~q(iprop(True)) => - pure ⟨_, hyps', q(false), Pin, q(sep_mono_right true_intro)⟩ - | _=> - iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [.autoframe .spatial] - + let ⟨e'', hyps'', p'', out'', pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD <| .autoframe .spatial] have : $out'' =Q $Pin := ⟨⟩ have : $p'' =Q false := ⟨⟩ From 7d818c368bad901b5df7b66fec6c6f41f98e8053 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 17:29:54 +0200 Subject: [PATCH 063/108] Towards restructuring `iInvCore` and `tac_inv_elim` to use `iCasesCore` instead of `iIntroCore` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 96 +++++++++++++--------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 95ddd4317..4da0b21f1 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -21,46 +21,36 @@ open Lean Elab Tactic Meta Qq BI Std def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Option PROP := mP.map (· x) -@[rocq_alias tac_inv_elim] theorem tac_inv_elim [BI PROP] {e e' e'' goal : PROP} {ϕ : Prop} {X : Type} {p close : Bool} {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) - (hAcc : ∀ x, e'' ⊢ Pout x -∗ mPclose.map (· x) -∗? Q' x) + (hAcc : ∀ x, e'' ∗ Pout x ∗ ((mPclose.map (· x)).getD emp) ⊢ Q' x) (pf : e ⊣⊢ e' ∗ □?p Pinv) (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ Pin) : - e ⊢ goal := by - have h0 := inst.elim_inv hϕ - have h1 : e ⊢ Pinv ∗ Pin ∗ e'' := calc - e ⊢ e' ∗ □?p Pinv := pf.mp - _ ⊢ □?p Pinv ∗ e' := sep_comm.mp - _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr - _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl - _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin - _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp - cases mPclose with simp_all - | none => calc - e ⊢ Pinv ∗ Pin ∗ e'' := h1 - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_intro hAcc - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ emp -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_mono <| fun _ => wand_mono_left sep_emp.mp - _ ⊢ goal := h0 - | some mPclose => calc - e ⊢ Pinv ∗ Pin ∗ e'' := h1 - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x -∗ mPclose x -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_intro hAcc - _ ⊢ Pinv ∗ Pin ∗ ∀ x, Pout x ∗ mPclose x -∗ Q' x := - sep_mono_right <| sep_mono_right <| forall_intro (forall_elim · |>.trans wand_curry.mp) - _ ⊢ goal := h0 - -private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q($prop)) - (ivar : IVarId) - (specPat : Option SpecPat) - (introPat : Syntax × IntroPat) - (closePat : Option <| Syntax × IntroPat) : + e ⊢ goal := sorry + + -- have h0 := inst.elim_inv hϕ + -- have h1 : e ⊢ Pinv ∗ Pin ∗ ∀ a, Pout a -∗ Option.map (fun x => x a) mPclose -∗? Q' a := calc + -- e ⊢ e' ∗ □?p Pinv := pf.mp + -- _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + -- _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + -- _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr + -- _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl + -- _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin + -- _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp + -- _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| hAcc ·) + -- apply h1.trans + -- cases mPclose with + -- | none => + -- apply (sep_mono_right <| sep_mono_right <| forall_mono <| fun _ => wand_mono_left sep_emp.mp).trans h0 + -- | some _ => + -- apply (sep_mono_right <| sep_mono_right <| forall_intro (forall_elim · |>.trans wand_curry.mp)).trans h0 + +private def iInvCore {u} {prop : Q(Type u)} {bi} {e} + (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) + (introPat : iCasesPat) (closePat : Option iCasesPat) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context let ⟨_, hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar @@ -80,25 +70,31 @@ private def iInvCore {u} {prop : Q(Type u)} {bi e} (hyps : Hyps bi e) (goal : Q( have : $out'' =Q $Pin := ⟨⟩ have : $p'' =Q false := ⟨⟩ - -- Create the wand proposition and apply the introduction pattern to destruct the premise - let hAcc : Q(∀ x : $X, $e'' ⊢ $Pout x -∗ optionMap $mPclose x -∗? $Q' x) ← - withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do - let body ← match mPclose with - | ~q(none) => - iIntroCore hyps'' q(iprop($Pout $x -∗ $Q' $x)) [introPat] - | ~q(some $f) => + -- Solve side conditions automatically if possible, otherwise add them into the proof state + let hϕ ← iSolveSidecondition q($ϕ) false + + match mPclose with + | ~q(some $f) => + + let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← + withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do match closePat with | some closePat => - let f : Q($X → $prop) := f - iIntroCore hyps'' q(iprop($Pout $x -∗ $f $x -∗ $Q' $x)) [introPat, closePat] + mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) (.conjunction [introPat, closePat]) q(false) q(iprop($Pout $x ∗ $f $x)) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: error" - mkLambdaFVars #[x] body - -- Solve side conditions automatically if possible, otherwise add them into the proof state - let hϕ ← iSolveSidecondition q($ϕ) false + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) + + | ~q(none) => + let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ⊢ $Q' x) ← + withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do + mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) introPat q(false) q($Pout $x) + + let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := + q(fun x => sep_assoc.mpr.trans <| sep_emp.mp.trans ($hAcc x)) - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) + return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) /-- Find an invariant hypothesis with a given `Namespace` value. -/ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} {e} @@ -116,14 +112,14 @@ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? - " with " colGt introPat (colGt introPat)? : tactic + " with " colGt icasesPat (colGt icasesPat)? : tactic elab_rules : tactic - | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $ipat:introPat $[$cpat:introPat]?) => do + | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $ipat:icasesPat $[$cpat:icasesPat]?) => do -- Parse the introduction and selection patterns let specPat ← liftMacroM <| spat.mapM SpecPat.parse - let introPat ← liftMacroM <| IntroPat.parse ipat - let closePat ← liftMacroM <| cpat.mapM IntroPat.parse + let introPat ← liftMacroM <| iCasesPat.parse ipat + let closePat ← liftMacroM <| cpat.mapM iCasesPat.parse ProofModeM.runTactic λ mvar { hyps, goal, .. } => do -- Find the invariant hypothesis From db58a893817c662783a677b2a48299d28d7d32ab Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 17:47:02 +0200 Subject: [PATCH 064/108] New proof for `tac_inv_elim`, adjust `iInvCore` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 60 +++++++++++----------------- 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 4da0b21f1..63982dcb7 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -18,35 +18,25 @@ namespace Iris.ProofMode public meta section open Lean Elab Tactic Meta Qq BI Std -def optionMap {PROP : Type u} {X : Type} (mP : Option (X → PROP)) (x : X) : Option PROP := - mP.map (· x) - +@[rocq_alias tac_inv_elim] theorem tac_inv_elim [BI PROP] {e e' e'' goal : PROP} {ϕ : Prop} {X : Type} {p close : Bool} {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) - (hAcc : ∀ x, e'' ∗ Pout x ∗ ((mPclose.map (· x)).getD emp) ⊢ Q' x) - (pf : e ⊣⊢ e' ∗ □?p Pinv) + (pf : ∀ x, e'' ∗ Pout x ∗ ((mPclose.map (· x)).getD emp) ⊢ Q' x) + (pfEq : e ⊣⊢ e' ∗ □?p Pinv) (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ Pin) : - e ⊢ goal := sorry - - -- have h0 := inst.elim_inv hϕ - -- have h1 : e ⊢ Pinv ∗ Pin ∗ ∀ a, Pout a -∗ Option.map (fun x => x a) mPclose -∗? Q' a := calc - -- e ⊢ e' ∗ □?p Pinv := pf.mp - -- _ ⊢ □?p Pinv ∗ e' := sep_comm.mp - -- _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - -- _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr - -- _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl - -- _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin - -- _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp - -- _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| hAcc ·) - -- apply h1.trans - -- cases mPclose with - -- | none => - -- apply (sep_mono_right <| sep_mono_right <| forall_mono <| fun _ => wand_mono_left sep_emp.mp).trans h0 - -- | some _ => - -- apply (sep_mono_right <| sep_mono_right <| forall_intro (forall_elim · |>.trans wand_curry.mp)).trans h0 + e ⊢ goal := calc + e ⊢ e' ∗ □?p Pinv := pfEq.mp + _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr + _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl + _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin + _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp + _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) + _ ⊢ goal := inst.elim_inv hϕ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) @@ -75,26 +65,22 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} match mPclose with | ~q(some $f) => - - let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← - withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do + let pf : Q(∀ x, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← + withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with | some closePat => mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) (.conjunction [introPat, closePat]) q(false) q(iprop($Pout $x ∗ $f $x)) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` - | none => throwError "iinv: error" - - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) - + | none => throwError "iinv: missing cases pattern for the closing hypothesis" + return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) | ~q(none) => - let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ⊢ $Q' x) ← - withLocalDeclDQ (u := 0) (← mkFreshUserName `x) X fun x => do + let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← + withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) introPat q(false) q($Pout $x) - - let hAcc : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := - q(fun x => sep_assoc.mpr.trans <| sep_emp.mp.trans ($hAcc x)) - - return q(tac_inv_elim $inst $hϕ $hAcc $pfEq $pfPin) + -- Insert `emp` so that the entailment matches the argument of `tac_inv_elim` + let pf : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := + q(fun x => sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf x) + return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) /-- Find an invariant hypothesis with a given `Namespace` value. -/ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} {e} From f8b24661d6e14ee14b59829f19193415138d5070 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 17:55:07 +0200 Subject: [PATCH 065/108] Formatting: monadic bind --- Iris/Iris/ProofMode/Tactics/Inv.lean | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 63982dcb7..957c583f6 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -69,14 +69,17 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with | some closePat => - mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) (.conjunction [introPat, closePat]) q(false) q(iprop($Pout $x ∗ $f $x)) + iCasesCore _ hyps'' q(iprop($Q' $x)) (.conjunction [introPat, closePat]) + q(false) q(iprop($Pout $x ∗ $f $x)) >>= + (mkLambdaFVars #[x] ·) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: missing cases pattern for the closing hypothesis" return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) | ~q(none) => let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do - mkLambdaFVars #[x] <| ← iCasesCore _ hyps'' q(iprop($Q' $x)) introPat q(false) q($Pout $x) + iCasesCore _ hyps'' q(iprop($Q' $x)) introPat q(false) q($Pout $x) >>= + (mkLambdaFVars #[x] ·) -- Insert `emp` so that the entailment matches the argument of `tac_inv_elim` let pf : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := q(fun x => sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf x) From 8e106bfda0ae2dc674df98cce2376cccded3197f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 18:20:41 +0200 Subject: [PATCH 066/108] Minor formatting --- Iris/Iris/ProofMode/Tactics/Inv.lean | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 957c583f6..982f11e02 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -40,7 +40,7 @@ theorem tac_inv_elim [BI PROP] private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) - (introPat : iCasesPat) (closePat : Option iCasesPat) : + (casesPat : iCasesPat) (closePat : Option iCasesPat) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context let ⟨_, hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar @@ -56,20 +56,22 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} let some inst ← ProofModeM.trySynthInstanceQ q(ElimInv $ϕ $X $Pinv $Pin $Pout $close $mPclose $goal $Q') | throwError "iinv: invalid invariant {Pinv} (ElimInv type class synthesis failed)" - let ⟨e'', hyps'', p'', out'', pfPin⟩ ← iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD <| .autoframe .spatial] + let ⟨e'', hyps'', p'', out'', pfPin⟩ ← + iSpecializeCore hyps' q(false) q(iprop($Pin -∗ $Pin)) [specPat.getD <| .autoframe .spatial] have : $out'' =Q $Pin := ⟨⟩ have : $p'' =Q false := ⟨⟩ -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + -- Add the new goal into the proof state upon applying the case destruction patterns match mPclose with | ~q(some $f) => let pf : Q(∀ x, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with | some closePat => - iCasesCore _ hyps'' q(iprop($Q' $x)) (.conjunction [introPat, closePat]) + iCasesCore _ hyps'' q($Q' $x) (.conjunction [casesPat, closePat]) q(false) q(iprop($Pout $x ∗ $f $x)) >>= (mkLambdaFVars #[x] ·) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` @@ -78,11 +80,11 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} | ~q(none) => let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do - iCasesCore _ hyps'' q(iprop($Q' $x)) introPat q(false) q($Pout $x) >>= + iCasesCore _ hyps'' q($Q' $x) casesPat q(false) q($Pout $x) >>= (mkLambdaFVars #[x] ·) -- Insert `emp` so that the entailment matches the argument of `tac_inv_elim` let pf : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := - q(fun x => sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf x) + q((sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf ·)) return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) /-- Find an invariant hypothesis with a given `Namespace` value. -/ @@ -104,11 +106,11 @@ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? " with " colGt icasesPat (colGt icasesPat)? : tactic elab_rules : tactic - | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $ipat:icasesPat $[$cpat:icasesPat]?) => do + | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $casesPat:icasesPat $[$closePat:icasesPat]?) => do -- Parse the introduction and selection patterns let specPat ← liftMacroM <| spat.mapM SpecPat.parse - let introPat ← liftMacroM <| iCasesPat.parse ipat - let closePat ← liftMacroM <| cpat.mapM iCasesPat.parse + let casesPat ← liftMacroM <| iCasesPat.parse casesPat + let closePat ← liftMacroM <| closePat.mapM iCasesPat.parse ProofModeM.runTactic λ mvar { hyps, goal, .. } => do -- Find the invariant hypothesis @@ -122,5 +124,5 @@ elab_rules : tactic | some ivar => pure ivar | none => throwError m!"iinv: invariant {N} not found" - let pf ← iInvCore hyps goal ivar specPat introPat closePat + let pf ← iInvCore hyps goal ivar specPat casesPat closePat mvar.assign pf From e4a39eb7adb5d9e451244e1ac886fee58a4a84f6 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 25 Jun 2026 22:49:04 +0200 Subject: [PATCH 067/108] Avoid using `Option.getD` in `elimInv_acc_with_close`, force simplification of `wandM` Using pattern matching directly in `elimInv_acc_with_close` enables auto reduction --- Iris/Iris/ProofMode/Instances.lean | 5 ++--- Iris/Iris/ProofMode/ProofModeM.lean | 14 ++++++++++++++ Iris/Iris/ProofMode/Tactics/Inv.lean | 12 ++++++++++-- Iris/Iris/Tests/Tactics.lean | 19 +++++-------------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 866281ece..a392e4f0f 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -934,8 +934,7 @@ instance elimInv_acc_with_close [BI PROP] {X : Type} ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin - α - true (some (fun x => iprop(β x -∗ M2 ((mγ x).getD emp)))) + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true + (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) Q (fun _ => Q') where elim_inv := sorry diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index f20e854f6..9b254ada5 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -96,6 +96,20 @@ def addMVarGoal (m : MVarId) (name : Name := .anonymous) : ProofModeM Unit := do m.setUserName name modify ({goals := ·.goals.push m}) +/-- + Create a new proof goal with the hypotheses `hyps` and the conclusion `goal`, + run a tactic (`tac`) and add all resultant subgoals into the proof state. + If the tactic fails, add the proof goal directly. +-/ +def addBIGoalRunTactic {u} {prop : Q(Type u)} {bi} {e} + (hyps : Hyps bi e) (goal : Q($prop)) (tac : ProofModeM <| TSyntax `tactic) : + ProofModeM Q($e ⊢ $goal) := do + let mvar ← mkBIGoal hyps goal + let gs ← (observing? <| evalTacticAt (← tac) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) + if !gs.isEmpty then + for g in gs do addMVarGoal g + return mvar + /-- Try to synthesize a typeclass instance, adding any created metavariables as proof mode goals. -/ def ProofModeM.trySynthInstanceQ (α : Q(Sort v)) : ProofModeM (Option Q($α)) := do let LOption.some (e, mvars) ← ProofMode.trySynthInstance α | return none diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 982f11e02..8d8340121 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -38,6 +38,14 @@ theorem tac_inv_elim [BI PROP] _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) _ ⊢ goal := inst.elim_inv hϕ +/-- + This is useful as `wandM` (`-∗?`) is not simplified automatically without + explicitly using `simp`, even when it is annotated with `@[reducible]`. +-/ +private def addBIGoalSimpWandM {u} {prop : Q(Type u)} {bi} {e} + (hyps : Hyps bi e) (goal : Q($prop)) : ProofModeM Q($e ⊢ $goal) := + addBIGoalRunTactic hyps goal `(tactic | simp_all only [BIBase.wandM]) + private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) (casesPat : iCasesPat) (closePat : Option iCasesPat) : @@ -72,7 +80,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} match closePat with | some closePat => iCasesCore _ hyps'' q($Q' $x) (.conjunction [casesPat, closePat]) - q(false) q(iprop($Pout $x ∗ $f $x)) >>= + q(false) q(iprop($Pout $x ∗ $f $x)) addBIGoalSimpWandM >>= (mkLambdaFVars #[x] ·) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: missing cases pattern for the closing hypothesis" @@ -80,7 +88,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} | ~q(none) => let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do - iCasesCore _ hyps'' q($Q' $x) casesPat q(false) q($Pout $x) >>= + iCasesCore _ hyps'' q($Q' $x) casesPat q(false) q($Pout $x) addBIGoalSimpWandM >>= (mkLambdaFVars #[x] ·) -- Insert `emp` so that the entailment matches the argument of `tac_inv_elim` let pf : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index e626f5bbb..2b355faed 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2796,8 +2796,7 @@ example {P : IProp GF} : inv N iprop( P) ={⊤}=∗ ▷ P := by imodintro isplit · iexact H - · simp [BIBase.wandM] - imodintro + · imodintro inext iexact H @@ -2823,8 +2822,7 @@ example {E} {P : IProp GF} {h : ↑N ⊆ E} : inv N iprop( P) ={E}=∗ ▷ imodintro isplit · iexact H - · simp - imodintro + · imodintro inext iexact H @@ -2840,7 +2838,6 @@ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ iinv Hinv with ⟨#HP, Hown⟩ - simp imodintro isplit iexact HP @@ -2854,7 +2851,6 @@ example [CInvG GF] {γ : GName} {p : Qp} : cinv N γ iprop( P) ∗ own γ p ⊢@{IProp GF} |={⊤}=> own γ p ∗ ▷ P := by iintro ⟨#Hinv, H⟩ iinv Hinv with ⟨#HP, Hown⟩ Hclose - simp imod Hclose $$ HP imodintro iframe @@ -2870,7 +2866,6 @@ example [CInvG GF] {γ : GName} {p1 p2 : Qp} {P : IProp GF} : iintro ⟨#Hinv, Hown1, Hown2⟩ iinv Hinv $$ [Hown2 //] with ⟨#HP, Hown2⟩ imodintro - simp iframe HP ∗ imodintro inext @@ -2885,8 +2880,7 @@ example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑ imod Hclose $$ [HP Hown2] · iframe iexact HP - · simp - iframe + · iframe imodintro inext iexact HP @@ -2901,8 +2895,7 @@ example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} imodintro isplitl [Hown1] · iframe HP ∗ - · simp - iintro Hown1 + · iintro Hown1 iframe imodintro inext @@ -2922,8 +2915,7 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} imodintro isplitl [Hown1] · iframe HP ∗ - · simp - iintro Hown1 + · iintro Hown1 iframe imodintro inext @@ -2940,7 +2932,6 @@ example [Language.Atomic ↑s e] (h : ↑N ⊆ E) : ⊢ inv N P -∗ (▷ P -∗ WP e @ s ; (E \ ↑N) {{ v, |={E \ ↑N}=> ▷ P ∗ Φ v }}) -∗ WP e @ s ; E {{ Φ }} := by iintro #Hinv Hwp iinv Hinv with H - simp iapply Hwp iexact H From a1d2566f3beac0a302f2799283e9c4801ab7d09f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 10:47:27 +0200 Subject: [PATCH 068/108] `findInvariantWithNamespace`: minor simplifications --- Iris/Iris/ProofMode/Tactics/Inv.lean | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 8d8340121..98b017c38 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -95,19 +95,15 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} q((sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf ·)) return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) -/-- Find an invariant hypothesis with a given `Namespace` value. -/ +/-- Given a `Namespace` value, find a corresponding invariant hypothesis. -/ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} {e} (N : Q(Namespace)) (hyps : Hyps bi e) : ProofModeM <| Option IVarId := do match hyps with | .emp _ => return none | .hyp _ _ ivar _ ty _ => - let inst ← ProofModeM.trySynthInstanceQ q(IntoInv $ty $N) - return if inst.isSome then some ivar else none + return (← ProofModeM.trySynthInstanceQ q(IntoInv $ty $N)) <&> (fun _ => ivar) | .sep _ _ _ _ lhs rhs => - let rhsInvariant ← findInvariantWithNamespace N rhs - match rhsInvariant with - | some ivar => return some ivar - | none => return ← findInvariantWithNamespace N lhs + return (← findInvariantWithNamespace N rhs) <|> (← findInvariantWithNamespace N lhs) /-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? From 03ac6bcc9d43ab357539f9535450c9c61b60e218 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 12:36:48 +0200 Subject: [PATCH 069/108] Add docstring for `iinv` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 98b017c38..52193e796 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -105,10 +105,29 @@ private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} | .sep _ _ _ _ lhs rhs => return (← findInvariantWithNamespace N rhs) <|> (← findInvariantWithNamespace N lhs) -/-- `iinv` opens an invariant in the proof state. -/ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? " with " colGt icasesPat (colGt icasesPat)? : tactic +/-- + `iinv H with casesPat` opens an invariant hypothesis `H` and uses the + cases pattern `casesPat` to destruct the result without any additional + hypothesis for closing the invariant. The type class + `elimInv_acc_without_close` is used with this tactic. + + `iinv H with casesPat closePat` opens an invariant hypothesis `H`, + uses the cases pattern `casesPat` to destruct the result and generates a + hypothesis for closing the invariant, which is destructed by the cases + pattern `closePat`. The type class `elimInv_acc_with_close` is used with + this tactic. + + Furthermore, the following syntax is available. + - `iinv N with casesPat`: similar to `iinv H with casesPat`, where + an invariant with the namespace `N` is opened. + - `iinv H $$ specPat with casesPat`: similar to `iinv H with casesPat`, + with a specialisation pattern `specPat` for resource consumption needed + to open the invariant. Without `specPat`, the specialisation pattern is + by default the auto-framing of spatial hypotheses. +-/ elab_rules : tactic | `(tactic| iinv $t:term $[$$ $spat:specPat]? with $casesPat:icasesPat $[$closePat:icasesPat]?) => do -- Parse the introduction and selection patterns From a1a480c52d33453950cd8f7d31b4d15399cf3c46 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 12:47:57 +0200 Subject: [PATCH 070/108] More precise error message when `Namespace` value does not correspond to any invariant hypothesis in the context --- Iris/Iris/ProofMode/Tactics/Inv.lean | 2 +- Iris/Iris/Tests/Tactics.lean | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 52193e796..694402e13 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -145,7 +145,7 @@ elab_rules : tactic let N ← elabTermEnsuringTypeQ t q(Namespace) match ← findInvariantWithNamespace N hyps with | some ivar => pure ivar - | none => throwError m!"iinv: invariant {N} not found" + | none => throwError m!"iinv: invariant hypothesis with the namespace {N} not found" let pf ← iInvCore hyps goal ivar specPat casesPat closePat mvar.assign pf diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 2b355faed..8765df6cd 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2921,8 +2921,22 @@ example {t : NaInvPoolName} [NaInvG GF] {N : Namespace} {E1 E2 : CoPset} inext iexact HP +/- + Tests `iinv` with a valid `Namespace` value that does not correspond to + any invariant hypothesis in the context. +-/ +/-- error: iinv: invariant hypothesis with the namespace N3 not found -/ +#guard_msgs in +example {t : NaInvPoolName} [NaInvG GF] {N1 N2 N3 : Namespace} {E1 E2 : CoPset} + {P Q : IProp GF} (h : ↑N1 ⊆ E1) : + NonAtomicInvariant.inv t N1 iprop( Q) ∗ + NonAtomicInvariant.inv t N2 iprop( P) ∗ + own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by + iintro ⟨#_, #_, Hown1, Hown2⟩ + iinv N3 $$ Hown1 with ⟨#HP, Hown1⟩ + /- Variables to test `iinv` with `WP` -/ -variable {hlc : outParam HasLC} {Expr State Obs Val : Type _} [Λ : Language Expr State Obs Val] +variable {hlc : outParam HasLC} {Expr State Obs Val} [Λ : 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 : IProp GF} From 626e9fa1734d3a00f1675796082a7ec796f986ca Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 12:57:24 +0200 Subject: [PATCH 071/108] Add description of `iinv` in `tactics.md` --- Iris/tactics.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Iris/tactics.md b/Iris/tactics.md index f9e465ec0..fbf3ed968 100644 --- a/Iris/tactics.md +++ b/Iris/tactics.md @@ -66,6 +66,11 @@ The proof mode maintains three contexts: the *pure* (Lean) context, the *intuiti - `iexfalso` — Change the goal to `False`. - `itrivial` — Try to solve the goal with simple tactics (`iassumption`, `ipureintro` followed by `simp`/`assumption`, ...). Used by the `//` patterns. Extensible by adding `macro_rules` for `itrivial`. +## Iris-Specific Tactics + +- `iinv` *H* (`$$` [*specPat*](#specialization-patterns))? `with` [*casesPat*](#cases-patterns) ([*casesPat*](#cases-patterns))? — opens an invariant hypothesis *H* and uses the first cases pattern to destruct the result. The second cases pattern is used for destructing the hypothesis for closing the invariant. The specialisation pattern is used for resource consumption needed for opening the invariant. If the specialisation pattern is not given as part of the tactic, it is, by default, the auto-framing of spatial hypotheses. +- `iinv` *N* (`$$` [*specPat*](#specialization-patterns))? `with` [*casesPat*](#cases-patterns) ([*casesPat*](#cases-patterns))? — same as above, except that a namespace *N* is given. The last invariant hypothesis in the context of this namespace is chosen. + ## Cases Patterns - *name* / `_` — Name the hypothesis *name* (or keep it anonymous). From 633bb641e7ddeb02a41a56e09454ac66648e4ba4 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 13:01:41 +0200 Subject: [PATCH 072/108] Update `Porting.lean` --- Iris/Iris/ProofMode/Porting.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Porting.lean b/Iris/Iris/ProofMode/Porting.lean index dad0da8f9..9ad782cfb 100644 --- a/Iris/Iris/ProofMode/Porting.lean +++ b/Iris/Iris/ProofMode/Porting.lean @@ -55,7 +55,7 @@ import Iris.Std.RocqPorting #rocq_concept proofmode "Tactics" "iLöb" ported "iloeb" #rocq_concept proofmode "Tactics" "iAssert" ported "ihave _ : _" #rocq_concept proofmode "Tactics" "iRewrite" ported "irewrite" -#rocq_concept proofmode "Tactics" "iInv" missing "" +#rocq_concept proofmode "Tactics" "iInv" ported "iinv" #rocq_concept proofmode "Tactics" "iAccu" missing "" #rocq_concept proofmode "Tactics" "rules for trivial" ported "itrivial" From 4453445e104bb856d9ba963d893dc681e0d7c5c4 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 16:42:23 +0200 Subject: [PATCH 073/108] Move the two type classes to a separate file (`InstancesIris.lean`) so that IPM tactics can be used for proving the type class instances --- Iris/Iris/ProofMode.lean | 1 + Iris/Iris/ProofMode/Instances.lean | 20 -------------- Iris/Iris/ProofMode/InstancesIris.lean | 37 ++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 20 deletions(-) create mode 100644 Iris/Iris/ProofMode/InstancesIris.lean diff --git a/Iris/Iris/ProofMode.lean b/Iris/Iris/ProofMode.lean index 399fffce4..72e01698d 100644 --- a/Iris/Iris/ProofMode.lean +++ b/Iris/Iris/ProofMode.lean @@ -8,6 +8,7 @@ public import Iris.ProofMode.Instances public import Iris.ProofMode.InstancesCmra public import Iris.ProofMode.InstancesFrame public import Iris.ProofMode.InstancesInternalEq +public import Iris.ProofMode.InstancesIris public import Iris.ProofMode.InstancesLater public import Iris.ProofMode.InstancesMake public import Iris.ProofMode.InstancesPlainly diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index a392e4f0f..8ebc6aa25 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -918,23 +918,3 @@ instance combineSepGives_persistently [BI PROP] (Q1 Q2 P : PROP) [h : CombineSepGives Q1 Q2 P] : CombineSepGives iprop( Q1) iprop( Q2) iprop( P) where combine_sep_gives := persistently_sep_mpr.trans (persistently_mono h.combine_sep_gives) - -set_option synthInstance.checkSynthOrder false in -@[rocq_alias elim_inv_acc_without_close] -instance elimInv_acc_without_close [BI PROP] {X : Type} - ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) - [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where - elim_inv := sorry - -set_option synthInstance.checkSynthOrder false in -@[rocq_alias elim_inv_acc_with_close] -instance elimInv_acc_with_close [BI PROP] {X : Type} - ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) - [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true - (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) - Q (fun _ => Q') where - elim_inv := sorry diff --git a/Iris/Iris/ProofMode/InstancesIris.lean b/Iris/Iris/ProofMode/InstancesIris.lean new file mode 100644 index 000000000..4dc5d6eaf --- /dev/null +++ b/Iris/Iris/ProofMode/InstancesIris.lean @@ -0,0 +1,37 @@ +/- +Copyright (c) 2026 Alvin Tang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Michael Sammler, Alvin Tang +-/ +module + +public import Iris.BI +public import Iris.ProofMode.Classes +public import Iris.ProofMode.ModalityInstances +public import Iris.Std.TC +public import Iris.Std.RocqPorting +public import Iris.ProofMode.Tactics +public import Iris.ProofMode.Display + +@[expose] public section + +namespace Iris.ProofMode +open Iris.BI Iris.Std + +@[rocq_alias elim_inv_acc_without_close] +instance elimInv_acc_without_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where + elim_inv := sorry + +@[rocq_alias elim_inv_acc_with_close] +instance elimInv_acc_with_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true + (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) + Q (fun _ => Q') where + elim_inv := sorry From 6b22ce32ce8bc5d20b5f7a9dd626d578cc543a3c Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 16:44:06 +0200 Subject: [PATCH 074/108] Use bare pattern matching in `ElimInv` to avoid unnecessary `emp` --- Iris/Iris/ProofMode/Classes.lean | 7 +++++-- Iris/Iris/ProofMode/Tactics/Inv.lean | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index e6857b3a9..1bc477e5f 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -251,8 +251,11 @@ set_option synthInstance.checkSynthOrder false in @[ipm_class, rocq_alias ElimInv] class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (Pinv : PROP) (Pin : outParam PROP) (Pout : outParam <| X → PROP) - (close : Bool) (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where - elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, Pout x ∗ ((mPclose.map (· x)).getD emp) -∗ Q' x) ⊢ Q + (close : Bool) (mPclose : outParam <| Option <| X → PROP) + (Q : PROP) (Q' : outParam <| X → PROP) where + elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, (match mPclose with + | none => iprop(Pout x -∗ Q' x) + | some Pclose => iprop(Pout x ∗ Pclose x -∗ Q' x))) ⊢ Q export ElimInv (elim_inv) #rocq_ignore elim_inv_tc_opaque "No tc_opaque in Lean" diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 694402e13..954a88eb0 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -36,7 +36,7 @@ theorem tac_inv_elim [BI PROP] _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) - _ ⊢ goal := inst.elim_inv hϕ + _ ⊢ goal := sorry -- inst.elim_inv hϕ /-- This is useful as `wandM` (`-∗?`) is not simplified automatically without From 568cba884894c258d71a4f47d473f73386e02b1e Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 17:12:59 +0200 Subject: [PATCH 075/108] Finish proofs for `elimInv_acc_without_close` and `elimInv_acc_with_close` Also avoid using `Option.getD` in definitions as they require in tedious unfolding --- Iris/Iris/ProofMode/Classes.lean | 2 +- Iris/Iris/ProofMode/InstancesIris.lean | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 1bc477e5f..104c253c4 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -232,7 +232,7 @@ class IntoInv [BI PROP] (P : PROP) (N : Namespace) @[rocq_alias accessor] def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) (mγ : X → Option PROP) : PROP := - M1 iprop(∃ x, α x ∗ (β x -∗ M2 ((mγ x).getD emp))) + M1 iprop(∃ x, α x ∗ (β x -∗ M2 (match mγ x with | none => emp | some p => p))) @[ipm_class, rocq_alias ElimAcc] class ElimAcc [BI PROP] {X : Type} (ϕ : outParam Prop) (M1 M2 : PROP → PROP) diff --git a/Iris/Iris/ProofMode/InstancesIris.lean b/Iris/Iris/ProofMode/InstancesIris.lean index 4dc5d6eaf..72321082c 100644 --- a/Iris/Iris/ProofMode/InstancesIris.lean +++ b/Iris/Iris/ProofMode/InstancesIris.lean @@ -24,7 +24,13 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where - elim_inv := sorry + elim_inv := by + intro ⟨hϕ1, _⟩ + iintro ⟨Hinv, Hin, Hcont⟩ + iapply h2.elim_acc $$ [Hcont] + · assumption + · iassumption + · iapply h1.into_acc hϕ1 $$ Hinv Hin @[rocq_alias elim_inv_acc_with_close] instance elimInv_acc_with_close [BI PROP] {X : Type} @@ -34,4 +40,11 @@ instance elimInv_acc_with_close [BI PROP] {X : Type} ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) Q (fun _ => Q') where - elim_inv := sorry + elim_inv := by + intro ⟨hϕ1, _⟩ + have hAcc := h1.into_acc + unfold accessor at hAcc + iintro ⟨Hinv, Hin, Hcont⟩ + imod hAcc hϕ1 $$ Hinv Hin with ⟨%_, Hα, Hclose⟩ + iapply Hcont + isplitl [Hα] <;> iassumption From 75e3986fb959a55610cee20cc421655487a343b4 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 17:25:27 +0200 Subject: [PATCH 076/108] Update `tac_inv_elim` and `iInvCore` according to the updated definitions --- Iris/Iris/ProofMode/Tactics/Inv.lean | 36 +++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 954a88eb0..bce7dc77c 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -24,19 +24,30 @@ theorem tac_inv_elim [BI PROP] {Pinv Pin : PROP} {mPclose : Option <| X → PROP} {Pout Q' : X → PROP} (inst : ElimInv ϕ X Pinv Pin Pout close mPclose goal Q') (hϕ : ϕ) - (pf : ∀ x, e'' ∗ Pout x ∗ ((mPclose.map (· x)).getD emp) ⊢ Q' x) + (pf : match mPclose with + | none => ∀ x, e'' ∗ Pout x ⊢ Q' x + | some Pclose => ∀ x, e'' ∗ Pout x ∗ Pclose x ⊢ Q' x) (pfEq : e ⊣⊢ e' ∗ □?p Pinv) (pfPin : e' ∗ (Pin -∗ Pin) ⊢ e'' ∗ Pin) : - e ⊢ goal := calc - e ⊢ e' ∗ □?p Pinv := pfEq.mp - _ ⊢ □?p Pinv ∗ e' := sep_comm.mp - _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim - _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr - _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl - _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin - _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp - _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) - _ ⊢ goal := sorry -- inst.elim_inv hϕ + e ⊢ goal := by + have h0 := inst.elim_inv + have h1 : e ⊢ Pinv ∗ Pin ∗ e'' := calc + e ⊢ e' ∗ □?p Pinv := pfEq.mp + _ ⊢ □?p Pinv ∗ e' := sep_comm.mp + _ ⊢ Pinv ∗ e' := sep_mono_left intuitionisticallyIf_elim + _ ⊢ Pinv ∗ e' ∗ emp := sep_mono_right sep_emp.mpr + _ ⊢ Pinv ∗ e' ∗ (Pin -∗ Pin) := sep_mono_right <| sep_mono_right wand_rfl + _ ⊢ Pinv ∗ e'' ∗ Pin := sep_mono_right pfPin + _ ⊢ Pinv ∗ Pin ∗ e'' := sep_mono_right sep_comm.mp + cases mPclose with simp_all + | none => calc + e ⊢ Pinv ∗ Pin ∗ e'' := h1 + _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) + _ ⊢ goal := h0 + | some Pclose => calc + e ⊢ Pinv ∗ Pin ∗ e'' := h1 + _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) + _ ⊢ goal := h0 /-- This is useful as `wandM` (`-∗?`) is not simplified automatically without @@ -90,9 +101,6 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do iCasesCore _ hyps'' q($Q' $x) casesPat q(false) q($Pout $x) addBIGoalSimpWandM >>= (mkLambdaFVars #[x] ·) - -- Insert `emp` so that the entailment matches the argument of `tac_inv_elim` - let pf : Q(∀ x : $X, $e'' ∗ $Pout x ∗ emp ⊢ $Q' x) := - q((sep_assoc.mpr.trans <| sep_emp.mp.trans <| $pf ·)) return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) /-- Given a `Namespace` value, find a corresponding invariant hypothesis. -/ From 49f28b1b723e83ada888207b93c7869946c3044d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 18:16:28 +0200 Subject: [PATCH 077/108] Complete proof for `intoAcc_inv` --- Iris/Iris/Instances/Lib/Invariants.lean | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/Instances/Lib/Invariants.lean b/Iris/Iris/Instances/Lib/Invariants.lean index 1be8cb6d6..bb233ce09 100644 --- a/Iris/Iris/Instances/Lib/Invariants.lean +++ b/Iris/Iris/Instances/Lib/Invariants.lean @@ -96,8 +96,17 @@ instance intoAcc_inv (N : Namespace) (P : IProp GF) E : IntoAcc (X := Unit) (inv N P) (↑N ⊆ E) iprop(True) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (λ _ => iprop(▷ P)) (λ _ => iprop(▷ P)) (λ _ => none) where into_acc := by - iintro %x #Hinv #Htrue - sorry + simp only [inv, accessor] + iintro %x #Hinv - + imod Hinv $$ %E [] with ⟨HP, Hclose⟩ + · itrivial + · iexists () + imodintro + isplitl [HP] + · iassumption + · iintro HP + iapply (BIFUpdate.mono true_emp.mp) + iapply Hclose $$ HP end Instances From 0cfec2f0c4016401bd40f06c0d3ca2a36f141f37 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 18:19:47 +0200 Subject: [PATCH 078/108] Complete proofs for `intoAcc_cinv` and `intoAcc_na` --- Iris/Iris/Instances/Lib/CInvariants.lean | 12 +++++++++++- Iris/Iris/Instances/Lib/NaInvariants.lean | 8 +++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index fe9400f74..18f344f15 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -289,7 +289,17 @@ set_option synthInstance.checkSynthOrder false in instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) (p : Qp) : IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where - into_acc := sorry + into_acc := by + simp only [accessor] + iintro %x #Hinv Hown + imod acc _ _ _ _ _ x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ + imodintro + iexists () + isplitl [HP Hγ] + · iframe + · iintro HP + iapply (BIFUpdate.mono true_emp.mp) + iapply Hcl $$ HP end CancelableInvariant end Iris diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index be3187088..deacd8f3e 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -231,7 +231,13 @@ instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IPro IntoAcc (X := Unit) (inv p N P) (↑N ⊆ E ∧ ↑N ⊆ F) (own p F) (fupd E E) (fupd E E) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (λ _ => some (own p F)) where - into_acc := sorry + into_acc := by + simp only [accessor] + intro ⟨hE, hF⟩ + iintro #Hinv Hown + imod inv_acc hE hF $$ Hinv Hown with ⟨_, Hown, _⟩ + iexists () + iframe end NonAtomicInvariant end Iris From fb846537b254139d82a7aa346dd7b139344b673e Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 18:59:11 +0200 Subject: [PATCH 079/108] Complete proof for `elimAcc_wp_atomic` and `elimAcc_wp_nonatomic` --- Iris/Iris/ProgramLogic/WeakestPre.lean | 32 +++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 4cde28962..d4c3dfabd 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -8,13 +8,14 @@ public import Iris.Algebra public import Iris.Instances.Lib.FUpd public import Iris.BI public import Iris.BI.WeakestPre +public import Iris.BI.DerivedLaws public import Iris.ProofMode public import Iris.ProgramLogic.Language public import Iris.Std.CoPset namespace Iris -open ProgramLogic Language.Notation Std +open ProgramLogic Language.Notation Std Iris.BI @[expose] public section @@ -695,12 +696,37 @@ instance (priority := low) elimAcc_wp_atomic {X} (E₁ E₂ : CoPset) α β (γ ElimAcc (Language.Atomic ↑s e) (fupd E₁ E₂) (fupd E₂ E₁) α β γ (WP e @ s ; E₁ {{ Φ }}) (fun x => WP e @ s ; E₂ {{ v, |={E₂}=> β x ∗ (γ x -∗? Φ v) }}) where - elim_acc := sorry + elim_acc := by + simp only [accessor, BIBase.wandM] + iintro %atomic Hinner Hacc + imod Hacc with ⟨%x, Hα, Hclose⟩ + iapply wp_wand $$ [Hinner Hα] + · iapply Hinner $$ Hα + · iintro %v >⟨H1, H2⟩ + ispecialize Hclose $$ H1 + imod Hclose + imodintro + cases (γ x) with + | none => iexact H2 + | some P => iapply H2 $$ Hclose @[rocq_alias elim_acc_wp_nonatomic] instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option (IProp GF)) : ElimAcc True (fupd E E) (fupd E E) α β γ (WP e @ s ; E {{ Φ }}) (fun x => WP e @ s ; E {{ v, |={E}=> β x ∗ (γ x -∗? Φ v) }}) where - elim_acc := sorry + elim_acc := by + simp only [accessor, BIBase.wandM] + iintro %_ Hinner Hacc + imod Hacc with ⟨%x, Hα, Hclose⟩ + iapply wp_fupd + iapply wp_wand $$ [Hinner Hα] + · iapply Hinner $$ Hα + · iintro %v >⟨H1, H2⟩ + ispecialize Hclose $$ H1 + imod Hclose + imodintro + cases (γ x) with + | none => iexact H2 + | some P => iapply H2 $$ Hclose end ProofModeClasses From b5384ffb8ab3046ada08864a2a3b29adf273a56d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 22:15:18 +0200 Subject: [PATCH 080/108] Finish proof for `elimAcc_fupd` --- Iris/Iris/Instances/Lib/CInvariants.lean | 2 +- Iris/Iris/ProofMode/InstancesUpdates.lean | 43 ++++++++++++++++++++--- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index 18f344f15..caba0d89e 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -292,7 +292,7 @@ instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) ( into_acc := by simp only [accessor] iintro %x #Hinv Hown - imod acc _ _ _ _ _ x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ + imod acc E N γ p P x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ imodintro iexists () isplitl [HP Hγ] diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index c739b57f2..b1ae1fe5e 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -7,7 +7,10 @@ module public import Iris.BI public import Iris.ProofMode.Classes +public import Iris.ProofMode.Instances public import Iris.Std.TC +public import Iris.ProofMode.Tactics +public import Iris.ProofMode.Display @[expose] public section @@ -201,18 +204,48 @@ instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PR elim_modal h := by cases h @[rocq_alias elim_acc_bupd] -instance elimAcc_bupd [BUpd PROP] {X} (α β : X → PROP) mγ (Q : PROP) : +instance elimAcc_bupd {X} (α β : X → PROP) mγ (Q : PROP) : ElimAcc True bupd bupd α β mγ iprop(|==> Q) - (fun x => iprop(|==> β x ∗ (mγ x -∗? |==> Q))) where - elim_acc := sorry + iprop(fun x => (|==> β x ∗ (mγ x -∗? |==> Q))) where + elim_acc := by + simp only [accessor, BIBase.wandM] + iintro %_ Hinner >⟨%x, Hα, Hclose⟩ + ispecialize Hinner $$ %x Hα + cases (mγ x) with simp_all + | none => + icases Hinner with ⟨Hβ, Hfin⟩ + imod Hβ + ispecialize Hclose $$ Hβ + imod Hclose + iexact Hfin + | some P => + icases Hinner with ⟨Hβ, Hfin⟩ + imod Hβ + ispecialize Hclose $$ Hβ + iapply Hfin + sorry @[rocq_alias elim_acc_fupd] -instance elimAcc_fupd [FUpd PROP] {X} E1 E2 E (α β : X → PROP) mγ (Q : PROP) : +instance elimAcc_fupd {X} E1 E2 E (α β : X → PROP) mγ (Q : PROP) : ElimAcc True (fupd E1 E2) (fupd E2 E1) α β mγ iprop(|={E1,E}=> Q) (fun x => iprop(|={E2}=> β x ∗ (mγ x -∗? |={E1,E}=> Q))) where - elim_acc := sorry + elim_acc := by + simp only [accessor, BIBase.wandM] + iintro %_ Hinner >⟨%x, Hα, Hclose⟩ + ispecialize Hinner $$ %x Hα + cases (mγ x) with simp_all + | none => + imod Hinner with ⟨Hβ, Hfin⟩ + ispecialize Hclose $$ Hβ + imod Hclose + iexact Hfin + | some p => + imod Hinner with ⟨Hβ, Hfin⟩ + ispecialize Hclose $$ Hβ + imod Hclose + iapply Hfin $$ Hclose end BIFancyUpdate From 47f09de662243ab0f84dae86217677a9ecde7ac7 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 22:30:58 +0200 Subject: [PATCH 081/108] Finish proof for `elimAcc_bupd` --- Iris/Iris/ProofMode/InstancesUpdates.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index b1ae1fe5e..cc9f41a48 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -222,9 +222,9 @@ instance elimAcc_bupd {X} (α β : X → PROP) mγ (Q : PROP) : | some P => icases Hinner with ⟨Hβ, Hfin⟩ imod Hβ - ispecialize Hclose $$ Hβ + imod Hclose $$ Hβ iapply Hfin - sorry + iexact Hclose @[rocq_alias elim_acc_fupd] instance elimAcc_fupd {X} E1 E2 E (α β : X → PROP) mγ (Q : PROP) : From aa39637f7563513fc83cdb5927870c730af2914c Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 22:43:14 +0200 Subject: [PATCH 082/108] Update `intoAcc_cinv` in response to changes --- Iris/Iris/Instances/Lib/CInvariants.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index affd70b46..6baf09fcb 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -301,7 +301,7 @@ instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) ( into_acc := by simp only [accessor] iintro %x #Hinv Hown - imod acc E N γ p P x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ + imod acc x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ imodintro iexists () isplitl [HP Hγ] From 77d33b9bfdf74b274f7972c67db3326cf88c1471 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 22:52:11 +0200 Subject: [PATCH 083/108] Rename hypotheses in `WeakestPre.lean` for consistency with the Rocq version --- Iris/Iris/ProgramLogic/WeakestPre.lean | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index c97bcd74b..9ad4c55ce 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -689,17 +689,16 @@ instance (priority := low) elimAcc_wp_atomic {X} (E₁ E₂ : CoPset) α β (γ (fun x => WP e @ s ; E₂ {{ v, |={E₂}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by simp only [accessor, BIBase.wandM] - iintro %atomic Hinner Hacc - imod Hacc with ⟨%x, Hα, Hclose⟩ + iintro %atomic Hinner >⟨%x, Hα, Hclose⟩ iapply wp_wand $$ [Hinner Hα] · iapply Hinner $$ Hα - · iintro %v >⟨H1, H2⟩ - ispecialize Hclose $$ H1 + · iintro %v >⟨Hβ, HΦ⟩ + ispecialize Hclose $$ Hβ imod Hclose imodintro cases (γ x) with - | none => iexact H2 - | some P => iapply H2 $$ Hclose + | none => iexact HΦ + | some P => iapply HΦ $$ Hclose @[rocq_alias elim_acc_wp_nonatomic] instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option (IProp GF)) : @@ -707,17 +706,16 @@ instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option (fun x => WP e @ s ; E {{ v, |={E}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by simp only [accessor, BIBase.wandM] - iintro %_ Hinner Hacc - imod Hacc with ⟨%x, Hα, Hclose⟩ + iintro %_ Hinner >⟨%x, Hα, Hclose⟩ iapply wp_fupd iapply wp_wand $$ [Hinner Hα] · iapply Hinner $$ Hα - · iintro %v >⟨H1, H2⟩ - ispecialize Hclose $$ H1 + · iintro %v >⟨Hβ, HΦ⟩ + ispecialize Hclose $$ Hβ imod Hclose imodintro cases (γ x) with - | none => iexact H2 - | some P => iapply H2 $$ Hclose + | none => iexact HΦ + | some P => iapply HΦ $$ Hclose end ProofModeClasses From f2b611fbf285b0dea8eb0041134ca6f14e2172d0 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 22:58:59 +0200 Subject: [PATCH 084/108] `rocq_alias` typo fix: `wandM_sound` -> `bi.wandM_sound` --- Iris/Iris/BI/DerivedLaws.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/BI/DerivedLaws.lean b/Iris/Iris/BI/DerivedLaws.lean index 1a13f7fd8..e4efb8eea 100644 --- a/Iris/Iris/BI/DerivedLaws.lean +++ b/Iris/Iris/BI/DerivedLaws.lean @@ -1041,7 +1041,7 @@ theorem emp_or [BI PROP] {P : PROP} [Affine P] : emp ∨ P ⊣⊢ emp := ⟨or_e theorem emp_wand [BI PROP] {P : PROP} : (emp -∗ P) ⊣⊢ P := ⟨emp_sep.mpr.trans wand_elim_right, wand_intro_left emp_sep.mp⟩ -@[rocq_alias wandM_sound] +@[rocq_alias bi.wandM_sound] theorem wandM_sound [BI PROP] {mP : Option PROP} {Q : PROP} : (mP -∗? Q) ⊣⊢ (mP.getD emp -∗ Q) := by cases mP <;> simp [BIBase.wandM] From 3f488af25c3acfedadc97486dc0b9d763a5ba849 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 23:20:19 +0200 Subject: [PATCH 085/108] Port `from_wand_wandM` and `into_wand_wandM` --- Iris/Iris/ProofMode/Instances.lean | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 8ebc6aa25..834152655 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -51,6 +51,12 @@ instance fromImp_imp [BI PROP] (P1 P2 : PROP) : FromImp iprop(P1 → P2) P1 P2 : @[rocq_alias from_wand_wand] instance fromWand_wand [BI PROP] (P1 P2 : PROP) : FromWand iprop(P1 -∗ P2) io P1 P2 := ⟨.rfl⟩ +-- FromWandM +@[rocq_alias from_wand_wandM] +instance fromWand_wandM [BI PROP] (mP1 : Option PROP) (P2 : PROP) : + FromWand iprop(mP1 -∗? P2) io (mP1.getD emp) P2 where + from_wand := wandM_sound.mpr + -- IntoWand #rocq_ignore into_wand_wand' "IntoWand' is not used in Lean" #rocq_ignore into_wand_impl' "IntoWand' is not used in Lean" @@ -84,6 +90,13 @@ instance intoWand_and_r (p q : Bool) [BI PROP] (R1 R2 P' Q' : PROP) instance intoWand_wandIff (p q : Bool) [BI PROP] (R1 R2 P' Q' : PROP) [h : IntoWand p q iprop((R1 -∗ R2) ∧ (R2 -∗ R1)) ioP P' ioQ Q'] : IntoWand p q iprop(R1 ∗-∗ R2) ioP P' ioQ Q' := h +@[rocq_alias into_wand_wandM] +instance intoWand_wandM (p q : Bool) [BI PROP] (mP' : Option PROP) (P Q : PROP) + [h : FromAssumption q ioP P (mP'.getD emp)] : + IntoWand p q iprop(mP' -∗? Q) ioP P ioQ Q where + into_wand := (intuitionisticallyIf_mono wandM_sound.mp).trans <| + (intuitionisticallyIf_mono <| wand_mono_left h.1).trans intuitionisticallyIf_elim + -- The set_option is ok since this is an instance for an IPM class and thus can create mvars. set_option synthInstance.checkSynthOrder false in @[rocq_alias into_wand_forall] @@ -846,6 +859,14 @@ instance elimModal_wand [BI PROP] φ p p' (P P' Q Q' R : PROP) [h : ElimModal φ wand_intro_left $ sep_assoc.2.trans _).trans (h.1 hφ)) apply (sep_mono_left sep_comm.1).trans (sep_assoc.1.trans $ wand_elim_swap $ wand_elim_swap .rfl) +@[rocq_alias elim_modal_wandM] +instance elimModal_wandM [BI PROP] φ p p' (P P' Q Q' : PROP) (mR : Option PROP) + [h : ElimModal φ p p' P P' Q Q'] : + ElimModal φ p p' P P' iprop(mR -∗? Q) iprop(mR -∗? Q') where + elim_modal hφ := + (sep_mono_right <| wand_mono_right wandM_sound.mp).trans <| + ((elimModal_wand φ p p' P P' Q Q' (mR.getD emp)).elim_modal hφ).trans wandM_sound.mpr + @[rocq_alias elim_modal_forall] instance elimModal_forall [BI PROP] φ p p' P P' (Φ Ψ : α → PROP) [h : ∀ x, ElimModal φ p p' P P' (Φ x) (Ψ x)] : ElimModal φ p p' P P' iprop(∀ x, Φ x) iprop(∀ x, Ψ x) where From c3e3f92ad84dee334e68b1dbd6c85bcbe76e4ad3 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 26 Jun 2026 16:34:59 +0200 Subject: [PATCH 086/108] Attempt to resolve circular depedency between `ProofMode.Tactics` and `ProofMode.Instances` Avoids importing `ProofMode.Instances` in `ProofMode.Tactics` --- Iris/Iris/ProofMode/Instances.lean | 1 + Iris/Iris/ProofMode/Tactics/Cases.lean | 16 +++++++++------- Iris/Iris/ProofMode/Tactics/Pure.lean | 16 ++++++++-------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 834152655..f15f02b33 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -10,6 +10,7 @@ public import Iris.ProofMode.Classes public import Iris.ProofMode.ModalityInstances public import Iris.Std.TC public import Iris.Std.RocqPorting +-- public import Iris.ProofMode.Tactics @[expose] public section diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index 9f5ec3854..f6c0467f0 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -48,11 +48,13 @@ theorem or_elim' [BI PROP] {p} {P A Q A1 A2 : PROP} [inst : IntoOr A A1 A2] (sep_mono_right <| (intuitionisticallyIf_mono inst.1).trans (intuitionisticallyIf_or _).1).trans <| BI.sep_or_left.1.trans <| or_elim h1 h2 theorem intuitionistic_elim_spatial [BI PROP] {A A' Q : PROP} - [IntoPersistently false A A'] [TCOr (Affine A) (Absorbing Q)] + (instPers: IntoPersistently false A A') (instAffineAbsorbing: TCOr (Affine A) (Absorbing Q)) (h : P ∗ □ A' ⊢ Q) : P ∗ A ⊢ Q := (replaces_r to_persistent_spatial).apply h -theorem intuitionistic_elim_intuitionistic [BI PROP] {A A' Q : PROP} [IntoPersistently true A A'] - (h : P ∗ □ A' ⊢ Q) : P ∗ □ A ⊢ Q := intuitionistic_elim_spatial h +theorem intuitionistic_elim_intuitionistic [BI PROP] {A A' Q : PROP} (inst : IntoPersistently true A A') + (h : P ∗ □ A' ⊢ Q) : P ∗ □ A ⊢ Q := + let instAffine := @TCOr.l (Affine iprop(□ A)) (Absorbing Q) ⟨(intuitionistically_affine A).affine⟩ + intuitionistic_elim_spatial ⟨persistently_of_intuitionistically.trans inst.into_persistently⟩ instAffine h theorem spatial_elim [BI PROP] {p} {A A' Q : PROP} [FromAffinely A' A p] (h : P ∗ A' ⊢ Q) : P ∗ □?p A ⊢ Q := @@ -154,15 +156,15 @@ private def iCasesIntuitionistic {prop : Q(Type u)} (_bi : Q(BI $prop)) (k : (B : Q($prop)) → ProofModeM Q($P ∗ □ $B ⊢ $goal)) : ProofModeM (Q($P ∗ □?$p $A ⊢ $goal)) := do let B ← mkFreshExprMVarQ q($prop) - let .some _ ← ProofModeM.trySynthInstanceQ q(IntoPersistently $p $A $B) + let .some instPers ← ProofModeM.trySynthInstanceQ q(IntoPersistently $p $A $B) | throwError "icases: {A} not persistent" match matchBool p with | .inl _ => - return q(intuitionistic_elim_intuitionistic $(← k B)) + return q(intuitionistic_elim_intuitionistic $instPers $(← k B)) | .inr _ => - let .some _ ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $goal)) + let .some instAffineAbsorbing ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $goal)) | throwError "icases: {A} not affine and the goal not absorbing" - return q(intuitionistic_elim_spatial (A := $A) $(← k B)) + return q(intuitionistic_elim_spatial $instPers $instAffineAbsorbing $(← k B)) /-- Destruct an affine/spatial hypothesis [A] by removing the affinely wrapper and continuing with diff --git a/Iris/Iris/ProofMode/Tactics/Pure.lean b/Iris/Iris/ProofMode/Tactics/Pure.lean index 6901ce5a2..295572803 100644 --- a/Iris/Iris/ProofMode/Tactics/Pure.lean +++ b/Iris/Iris/ProofMode/Tactics/Pure.lean @@ -5,7 +5,6 @@ Authors: Lars König, Mario Carneiro, Michael Sammler -/ module -public import Iris.ProofMode.Instances public meta import Iris.ProofMode.Tactics.Basic namespace Iris.ProofMode @@ -14,7 +13,7 @@ public section open BI Std theorem pure_elim_spatial [BI PROP] {P P' A Q : PROP} {φ : Prop} - [hA : IntoPure A φ] [or : TCOr (Affine A) (Absorbing Q)] + (hA : IntoPure A φ) (or : TCOr (Affine A) (Absorbing Q)) (h : P ⊣⊢ P' ∗ A) (h_entails : φ → P' ⊢ Q) : P ⊢ Q := h.1.trans <| match or with | TCOr.l => @@ -26,8 +25,9 @@ theorem pure_elim_spatial [BI PROP] {P P' A Q : PROP} {φ : Prop} pure_elim_right fun hφ => (absorbingly_mono <| h_entails hφ).trans absorbing theorem pure_elim_intuitionistic [BI PROP] {P P' A Q : PROP} {φ : Prop} - [IntoPure A φ] (h : P ⊣⊢ P' ∗ □ A) (h' : φ → P' ⊢ Q) : P ⊢ Q := - pure_elim_spatial h h' + (instIntoPure : IntoPure A φ) (h : P ⊣⊢ P' ∗ □ A) (h' : φ → P' ⊢ Q) : P ⊢ Q := + have instAffine := @TCOr.l (Affine iprop(□ A)) (Absorbing Q) ⟨(intuitionistically_affine A).affine⟩ + pure_elim_spatial ⟨intuitionistically_elim.trans instIntoPure.into_pure⟩ instAffine h h' public meta section open Lean Elab Tactic Meta Qq @@ -36,7 +36,7 @@ def iPureCore {prop : Q(Type u)} (_bi : Q(BI $prop)) (P P' : Q($prop)) (p : Q(Bool)) (A Q : Q($prop)) (name : TSyntax ``binderIdent) (pf : Q($P ⊣⊢ $P' ∗ □?$p $A)) (k : (φ : Q(Prop)) → Q($φ) → ProofModeM (Q($P' ⊢ $Q))) : ProofModeM (Q($P ⊢ $Q)) := do let φ : Q(Prop) ← mkFreshExprMVarQ q(Prop) - let .some _ ← ProofModeM.trySynthInstanceQ q(IntoPure $A $φ) + let .some instIntoPure ← ProofModeM.trySynthInstanceQ q(IntoPure $A $φ) | throwError "ipure: {A} is not pure" let (name, ref) ← getFreshName name @@ -47,11 +47,11 @@ def iPureCore {prop : Q(Type u)} (_bi : Q(BI $prop)) match matchBool p with | .inl _ => - return (q(pure_elim_intuitionistic $pf $f)) + return (q(pure_elim_intuitionistic $instIntoPure $pf $f)) | .inr _ => - let .some _ ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $Q)) + let .some instAffineAbsorbing ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $Q)) | throwError "ipure: {A} is not affine and the goal not absorbing" - return q(pure_elim_spatial (A:=$A) $pf $f) + return q(pure_elim_spatial $instIntoPure $instAffineAbsorbing $pf $f) /-- `ipure H` moves a pure hypothesis `H` from the Iris context into the regular From 9146b465cb015a2307e48502940fce613a7e4d14 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sat, 27 Jun 2026 00:09:43 +0200 Subject: [PATCH 087/108] Separate `AsEmpValid` from the rest in `InstancesInit.lean`, dependency issues --- Iris/Iris/ProofMode.lean | 2 +- Iris/Iris/ProofMode/Instances.lean | 63 ++++++++++++++------------ Iris/Iris/ProofMode/InstancesInit.lean | 44 ++++++++++++++++++ Iris/Iris/ProofMode/InstancesIris.lean | 50 -------------------- Iris/Iris/ProofMode/ProofModeM.lean | 1 + 5 files changed, 80 insertions(+), 80 deletions(-) create mode 100644 Iris/Iris/ProofMode/InstancesInit.lean delete mode 100644 Iris/Iris/ProofMode/InstancesIris.lean diff --git a/Iris/Iris/ProofMode.lean b/Iris/Iris/ProofMode.lean index 72e01698d..e71eac7c7 100644 --- a/Iris/Iris/ProofMode.lean +++ b/Iris/Iris/ProofMode.lean @@ -7,8 +7,8 @@ public meta import Iris.ProofMode.Expr public import Iris.ProofMode.Instances public import Iris.ProofMode.InstancesCmra public import Iris.ProofMode.InstancesFrame +public import Iris.ProofMode.InstancesInit public import Iris.ProofMode.InstancesInternalEq -public import Iris.ProofMode.InstancesIris public import Iris.ProofMode.InstancesLater public import Iris.ProofMode.InstancesMake public import Iris.ProofMode.InstancesPlainly diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index f15f02b33..5858a62b0 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2022 Lars König. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Lars König, Mario Carneiro, Alvin Tang +Authors: Lars König, Mario Carneiro, Michael Sammler, Alvin Tang -/ module @@ -10,40 +10,14 @@ public import Iris.ProofMode.Classes public import Iris.ProofMode.ModalityInstances public import Iris.Std.TC public import Iris.Std.RocqPorting --- public import Iris.ProofMode.Tactics +public import Iris.ProofMode.Tactics +public import Iris.ProofMode.Display @[expose] public section namespace Iris.ProofMode open Iris.BI Iris.Std --- AsEmpValid -@[rocq_alias as_emp_valid_emp_valid] -instance (priority := default + 10) asEmpValidEmpValid - [bi : BI PROP] d (P : PROP) : - AsEmpValid d (⊢ P) io1 PROP io2 bi P := ⟨by simp⟩ - -@[rocq_alias as_emp_valid_entails] -instance asEmpValid_entails [bi : BI PROP] d (P Q : PROP) -: AsEmpValid d (P ⊢ Q) io1 PROP io2 bi iprop(P -∗ Q) where - as_emp_valid := ⟨λ _ => entails_wand, λ _ => wand_entails⟩ - -instance asEmpValid_bientails [bi : BI PROP] (P Q : PROP) -: AsEmpValid d (P ⊣⊢ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where - as_emp_valid := ⟨λ _ => equiv_wandIff, λ _ => wandIff_equiv⟩ - -@[rocq_alias as_emp_valid_equiv] -instance asEmpValid_equiv [bi : BI PROP] (P Q : PROP) -: AsEmpValid d (P ≡ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where - as_emp_valid := ⟨λ _ h => equiv_wandIff (equiv_iff.1 h), λ _ h => (equiv_iff.2 (wandIff_equiv h))⟩ - -@[rocq_alias as_emp_valid_forall] -instance asEmpValid_forall {α} [bi : BI PROP] (Φ : α → Prop) (P : α → PROP) - [hP : ∀ x, AsEmpValid d (Φ x) io1 PROP io2 bi iprop(P x)] -: AsEmpValid d (∀ x, Φ x) io1 PROP io2 bi iprop(∀ x, P x) where - as_emp_valid := ⟨λ hd h => forall_intro λ x => (hP x).1.1 hd (h x), - λ hd h x => (hP x).1.2 hd $ h.trans (forall_elim x)⟩ - -- FromImp @[rocq_alias from_impl_impl] instance fromImp_imp [BI PROP] (P1 P2 : PROP) : FromImp iprop(P1 → P2) P1 P2 := ⟨.rfl⟩ @@ -940,3 +914,34 @@ instance combineSepGives_persistently [BI PROP] (Q1 Q2 P : PROP) [h : CombineSepGives Q1 Q2 P] : CombineSepGives iprop( Q1) iprop( Q2) iprop( P) where combine_sep_gives := persistently_sep_mpr.trans (persistently_mono h.combine_sep_gives) + +@[rocq_alias elim_inv_acc_without_close] +instance elimInv_acc_without_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where + elim_inv := by + intro ⟨hϕ1, _⟩ + iintro ⟨Hinv, Hin, Hcont⟩ + iapply h2.elim_acc $$ [Hcont] + · assumption + · iassumption + · iapply h1.into_acc hϕ1 $$ Hinv Hin + +@[rocq_alias elim_inv_acc_with_close] +instance elimInv_acc_with_close [BI PROP] {X : Type} + ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) + [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] + [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : + ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true + (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) + Q (fun _ => Q') where + elim_inv := by + intro ⟨hϕ1, _⟩ + have hAcc := h1.into_acc + unfold accessor at hAcc + iintro ⟨Hinv, Hin, Hcont⟩ + imod hAcc hϕ1 $$ Hinv Hin with ⟨%_, Hα, Hclose⟩ + iapply Hcont + isplitl [Hα] <;> iassumption diff --git a/Iris/Iris/ProofMode/InstancesInit.lean b/Iris/Iris/ProofMode/InstancesInit.lean new file mode 100644 index 000000000..43cb122ba --- /dev/null +++ b/Iris/Iris/ProofMode/InstancesInit.lean @@ -0,0 +1,44 @@ +/- +Copyright (c) 2022 Lars König. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Lars König, Mario Carneiro +-/ +module + +public import Iris.BI +public import Iris.ProofMode.Classes +public import Iris.ProofMode.ModalityInstances +public import Iris.Std.TC +public import Iris.Std.RocqPorting + +@[expose] public section + +namespace Iris.ProofMode +open Iris.BI Iris.Std + +-- AsEmpValid +@[rocq_alias as_emp_valid_emp_valid] +instance (priority := default + 10) asEmpValidEmpValid + [bi : BI PROP] d (P : PROP) : + AsEmpValid d (⊢ P) io1 PROP io2 bi P := ⟨by simp⟩ + +@[rocq_alias as_emp_valid_entails] +instance asEmpValid_entails [bi : BI PROP] d (P Q : PROP) +: AsEmpValid d (P ⊢ Q) io1 PROP io2 bi iprop(P -∗ Q) where + as_emp_valid := ⟨λ _ => entails_wand, λ _ => wand_entails⟩ + +instance asEmpValid_bientails [bi : BI PROP] (P Q : PROP) +: AsEmpValid d (P ⊣⊢ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where + as_emp_valid := ⟨λ _ => equiv_wandIff, λ _ => wandIff_equiv⟩ + +@[rocq_alias as_emp_valid_equiv] +instance asEmpValid_equiv [bi : BI PROP] (P Q : PROP) +: AsEmpValid d (P ≡ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where + as_emp_valid := ⟨λ _ h => equiv_wandIff (equiv_iff.1 h), λ _ h => (equiv_iff.2 (wandIff_equiv h))⟩ + +@[rocq_alias as_emp_valid_forall] +instance asEmpValid_forall {α} [bi : BI PROP] (Φ : α → Prop) (P : α → PROP) + [hP : ∀ x, AsEmpValid d (Φ x) io1 PROP io2 bi iprop(P x)] +: AsEmpValid d (∀ x, Φ x) io1 PROP io2 bi iprop(∀ x, P x) where + as_emp_valid := ⟨λ hd h => forall_intro λ x => (hP x).1.1 hd (h x), + λ hd h x => (hP x).1.2 hd $ h.trans (forall_elim x)⟩ diff --git a/Iris/Iris/ProofMode/InstancesIris.lean b/Iris/Iris/ProofMode/InstancesIris.lean deleted file mode 100644 index 72321082c..000000000 --- a/Iris/Iris/ProofMode/InstancesIris.lean +++ /dev/null @@ -1,50 +0,0 @@ -/- -Copyright (c) 2026 Alvin Tang. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Michael Sammler, Alvin Tang --/ -module - -public import Iris.BI -public import Iris.ProofMode.Classes -public import Iris.ProofMode.ModalityInstances -public import Iris.Std.TC -public import Iris.Std.RocqPorting -public import Iris.ProofMode.Tactics -public import Iris.ProofMode.Display - -@[expose] public section - -namespace Iris.ProofMode -open Iris.BI Iris.Std - -@[rocq_alias elim_inv_acc_without_close] -instance elimInv_acc_without_close [BI PROP] {X : Type} - ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : X → PROP) - [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ElimAcc ϕ2 M1 M2 α β mγ Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α false none Q Q' where - elim_inv := by - intro ⟨hϕ1, _⟩ - iintro ⟨Hinv, Hin, Hcont⟩ - iapply h2.elim_acc $$ [Hcont] - · assumption - · iassumption - · iapply h1.into_acc hϕ1 $$ Hinv Hin - -@[rocq_alias elim_inv_acc_with_close] -instance elimInv_acc_with_close [BI PROP] {X : Type} - ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) - [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : - ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true - (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) - Q (fun _ => Q') where - elim_inv := by - intro ⟨hϕ1, _⟩ - have hAcc := h1.into_acc - unfold accessor at hAcc - iintro ⟨Hinv, Hin, Hcont⟩ - imod hAcc hϕ1 $$ Hinv Hin with ⟨%_, Hα, Hclose⟩ - iapply Hcont - isplitl [Hα] <;> iassumption diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index 9b254ada5..b2c0a7b74 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -7,6 +7,7 @@ module public meta import Iris.ProofMode.Expr public import Iris.ProofMode.Classes +public import Iris.ProofMode.InstancesInit public meta section From d697447df928df7264467b9a0e0d0a84be08ec42 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sat, 27 Jun 2026 14:23:58 +0200 Subject: [PATCH 088/108] Define `pmReduce` so that there is no need for `addBIGoalRunTactic` or `addBIGoalSimpWandM` Call `simp` directly is too strong. Instead `pmReduce` simplifies wandM, Option.getD, etc. in specific propositions before `iCasesCore` call --- Iris/Iris/ProofMode/ProofModeM.lean | 14 ------------ Iris/Iris/ProofMode/Tactics/Inv.lean | 32 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index b2c0a7b74..b40ed8faf 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -97,20 +97,6 @@ def addMVarGoal (m : MVarId) (name : Name := .anonymous) : ProofModeM Unit := do m.setUserName name modify ({goals := ·.goals.push m}) -/-- - Create a new proof goal with the hypotheses `hyps` and the conclusion `goal`, - run a tactic (`tac`) and add all resultant subgoals into the proof state. - If the tactic fails, add the proof goal directly. --/ -def addBIGoalRunTactic {u} {prop : Q(Type u)} {bi} {e} - (hyps : Hyps bi e) (goal : Q($prop)) (tac : ProofModeM <| TSyntax `tactic) : - ProofModeM Q($e ⊢ $goal) := do - let mvar ← mkBIGoal hyps goal - let gs ← (observing? <| evalTacticAt (← tac) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) - if !gs.isEmpty then - for g in gs do addMVarGoal g - return mvar - /-- Try to synthesize a typeclass instance, adding any created metavariables as proof mode goals. -/ def ProofModeM.trySynthInstanceQ (α : Q(Sort v)) : ProofModeM (Option Q($α)) := do let LOption.some (e, mvars) ← ProofMode.trySynthInstance α | return none diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index bce7dc77c..404d4924c 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -50,23 +50,30 @@ theorem tac_inv_elim [BI PROP] _ ⊢ goal := h0 /-- - This is useful as `wandM` (`-∗?`) is not simplified automatically without - explicitly using `simp`, even when it is annotated with `@[reducible]`. + An annotation of `wandM` with `@[reducible]` is useful when `whnf` is called, + but `whnf` is not strong enough to simplify occurrences of `wandM` in the + proof goal. This funnction is similar to `pm_reduce` in the Rocq version, + which forces the reduction of `wandM` (`-∗?`), occurrences of `Option.getD`, + occurrences of `Option.map` and pattern matching (`match … with …`). -/ -private def addBIGoalSimpWandM {u} {prop : Q(Type u)} {bi} {e} - (hyps : Hyps bi e) (goal : Q($prop)) : ProofModeM Q($e ⊢ $goal) := - addBIGoalRunTactic hyps goal `(tactic | simp_all only [BIBase.wandM]) +def pmReduce (e : Expr) : ProofModeM Expr := do + let mut thms : SimpTheorems := {} + for n in #[``BIBase.wandM, ``Option.getD, ``Option.map] do + thms ← thms.addDeclToUnfold n + let ctx ← Simp.mkContext { beta := true, iota := true, proj := true, zeta := false } + #[thms] (← getSimpCongrTheorems) + return (← Lean.Meta.dsimp e ctx).1 private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) (casesPat : iCasesPat) (closePat : Option iCasesPat) : ProofModeM Q($e ⊢ $goal) := do -- Find the hypothesis from the context - let ⟨_, hyps', _, Pinv, _, _ , pfEq⟩ := hyps.remove false ivar + let ⟨_, hyps', _, Pinv, _, _, pfEq⟩ := hyps.remove false ivar let ϕ ← mkFreshExprMVarQ q(Prop) let Pin : Q($prop) ← mkFreshExprMVarQ q($prop) - let X ← mkFreshExprMVarQ q(Type) + let X : Q(Type) ← mkFreshExprMVarQ q(Type) let Pout ← mkFreshExprMVarQ q($X → $prop) -- Decide whether to use `elimInv_acc_with_close` or `elimInv_acc_without_close` let close := if closePat.isSome then q(true) else q(false) @@ -83,15 +90,20 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} -- Solve side conditions automatically if possible, otherwise add them into the proof state let hϕ ← iSolveSidecondition q($ϕ) false + -- Simplify occurrences of `wandM`, `Option.getD`, pattern matching, etc. + let Pout' : Q($X → $prop) ← pmReduce Pout + let Q'' : Q($X → $prop) ← pmReduce Q' + -- Add the new goal into the proof state upon applying the case destruction patterns match mPclose with | ~q(some $f) => + let f' : Q($X → $prop) ← pmReduce f let pf : Q(∀ x, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with | some closePat => - iCasesCore _ hyps'' q($Q' $x) (.conjunction [casesPat, closePat]) - q(false) q(iprop($Pout $x ∗ $f $x)) addBIGoalSimpWandM >>= + iCasesCore _ hyps'' q($Q'' $x) (.conjunction [casesPat, closePat]) + q(false) q(iprop($Pout' $x ∗ $f' $x)) >>= (mkLambdaFVars #[x] ·) -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: missing cases pattern for the closing hypothesis" @@ -99,7 +111,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} | ~q(none) => let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do - iCasesCore _ hyps'' q($Q' $x) casesPat q(false) q($Pout $x) addBIGoalSimpWandM >>= + iCasesCore _ hyps'' q($Q'' $x) casesPat q(false) q($Pout' $x) >>= (mkLambdaFVars #[x] ·) return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) From 7146caf102588e9cb8a2030bcedc7ecdc72d7a54 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sat, 27 Jun 2026 14:37:59 +0200 Subject: [PATCH 089/108] Simplify type class instance definitions with the use of `Option.getD` --- Iris/Iris/Instances/Lib/CInvariants.lean | 2 +- Iris/Iris/Instances/Lib/Invariants.lean | 2 +- Iris/Iris/Instances/Lib/NaInvariants.lean | 2 +- Iris/Iris/ProgramLogic/WeakestPre.lean | 4 ++-- Iris/Iris/ProofMode/Classes.lean | 6 +++--- Iris/Iris/ProofMode/Instances.lean | 2 +- Iris/Iris/Tests/Tactics.lean | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index 6baf09fcb..61cf15f5c 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -299,7 +299,7 @@ instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) ( IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where into_acc := by - simp only [accessor] + simp only [accessor, Option.getD] iintro %x #Hinv Hown imod acc x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ imodintro diff --git a/Iris/Iris/Instances/Lib/Invariants.lean b/Iris/Iris/Instances/Lib/Invariants.lean index 164e1477f..98cd71878 100644 --- a/Iris/Iris/Instances/Lib/Invariants.lean +++ b/Iris/Iris/Instances/Lib/Invariants.lean @@ -96,7 +96,7 @@ instance intoAcc_inv (N : Namespace) (P : IProp GF) E : IntoAcc (X := Unit) (inv N P) (↑N ⊆ E) iprop(True) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (λ _ => iprop(▷ P)) (λ _ => iprop(▷ P)) (λ _ => none) where into_acc := by - simp only [inv, accessor] + simp only [inv, accessor, Option.getD] iintro %x #Hinv - imod Hinv $$ %E [] with ⟨HP, Hclose⟩ · itrivial diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index 2772661e6..dc1fd5bf7 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -232,7 +232,7 @@ instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IPro (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (λ _ => some (own p F)) where into_acc := by - simp only [accessor] + simp only [accessor, Option.getD] intro ⟨hE, hF⟩ iintro #Hinv Hown imod inv_acc hE hF $$ Hinv Hown with ⟨_, Hown, _⟩ diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 9ad4c55ce..43c776025 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -688,7 +688,7 @@ instance (priority := low) elimAcc_wp_atomic {X} (E₁ E₂ : CoPset) α β (γ (WP e @ s ; E₁ {{ Φ }}) (fun x => WP e @ s ; E₂ {{ v, |={E₂}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by - simp only [accessor, BIBase.wandM] + simp only [accessor, BIBase.wandM, Option.getD] iintro %atomic Hinner >⟨%x, Hα, Hclose⟩ iapply wp_wand $$ [Hinner Hα] · iapply Hinner $$ Hα @@ -705,7 +705,7 @@ instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option ElimAcc True (fupd E E) (fupd E E) α β γ (WP e @ s ; E {{ Φ }}) (fun x => WP e @ s ; E {{ v, |={E}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by - simp only [accessor, BIBase.wandM] + simp only [accessor, BIBase.wandM, Option.getD] iintro %_ Hinner >⟨%x, Hα, Hclose⟩ iapply wp_fupd iapply wp_wand $$ [Hinner Hα] diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 104c253c4..3e1fea0a9 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -232,7 +232,7 @@ class IntoInv [BI PROP] (P : PROP) (N : Namespace) @[rocq_alias accessor] def accessor [BI PROP] {X : Type} (M1 M2 : PROP → PROP) (α β : X → PROP) (mγ : X → Option PROP) : PROP := - M1 iprop(∃ x, α x ∗ (β x -∗ M2 (match mγ x with | none => emp | some p => p))) + M1 iprop(∃ x, α x ∗ (β x -∗ M2 (mγ x |>.getD emp))) @[ipm_class, rocq_alias ElimAcc] class ElimAcc [BI PROP] {X : Type} (ϕ : outParam Prop) (M1 M2 : PROP → PROP) @@ -254,8 +254,8 @@ class ElimInv [BI PROP] (φ : outParam Prop) (X : outParam Type) (close : Bool) (mPclose : outParam <| Option <| X → PROP) (Q : PROP) (Q' : outParam <| X → PROP) where elim_inv : φ → Pinv ∗ Pin ∗ (∀ x, (match mPclose with - | none => iprop(Pout x -∗ Q' x) - | some Pclose => iprop(Pout x ∗ Pclose x -∗ Q' x))) ⊢ Q + | some Pclose => iprop(Pout x ∗ Pclose x -∗ Q' x) + | none => iprop(Pout x -∗ Q' x))) ⊢ Q export ElimInv (elim_inv) #rocq_ignore elim_inv_tc_opaque "No tc_opaque in Lean" diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 5858a62b0..82b839e18 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -935,7 +935,7 @@ instance elimInv_acc_with_close [BI PROP] {X : Type} [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true - (some (fun x => iprop(β x -∗ M2 (match mγ x with | none => emp | some m => m)))) + (some (fun x => iprop(β x -∗ M2 (mγ x |>.getD emp)))) Q (fun _ => Q') where elim_inv := by intro ⟨hϕ1, _⟩ diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index 8765df6cd..26ec0b84a 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -2876,8 +2876,8 @@ example {t : NaInvPoolName} [NaInvG GF] {E1 E2 : CoPset} {P : IProp GF} (h : ↑ NonAtomicInvariant.inv t N iprop( P) ∗ own t E1 ∗ own t E2 ={⊤}=∗ own t E1 ∗ own t E2 ∗ ▷ P := by iintro ⟨#Hinv, Hown1, Hown2⟩ - iinv Hinv $$ [Hown1 //] with ⟨#HP, Hown2⟩ Hclose - imod Hclose $$ [HP Hown2] + iinv Hinv $$ [Hown1 //] with ⟨#HP, Hown1⟩ Hclose + imod Hclose $$ [HP Hown1] · iframe iexact HP · iframe From 555e3be2b4bfb3bb007f131d5665e53922a4fab8 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sat, 27 Jun 2026 14:40:36 +0200 Subject: [PATCH 090/108] Replace some `simp` usage with `dsimp` --- Iris/Iris/Instances/Lib/CInvariants.lean | 2 +- Iris/Iris/Instances/Lib/Invariants.lean | 2 +- Iris/Iris/Instances/Lib/NaInvariants.lean | 2 +- Iris/Iris/ProgramLogic/WeakestPre.lean | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Iris/Iris/Instances/Lib/CInvariants.lean b/Iris/Iris/Instances/Lib/CInvariants.lean index 61cf15f5c..547d7dea1 100644 --- a/Iris/Iris/Instances/Lib/CInvariants.lean +++ b/Iris/Iris/Instances/Lib/CInvariants.lean @@ -299,7 +299,7 @@ instance intoAcc_cinv (E : CoPset) (N : Namespace) (γ : GName) (P : IProp GF) ( IntoAcc (X := Unit) (cinv N γ P) (↑N ⊆ E) (own γ p) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (fun _ => iprop(▷ P ∗ own γ p)) (fun _ => iprop(▷ P)) (λ _ => none) where into_acc := by - simp only [accessor, Option.getD] + dsimp only [accessor, Option.getD] iintro %x #Hinv Hown imod acc x $$ Hinv Hown with ⟨HP, Hγ, Hcl⟩ imodintro diff --git a/Iris/Iris/Instances/Lib/Invariants.lean b/Iris/Iris/Instances/Lib/Invariants.lean index 98cd71878..ebdecdf5b 100644 --- a/Iris/Iris/Instances/Lib/Invariants.lean +++ b/Iris/Iris/Instances/Lib/Invariants.lean @@ -96,7 +96,7 @@ instance intoAcc_inv (N : Namespace) (P : IProp GF) E : IntoAcc (X := Unit) (inv N P) (↑N ⊆ E) iprop(True) (fupd E (E \ ↑N)) (fupd (E \ ↑N) E) (λ _ => iprop(▷ P)) (λ _ => iprop(▷ P)) (λ _ => none) where into_acc := by - simp only [inv, accessor, Option.getD] + dsimp only [inv, accessor, Option.getD] iintro %x #Hinv - imod Hinv $$ %E [] with ⟨HP, Hclose⟩ · itrivial diff --git a/Iris/Iris/Instances/Lib/NaInvariants.lean b/Iris/Iris/Instances/Lib/NaInvariants.lean index dc1fd5bf7..c58eec68d 100644 --- a/Iris/Iris/Instances/Lib/NaInvariants.lean +++ b/Iris/Iris/Instances/Lib/NaInvariants.lean @@ -232,7 +232,7 @@ instance intoAcc_na (p : NaInvPoolName) (E F : CoPset) (N : Namespace) (P : IPro (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (fun _ => iprop(▷ P ∗ own p (F \ ↑N))) (λ _ => some (own p F)) where into_acc := by - simp only [accessor, Option.getD] + dsimp only [accessor, Option.getD] intro ⟨hE, hF⟩ iintro #Hinv Hown imod inv_acc hE hF $$ Hinv Hown with ⟨_, Hown, _⟩ diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index 43c776025..36fe1f326 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -688,7 +688,7 @@ instance (priority := low) elimAcc_wp_atomic {X} (E₁ E₂ : CoPset) α β (γ (WP e @ s ; E₁ {{ Φ }}) (fun x => WP e @ s ; E₂ {{ v, |={E₂}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by - simp only [accessor, BIBase.wandM, Option.getD] + dsimp only [accessor, BIBase.wandM, Option.getD] iintro %atomic Hinner >⟨%x, Hα, Hclose⟩ iapply wp_wand $$ [Hinner Hα] · iapply Hinner $$ Hα @@ -705,7 +705,7 @@ instance elimAcc_wp_nonatomic {X} E (α β : X → IProp GF) (γ : X → Option ElimAcc True (fupd E E) (fupd E E) α β γ (WP e @ s ; E {{ Φ }}) (fun x => WP e @ s ; E {{ v, |={E}=> β x ∗ (γ x -∗? Φ v) }}) where elim_acc := by - simp only [accessor, BIBase.wandM, Option.getD] + dsimp only [accessor, BIBase.wandM, Option.getD] iintro %_ Hinner >⟨%x, Hα, Hclose⟩ iapply wp_fupd iapply wp_wand $$ [Hinner Hα] From a38f0285bf2c450278093fc3e21f705d4afa347b Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Sat, 27 Jun 2026 14:53:01 +0200 Subject: [PATCH 091/108] Condense `pmReduce` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 404d4924c..36b939349 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -53,16 +53,13 @@ theorem tac_inv_elim [BI PROP] An annotation of `wandM` with `@[reducible]` is useful when `whnf` is called, but `whnf` is not strong enough to simplify occurrences of `wandM` in the proof goal. This funnction is similar to `pm_reduce` in the Rocq version, - which forces the reduction of `wandM` (`-∗?`), occurrences of `Option.getD`, - occurrences of `Option.map` and pattern matching (`match … with …`). + which forces the reduction of `wandM` (`-∗?`), occurrences of `Option.getD` + and pattern matching (`match … with …`). -/ def pmReduce (e : Expr) : ProofModeM Expr := do - let mut thms : SimpTheorems := {} - for n in #[``BIBase.wandM, ``Option.getD, ``Option.map] do - thms ← thms.addDeclToUnfold n - let ctx ← Simp.mkContext { beta := true, iota := true, proj := true, zeta := false } - #[thms] (← getSimpCongrTheorems) - return (← Lean.Meta.dsimp e ctx).1 + #[``BIBase.wandM, ``Option.getD].foldlM (init := {}) (·.addDeclToUnfold ·) >>= + (Simp.mkContext { beta := true, iota := true, proj := true } #[·] (← getSimpCongrTheorems) >>= + (Lean.Meta.dsimp e · <&> Prod.fst)) private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) From 2db2ae8fee140a8da5f265411d3c17369da76770 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 1 Jul 2026 14:39:50 +0200 Subject: [PATCH 092/108] Simplify `pmReduce` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 36b939349..4fbbf3a7f 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -57,9 +57,8 @@ theorem tac_inv_elim [BI PROP] and pattern matching (`match … with …`). -/ def pmReduce (e : Expr) : ProofModeM Expr := do - #[``BIBase.wandM, ``Option.getD].foldlM (init := {}) (·.addDeclToUnfold ·) >>= - (Simp.mkContext { beta := true, iota := true, proj := true } #[·] (← getSimpCongrTheorems) >>= - (Lean.Meta.dsimp e · <&> Prod.fst)) + #[``BIBase.wandM, ``Option.getD].foldlM (·.addDeclToUnfold ·) {} >>= + (Simp.mkContext {} #[·] (← getSimpCongrTheorems) >>= (Lean.Meta.dsimp e · <&> Prod.fst)) private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) From 0592d7da48c058398cfb02d34a84283e42be7346 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Fri, 3 Jul 2026 10:08:10 +0200 Subject: [PATCH 093/108] Code refactoring: moving theorem to `public section` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 4fbbf3a7f..c6aff333b 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -15,8 +15,8 @@ public meta import Iris.ProofMode.ClassesMake namespace Iris.ProofMode -public meta section -open Lean Elab Tactic Meta Qq BI Std +public section +open BI @[rocq_alias tac_inv_elim] theorem tac_inv_elim [BI PROP] @@ -49,6 +49,9 @@ theorem tac_inv_elim [BI PROP] _ ⊢ _ := sep_mono_right <| sep_mono_right <| forall_intro (wand_intro <| pf ·) _ ⊢ goal := h0 +public meta section +open Lean Elab Tactic Meta Qq BI Std + /-- An annotation of `wandM` with `@[reducible]` is useful when `whnf` is called, but `whnf` is not strong enough to simplify occurrences of `wandM` in the From 526d27c9a5d7f9033a276d3d325c3c1e2c9d342d Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Tue, 14 Jul 2026 09:09:10 +0200 Subject: [PATCH 094/108] No need for separate file `InstancesInit.lean` --- Iris/Iris/ProofMode.lean | 1 - Iris/Iris/ProofMode/Instances.lean | 27 ++++++++++++++++ Iris/Iris/ProofMode/InstancesInit.lean | 44 -------------------------- Iris/Iris/ProofMode/ProofModeM.lean | 1 - 4 files changed, 27 insertions(+), 46 deletions(-) delete mode 100644 Iris/Iris/ProofMode/InstancesInit.lean diff --git a/Iris/Iris/ProofMode.lean b/Iris/Iris/ProofMode.lean index e71eac7c7..399fffce4 100644 --- a/Iris/Iris/ProofMode.lean +++ b/Iris/Iris/ProofMode.lean @@ -7,7 +7,6 @@ public meta import Iris.ProofMode.Expr public import Iris.ProofMode.Instances public import Iris.ProofMode.InstancesCmra public import Iris.ProofMode.InstancesFrame -public import Iris.ProofMode.InstancesInit public import Iris.ProofMode.InstancesInternalEq public import Iris.ProofMode.InstancesLater public import Iris.ProofMode.InstancesMake diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 82b839e18..4063f46ad 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -18,6 +18,33 @@ public import Iris.ProofMode.Display namespace Iris.ProofMode open Iris.BI Iris.Std +-- AsEmpValid +@[rocq_alias as_emp_valid_emp_valid] +instance (priority := default + 10) asEmpValidEmpValid + [bi : BI PROP] d (P : PROP) : + AsEmpValid d (⊢ P) io1 PROP io2 bi P := ⟨by simp⟩ + +@[rocq_alias as_emp_valid_entails] +instance asEmpValid_entails [bi : BI PROP] d (P Q : PROP) +: AsEmpValid d (P ⊢ Q) io1 PROP io2 bi iprop(P -∗ Q) where + as_emp_valid := ⟨λ _ => entails_wand, λ _ => wand_entails⟩ + +instance asEmpValid_bientails [bi : BI PROP] (P Q : PROP) +: AsEmpValid d (P ⊣⊢ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where + as_emp_valid := ⟨λ _ => equiv_wandIff, λ _ => wandIff_equiv⟩ + +@[rocq_alias as_emp_valid_equiv] +instance asEmpValid_equiv [bi : BI PROP] (P Q : PROP) +: AsEmpValid d (P ≡ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where + as_emp_valid := ⟨λ _ h => equiv_wandIff (equiv_iff.1 h), λ _ h => (equiv_iff.2 (wandIff_equiv h))⟩ + +@[rocq_alias as_emp_valid_forall] +instance asEmpValid_forall {α} [bi : BI PROP] (Φ : α → Prop) (P : α → PROP) + [hP : ∀ x, AsEmpValid d (Φ x) io1 PROP io2 bi iprop(P x)] +: AsEmpValid d (∀ x, Φ x) io1 PROP io2 bi iprop(∀ x, P x) where + as_emp_valid := ⟨λ hd h => forall_intro λ x => (hP x).1.1 hd (h x), + λ hd h x => (hP x).1.2 hd $ h.trans (forall_elim x)⟩ + -- FromImp @[rocq_alias from_impl_impl] instance fromImp_imp [BI PROP] (P1 P2 : PROP) : FromImp iprop(P1 → P2) P1 P2 := ⟨.rfl⟩ diff --git a/Iris/Iris/ProofMode/InstancesInit.lean b/Iris/Iris/ProofMode/InstancesInit.lean deleted file mode 100644 index 43cb122ba..000000000 --- a/Iris/Iris/ProofMode/InstancesInit.lean +++ /dev/null @@ -1,44 +0,0 @@ -/- -Copyright (c) 2022 Lars König. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Lars König, Mario Carneiro --/ -module - -public import Iris.BI -public import Iris.ProofMode.Classes -public import Iris.ProofMode.ModalityInstances -public import Iris.Std.TC -public import Iris.Std.RocqPorting - -@[expose] public section - -namespace Iris.ProofMode -open Iris.BI Iris.Std - --- AsEmpValid -@[rocq_alias as_emp_valid_emp_valid] -instance (priority := default + 10) asEmpValidEmpValid - [bi : BI PROP] d (P : PROP) : - AsEmpValid d (⊢ P) io1 PROP io2 bi P := ⟨by simp⟩ - -@[rocq_alias as_emp_valid_entails] -instance asEmpValid_entails [bi : BI PROP] d (P Q : PROP) -: AsEmpValid d (P ⊢ Q) io1 PROP io2 bi iprop(P -∗ Q) where - as_emp_valid := ⟨λ _ => entails_wand, λ _ => wand_entails⟩ - -instance asEmpValid_bientails [bi : BI PROP] (P Q : PROP) -: AsEmpValid d (P ⊣⊢ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where - as_emp_valid := ⟨λ _ => equiv_wandIff, λ _ => wandIff_equiv⟩ - -@[rocq_alias as_emp_valid_equiv] -instance asEmpValid_equiv [bi : BI PROP] (P Q : PROP) -: AsEmpValid d (P ≡ Q) io1 PROP io2 bi iprop(P ∗-∗ Q) where - as_emp_valid := ⟨λ _ h => equiv_wandIff (equiv_iff.1 h), λ _ h => (equiv_iff.2 (wandIff_equiv h))⟩ - -@[rocq_alias as_emp_valid_forall] -instance asEmpValid_forall {α} [bi : BI PROP] (Φ : α → Prop) (P : α → PROP) - [hP : ∀ x, AsEmpValid d (Φ x) io1 PROP io2 bi iprop(P x)] -: AsEmpValid d (∀ x, Φ x) io1 PROP io2 bi iprop(∀ x, P x) where - as_emp_valid := ⟨λ hd h => forall_intro λ x => (hP x).1.1 hd (h x), - λ hd h x => (hP x).1.2 hd $ h.trans (forall_elim x)⟩ diff --git a/Iris/Iris/ProofMode/ProofModeM.lean b/Iris/Iris/ProofMode/ProofModeM.lean index b40ed8faf..f20e854f6 100644 --- a/Iris/Iris/ProofMode/ProofModeM.lean +++ b/Iris/Iris/ProofMode/ProofModeM.lean @@ -7,7 +7,6 @@ module public meta import Iris.ProofMode.Expr public import Iris.ProofMode.Classes -public import Iris.ProofMode.InstancesInit public meta section From e73137b8d199f3b9d6e932168d05d6a9dccfabf8 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:23:12 +0200 Subject: [PATCH 095/108] Function renaming `pmReduce` -> `reduceWandM`, set as private --- Iris/Iris/ProofMode/Tactics/Inv.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index c6aff333b..0fb7e932f 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -59,7 +59,7 @@ open Lean Elab Tactic Meta Qq BI Std which forces the reduction of `wandM` (`-∗?`), occurrences of `Option.getD` and pattern matching (`match … with …`). -/ -def pmReduce (e : Expr) : ProofModeM Expr := do +private def reduceWandM (e : Expr) : ProofModeM Expr := do #[``BIBase.wandM, ``Option.getD].foldlM (·.addDeclToUnfold ·) {} >>= (Simp.mkContext {} #[·] (← getSimpCongrTheorems) >>= (Lean.Meta.dsimp e · <&> Prod.fst)) @@ -90,13 +90,13 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} let hϕ ← iSolveSidecondition q($ϕ) false -- Simplify occurrences of `wandM`, `Option.getD`, pattern matching, etc. - let Pout' : Q($X → $prop) ← pmReduce Pout - let Q'' : Q($X → $prop) ← pmReduce Q' + let Pout' : Q($X → $prop) ← reduceWandM Pout + let Q'' : Q($X → $prop) ← reduceWandM Q' -- Add the new goal into the proof state upon applying the case destruction patterns match mPclose with | ~q(some $f) => - let f' : Q($X → $prop) ← pmReduce f + let f' : Q($X → $prop) ← reduceWandM f let pf : Q(∀ x, $e'' ∗ $Pout x ∗ $f x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with From caacc5408446ea8425cfd060d85b0f9879edb90f Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:24:02 +0200 Subject: [PATCH 096/108] `iSolveSidecondition`: use `simp [*]` instead --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 1f5f078c5..959f84595 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -30,7 +30,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM throwError "{msg}" | _ => let gs ← (observing? <| - evalTacticAt (← `(tactic | and_intros <;> first | trivial | simp_all)) mvar.mvarId!) <&> + evalTacticAt (← `(tactic | simp [*])) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then From 4fc2ef853e304e74d3ae84b68d24e2fe75f80700 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:25:42 +0200 Subject: [PATCH 097/108] Remove misleading description of the tactic --- Iris/Iris/ProofMode/Tactics/Inv.lean | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 0fb7e932f..d6d87dac8 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -130,14 +130,12 @@ syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? /-- `iinv H with casesPat` opens an invariant hypothesis `H` and uses the cases pattern `casesPat` to destruct the result without any additional - hypothesis for closing the invariant. The type class - `elimInv_acc_without_close` is used with this tactic. + hypothesis for closing the invariant. `iinv H with casesPat closePat` opens an invariant hypothesis `H`, uses the cases pattern `casesPat` to destruct the result and generates a hypothesis for closing the invariant, which is destructed by the cases - pattern `closePat`. The type class `elimInv_acc_with_close` is used with - this tactic. + pattern `closePat`. Furthermore, the following syntax is available. - `iinv N with casesPat`: similar to `iinv H with casesPat`, where From 42a2ad7a5251deeb8836cb62ded6974fddb5e45a Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:46:43 +0200 Subject: [PATCH 098/108] Typo fix --- Iris/Iris/Tests/Tactics.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/Tests/Tactics.lean b/Iris/Iris/Tests/Tactics.lean index f31788c9e..b40d2ef45 100644 --- a/Iris/Iris/Tests/Tactics.lean +++ b/Iris/Iris/Tests/Tactics.lean @@ -3555,4 +3555,4 @@ example [BI PROP] {P : PROP} {m n : Nat} {T : Nat → Prop} | zero | succ n IH => itrivial -end iInduction +end iinduction From b90c8b6de0207e0046e94e5953068e39b05aa5df Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:46:48 +0200 Subject: [PATCH 099/108] Implement `Hyps.findM?`, use it for `iinv` --- Iris/Iris/ProofMode/Expr.lean | 14 ++++++++++++++ Iris/Iris/ProofMode/Tactics/Inv.lean | 17 ++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Iris/Iris/ProofMode/Expr.lean b/Iris/Iris/ProofMode/Expr.lean index 8e0293403..bea4de162 100644 --- a/Iris/Iris/ProofMode/Expr.lean +++ b/Iris/Iris/ProofMode/Expr.lean @@ -184,6 +184,20 @@ partial def Hyps.find? {u prop bi} (name : Name) : | _, .hyp _ name' ivar _ ty _ => if name == name' then (ivar, ty) else none | _, .sep _ _ _ _ lhs rhs => rhs.find? name <|> lhs.find? name +partial def Hyps.findM? [Monad m] {prop : Q(Type u)} {bi : Q(BI $prop)} + (p : Name → IVarId → Q(Bool) → Q($prop) → m Bool) : + ∀ {e}, Hyps bi e → m (Option (Name × IVarId × Q(Bool) × Q($prop))) + | _, .emp _ => return none + | _, .hyp _ name ivar bp ty _ => do + if ← p name ivar bp ty then + return some (name, ivar, bp, ty) + else + return none + | _, .sep _ _ _ _ lhs rhs => do + match ← rhs.findM? p with + | some res => return some res + | none => lhs.findM? p + partial def Hyps.getDecl? {u prop bi} (ivar : IVarId) {s}: @Hyps u prop bi s → Option (Name × IVarId × Q(Bool) × Q($prop)) | .emp _ => none diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index d6d87dac8..cec718e8b 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -114,16 +114,6 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (mkLambdaFVars #[x] ·) return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) -/-- Given a `Namespace` value, find a corresponding invariant hypothesis. -/ -private def findInvariantWithNamespace {u} {prop : Q(Type u)} {bi : Q(BI $prop)} {e} - (N : Q(Namespace)) (hyps : Hyps bi e) : ProofModeM <| Option IVarId := do - match hyps with - | .emp _ => return none - | .hyp _ _ ivar _ ty _ => - return (← ProofModeM.trySynthInstanceQ q(IntoInv $ty $N)) <&> (fun _ => ivar) - | .sep _ _ _ _ lhs rhs => - return (← findInvariantWithNamespace N rhs) <|> (← findInvariantWithNamespace N lhs) - syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)? " with " colGt icasesPat (colGt icasesPat)? : tactic @@ -160,9 +150,10 @@ elab_rules : tactic -- Namespace supplied by the user: use `IntoInv` to find the corresponding hypothesis | none => let N ← elabTermEnsuringTypeQ t q(Namespace) - match ← findInvariantWithNamespace N hyps with - | some ivar => pure ivar - | none => throwError m!"iinv: invariant hypothesis with the namespace {N} not found" + let some (_, ivar, _, _) ← hyps.findM? fun _ _ _ ty => + return (← ProofModeM.trySynthInstanceQ q(IntoInv $ty $N)).isSome + | throwError m!"iinv: invariant hypothesis with the namespace {N} not found" + pure ivar let pf ← iInvCore hyps goal ivar specPat casesPat closePat mvar.assign pf From 54596a445e3d7144df7b57904e02aac38d6cd0da Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:51:52 +0200 Subject: [PATCH 100/108] Revert "`iSolveSidecondition`: use `simp [*]` instead" This changes seems to be breaking wp tactics This reverts commit caacc5408446ea8425cfd060d85b0f9879edb90f. --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 959f84595..1f5f078c5 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -30,7 +30,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM throwError "{msg}" | _ => let gs ← (observing? <| - evalTacticAt (← `(tactic | simp [*])) mvar.mvarId!) <&> + evalTacticAt (← `(tactic | and_intros <;> first | trivial | simp_all)) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then From 5578728ae61407a129e35805241a9e75ea652b74 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:24:02 +0200 Subject: [PATCH 101/108] `iSolveSidecondition`: use `simp [*]` instead --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 1f5f078c5..959f84595 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -30,7 +30,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM throwError "{msg}" | _ => let gs ← (observing? <| - evalTacticAt (← `(tactic | and_intros <;> first | trivial | simp_all)) mvar.mvarId!) <&> + evalTacticAt (← `(tactic | simp [*])) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then From 88e343685a1f2765071c0df0ca04d351ccaccf92 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Wed, 22 Jul 2026 17:57:24 +0200 Subject: [PATCH 102/108] Use `first | trivial | simp [*]` instead of `simp [*]` --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index 959f84595..ff503afc3 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -30,7 +30,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM throwError "{msg}" | _ => let gs ← (observing? <| - evalTacticAt (← `(tactic | simp [*])) mvar.mvarId!) <&> + evalTacticAt (← `(tactic | first | trivial | simp [*])) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then From fb3b855b0b3ecbc70bae001ed2e5b9906c632392 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 16:59:26 +0200 Subject: [PATCH 103/108] Make type class premises implicit --- Iris/Iris/ProofMode/Tactics/Cases.lean | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Cases.lean b/Iris/Iris/ProofMode/Tactics/Cases.lean index b6eaf99b5..1ded13245 100644 --- a/Iris/Iris/ProofMode/Tactics/Cases.lean +++ b/Iris/Iris/ProofMode/Tactics/Cases.lean @@ -47,14 +47,15 @@ theorem or_elim' [BI PROP] {p} {P A Q A1 A2 : PROP} [inst : IntoOr A A1 A2] (h1 : P ∗ □?p A1 ⊢ Q) (h2 : P ∗ □?p A2 ⊢ Q) : P ∗ □?p A ⊢ Q := (sep_mono_right <| (intuitionisticallyIf_mono inst.1).trans (intuitionisticallyIf_or _).1).trans <| BI.sep_or_left.1.trans <| or_elim h1 h2 -theorem intuitionistic_elim_spatial [BI PROP] {A A' Q : PROP} - (instPers: IntoPersistently false A A') (instAffineAbsorbing: TCOr (Affine A) (Absorbing Q)) +theorem intuitionistic_elim_spatial [BI PROP] {A A' P Q : PROP} + [IntoPersistently false A A'] [TCOr (Affine A) (Absorbing Q)] (h : P ∗ □ A' ⊢ Q) : P ∗ A ⊢ Q := (replaces_r to_persistent_spatial).apply h -theorem intuitionistic_elim_intuitionistic [BI PROP] {A A' Q : PROP} (inst : IntoPersistently true A A') - (h : P ∗ □ A' ⊢ Q) : P ∗ □ A ⊢ Q := - let instAffine := @TCOr.l (Affine iprop(□ A)) (Absorbing Q) ⟨(intuitionistically_affine A).affine⟩ - intuitionistic_elim_spatial ⟨persistently_of_intuitionistically.trans inst.into_persistently⟩ instAffine h +theorem intuitionistic_elim_intuitionistic [BI PROP] {A A' Q : PROP} + [inst : IntoPersistently true A A'] (h : P ∗ □ A' ⊢ Q) : P ∗ □ A ⊢ Q := + have : IntoPersistently false iprop(□ A) A' := + ⟨persistently_of_intuitionistically.trans inst.into_persistently⟩ + intuitionistic_elim_spatial (A := iprop(□ A)) h theorem spatial_elim [BI PROP] {p} {A A' Q : PROP} [FromAffinely A' A p] (h : P ∗ A' ⊢ Q) : P ∗ □?p A ⊢ Q := @@ -156,15 +157,15 @@ private def iCasesIntuitionistic {prop : Q(Type u)} (_bi : Q(BI $prop)) (k : (B : Q($prop)) → ProofModeM Q($P ∗ □ $B ⊢ $goal)) : ProofModeM (Q($P ∗ □?$p $A ⊢ $goal)) := do let B ← mkFreshExprMVarQ q($prop) - let .some instPers ← ProofModeM.trySynthInstanceQ q(IntoPersistently $p $A $B) + let .some _ ← ProofModeM.trySynthInstanceQ q(IntoPersistently $p $A $B) | throwError "icases: {A} not persistent" match matchBool p with | .inl _ => - return q(intuitionistic_elim_intuitionistic $instPers $(← k B)) + return q(intuitionistic_elim_intuitionistic $(← k B)) | .inr _ => - let .some instAffineAbsorbing ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $goal)) + let .some _ ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $goal)) | throwError "icases: {A} not affine and the goal not absorbing" - return q(intuitionistic_elim_spatial $instPers $instAffineAbsorbing $(← k B)) + return q(intuitionistic_elim_spatial (A := $A) $(← k B)) /-- Destruct an affine/spatial hypothesis [A] by removing the affinely wrapper and continuing with From 956c7844e237a90691f18873ab47ebcbdd830629 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 17:07:20 +0200 Subject: [PATCH 104/108] Make more type classes implicit --- Iris/Iris/ProofMode/Tactics/Pure.lean | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Pure.lean b/Iris/Iris/ProofMode/Tactics/Pure.lean index 295572803..690d78f1e 100644 --- a/Iris/Iris/ProofMode/Tactics/Pure.lean +++ b/Iris/Iris/ProofMode/Tactics/Pure.lean @@ -13,7 +13,7 @@ public section open BI Std theorem pure_elim_spatial [BI PROP] {P P' A Q : PROP} {φ : Prop} - (hA : IntoPure A φ) (or : TCOr (Affine A) (Absorbing Q)) + [hA : IntoPure A φ] [or : TCOr (Affine A) (Absorbing Q)] (h : P ⊣⊢ P' ∗ A) (h_entails : φ → P' ⊢ Q) : P ⊢ Q := h.1.trans <| match or with | TCOr.l => @@ -25,9 +25,9 @@ theorem pure_elim_spatial [BI PROP] {P P' A Q : PROP} {φ : Prop} pure_elim_right fun hφ => (absorbingly_mono <| h_entails hφ).trans absorbing theorem pure_elim_intuitionistic [BI PROP] {P P' A Q : PROP} {φ : Prop} - (instIntoPure : IntoPure A φ) (h : P ⊣⊢ P' ∗ □ A) (h' : φ → P' ⊢ Q) : P ⊢ Q := - have instAffine := @TCOr.l (Affine iprop(□ A)) (Absorbing Q) ⟨(intuitionistically_affine A).affine⟩ - pure_elim_spatial ⟨intuitionistically_elim.trans instIntoPure.into_pure⟩ instAffine h h' + [inst : IntoPure A φ] (h : P ⊣⊢ P' ∗ □ A) (h' : φ → P' ⊢ Q) : P ⊢ Q := + have : IntoPure iprop(□ A) φ := ⟨intuitionistically_elim.trans inst.into_pure⟩ + pure_elim_spatial h h' public meta section open Lean Elab Tactic Meta Qq @@ -36,7 +36,7 @@ def iPureCore {prop : Q(Type u)} (_bi : Q(BI $prop)) (P P' : Q($prop)) (p : Q(Bool)) (A Q : Q($prop)) (name : TSyntax ``binderIdent) (pf : Q($P ⊣⊢ $P' ∗ □?$p $A)) (k : (φ : Q(Prop)) → Q($φ) → ProofModeM (Q($P' ⊢ $Q))) : ProofModeM (Q($P ⊢ $Q)) := do let φ : Q(Prop) ← mkFreshExprMVarQ q(Prop) - let .some instIntoPure ← ProofModeM.trySynthInstanceQ q(IntoPure $A $φ) + let .some _ ← ProofModeM.trySynthInstanceQ q(IntoPure $A $φ) | throwError "ipure: {A} is not pure" let (name, ref) ← getFreshName name @@ -47,11 +47,11 @@ def iPureCore {prop : Q(Type u)} (_bi : Q(BI $prop)) match matchBool p with | .inl _ => - return (q(pure_elim_intuitionistic $instIntoPure $pf $f)) + return (q(pure_elim_intuitionistic $pf $f)) | .inr _ => - let .some instAffineAbsorbing ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $Q)) + let .some _ ← trySynthInstanceQ q(TCOr (Affine $A) (Absorbing $Q)) | throwError "ipure: {A} is not affine and the goal not absorbing" - return q(pure_elim_spatial $instIntoPure $instAffineAbsorbing $pf $f) + return q(pure_elim_spatial (A := $A) $pf $f) /-- `ipure H` moves a pure hypothesis `H` from the Iris context into the regular From a67f1dcd56ba4de2658221c66505c7ebf5a3417c Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 17:39:35 +0200 Subject: [PATCH 105/108] Replace `outParam <| uncheckedInParam` with `semiOutParamIPM` --- Iris/Iris/HeapLang/ProofMode.lean | 2 +- Iris/Iris/Instances/Lib/FUpd.lean | 8 +++--- Iris/Iris/Instances/Lib/LaterCredits.lean | 4 +-- Iris/Iris/ProgramLogic/WeakestPre.lean | 12 ++++---- Iris/Iris/ProofMode/Classes.lean | 6 ++-- Iris/Iris/ProofMode/Instances.lean | 35 +++++++++++++---------- Iris/Iris/ProofMode/InstancesLater.lean | 4 +-- Iris/Iris/ProofMode/InstancesUpdates.lean | 24 ++++++++-------- Iris/Iris/ProofMode/Tactics/Mod.lean | 4 +-- 9 files changed, 52 insertions(+), 47 deletions(-) diff --git a/Iris/Iris/HeapLang/ProofMode.lean b/Iris/Iris/HeapLang/ProofMode.lean index 9492c94b2..0e413ae7c 100644 --- a/Iris/Iris/HeapLang/ProofMode.lean +++ b/Iris/Iris/HeapLang/ProofMode.lean @@ -230,7 +230,7 @@ public meta def iWpValueHead {u} let p' : Q(Bool) ← mkFreshExprMVarQ q(Bool) let A' : Q(IProp $GF) ← mkFreshExprMVarQ q(IProp $GF) let Q' : Q(IProp $GF) ← mkFreshExprMVarQ q(IProp $GF) - if let .some _ ← ProofModeM.trySynthInstanceQ q(ElimModal $c false $p' iprop(|={$E}=> $goal) $A' $goal $Q') then + if let .some _ ← ProofModeM.trySynthInstanceQ q(ElimModal $c false .out $p' iprop(|={$E}=> $goal) $A' $goal $Q') then if let some _ ← try? <| iSolveSidecondition c then let pf ← addBIGoal hyps q($goal) return some q(tac_wp_value_nofupd (s:=$s) (E:=$E) $pf) diff --git a/Iris/Iris/Instances/Lib/FUpd.lean b/Iris/Iris/Instances/Lib/FUpd.lean index 9fcc8dab9..599c96c08 100644 --- a/Iris/Iris/Instances/Lib/FUpd.lean +++ b/Iris/Iris/Instances/Lib/FUpd.lean @@ -379,8 +379,8 @@ instance isExcept0_fupd_finally (E : CoPset) (P : IProp GF) : IsExcept0 iprop(|= iapply except0_mono fupd_intro $$ H @[rocq_alias elim_modal_bupd_fupd_finally] -instance elimModal_bupd_fupd_finally p (E : CoPset) (P Q : IProp GF) : - ElimModal True p false iprop(|==> P) P iprop(|={E|}=> Q) iprop(|={E|}=> Q) where +instance elimModal_bupd_fupd_finally p io (E : CoPset) (P Q : IProp GF) : + ElimModal True p io false iprop(|==> P) P iprop(|={E|}=> Q) iprop(|={E|}=> Q) where elim_modal _ := by iintro ⟨H1, H2⟩ iapply fupd_fupd_finally E E @@ -390,8 +390,8 @@ instance elimModal_bupd_fupd_finally p (E : CoPset) (P Q : IProp GF) : iapply H2 $$ H1 @[rocq_alias elim_modal_fupd_fupd_finally] -instance elimModal_fupd_fupd_finally p (E1 E2 : CoPset) (P Q : IProp GF) : - ElimModal True p false iprop(|={E1,E2}=> P) P iprop(|={E1|}=> Q) iprop(|={E2|}=> Q) where +instance elimModal_fupd_fupd_finally p io (E1 E2 : CoPset) (P Q : IProp GF) : + ElimModal True p io false iprop(|={E1,E2}=> P) P iprop(|={E1|}=> Q) iprop(|={E2|}=> Q) where elim_modal _ := by iintro ⟨H1, H2⟩ iapply fupd_fupd_finally E1 E2 diff --git a/Iris/Iris/Instances/Lib/LaterCredits.lean b/Iris/Iris/Instances/Lib/LaterCredits.lean index 0bf9dc67d..f915d6a71 100644 --- a/Iris/Iris/Instances/Lib/LaterCredits.lean +++ b/Iris/Iris/Instances/Lib/LaterCredits.lean @@ -429,7 +429,7 @@ open ProofMode variable {GF : BundledGFunctors} {hlc : HasLC} [LcGS hlc GF] @[rocq_alias le_upd.elim_bupd_le_upd] -instance {P : IProp GF} : ElimModal True p false (bupd P) P (le_upd Q) (le_upd Q) where +instance {P Q : IProp GF} : ElimModal True p io false (bupd P) P (le_upd Q) (le_upd Q) where elim_modal := by cases p <;> (dsimp; intro _) · iintro ⟨H1, H2⟩ @@ -466,7 +466,7 @@ instance {P : IProp GF} : FromModal True modality_id (le_upd P) (le_upd P) P whe iapply le_upd_intro @[rocq_alias le_upd.elim_modal_le_upd] -instance {P : IProp GF} : ElimModal True p false (le_upd P) P (le_upd Q) (le_upd Q) where +instance {P Q : IProp GF} : ElimModal True p io false (le_upd P) P (le_upd Q) (le_upd Q) where elim_modal := by intro _ cases p <;> dsimp diff --git a/Iris/Iris/ProgramLogic/WeakestPre.lean b/Iris/Iris/ProgramLogic/WeakestPre.lean index f8ecbb931..05cd7c3e0 100644 --- a/Iris/Iris/ProgramLogic/WeakestPre.lean +++ b/Iris/Iris/ProgramLogic/WeakestPre.lean @@ -639,7 +639,7 @@ instance isExcept0Wp : IsExcept0 (WP e @ s ; E {{ Φ }}) where -- this should have higher priority than elimModalFupdWpAtomic @[rocq_alias elim_modal_fupd_wp] instance (priority := default + 10) elimModalFupdWp p : - ElimModal True p false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where + ElimModal True p io false iprop(|={E}=> P) P (WP e @ s ; E {{ Φ }}) (WP e @ s ; E {{ Φ }}) where elim_modal := by iintro %_ ⟨H, G⟩ icases BI.intuitionisticallyIf_elim $$ H with H @@ -649,11 +649,11 @@ instance (priority := default + 10) elimModalFupdWp p : @[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 + ElimModal True p io 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 ⟨⟩ + exact elimModalFupdWp _ |>.elim_modal ⟨⟩ (io := io) /-- 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 @@ -662,12 +662,12 @@ 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 + p io 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 + ElimModal (Language.Atomic ↑s e) p io 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, G⟩ icases BI.intuitionisticallyIf_elim $$ H with H @@ -679,7 +679,7 @@ instance elimModalFupdWpAtomic : 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 + p io false iprop(|={E₁,E₂}=> P) iprop(False) (WP e @ s ; E₁ {{ Φ }}) iprop(False) where elim_modal := nofun @[rocq_alias elim_acc_wp_atomic] diff --git a/Iris/Iris/ProofMode/Classes.lean b/Iris/Iris/ProofMode/Classes.lean index 4f588f416..0c7e1e952 100644 --- a/Iris/Iris/ProofMode/Classes.lean +++ b/Iris/Iris/ProofMode/Classes.lean @@ -206,9 +206,9 @@ export FromModal (from_modal) /-- `ElimModal` turns `□?p P` into `□?p' P'` and `Q` into `Q'` under condition `φ`. -/ @[ipm_class, rocq_alias ElimModal] -class ElimModal {PROP} [BI PROP] (φ : outParam $ Prop) (p : Bool) - (p' : outParam $ uncheckedInParam Bool) (P : PROP) - (P' : outParam $ uncheckedInParam PROP) (Q : PROP) (Q' : outParam $ PROP) where +class ElimModal {PROP} [BI PROP] (φ : outParam $ Prop) (p : Bool) (io : InOut) + (p' : semiOutParamIPM io Bool) (P : PROP) + (P' : semiOutParamIPM io PROP) (Q : PROP) (Q' : outParam $ PROP) where elim_modal : φ → □?p P ∗ (□?p' P' -∗ Q') ⊢ Q export ElimModal (elim_modal) diff --git a/Iris/Iris/ProofMode/Instances.lean b/Iris/Iris/ProofMode/Instances.lean index 640c99b7c..01b5693be 100644 --- a/Iris/Iris/ProofMode/Instances.lean +++ b/Iris/Iris/ProofMode/Instances.lean @@ -856,29 +856,29 @@ instance fromModal_absorbingly [BI PROP] (P : PROP) : -- ElimModal @[rocq_alias elim_modal_wand] -instance elimModal_wand [BI PROP] φ p p' (P P' Q Q' R : PROP) [h : ElimModal φ p p' P P' Q Q'] : - ElimModal φ p p' P P' iprop(R -∗ Q) iprop(R -∗ Q') where +instance elimModal_wand [BI PROP] φ p p' io (P P' Q Q' R : PROP) [h : ElimModal φ p io p' P P' Q Q'] : + ElimModal φ p io p' P P' iprop(R -∗ Q) iprop(R -∗ Q') where elim_modal hφ := by apply wand_intro ((sep_assoc.1.trans $ sep_mono_right $ wand_elim $ wand_intro_left $ wand_intro_left $ sep_assoc.2.trans _).trans (h.1 hφ)) apply (sep_mono_left sep_comm.1).trans (sep_assoc.1.trans $ wand_elim_swap $ wand_elim_swap .rfl) @[rocq_alias elim_modal_wandM] -instance elimModal_wandM [BI PROP] φ p p' (P P' Q Q' : PROP) (mR : Option PROP) - [h : ElimModal φ p p' P P' Q Q'] : - ElimModal φ p p' P P' iprop(mR -∗? Q) iprop(mR -∗? Q') where +instance elimModal_wandM [BI PROP] φ p p' io (P P' Q Q' : PROP) (mR : Option PROP) + [h : ElimModal φ p io p' P P' Q Q'] : + ElimModal φ p io p' P P' iprop(mR -∗? Q) iprop(mR -∗? Q') where elim_modal hφ := (sep_mono_right <| wand_mono_right wandM_sound.mp).trans <| - ((elimModal_wand φ p p' P P' Q Q' (mR.getD emp)).elim_modal hφ).trans wandM_sound.mpr + ((elimModal_wand φ p p' io P P' Q Q' (mR.getD emp)).elim_modal hφ).trans wandM_sound.mpr @[rocq_alias elim_modal_forall] -instance elimModal_forall [BI PROP] φ p p' P P' (Φ Ψ : α → PROP) [h : ∀ x, ElimModal φ p p' P P' (Φ x) (Ψ x)] : - ElimModal φ p p' P P' iprop(∀ x, Φ x) iprop(∀ x, Ψ x) where +instance elimModal_forall [BI PROP] φ p p' io P P' (Φ Ψ : α → PROP) [h : ∀ x, ElimModal φ p io p' P P' (Φ x) (Ψ x)] : + ElimModal φ p io p' P P' iprop(∀ x, Φ x) iprop(∀ x, Ψ x) where elim_modal hφ := forall_intro λ a => Entails.trans (sep_mono_right (wand_mono_right (forall_elim a))) ((h a).1 hφ) @[rocq_alias elim_modal_absorbingly_here] -instance elimModal_absorbingly_here [BI PROP] p (P Q : PROP) [Absorbing Q] : - ElimModal True p false iprop( P) P Q Q where +instance elimModal_absorbingly_here [BI PROP] p io (P Q : PROP) [Absorbing Q] : + ElimModal True p io false iprop( P) P Q Q where elim_modal _ := (sep_mono_left intuitionisticallyIf_elim).trans $ absorbingly_sep_left.1.trans $ absorbing_absorbingly.1.trans wand_elim_right -- CombineSepAs @@ -962,18 +962,23 @@ instance elimInv_acc_without_close [BI PROP] {X : Type} instance elimInv_acc_with_close [BI PROP] {X : Type} ϕ1 ϕ2 Pinv Pin (M1 M2 : PROP → PROP) α β mγ Q (Q' : PROP) [h1 : IntoAcc Pinv ϕ1 Pin M1 M2 α β mγ] - [h2 : ∀ R, ElimModal ϕ2 false false (M1 R) R Q Q'] : + [h2 : ∀ R, ElimModal ϕ2 false .in false (M1 R) R Q Q'] : ElimInv (ϕ1 ∧ ϕ2) X Pinv Pin α true (some (fun x => iprop(β x -∗ M2 (mγ x |>.getD emp)))) Q (fun _ => Q') where elim_inv := by - intro ⟨hϕ1, _⟩ + intro ⟨hϕ1, hϕ2⟩ have hAcc := h1.into_acc unfold accessor at hAcc iintro ⟨Hinv, Hin, Hcont⟩ - imod hAcc hϕ1 $$ Hinv Hin with ⟨%_, Hα, Hclose⟩ - iapply Hcont - isplitl [Hα] <;> iassumption + iapply (h2 _ |>.elim_modal hϕ2) + isplitl [Hinv Hin] + · dsimp + iapply hAcc hϕ1 $$ Hinv Hin + · dsimp + iintro ⟨%_, Hα, Hclose⟩ + iapply Hcont + isplitl [Hα] <;> iassumption @[rocq_alias into_ih_entails] instance intoIH_entails [BI PROP] (P Q : PROP) : IntoIH (Entails' P Q) P Q where diff --git a/Iris/Iris/ProofMode/InstancesLater.lean b/Iris/Iris/ProofMode/InstancesLater.lean index 596881c7c..2d80151e8 100644 --- a/Iris/Iris/ProofMode/InstancesLater.lean +++ b/Iris/Iris/ProofMode/InstancesLater.lean @@ -314,8 +314,8 @@ instance intoExcept0_persistently [BI PROP] (P Q : PROP) /-- ElimModal -/ @[ipm_backtrack, rocq_alias elim_modal_timeless] -instance (priority := default - 10) elimModal_timeless [BI PROP] p (P P' Q : PROP) [IntoExcept0 P P'] [IsExcept0 Q] : - ElimModal True p p P P' Q Q where +instance (priority := default - 10) elimModal_timeless [BI PROP] p io (P P' Q : PROP) [IntoExcept0 P P'] [IsExcept0 Q] : + ElimModal True p io p P P' Q Q where elim_modal _ := ((sep_mono ((intuitionisticallyIf_mono into_except0).trans except0_intuitionisticallyIf) except0_intro).trans $ except0_sep.2.trans (except0_mono wand_elim_right)).trans is_except0 /-- IntoLaterN -/ diff --git a/Iris/Iris/ProofMode/InstancesUpdates.lean b/Iris/Iris/ProofMode/InstancesUpdates.lean index cc9f41a48..34786e4ad 100644 --- a/Iris/Iris/ProofMode/InstancesUpdates.lean +++ b/Iris/Iris/ProofMode/InstancesUpdates.lean @@ -86,8 +86,8 @@ instance fromModal_bupd (P : PROP) : from_modal := by simp [modality_id]; exact BIUpdate.intro @[rocq_alias elim_modal_bupd] -instance elimModal_bupd p (P Q : PROP) : - ElimModal True p false iprop(|==> P) P iprop(|==> Q) iprop(|==> Q) where +instance elimModal_bupd p io (P Q : PROP) : + ElimModal True p io false iprop(|==> P) P iprop(|==> Q) iprop(|==> Q) where elim_modal _ := (sep_mono_left intuitionisticallyIf_elim).trans <| bupd_frame_right.trans <| (BIUpdate.mono wand_elim_right).trans BIUpdate.trans @@ -98,14 +98,14 @@ section SBIBasicUpdate variable {PROP} [Sbi PROP] [BIUpdate PROP] [BIBUpdateSbi PROP] @[ipm_backtrack, rocq_alias elim_modal_bupd_plain_goal] -instance elimModal_bupd_plain_goal [BIAffine PROP] p (P Q : PROP) [Plain Q] : - ElimModal True p false iprop(|==> P) P Q Q where +instance elimModal_bupd_plain_goal [BIAffine PROP] p io (P Q : PROP) [Plain Q] : + ElimModal True p io false iprop(|==> P) P Q Q where elim_modal _ := (sep_mono_left intuitionisticallyIf_elim).trans <| bupd_frame_right.trans <| (BIUpdate.mono wand_elim_right).trans bupd_elim @[ipm_backtrack, rocq_alias elim_modal_bupd_plain] -instance elimModal_bupd_plain [BIAffine PROP] p (P Q : PROP) [Plain P] : - ElimModal True p p iprop(|==> P) P Q Q where +instance elimModal_bupd_plain [BIAffine PROP] p io (P Q : PROP) [Plain P] : + ElimModal True p io p iprop(|==> P) P Q Q where elim_modal _ := (sep_mono_left (intuitionisticallyIf_mono bupd_elim)).trans wand_elim_right end SBIBasicUpdate @@ -184,23 +184,23 @@ instance (priority := low) fromModal_fupd_wrongMask E1 E2 (P : PROP) : from_modal h := by cases h @[rocq_alias elim_modal_bupd_fupd] -instance elimModal_bupd_fupd p E1 E2 (P Q : PROP) : - ElimModal True p false iprop(|==> P) P iprop(|={E1,E2}=> Q) iprop(|={E1,E2}=> Q) where +instance elimModal_bupd_fupd p io E1 E2 (P Q : PROP) : + ElimModal True p io false iprop(|==> P) P iprop(|={E1,E2}=> Q) iprop(|={E1,E2}=> Q) where elim_modal _ := (sep_mono_left intuitionisticallyIf_elim).trans <| (sep_mono_left BIUpdateFUpdate.fupd_of_bupd).trans <| fupd_frame_right.trans <| (BIFUpdate.mono wand_elim_right).trans BIFUpdate.trans @[rocq_alias elim_modal_fupd_fupd] -instance (priority := high) elimModal_fupd_fupd p E1 E2 E3 (P Q : PROP) : - ElimModal True p false iprop(|={E1,E2}=> P) P iprop(|={E1,E3}=> Q) iprop(|={E2,E3}=> Q) where +instance (priority := high) elimModal_fupd_fupd p io E1 E2 E3 (P Q : PROP) : + ElimModal True p io false iprop(|={E1,E2}=> P) P iprop(|={E1,E3}=> Q) iprop(|={E2,E3}=> Q) where elim_modal _ := (sep_mono_left intuitionisticallyIf_elim).trans <| fupd_frame_right.trans <| (BIFUpdate.mono wand_elim_right).trans BIFUpdate.trans @[rocq_alias elim_modal_fupd_fupd_wrong_mask] -instance (priority := low) elimModal_fupd_fupd_wrongMask p E0 E1 E2 E3 (P Q : PROP) : +instance (priority := low) elimModal_fupd_fupd_wrongMask p io E0 E1 E2 E3 (P Q : PROP) : ElimModal (PMError "Goal and eliminated modality must have the same mask. Use `BIFUpdate.subset` to adjust the goal mask before using `imod`.") - p false iprop(|={E1,E2}=> P) iprop(False) iprop(|={E0,E3}=> Q) iprop(False) where + p io false iprop(|={E1,E2}=> P) iprop(False) iprop(|={E0,E3}=> Q) iprop(False) where elim_modal h := by cases h @[rocq_alias elim_acc_bupd] diff --git a/Iris/Iris/ProofMode/Tactics/Mod.lean b/Iris/Iris/ProofMode/Tactics/Mod.lean index df7caf352..46c9c3b85 100644 --- a/Iris/Iris/ProofMode/Tactics/Mod.lean +++ b/Iris/Iris/ProofMode/Tactics/Mod.lean @@ -14,7 +14,7 @@ namespace Iris.ProofMode public section open BI -theorem mod [BI PROP] {e} {Φ} {p p'} {A A' Q Q' : PROP} [he : ElimModal Φ p p' A A' Q Q'] +theorem mod [BI PROP] {e} {Φ} {p p'} {A A' Q Q' : PROP} [he : ElimModal Φ p .out p' A A' Q Q'] (h1 : e ∗ □?p' A' ⊢ Q') (hΦ : Φ) : e ∗ □?p A ⊢ Q := (sep_comm.1.trans (sep_mono_right (wand_intro h1))).trans (he.1 hΦ) @@ -41,7 +41,7 @@ def iModCore {prop : Q(Type u)} (_bi : Q(BI $prop)) (P Q : Q($prop)) (p : Q(Bool let A' : Q($prop) ← mkFreshExprMVarQ q($prop) let Q' : Q($prop) ← mkFreshExprMVarQ q($prop) -- transform `Q` to `Q'` and `A` to `A'` - let .some _ ← ProofModeM.trySynthInstanceQ q(ElimModal $Φ $p $p' $A $A' $Q $Q') + let .some _ ← ProofModeM.trySynthInstanceQ q(ElimModal $Φ $p .out $p' $A $A' $Q $Q') | throwError "imod: {A} is not a modality" let hΦ ← iSolveSidecondition q($Φ) let p'' : Q(Bool) ← instantiateMVars p' From 719aaa2e4d13b5f63fd898c29ecbc9786755925c Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 17:40:58 +0200 Subject: [PATCH 106/108] Replace `simp [*]` with `simp [*] <;> done` --- Iris/Iris/ProofMode/Tactics/Basic.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Iris/Iris/ProofMode/Tactics/Basic.lean b/Iris/Iris/ProofMode/Tactics/Basic.lean index ff503afc3..4b6a20b1d 100644 --- a/Iris/Iris/ProofMode/Tactics/Basic.lean +++ b/Iris/Iris/ProofMode/Tactics/Basic.lean @@ -30,7 +30,7 @@ def iSolveSidecondition (target : Q(Prop)) (failOnUnsolved := true) : ProofModeM throwError "{msg}" | _ => let gs ← (observing? <| - evalTacticAt (← `(tactic | first | trivial | simp [*])) mvar.mvarId!) <&> + evalTacticAt (← `(tactic | first | trivial | (simp [*] <;> done))) mvar.mvarId!) <&> (·.getD [mvar.mvarId!]) if !gs.isEmpty then if failOnUnsolved then From 3c4721ebfd36f5c8214e36f883b13d0f0335b203 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 17:41:57 +0200 Subject: [PATCH 107/108] `iSolveSidecondition` update --- Iris/Iris/ProofMode/Tactics/Inv.lean | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index cec718e8b..74fa6e81c 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -86,8 +86,7 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} have : $out'' =Q $Pin := ⟨⟩ have : $p'' =Q false := ⟨⟩ - -- Solve side conditions automatically if possible, otherwise add them into the proof state - let hϕ ← iSolveSidecondition q($ϕ) false + let hϕ ← iSolveSidecondition (failOnUnsolved := false) q($ϕ) -- Simplify occurrences of `wandM`, `Option.getD`, pattern matching, etc. let Pout' : Q($X → $prop) ← reduceWandM Pout From 45bdcb50d326ca26cc50fec7bbebc63b811c1a63 Mon Sep 17 00:00:00 2001 From: Alvin Tang Date: Thu, 23 Jul 2026 17:45:45 +0200 Subject: [PATCH 108/108] Replace `>>=` with `let ...` --- Iris/Iris/ProofMode/Tactics/Inv.lean | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Iris/Iris/ProofMode/Tactics/Inv.lean b/Iris/Iris/ProofMode/Tactics/Inv.lean index 74fa6e81c..13f55a57d 100644 --- a/Iris/Iris/ProofMode/Tactics/Inv.lean +++ b/Iris/Iris/ProofMode/Tactics/Inv.lean @@ -60,8 +60,9 @@ open Lean Elab Tactic Meta Qq BI Std and pattern matching (`match … with …`). -/ private def reduceWandM (e : Expr) : ProofModeM Expr := do - #[``BIBase.wandM, ``Option.getD].foldlM (·.addDeclToUnfold ·) {} >>= - (Simp.mkContext {} #[·] (← getSimpCongrTheorems) >>= (Lean.Meta.dsimp e · <&> Prod.fst)) + let simpThms ← #[``BIBase.wandM, ``Option.getD].foldlM (·.addDeclToUnfold ·) {} + let simpContext ← Simp.mkContext {} #[simpThms] (← getSimpCongrTheorems) + Lean.Meta.dsimp e simpContext <&> Prod.fst private def iInvCore {u} {prop : Q(Type u)} {bi} {e} (hyps : Hyps bi e) (goal : Q($prop)) (ivar : IVarId) (specPat : Option SpecPat) @@ -100,17 +101,17 @@ private def iInvCore {u} {prop : Q(Type u)} {bi} {e} withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do match closePat with | some closePat => - iCasesCore _ hyps'' q($Q'' $x) (.conjunction [casesPat, closePat]) - q(false) q(iprop($Pout' $x ∗ $f' $x)) >>= - (mkLambdaFVars #[x] ·) + let pf' ← iCasesCore _ hyps'' q($Q'' $x) (.conjunction [casesPat, closePat]) + q(false) q(iprop($Pout' $x ∗ $f' $x)) + mkLambdaFVars #[x] pf' -- Throw an error if `hclose` is not given, but `mPclose` is not `none` | none => throwError "iinv: missing cases pattern for the closing hypothesis" return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) | ~q(none) => let pf : Q(∀ x, $e'' ∗ $Pout x ⊢ $Q' x) ← withLocalDeclDQ (← mkFreshUserName .anonymous) X fun x => do - iCasesCore _ hyps'' q($Q'' $x) casesPat q(false) q($Pout' $x) >>= - (mkLambdaFVars #[x] ·) + let pf' ← iCasesCore _ hyps'' q($Q'' $x) casesPat q(false) q($Pout' $x) + mkLambdaFVars #[x] pf' return q(tac_inv_elim $inst $hϕ $pf $pfEq $pfPin) syntax (name := iinv) "iinv " colGt term (" $$ " colGt ppSpace specPat)?