The Iris Tutorial in Lean

2.3. Basic Separation Logic🔗

The core connective in separation logic is the separating conjunction, written P ∗ Q, for propositions P and Q. Separating conjunction differs from regular conjunction, particularly in its introduction rule:

       P₁ ⊢ Q₁     P₂ ⊢ Q₂
       ─────────────────────
         P₁ ∗ P₂ ⊢ Q₁ ∗ Q₂

That is, if we want to prove Q₁ ∗ Q₂, we must decide which of our owned resources we use to prove Q₁ and which we use to prove Q₂. To see this in action, let us prove that separating conjunction is commutative.

theorem sep_comm (P Q : IProp GF) : P Q Q P := GF:BundledGFunctorsP:IProp GFQ:IProp GFP Q Q P GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P ∗HQ : Q Q P GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HQ : Q QGF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HQ : Q Q All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P All goals completed! 🐙

To eliminate a separating conjunction we use the cases pattern ⟨HP, HQ⟩ in iintro — analogous to Lean's anonymous-constructor notation.

Unlike , is not idempotent. Specifically, there are Iris propositions for which ¬(P ⊢ P ∗ P). Because of this, it is generally not possible to use isplit to introduce . The isplit tactic would duplicate the spatial context and is therefore not available when the context is non-empty.

Instead, Iris introduces the tactics isplitl and isplitr. These allow you to specify how you want to separate your resources to prove each subgoal. The hypotheses listed in brackets — space-separated, e.g. [HP HQ] — are passed to the left subgoal (for isplitl), and the remaining to the right; conversely for isplitr.

Separating conjunction has an analogue to implication which, instead of introducing the antecedent to the assumptions with conjunction, introduces it with separating conjunction. This connective is written as P -∗ Q and pronounced "magic wand" or simply "wand". Separation is so widely used that P -∗ Q is treated specially; instead of writing P ⊢ Q, we can write P -∗ Q, with the being implicit. That is, ⊢ P -∗ Q is notationally equivalent to P -∗ Q.

Writing a wand instead of entailment makes currying more natural. Here is the Iris version of modus ponens. It is provable using only iintro and iapply.

theorem modus_ponens (P Q : IProp GF) : P -∗ (P -∗ Q) -∗ Q := GF:BundledGFunctorsP:IProp GFQ:IProp GF P -∗ (P -∗ Q) -∗ Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P ∗HPQ : P -∗ Q Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P All goals completed! 🐙

Just as with Lean tactics, Iris allows nesting of introduction patterns. In fact, like Lean, Iris supports patterns of the form ⟨H1, H2, H3⟩ as a shorthand for nested ⟨H1, ⟨H2, H3⟩⟩.

Note that is right-associative, so P ∗ Q ∗ R is parsed as P ∗ (Q ∗ R).

theorem sep_assoc_1 (P Q R : IProp GF) : P Q R (P Q) R := GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GFP Q R (P Q) R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q ∗HR : R (P Q) R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P QGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HR : R R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P Q GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P PGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P P All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HR : R R All goals completed! 🐙

Manually splitting a separation can become tedious. To alleviate this, we can use the iframe tactic. This tactic pairs up hypotheses with pieces of a separation sequence.

theorem sep_comm_v2 (P Q : IProp GF) : P Q Q P := GF:BundledGFunctorsP:IProp GFQ:IProp GFP Q Q P GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P ∗HQ : Q Q P All goals completed! 🐙

Bi-entailment of Iris propositions is denoted P ⊣⊢ Q. It is an equivalence relation, and most connectives preserve it. Bi-entailment is defined as the conjunction of P -∗ Q and Q -∗ P, so it can be decomposed using the isplit tactic (which is permitted here because the spatial context is empty at the point of splitting).

For hypotheses with multiple curried wands, we use the proof-mode term syntax of iapply: the form iapply H $$ pat₁ … patₙ supplies arguments for the wand premises of H.

theorem wand_adj_1 (P Q R : IProp GF) : (P -∗ Q -∗ R) P Q R := GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF(P -∗ Q -∗ R) P Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H : P -∗ Q -∗ R ∗HP : P ∗HQ : Q R All goals completed! 🐙

Hypotheses that fit arguments exactly can be supplied directly without generating a trivial subgoal.

