2. Basics of Iris
2.1. Introduction
In short, Iris is a higher-order concurrent separation logic framework. That is quite a mouthful, so let us break it down.
Firstly, the framework part means that Iris is not tied to any single programming language — it consists of a base logic and can be instantiated with any language one sees fit.
Secondly, a separation logic is a logic used to reason about programs
by introducing a notion of resource ownership. The idea is that one
must own a resource before one can interact with it. Ownership is
generally exclusive but can be transferred. To support this notion,
separation logic introduces a new connective called separating
conjunction, written P ∗ Q. This asserts ownership of the resources
described by propositions P and Q, and, in particular, P and Q
describe separate resources. So what is a resource? In Iris, we may
define our own notion of resources by creating a so-called resource
algebra, which we discuss later. For languages with a heap, a
canonical example of a resource is a heap fragment. Owning a resource
then amounts to controlling a fragment of the heap, allowing one to
read and update the associated locations.
Thirdly, a concurrent separation logic (CSL) extends on the above by
adding rules supporting concurrent constructions, such as Fork. As
ownership is exclusive, a program that spawns threads must decide how
to separate and delegate its resources to its threads, so that they
may perform their desired actions.
Finally, higher-order refers to the fact that predicates may depend on other predicates. Being a program logic means that programs are proved correct with respect to some specification — a description of the program's behavior and interaction with resources. As programs are usually composed of other programs, we would want our specifications to be generic so that they may be used in a myriad of contexts. Having support for higher-order predicates means that program specifications can be parametrized by arbitrary propositions. This allows one to write specifications for libraries independently of their clients — the clients will instantiate the propositions to specialize the specification to fit their needs.
In this chapter, we introduce basic separation logic in Iris.
2.2. Iris in Lean
The type of propositions in Iris is IProp GF. All proofs in Iris
are performed in a context with a GF : BundledGFunctors, used to
specify available resources. The details of GF will come later when
we introduce resource algebras. For now, just remember to work inside
a variable {GF} block, which makes a fixed but arbitrary collection
of resources available throughout the section.
open Iris Iris.BI
namespace Basics
variable {GF : BundledGFunctors}
Iris provides two kinds of propositional statements:
-
⊢ Pasks whetherPholds with no assumptions; -
P ⊢ Qasks whetherQholds assumingP.
In Lean, we work in the Iris Proof Mode (IPM/MoSeL). The practical implication is that we get a new context, called the spatial context, in addition to the usual Lean context, now called the non-spatial context. Hypotheses from both contexts can be used to prove the goal.
The regular Lean tactics can still be used when we work within the
non-spatial context, but, in general, we shall use new tactics that
work natively with the spatial context. These new tactics start with
the letter i: instead of intro H we use iintro H, and instead of
apply H we use iapply H. Note that identifiers for hypotheses in
the spatial context are ordinary Lean identifiers.
To see this in action we will prove the statement P ⊢ P, for all
P.
theorem asm (P : IProp GF) : P ⊢ P := GF:BundledGFunctorsP:IProp GF⊢ P ⊢ P
GF:BundledGFunctorsP:IProp GF⊢
∗H : P
⊢ P
All goals completed! 🐙
The tactic iintro adds P to the spatial context with the
identifier H. To finish the proof, one would normally use either
exact or apply. So in Iris, we use either iexact or iapply.
2.2.1. Technical Details
In Lean, the context and the goal form a sequent (writing ⊢ₓ for the
Lean entailment to distinguish it from the Iris entailment ⊢):
H₁ : Φ₁, ..., Hₙ : Φₙ ⊢ₓ Ψ
This is equivalent to the proposition Φ₁ ∧ ... ∧ Φₙ ⊢ₓ Ψ.
The Iris Proof Mode mimics this in the sense that the spatial context and the goal form an Iris sequent:
H₁ : Φ₁, ..., Hₙ : Φₙ ⊢ Ψ
However, as Iris is a separation logic, this is equivalent to the
entailment Φ₁ ∗ ... ∗ Φₙ ⊢ Ψ.
Technically, since Iris is built on top of Lean, proving an Iris
entailment in Lean corresponds to proving ⊢ₓ (P ⊢ Q). In other
words, the spatial context is part of the Lean goal. This is the
reason why the regular Lean tactics no longer suffice. The new tactics
work with both the non-spatial and the spatial contexts.
Iris propositions include many of the usual logical connectives such
as conjunction P ∧ Q. These notations are overloaded directly on the
IProp GF type, so they can be used without any extra scope
annotation.
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