Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
547a7f1
Port `AddModal` and some of its instances
alvinylt Jul 10, 2026
ae01a25
Start implement `inext` for later credits
alvinylt Jul 10, 2026
be8e829
Test `inext` with later credits
alvinylt Jul 10, 2026
1711c42
Towards implementing `inext` with later credits
alvinylt Jul 10, 2026
bb7f745
Finish proving `tac_lc_add_laterN_split` and the implementation
alvinylt Jul 10, 2026
4e3ef43
Use IPM for `tac_lc_add_laterN_split`
alvinylt Jul 10, 2026
48c851e
Simplify proofs
alvinylt Jul 10, 2026
3cbecf1
Minor formatting
alvinylt Jul 10, 2026
5c517a9
Discard the hypothesis once later credits are used up, more tests
alvinylt Jul 10, 2026
3ca3b62
Update documentation
alvinylt Jul 10, 2026
4fd2147
Catch error gracefully to avoid timeout
alvinylt Jul 10, 2026
37c8067
Merge remote-tracking branch 'upstream/master' into iNextLaterCredits
alvinylt Jul 27, 2026
98d0815
Merge remote-tracking branch 'upstream/master' into iNextLaterCredits
alvinylt Jul 28, 2026
4240629
Use `Hyps.remove` instead of `Hyps.removeG` to simplify implementation
alvinylt Jul 28, 2026
9056733
Use Qq syntax matching to avoid the use of `mkApp` and `let_expr`
alvinylt Jul 28, 2026
9b1963a
Further use Qq to eliminate `mkApp` usage
alvinylt Jul 28, 2026
7840ec1
Merge remote-tracking branch 'upstream/master' into iNextLaterCredits
alvinylt Jul 28, 2026
d2a4dc1
Remove unnecessary `iprop(...)` wrapper
alvinylt Jul 28, 2026
23dd74d
Use `ElimModal` instead of `AddModal`
alvinylt Jul 29, 2026
52f722d
Merge remote-tracking branch 'upstream/master' into iNextLaterCredits
alvinylt Jul 30, 2026
5f4def9
Adjust for the changed definition of `Hyps.add`
alvinylt Jul 30, 2026
680185f
`iSolveSidecondition`: set `failOnUnsolved` as `true`
alvinylt Jul 30, 2026
abab86c
Remove unnecessary `iprop(...)` wrappers
alvinylt Jul 30, 2026
451ddb8
Revert the `AddModal` type class and its instances
alvinylt Jul 30, 2026
8011adc
Use `NatCancel` for handling numerals
alvinylt Jul 30, 2026
ed9eedd
Remove redundant line
alvinylt Jul 30, 2026
02a3589
Remove commented out code
alvinylt Jul 30, 2026
4b3338f
Open `Iris.Std` namespace for `NatCancel`
alvinylt Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions Iris/Iris/Instances/Lib/FUpd.lean
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public import Iris.Instances.IProp
public import Iris.Instances.Lib.WSat
public import Iris.Instances.Lib.LaterCredits
public import Iris.BI.Plainly
public import Iris.Std

@[expose] public section

Expand Down Expand Up @@ -583,4 +584,116 @@ end StepIndexed

end Iris

public section

open Lean Elab Tactic Meta Qq Iris.BI Iris Iris.ProofMode Iris.Std

@[rocq_alias tac_lc_add_laterN_split]
theorem tac_lc_add_laterN_split {GF : BundledGFunctors} [InvGS GF]
{φ : Prop} {n m newM : Nat} {E : CoPset} {P Q goal : IProp GF}
(inst : ElimModal φ false .in false iprop(|={E}=> goal) goal goal goal) (hφ : φ)
(h1 : NatCancel m n newM 0) (h2 : P ∗ £ newM ⊢ ▷^[n] Q) (h3 : Q ⊢ goal) :
P ∗ £ m ⊢ goal := by
have h1 : m = n + newM := by have := h1.nat_cancel; omega
subst h1
iintro ⟨HP, Hcred⟩
iapply inst.elim_modal hφ
isplitl
· icases lc_split.mp $$ Hcred with ⟨Hn, Hm⟩
icombine HP Hm as H
ihave H := h2 $$ H
iapply lc_fupd_add_laterN n $$ Hn
inext
imodintro
iapply h3 $$ H
· iintro _ //

theorem tac_lc_add_laterN_full {GF : BundledGFunctors} [InvGS GF]
{φ : Prop} {m : Nat} {E : CoPset} {P Q goal : IProp GF}
(inst : ElimModal φ false .in false iprop(|={E}=> goal) goal goal goal) (hφ : φ)
(h1 : NatCancel m n 0 0)
(h2 : P ⊢ ▷^[n] Q) (h3 : Q ⊢ goal) :
P ∗ £ m ⊢ goal := by
iintro ⟨HP, Hcred⟩
iapply inst.elim_modal hφ
isplitl
· ihave H := h2 $$ HP
iapply lc_fupd_add_laterN m $$ Hcred
inext
imodintro
iapply h3 $$ H
· iintro _ //