theorem wand_adj (P Q R : IProp GF) : (P -∗ Q -∗ R) ⊣⊢ (P Q -∗ R) := GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF(P -∗ Q -∗ R) ⊣⊢ (P Q -∗ R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF (P -∗ Q -∗ R) -∗ P Q -∗ RGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF (P Q -∗ R) -∗ P -∗ Q -∗ R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF (P -∗ Q -∗ R) -∗ P Q -∗ R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H : P -∗ Q -∗ R ∗HP : P ∗HQ : Q R All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF (P Q -∗ R) -∗ P -∗ Q -∗ R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H : P Q -∗ R ∗HP : P ∗HQ : Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P Q GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P PGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P P All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q All goals completed! 🐙

Disjunctions are treated just like disjunctions in Lean. The introduction pattern (HP | HQ) allows us to eliminate a disjunction, while the tactics ileft and iright let us introduce them.

theorem or_comm (P Q : IProp GF) : Q P P Q := GF:BundledGFunctorsP:IProp GFQ:IProp GFQ P P Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HQ : Q P QGF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HQ : Q P Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HQ : Q Q; All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P Q GF:BundledGFunctorsP:IProp GFQ:IProp GF ∗HP : P P; All goals completed! 🐙

We can even prove the usual elimination rule for or-elimination written with separation. This version is, however, not very useful, as it does not allow the two cases to share resources.

theorem or_elim (P Q R : IProp GF) : (P -∗ R) -∗ (Q -∗ R) -∗ P Q -∗ R := GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF (P -∗ R) -∗ (Q -∗ R) -∗ P Q -∗ R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H1 : P -∗ R ∗H2 : Q -∗ R ∗HP : P RGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H1 : P -∗ R ∗H2 : Q -∗ R ∗HQ : Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H1 : P -∗ R ∗H2 : Q -∗ R ∗HP : P R All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗H1 : P -∗ R ∗H2 : Q -∗ R ∗HQ : Q R All goals completed! 🐙

Separating conjunction distributes over disjunction (for the same reason as ordinary conjunction).

theorem sep_or_distr (P Q R : IProp GF) : P (Q R) ⊣⊢ P Q P R := GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GFP (Q R) ⊣⊢ P Q P R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF P (Q R) -∗ P Q P RGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF P Q P R -∗ P (Q R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF P (Q R) -∗ P Q P R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P Q P RGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HR : R P Q P R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P Q P R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P Q; All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HR : R P Q P R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HR : R P R; All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF P Q P R -∗ P (Q R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P (Q R)GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HR : R P (Q R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HQ : Q P (Q R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P PGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P P All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HQ : Q Q; All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P ∗HR : R P (Q R) GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P PGF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HR : R Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HP : P P All goals completed! 🐙 GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HR : R Q R GF:BundledGFunctorsP:IProp GFQ:IProp GFR:IProp GF ∗HR : R R; All goals completed! 🐙

Iris has existential and universal quantifiers over any Lean type. Existential quantifiers are proved using the iexists tactic. Elimination of existentials uses the pattern %x (with a % in front of the bound variable) to move it to the pure (Lean) context.

theorem sep_ex_distr {α : Type} (P : IProp GF) (Φ : α IProp GF) : (P x, Φ x) ⊣⊢ x, P Φ x := GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GF(P x, Φ x) ⊣⊢ x, P Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GF (P x, Φ x) -∗ x, P Φ xGF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GF ( x, P Φ x) -∗ P x, Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GF (P x, Φ x) -∗ x, P Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HP : P ∗HΦ : Φ x x, P Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HP : P ∗HΦ : Φ x P Φ x All goals completed! 🐙 GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GF ( x, P Φ x) -∗ P x, Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HP : P ∗HΦ : Φ x P x, Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HP : P PGF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HΦ : Φ x x, Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HP : P P All goals completed! 🐙 GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HΦ : Φ x x, Φ x GF:BundledGFunctorsα:TypeP:IProp GFΦ:α IProp GFx:α ∗HΦ : Φ x Φ x All goals completed! 🐙

Likewise, forall quantification works almost as in Lean. To introduce a universally quantified variable in the Iris context, you use the intro pattern %x. To specialise a hypothesis at a concrete value x, you write H $$ %x.

theorem sep_all_distr {α : Type} (P Q : α IProp GF) : ( x, P x) ( x, Q x) -∗ ( x, P x Q x) := GF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GF (( x, P x) x, Q x) -∗ x, P x Q x GF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GFx:α ∗HP : x, P x ∗HQ : x, Q x P x Q x GF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GFx:α ∗HP : x, P x P xGF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GFx:α ∗HQ : x, Q x Q x GF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GFx:α ∗HP : x, P x P x All goals completed! 🐙 GF:BundledGFunctorsα:TypeP:α IProp GFQ:α IProp GFx:α ∗HQ : x, Q x Q x All goals completed! 🐙 end Basics