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 GF⊢ P ∗ 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 GF⊢ P ∗ 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 GF⊢ P ∗ 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 GF⊢ Q ∨ 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 GF⊢ P ∗ (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