public meta section

elab "inext" n:(ppSpace num)? " credit: " h:ident : tactic => do
let natN := match n with | none => 1 | some n => n.raw.toNat

ProofModeM.runTactic λ mvar { u, prop, bi, e, hyps, goal, .. } => do
let .defEq _ ← isLevelDefEqQ u 0
| throwError "inext: the goal must be an `IProp` at universe level 0"
let ~q(IProp $GF) := prop
| throwError "inext: the goal must be an `IProp`"
let .some _ ← trySynthInstanceQ q(InvGS $GF)
| throwError "inext: requires an InvGS (HasLC) context"
let ~q(UPred.instBIUPred) := bi
| throwError "Expected the BI implementation of `IProp` to be `UPred.instBIUPred`"

-- Search for the later credit hypothesis from the context
let ivar ← hyps.findWithInfo h
let some ⟨name, _, p, ty⟩ := hyps.getDecl? ivar
| throwError m!"inext: unknown hypothesis {h}"
if isTrue p then throwError "inext: {h} is not in the spatial context"
let ~q(£ $c) := ty
| throwError m!"inext: {h} is not a spatial later credit hypothesis"
let ⟨e', hyps', _, _, _, _, pfEq⟩ := hyps.remove false ivar

let φ ← mkFreshExprMVarQ q(Prop)
let E ← mkFreshExprMVarQ q(CoPset)
let Q' ← mkFreshExprMVarQ q($prop)
let some inst ← ProofModeM.trySynthInstanceQ q(ElimModal $φ false .in false iprop(|={$E}=> $goal) $goal $goal $Q')
| throwError "inext: ElimModal type class synthesis failed with {goal}"
unless ← isDefEq Q' goal do
throwError "inext: eliminating the fancy update does not preserve the goal {goal}"
have inst : Q(ElimModal $φ false .in false iprop(|={$E}=> $goal) $goal $goal $goal) := inst

let hφ ← iSolveSidecondition q($φ)

-- Generate the proof goal with the updated hypothesis
let n : Q(Credit) ← pure <| mkNatLit natN

let newC ← mkFreshExprMVarQ q(Nat)
let newN ← mkFreshExprMVarQ q(Nat)
let some hcancel ← synthInstance? q(NatCancel $c $n $newC $newN)
| throwError "inext: unable to cancel {n} later credits from {c}"
unless ← isDefEq newN q(0) do
throwError "inext: insufficient credits"

let modality := q(@modality_laterN $prop $n $bi)

match newC.nat? with
-- Later credits used up, discard the later credits hypothesis
| some 0 =>
let ⟨_, newHyps', pfModAction⟩ ← iModAction hyps' modality
let pf ← addBIGoal newHyps' goal
have pfEq : Q($e ⊣⊢ $e' ∗ £ $c) := pfEq
have hcancel : Q(NatCancel $c $n 0 0) := hcancel
let pf' : Q($e' ∗ £ $c ⊢ $goal) := q(tac_lc_add_laterN_full $inst $hφ $hcancel $pfModAction $pf)
mvar.assign q($(pfEq).mp.trans $pf')
-- Update the later credits hypothesis and introduce it into the context
| _ =>
let newTy : Q($prop) := q(£ $newC)
let ⟨_, newHyps, pfNewHyps⟩ := Hyps.add _ name ivar q(false) newTy hyps'
let ⟨_, newHyps', pfModAction⟩ ← iModAction newHyps modality
let pf ← addBIGoal newHyps' goal
have hcancel : Q(NatCancel $c $n $newC 0) := hcancel
have pfEq : Q($e ⊣⊢ $e' ∗ £ $c) := pfEq
let pf'' : Q($e' ∗ £ $c ⊢ $goal) :=
q(tac_lc_add_laterN_split $inst $hφ $hcancel ($(pfNewHyps).mp.trans $pfModAction) $pf)
mvar.assign q($(pfEq).mp.trans $pf'')

end

end

end
2 changes: 1 addition & 1 deletion Iris/Iris/ProofMode/Porting.lean
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import Iris.Std.RocqPorting
#rocq_concept proofmode "Tactics" "iExists" ported "iexists"
#rocq_concept proofmode "Tactics" "iModIntro" ported "imodintro"
#rocq_concept proofmode "Tactics" "iNext (basic)" ported "inext"
#rocq_concept proofmode "Tactics" "iNext (with later credits)" missing ""
#rocq_concept proofmode "Tactics" "iNext (with later credits)" ported "inext _ credit: _"
#rocq_concept proofmode "Tactics" "iMod" ported "imod"
#rocq_concept proofmode "Tactics" "iDestruct (basic)" ported "icases"
#rocq_concept proofmode "Tactics" "iDestruct (all destruct patterns)" missing ""
Expand Down
64 changes: 64 additions & 0 deletions Iris/Iris/Tests/Tactics.lean
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,70 @@ example [BI PROP] (P : PROP) : P ⊢ P := by
iintro HP
inext

/-- Tests `inext`. -/
example [BI PROP] (P Q : PROP) : ⊢ ▷ P -∗ Q -∗ ▷ (P ∗ Q) := by
iintro HP HQ
inext
icombine HP HQ as HPQ
iassumption

variable {GF : BundledGFunctors} [InvGS GF]

/- Tests `inext` with later credits consumption. -/
example (E : CoPset) (P : IProp GF) : ⊢ £ 1 -∗ ▷ (|={E}=> P) -∗ |={E}=> P := by
iintro Hcred HP
-- No later credits consumed, equivalent to a no-op
inext 0 credit: Hcred
-- One later credit is consumed by default when the amount is not specified
inext credit: Hcred
iassumption

/- Tests `inext` with insufficient credits. -/
/-- error: inext: insufficient credits -/
#guard_msgs in
example (E : CoPset) (P : IProp GF) : ⊢ £ 1 -∗ ▷ (|={E}=> P) -∗ |={E}=> P := by
iintro Hcred HP
inext 2 credit: Hcred

/- Tests `inext` with multiple credits consumed at once. -/
example (E : CoPset) (P : IProp GF) : ⊢ £ 3 -∗ ▷▷▷ (|={E}=> P) -∗ |={E}=> P := by
iintro Hcred HP
inext 3 credit: Hcred
iassumption

/- Tests `inext` for later credits with an invalid hypothesis choice. -/
/-- error: inext: Hcred is not a spatial later credit hypothesis -/
#guard_msgs in
example (E : CoPset) (P Q : IProp GF) : ⊢ Q -∗ ▷ (|={E}=> P) -∗ |={E}=> P := by
iintro Hcred HP
inext credit: Hcred

/- Tests `inext` for later credits with the hypothesis not in the spatial context. -/
/-- error: inext: Hcred is not in the spatial context -/
#guard_msgs in
example (E : CoPset) (P : IProp GF) : ⊢ □ £ 1 -∗ ▷ (|={E}=> P) -∗ |={E}=> P := by
iintro #Hcred HP
inext credit: Hcred

/- Tests `inext` with an `IProp GF` entailment where `InvGS GF` is not available. -/
/-- error: inext: requires an InvGS (HasLC) context -/
#guard_msgs in
example [InvGS_gen .hasNoLC GF] (E : CoPset) (P : IProp GF) :
⊢ £ 1 -∗ ▷ (|={E}=> P) -∗ |={E}=> P := by
iintro Hcred HP
inext credit: Hcred

variable {Expr State Obs Val} [Λ : Language Expr State Obs Val]
variable {GF : BundledGFunctors}
variable [IrisGS_gen .hasLC Expr GF]
variable {E : CoPset} {e : Expr} {Φ : Val → IProp GF}

/- Tests `inext` for later credits with `WP`. -/
example : £ 1 ∗ ▷ WP e @ E {{ Φ }} ⊢ WP e @ E {{ Φ }} := by
iintro ⟨Hcred, Hwp⟩
inext credit: Hcred
iassumption

end inext

section irewrite
Expand Down
2 changes: 2 additions & 0 deletions Iris/tactics.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ The proof mode maintains three contexts: the *pure* (Lean) context, the *intuiti
- `imodintro` — Introduce the modality at the top of the goal (e.g. `□`, `<pers>`, `▷`, `|==>`), adjusting the context as required by the modality.
- `imodintro` *sel* — Like `imodintro`, but only succeed if the modality matches the selector term *sel*, e.g. `imodintro (<pers> _)` or `imodintro (□ _)`.
- `inext` — Introduce one or more later modalities; equivalent to `imodintro (▷^[_] _)`.
- `inext` *n* `credit:` *H* — given a later credit hypothesis *H*, reduce the later credits by *n* and strip *n* later modalities from all hypotheses while the goal remains unchanged. This tactic requires the goal to be a fancy update modality and `InvGS GF` to hold.
- `inext credit:` *H* — equivalent to `inext 1 credit:` *H*.
- `imod` [*pmTerm*](#proof-mode-terms) `with` [*casesPat*](#cases-patterns) — Eliminate the modality at the top of [*pmTerm*](#proof-mode-terms) into the goal and destruct the result with [*casesPat*](#cases-patterns). Equivalent to `icases ... with >pat`.
- `imod` [*pmTerm*](#proof-mode-terms) — Like above; if [*pmTerm*](#proof-mode-terms) is a hypothesis, its name is kept.

Expand Down