Ralf Jung
email: jung@mpi-sws.org

Robbert Krebbers

Jacques-Henri Jourdan
email: jjourdan@mpi-sws.org

E Q}

{p

R} E Hoare-Val

Hoare-Fork

A modular foundation for higher-order concurrent separation logic

Keywords: | z | true | false | | λ x, e |, | x | e 1 (e 2 ) | fork {e} | assert

Iris is a framework for higher-order concurrent separation logic, which has been implemented in the Coq proof assistant and deployed very effectively in a wide variety of verification projects. Iris was designed with the express goal of simplifying and consolidating the foundations of modern separation logics, but it has evolved over time, and the design and semantic foundations of Iris itself have yet to be fully written down and explained together properly in one place. Here, we attempt to fill this gap, presenting a reasonably complete picture of the latest version of Iris (version 3.1), from first principles and in one coherent narrative.

Introduction

Iris is a framework for higher-order concurrent separation logic, implemented in the Coq proof assistant, which we and a growing network of collaborators have been developing actively since 2014. It is the only verification tool proposed so far that supports

• foundational machine-checked proofs of • deep correctness properties for • fine-grained concurrent programs in • higher-order imperative languages.

By foundational machine-checked proofs, we mean proofs that are performed directly in a proof assistant against the operational semantics of the programming languages under consideration and assuming only the low-level axioms of mathematical logic (as encoded in the type theory of Coq). 1 By deep correctness properties, we mean properties that go beyond mere safety, such as contextual refinement or full functional correctness. By fine-grained concurrent programs, we mean programs that use low-level atomic synchronization instructions like compare-and-set (CAS) in order to maximize opportunities for parallelism. And by higher-order imperative languages, we mean languages like ML and Rust that combine first-class functions, abstract types, and higher-order mutable references. Moreover, Iris is general in the sense that it is not tied to a particular language semantics and can be used to derive and deploy a range of different formal systems, including but not limited to: logics for atomicity refinement of fine-grained concurrent data structures [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF], Kripke logical-relations models for relational reasoning in ML-like languages [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF][START_REF] Krogh-Jespersen | A relational model of typesand-effects in higher-order concurrent separation logic[END_REF]Timany et al., 2018;[START_REF] Frumin | ReLoC: A mechanised relational logic for fine-grained concurrency[END_REF], program logics for relaxed memory models [START_REF] Kaiser | Strong logic for weak memory: Reasoning about release-acquire consistency in Iris[END_REF], a program logic for object capability patterns in a JavaScript-like language [START_REF] Jung | Robust and compositional verification of object capability patterns[END_REF], and a safety proof for a realistic subset of the Rust programming language [START_REF] Jung | RustBelt: Securing the foundations of the Rust programming language[END_REF].

In this paper, we report on the semantic and logical foundations of Iris, in particular its latest iteration ("Iris 3.1"). Before getting to those foundations and what is so interesting about them, we begin with a bit of historical background on concurrent separation logic.

A brief history of concurrent separation logic

Around the turn of the millennium, Peter O'Hearn, John Reynolds, Hongseok Yang, and collaborators proposed separation logic, a derivative of Hoare logic geared toward reasoning more modularly and scalably about pointer-manipulating programs [START_REF] O'hearn | Local reasoning about programs that alter data structures[END_REF][START_REF] Reynolds | Separation logic: A logic for shared mutable data structures[END_REF]. Inheriting ideas from earlier work on BI-the logic of "bunched implications" [START_REF] O'hearn | The logic of bunched implications[END_REF][START_REF] Ishtiaq | BI as an assertion language for mutable data structures[END_REF])-separation logic is a "resource" logic, in which propositions denote not only facts about the state of the program, but also ownership of resources. The notion of "resource" in the original separation logic is fixed to be a "heap"-i.e., a piece of the global memory represented as a finite partial mapping from memory locations to the values stored there-and the key primitive proposition about heaps is the "points-to" connective → v, asserting ownership of the singleton heap mapping to v. If we are verifying an expression e which has → v in its precondition, then we can assume not only that the location currently points to v, but also that the "right" to update is "owned" by e. Hence, while verifying e, we need not consider the possibility that another piece of code in the program might "interfere" with e

Iris

Toward this end, we developed Iris [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF], a higher-order concurrent separation logic with the express goal of simplification and consolidation. The key idea of Iris is that even the fanciest of the interference-control mechanisms in recent concurrency logics can be expressed by a combination of two orthogonal (and already well-known) ingredients: partial commutative monoids (PCMs) and invariants. PCMs enable the user of the logic to roll their own type of fictional (or "logical" or "ghost") state, which is essential to encoding a wide variety of reasoning mechanisms-like permissions, tokens, capabilities, histories, and protocols-which feature in advanced CSLs. Invariants serve to tie that fictional state to the underlying physical state of the program. Using just these two mechanisms, [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF] showed how to take complex primitive proof rules from prior logics and derive them within Iris, leading to the slogan: "Monoids and invariants are all you need."

Unfortunately, in Iris's initial incarnation (later dubbed "Iris 1.0"), this slogan turned out to be misleading in two ways:

• Monoids are not enough. There are certain useful varieties of ghost state, such as "named propositions" (see §3.3), in which the structure of the ghost state must be defined mutually recursively with the language of separation-logic propositions. We refer to such ghost state as higher-order ghost state, and for encoding higher-order ghost state, it seems we need something more sophisticated than monoids. • Iris is not just monoids + invariants. Although monoids and invariants did indeed constitute the two main conceptual elements of Iris 1.0-and they are arguably "canonical" in their simplicity and universality-the realization of these concepts in the logic involved a number of interacting logical mechanisms, some of which were simple and canonical, others not so much. For example, several mechanisms for controlling ghost state and invariant namespaces were built in as primitive, along with a notion of mask-changing view shift (for performing logical updates to resources that may involve "opening" or "closing" invariants) and weakest preconditions (for encoding Hoare triples). Moreover, the primitive proof rules for these mechanisms were non-standard, and their semantic model quite involved, making the justification of the primitive rules-not to mention the very meaning of Iris's Hoare-style program specifications-very difficult to understand or explain. Indeed, the Iris 1.0 paper [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF] avoided even attempting to present the formal model of program specifications in any detail at all. Iris from the ground up 5

Our subsequent work on Iris 2.0 [START_REF] Jung | Higher-order ghost state[END_REF] and Iris 3.0 (Krebbers et al., 2017a) has addressed these two points, respectively:

• In Iris 2.0, to support higher-order ghost state, we proposed a generalization of

PCMs to what we call cameras. Roughly speaking, a camera can be thought of as a kind of "step-indexed PCM", that is, a PCM equipped with a step-indexed notion of equality [START_REF] Appel | An indexed model of recursive types for foundational proof-carrying code[END_REF][START_REF] Birkedal | First steps in synthetic guarded domain theory: step-indexing in the topos of trees[END_REF] such that the composition operator of the PCM is appropriately "compatible" with the step-indexed equality. For reasons discussed in §4, step-indexing has always been essential to Iris's model of higher-order separation-logic propositions, and by incorporating stepindexed equality into PCMs, cameras enable us to model ghost state that can embed propositions. • In Iris 3.0, we aimed to simplify the remaining sources of complexity in Iris by taking the Iris story to its (so to speak) logical conclusion: applying the reductionist Iris methodology to Iris itself! Specifically, at the core of Iris 3.0 is a small, resourceful base logic, which distills the essence of Iris to what we argue is a bare minimum: it is a higher-order logic extended with the basic connectives of BI (separating conjunction and magic wand), a predicate for resource ownership, and a handful of simple modalities, but it does not bake in any propositions about programs as primitive. It is only this base logic whose soundness must be proven directly against an underlying semantic model (employing the aforementioned cameras). Moreover, using the handful of mechanisms provided by the base logic, Iris 1.0's fancier mechanisms of mask-changing view shifts and weakest preconditions-and their associated proof rules-can all be derived within the logic. And by expressing the fancier mechanisms as derived forms, we can now explain the meaning of Iris's program specifications at a much higher level of abstraction than was previously possible.

Overview of the paper

One unfortunate consequence of the gradual development of Iris across several conference papers is that (1) these papers are not entirely consistent with one another (since the logic has changed over time), and (2) the design and the semantic foundations of Iris have yet to be fully written down and explained together properly in one place.

Here, we attempt to fill this gap by presenting the latest version of Iris (namely, Iris 3.1we will discuss the minor changes since Iris 3.0 in §8.1) in as unified and self-contained a manner as possible. Our goal is not to convince the reader that Iris is useful, although it certainly is-a number of papers (mentioned at the beginning of this introduction) have already demonstrated this, and a more consolidated presentation of the lessons learned from those papers deserves its own journal article. Rather, our goal here is to present a reasonably complete picture of Iris, from first principles and in one coherent narrative.

Toward that end, we begin in §2 and §3 with a tour of the key features of Iris via a simple illustrative example. We continue in §4 by presenting the key algebraic constructions that we need in order to build a semantic model of Iris, including our novel notion of cameras. In §5, we describe the Iris 3.1 base logic and show how to give a model of this base logic in terms of the constructions from the previous section. Then, in §6 and §7, we show how to recover the full-blown program logic of Iris by encoding it on top of the Iris base logic.

(We omit the usual operations on pairs and sums.)

The logic includes the usual connectives and rules of higher-order separation logic, some of which are shown in the grammar below. (Actually, many of the connectives given in this grammar are defined as derived forms in Iris, and this flexibility is an important aspect of the logic, but we leave further discussion of this point until §5- §7.)

P, Q, R ::= True | False | P ∧ Q | P ∨ Q | P ⇒ Q | ∀x. P | ∃x. P | P * Q | → v | P | t = u | P N | a γ | V(a) | {P} e {v. Q} E | P E Q | . . .
The Iris proof rules include the usual rules of concurrent separation logic for Hoare triples as given in Figure 1. Note that HOARE-BIND is a generalization of the usual sequencing rule to arbitrary evaluation contexts.

What makes Iris a higher-order separation logic is that universal and existential quantifiers can range over any type, including that of propositions and (higher-order) predicates. Furthermore, notice that Hoare triples {P} e {v. Q} E are part of the proposition logic (also often called "assertion logic") instead of being a separate entity. As a consequence, triples can be used in the same way as any logical proposition, and in particular, they can be nested to give specifications of higher-order functions. Hoare triples {P} e {v. Q} E are moreover annotated with a mask E to keep track of which invariants are currently in force. We will come back to invariants and masks in §2.2, but for the time being we omit them. {True} mk oneshot() { c. ∀v. {True} c.tryset(v) {w. w ∈ {true, false}} * {True} c.check() { f . {True} f () {True}} } Fig. 2. Example code and specification.

A motivating example. We will demonstrate the higher-order aspects of Iris, and some other of its core features, by verifying the safety of the simple higher-order program given in Figure 2. This program is of course rather contrived, but it serves to showcase the core features of Iris.

The function mk oneshot() allocates a oneshot location at x and returns a record c with two closures. (Formally, records are syntactic sugar for pairs.) The function c.tryset(n) tries to set the location x to n, which will fail if the location has already been set. We use CAS to ensure correctness of the check even if two threads concurrently try to set the location. The function c.check() records the current state of the location x and then returns a closure which, if the location x has already been initialized, checks that it does not change.

The specification looks a little funny with most pre-and postconditions being True. The reason for this is that all we are aiming to show here is that the code is safe, i.e., that the assertions2 "succeed", meaning that the branch with assert(false) will never get executed, and in the final branch, n will always equal m. In Iris, Hoare triples imply safety, so we do not need to impose any further conditions.

As is common for Hoare triples about functional programs, the postcondition of every Hoare triple has a binder to refer to the return value. We will omit the binder if the result is always unit.

We use nested Hoare triples to express that mk oneshot() returns closures: Since Hoare triples are just propositions, we can put them into the postcondition of mk oneshot() to describe what the client can assume about c. Furthermore, since Iris is a concurrent program logic, the specification for mk oneshot() actually permits clients to call c.tryset(n) and c.check(), as well as the closure f returned by c.check(), concurrently from multiple threads and in any combination.

It is worth pointing out that Iris is an affine separation logic, which means that it enjoys the weakening rule P * Q ⇒ P. Intuitively, this rule lets one throw away resources; for example, in the postcondition of a Hoare triple one can throw away ownership of unused memory locations. Since Iris is affine, it does not have the Emp connective, which asserts ownership of no resources [START_REF] O'hearn | Local reasoning about programs that alter data structures[END_REF]; rather, Iris's True connective, which describes ownership of any resources, is the identity of separating conjunction (i.e., P * True ⇔ P). We discuss this design choice further in §9.5.

High-level proof structure. To perform this proof, we need to somehow encode the fact that we are only performing a oneshot update to x. To this end, we will allocate a ghost location a γ with name γ and value a, which mirrors the current state of x. This may at first sound rather pointless; why should we record a value in the ghost state that is exactly the same as the value in a particular physical location?

The point is that using ghost state lets us choose what kind of sharing is possible on the location. For a physical location , the proposition → v expresses full ownership of (and hence the absence of any sharing of it). In contrast, Iris permits us to choose whatever kind of structure and ownership we want for our ghost location γ; in particular, we can define it in such a way that, although the contents of γ mirror the contents of x, we can freely share ownership of γ once it has been initialized (by a call to tryset). This in turn will allow the closure returned by check to own a piece of γ witnessing its value after initialization. We will then have an invariant (see §2.2) tying the value of γ to the value of x, so we know which value that closure is going to see when it reads from x, and we know that that value is going to match y.

Another way to describe what is happening is to say that we are applying the idea of fictional separation [START_REF] Dodds | Deny-guarantee reasoning[END_REF]: The separation on γ is "fictional" in the sense that multiple threads can own parts of γ and therefore manipulate the same shared variable x, the two being tied together by an invariant.

With this high-level proof structure in mind, we now explain how exactly ownership and sharing of ghost state can be controlled. Iris allows one to use ghost state via the proposition a γ , which asserts ownership of a piece a of a ghost location γ. The flexibility of Iris stems from the fact that for each ghost location γ, the user of the logic can pick the type M of its value a, instead of this type being fixed in advance by the logic. However, in order to make it possible to use a γ in interesting ways, M cannot just be any type, but should have some additional structure, namely:

• It should be possible to compose ownership of different threads. To make this possible, the type M should have an operator (•) for composition. The crucial rule of this operation in the logic is a 4).

• b γ ⇔ a γ * b γ (see GHOST-OP in Figure
• Combinations of ownership a γ * b γ that do not make sense should be ruled out by the logic (i.e., they should entail False). This happens, for example, when multiple threads claim to have ownership of an exclusive resource. To make this possible, the operator (•) should be partial.

Composition of ownership should moreover be associative and commutative, to reflect the associative and commutative nature of separating conjunction. For that reason, partial commutative monoids (PCMs) have become the canonical structure for representing ghost state in separation logics. In Iris, we are deviating slightly from this, using our own notion of a resource algebra (RA), whose definition is in Figure 3. Every PCM is an RA, but not vice versa-and as we will see in our example, the additional flexibility afforded by RAs results in additional logical expressiveness.

There are two key differences between RAs and PCMs:

1. Instead of partiality, RAs use validity to rule out invalid combinations of ownership. Specifically, there is a predicate V : M → Prop identifying valid elements. Validity is compatible with the composition operation (RA-VALID-OP). We will see in §4.3 that this take on partiality is necessary when defining the structure of higher-order ghost state, i.e., ghost state whose structure depends on iProp, the type of propositions of the Iris logic.3 2. Instead of having a single unit ε that is an identity to every element (i.e., that enjoys ε • a = a for any a), RAs have a partial function |-| that assigns to an element a its (duplicable) core |a|, as demanded by RA-CORE-ID. We further demand that |-| is idempotent (RA-CORE-IDEM) and monotone (RA-CORE-MONO) with respect to the extension order, defined similarly to that for PCMs (RA-INCL).

An element can have no core, which is indicated by |a| = ⊥. In order to conveniently deal with partial cores, we use the metavariable a ? to range over elements of M ? M {⊥} and lift the composition (•) to M ? . As we will see in §3.1, partial cores help us to build interesting composite RAs from smaller primitives.

In the special case that an RA does have a unit ε, we call it a unital RA (uRA). From RA-CORE-MONO, it follows that the core of a uRA is a total function, i.e., |a| = ⊥.

The idea of a duplicable core is not new; we discuss related work in §9.3.

A resource algebra (RA) is a tuple (M, V : M → Prop, |-| : M → M ? , (•) : M × M → M) satisfying: ∀a, b, c. (a • b) • c = a • (b • c) (RA-ASSOC) ∀a, b. a • b = b • a (RA-COMM) ∀a. |a| ∈ M ⇒ |a| • a = a (RA-CORE-ID) ∀a. |a| ∈ M ⇒ ||a|| = |a| (RA-CORE-IDEM) ∀a, b. |a| ∈ M ∧ a b ⇒ |b| ∈ M ∧ |a| |b| (RA-CORE-MONO) ∀a, b. V(a • b) ⇒ V(a) (RA-VALID-OP)
where

M ? M {⊥} with a ? • ⊥ ⊥ • a ? a ? a b ∃c ∈ M. b = a • c (RA-INCL) a B ∀c ? ∈ M ? . V(a • c ? ) ⇒ ∃b ∈ B. V(b • c ? ) a b a {b}
A unital resource algebra (uRA) is a resource algebra M with an element ε satisfying: A resource algebra for our example. We will now define the RA that can be used to verify our example, which we call the oneshot RA. 4 The goal of this RA is to appropriately reflect the state of the physical location x. The carrier is defined using a datatype-like notation as follows: 5

V(ε) ∀a ∈ M. ε • a = a |ε| = ε
M pending | shot(n : Z) |
The two important states of the ghost location are: pending, to represent the fact that the single update has not yet happened, and shot(n), saying that the location has been set to n. We need an additional element to account for partiality; it is the only invalid element:

V(a) a =
The most interesting part of an RA is, of course, its composition: What happens when ownership of two threads is combined? (Compositions not defined by the following equation are mapped to .)

shot(n) • shot(m)    shot(n) if n = m otherwise 4
In this section, we give an explicit definition of the oneshot RA. This may feel a bit verbose, and indeed, in §3.1, we show that this RA can in fact be defined using a couple of combinators. 5 Notations for invalid elements have not been entirely consistent among earlier Iris papers. In this paper we use to denote the invalid element of an RA (if the RA has such an element), ⊥ to denote the absence of a core, and ε to denote the global unit (if it exists).

The property PENDING-EXCL says that composition of pending with anything else is invalid.

As a result of this, if we own pending, we know that no other thread can own another part of this location. Furthermore, SHOT-AGREE says that composition of two shot(-) elements is valid only if the parameters (i.e., the values picked for the oneshot) are the same. This reflects the idea that once a value has been picked, it becomes the only possible value of the location; every thread agrees on what that value is. Finally, SHOT-IDEM says that we can also duplicate ownership of the location as much as we want, once it has been set to some n. This allows us to share ownership of this piece of ghost state among any number of threads.

Notice that we use the term "ownership" in a rather loose sense here: any element of an RA can be "owned". For elements like shot(n) that satisfy the property a = a • a, owning a is equivalent to owning multiple copies of a-in this particular case, ownership is no longer exclusive, and it may be more appropriate to call this knowledge. We can hence think of shot(n) as representing the knowledge that the value of the location has been set to n. Hence, resource algebras use the same mechanism to serve a double purpose: modeling both (1) ownership of resources and (2) sharing of knowledge.

Coming back to our oneshot RA, we still have to define the core |-|:

|pending| ⊥ |shot(n)| shot(n) | |
Note that, since ownership of pending is exclusive, it has no suitable unit element, so we assign no core. This completes the definition of the oneshot RA. It is now straightforward to verify that this RA satisfies the RA axioms.

Frame-preserving updates. So far, we have defined which states our ghost location can be in and how the state of the location can be distributed across multiple threads. What is still missing, however, is a way of changing the ghost location's state. When the ghost state changes, it is important that it remains valid-Iris always maintains the invariant that the state obtained by composing the contributions of all threads is a valid RA element. We call state changes that maintain this invariant frame-preserving updates.

The simplest form of frame-preserving update is deterministic. We can do a framepreserving update from a to b (written a b) when the following condition is met:

∀c ? ∈ M ? . V(a • c ? ) ⇒ V(b • c ? )
In other words, for any resource (called a frame) c ? ∈ M ? such that a is compatible with c ? (i.e., V(a • c ? )), it has to be the case that b is also compatible with c ? .

For example, with our oneshot RA it is possible to pick a value if it is still pending:

pending shot(n) (ONESHOT-SHOOT)

GHOST-ALLOC

V(a)

True E ∃γ. a γ GHOST-OP a • b γ ⇔ a γ * b γ GHOST-VALID a γ ⇒ V(a) GHOST-UPDATE a B a γ E ∃b ∈ B. b γ HOARE-VS P E P {P } e {v. Q } E ∀v. Q E Q {P} e {v. Q} E VS-TRANS P E Q Q E R P E R VS-FRAME P E Q P * R E Q * R INV-ALLOC P E P N HOARE-INV { P * Q 1 } e {v. P * Q 2 } E \N atomic(e) N ⊆ E { P N * Q 1 } e {v. P N * Q 2 } E HOARE-CTX {P * Q} e {v. R} E persistent(Q) Q - * {P} e {v. R} E PERSISTENT-DUP
persistent(P)

P ⇔ P * P PERSISTENT-INV persistent( P N ) PERSISTENT-SEP persistent(P) persistent(Q) persistent(P * Q) PERSISTENT-GHOST |a| = a persistent( a γ )
Fig. 4. Some Iris proof rules.

The reason for this is PENDING-EXCL: pending actually is not compatible with any element; composition always yields . It is thus the case that from V(pending • c ? ), we know c ? = ⊥. This makes the rest of the proof trivial.

If we think of the frame c ? as being the composition of the resources owned by all the other threads, then a frame-preserving update is guaranteed not to invalidate the resources of concurrently running threads. The frame can be ⊥ if no other thread has any ownership of this ghost location. 6 By doing only frame-preserving updates, we know we will never "step on anybody else's toes".

In general, we also permit non-deterministic frame-preserving updates (written a B) where the target element b is not fixed a priori, but instead a set B is fixed and some element b ∈ B is picked depending on the current frame. This is formally defined in Figure 3. We will discuss non-deterministic frame-preserving updates further when we encounter our first example of such an update in §3.2.

Proof rules for ghost state. Resource algebras are embedded into the logic using the proposition a γ , which asserts ownership of a piece a of the ghost location γ. Iris from the ground up 13 and a postcondition. They do not need any code because they only touch the ghost state, which does not correspond to any operation in the actual program.

The proof rule GHOST-ALLOC in Figure 4 can be used to allocate a new ghost location, with an arbitrary initial state a so long as a is valid according to the chosen RA. The rule GHOST-UPDATE says that we can perform frame-preserving updates on ghost locations, as described above.

All the usual structure rules for Hoare triples also hold for view shifts, like framing (VS-FRAME). The rule HOARE-VS illustrates how view shifts are used in program verification: we can apply view shifts in the pre-and postconditions of Hoare triples. This corresponds to composing the "triples for ghost moves" (i.e., view shifts) with a Hoare triple for e. Doing so does not change the expression in the triple because the ghost state actions performed by the view shifts do not speak about any actual code.

The rule GHOST-OP says that ghost state can be separated (in the sense of separation logic) following the composition operation (•) defined for the RA, and GHOST-VALID encodes the fact that only valid RA elements can ever be owned.

Notice that view shifts ( ) are very different from implications (⇒) and wands (- * ): the implication P ⇒ Q says that whenever P holds, Q necessarily holds too. In contrast, the view shift P Q says that whenever P holds, Q holds under the proviso that we change the ghost state. Hence, unlike implication and wand, the only way to eliminate a view shift is through the rule HOARE-VS.

Invariants

Now that we have set up the structure of our ghost location γ, we have to connect the state of γ to the actual physical value of x. This is done using an invariant.

An invariant [START_REF] Ashcroft | Proving assertions about parallel programs[END_REF]) is a property that holds at all times: each thread accessing the state may assume the invariant holds before each step of its computation, but it must also ensure that it continues to hold after each step. Since we work in a separation logic, the invariant does not just "hold"; it expresses ownership of some resources, and threads accessing the invariant get access to those resources. The rule HOARE-INV realizes this idea as follows:

{ P * Q 1 } e {v. P * Q 2 } E \N atomic(e) N ⊆ E { P N * Q 1 } e {v. P N * Q 2 } E
This rule is quite a mouthful, so we will go over it carefully. First, there is the proposition P N , which states that P (an arbitrary proposition) is maintained as an invariant. The rule says that having this proposition in the context permits us to access the invariant, which involves acquiring ownership of P before the verification of e and giving back ownership of P after said verification. Crucially, we require that e is atomic, meaning that computation is guaranteed to complete in a single step. This is essential for soundness: the rule allows us to temporarily use and even break the invariant, but after a single atomic step (i.e., before any other thread could take a turn), we have to establish it again. Notice that P N is just another kind of proposition, and it can be used anywhere that normal propositions can be used-including the pre-and postconditions of Hoare triples, and invariants themselves, resulting in nested invariants. The latter property is sometimes referred to as impredicativity. Impredicativity is the reason that the invariant P appears with a "later" modality P in the pre-and postcondition; without the later modality, the rule for opening invariants is unsound (see §8.2). However, if one does not store Hoare triples or invariants inside invariants, one can generally ignore the later modality, which we will do throughout this section. We will further discuss the later modality in §5.5. Finally, we come to the mask E and namespace N : they avoid the issue of reentrancy. We have to make sure that the same invariant is not accessed twice at the same time, as that would incorrectly duplicate the underlying resource. To this end, each invariant has a namespace N identifying it. Furthermore, each Hoare triple is annotated with a mask to keep track of which invariants are still enabled. 7 Accessing an invariant removes its namespace from the mask, ensuring that it cannot be accessed again in a nested fashion.

Invariants are created using the INV-ALLOC rule (Figure 4): Whenever a proposition P has been established, it can be turned into an invariant. This can be viewed as a transfer of the resources backing up P from being locally owned by some thread to being shared by all threads via the invariant. Creating an invariant is a view shift; we will see the reason for this in §7, where we will see how exactly invariants in Iris are working "under the hood". Until then we will ignore the namespaces N of invariants ( P N ) and the masks E of Hoare triples ({P} e {v. Q} E ) and view shifts (P E Q).

Persistent propositions

We have seen that Iris can both express ownership of exclusive resources (like → v or pending γ ), as well as knowledge of properties like P N and shot(n) γ that, once true, hold true forever. We call the latter class of propositions persistent. Further examples of persistent propositions are validity (V(a)), equality (t = u), Hoare triples ({P} e {v. Q}) and view shifts (P Q). Persistent propositions can be freely duplicated (PERSISTENT-DUP); the usual restriction of resources being usable only once does not apply to them.

The versatility of ghost ownership is also visible in its relation to persistence: while ghost ownership of some elements (like pending) is ephemeral, ownership of a core is persistent (PERSISTENT-GHOST). This manifests the idea mentioned in §2.1 that RAs can express both ownership and knowledge in one unified framework, with knowledge merely referring to ownership of a persistent resource. In this view, the core |a| is a function which extracts the knowledge out of RA element a. In the proof of check, persistent ghost ownership will be crucial.

One important role of persistent propositions is related to nested Hoare triples: as expressed by the rule HOARE-CTX, the nested Hoare triple may only use propositions Q from the "outer" context that are persistent. Persistence guarantees that Q will still hold when the Hoare triple is "invoked" (i.e., when the code it specifies is executed), even if that happens multiple times.

A closely related concept is the notion of duplicable propositions, i.e., propositions P for which one has P ⇔ P * P. This is a strictly weaker notion, however: not all duplicable propositions are persistent. For example, considering the points-to connective q -→ v with a fractional permission q [START_REF] Boyland | Checking interference with fractional permissions[END_REF][START_REF] Bornat | Permission accounting in separation logic[END_REF], the proposition ∃q. q -→ v is duplicable (which follows from halving the fractional permission q), but it is not persistent.

Proof of the example

We have now seen enough features of Iris to proceed with the actual verification problem outlined in Figure 2. We show a Hoare outline of the proof in Figure 5. Notice that we letexpanded the load of x in the match. This is solely to facilitate writing the proof outline; Iris is perfectly capable of verifying the correctness of the code as given originally.

Proof of mk oneshot. First of all, from the allocation performed by the ref construct, we obtain x → inl(0). Next, we allocate a new ghost location γ with the structure of the oneshot RA defined above, picking the initial state pending. (This is implicitly using HOARE-VS to justify applying a view shift while verifying a Hoare triple.) Finally, we establish and create the following invariant: Since x is initialized with inl(0), the invariant I initially holds. What remains to be done is establishing our postcondition, which consists of two Hoare triples. Thanks to HOARE-CTX, we can use the freshly allocated invariant for the proofs of these triples. (In the proof outline, HOARE-CTX allows us to keep resources when we "step over a λ ", but only when the resources are persistent. This corresponds to the fact that the function can be called several times, so that the resources should not be used up by one call.)

I (x → inl(0) * pending γ ) ∨ (∃n. x → inr(n) * shot(n) γ )
Proof of tryset. The function tryset accesses location x, so we start by opening the invariant around the CAS. CAS is atomic, so the rule HOARE-INV applies. The invariant provides x → no matter which side of the disjunction we obtain, so safety of the memory operation is justified. In case the CAS fails, no change is made to x, so reestablishing the invariant is immediate.

The case in which the CAS succeeds is more subtle. Here, we know that x originally had value inl(0), so we obtain the invariant in its left disjunct. Thus, after the CAS, we have the following:

x → inr(n) * pending γ How can we reestablish the invariant I after this CAS? Clearly, we must pick the right disjunct, since x → inr(n). Hence we have to update the ghost state to match the physical state. To this end, we apply GHOST-UPDATE with the frame-preserving update ONESHOT-SHOOT, which allows us to update the ghost location to shot(n) if we own pending, which we do. We then have I again and can finish the proof.

Notice that we could not complete the proof if tryset would ever change x again, since ONESHOT-SHOOT can only ever be used once on a particular ghost location. We have to be in the pending state if we want to pick the n in shot(n). This is exactly what we would expect, since check indeed relies on x not being modified once it has been set to inr(n).

Proof of check. What remains is to prove correctness of check. We open our invariant I to justify the safety of ! x, which is immediate since I always provides x → , but we will not immediately close I again. Instead, we will have to acquire some piece of ghost state that shows that if we read an inr(n), then x will not change its value. At this point in the proof, we have the following proposition:

x → y * (y = inl(0) * pending γ ) ∨ (∃n. y = inr(n) * shot(n) γ )
We use the identity shot(n) = shot(n) • shot(n) with GHOST-OP to show that this logically implies:

x → y * (y = inl(0) * pending γ ) ∨ (∃n. y = inr(n) * shot(n) γ * shot(n) γ ))
which in turn implies:

I * (y = inl(0) ∨ (∃n. y = inr(n) * shot(n) γ ) P )
We can thus reestablish the invariant I, but we keep P, the information we gathered about y. The plan is to use this in the proof of the closure that we return.

To do so, we have to show that P is persistent as mandated by HOARE-CTX. We already discussed that shot(n)

γ and equalities are persistent, and it is indeed the case that all the Iris from the ground up 17 standard connectives preserve persistence. This matches the intuition that, once we observe that x has been set, we can then forever assume it will not change again.

To finish this proof, let us look at the closure returned by check in more detail: Again, we will open our invariant to justify the safety of ! x. Our proposition then is I * P. In order to proceed, we now distinguish the possible cases in P and I.

1. Case 1 (P "on the left"): We have I * y = inl(0). In this case, the match will always pick the first arm, and there is nothing left to show. 2. Case 2 (P "on the right", I "on the left"): We have:

x → inl(0) * pending γ * (∃n. y = inr(n) * shot(n) γ )
Using GHOST-OP, we can obtain pending • shot(n) γ and hence γ , which according to GHOST-VALID is a contradiction. 3. Case 2 (P "on the right", I "on the right"): We have:

(∃m. x → inr(m) * shot(m) γ ) * (∃n. y = inr(n) * shot(n) γ )
In particular, we obtain shot(n) • shot(m) γ . Using GHOST-VALID, this yields V(shot(n)• shot(m)), implying n = m by SHOT-AGREE. We are hence left with:

x → inr(n) * y = inr(n) * shot(n) γ
In particular, we know that we will take the third arm of the match, and the assertion reduces to assert(n = n), i.e., the assertion succeeds.

3 Advanced ghost state constructions

In the previous section we have seen that user-defined ghost state plays an important role in Iris. In this section, we will have a more thorough look at ghost state. First of all, in §3.1, we show that many frequently needed RAs can be constructed by composing smaller, reusable pieces. In §3.2 we then show that the ownership connective a γ can in fact be defined in terms of an even more primitive notion of "global" ghost ownership. In §3.3 we introduce the more advanced notion of higher-order ghost state and show that in its naive form it is inconsistent.

RA constructions

One of the key features of Iris is that it leaves the structure of ghost state entirely up to the user of the logic. If there is the need for some special-purpose RA, the user has the freedom to directly use it. However, it turns out that many frequently needed RAs can be constructed by composing smaller, reusable pieces-so while we have the entire space of RAs available when needed, we do not have to construct custom RAs for every new proof. For example, looking at the oneshot RA from §2.1, it really does three things:

1. It separates the allocation of an element of the RA from the decision about what value to store there (ONESHOT-SHOOT). 2. While the oneshot location is uninitialized, ownership is exclusive, i.e., at most one thread can own the location. 3. Once the value has been decided on, it makes sure everybody agrees on that value.

We can thus decompose the oneshot RA into the sum, exclusive and agreement RAs as described below. (In the definitions of all the RAs, the omitted cases of the composition and core are all .)

Sum. The sum RA M 1 + M 2 for any RAs M 1 and M 2 is:

M 1 + M 2 inl(a 1 : M 1 ) | inr(a 2 : M 2 ) | V(a) (∃a 1 ∈ M 1 . a = inl(a 1 ) ∧ V 1 (a 1 )) ∨ (∃a 2 ∈ M 2 . a = inr(a 2 ) ∧ V 2 (a 2 )) inl(a 1 ) • inl(a 2 ) inl(a 1 • a 2 ) inr(a 1 ) • inr(a 2 ) inr(a 1 • a 2 ) |inl(a 1 )|    ⊥ if |a 1 | = ⊥ inl(|a 1 |) otherwise |inr(a 2 )|    ⊥ if |a 2 | = ⊥ inr(|a 2 |) otherwise
Exclusive. Given a set X, the task of the exclusive RA EX(X) is to make sure that one party exclusively owns a value x ∈ X. We define EX as follows:

EX(X) ex(x : X) | V(a) a = |ex(x)| ⊥
Composition is always to ensure that ownership is exclusive-just like the composition of pending with anything else was (PENDING-EXCL).

Agreement. Given a set X, the task of the agreement RA AG 0 (X) is to make sure multiple parties can agree upon which value x ∈ X has been picked. (We call this AG 0 because we will refine this definition in §4.3 to obtain the final version, AG.) We define AG 0 as follows:

AG 0 (X) ag 0 (x : X) | V(a) ∃x ∈ X. a = ag 0 (x) ag 0 (x) • ag 0 (y)    ag 0 (x) if x = y otherwise |ag 0 (x)| ag 0 (x)
In particular, agreement satisfies the following property corresponding to SHOT-AGREE:

V(ag 0 (x) • ag 0 (y)) ⇒ x = y (AG0-AGREE)
Oneshot. We can now define the general idea of the oneshot RA as ONESHOT(X) EX(1) + AG 0 (X), and recover the RA for the example as ONESHOT(Z).

Frame-preserving updates. Another advantage of decomposing RAs into separate pieces is that for these pieces we can prove generic frame-preserving updates. We have the fol- The agreement RA permits no non-trivial frame-preserving updates.

Considering the oneshot RA, the above rules are not yet sufficient. In order to prove a variant of the frame-preserving update ONESHOT-SHOOT, as needed for the running example, we need to change the left summand into the right:

inl(ex()) inr(ag 0 (x))
This frame-preserving update can be proven because inl(ex()) has no frame, i.e., there is no c with V(inl(ex()) • c). The absence of a frame allows us to pick any valid RA element on the right-hand side. It turns out that we can easily generalize this idea to any RA: we say that an RA element a is exclusive (or has no frame) if: exclusive(a) ∀c. ¬V(a • c)

For exclusive elements we have the following generic frame-preserving update:

EXCLUSIVE-UPDATE exclusive(a) V(b) a b
Notice that the above frame-preserving update is only useful for an RA with a partial core: exclusive(a) is always false for any valid RA element a with |a| = ⊥. Obtaining frame-preserving updates for switching the "side" of a sum is one of our key motivations for making the core a partial function instead of a total function.

Derived forms and the global ghost state

In Iris, there is a strong emphasis on only providing a minimal core logic, and deriving as much as possible within the logic rather than baking it in as a primitive. For example, both Hoare triples and propositions of the form l → v are actually derived forms. As we will see in §4 and §5, this has the advantage that the model can be kept simpler, since it only has to justify soundness of a minimal core logic.

In this section we discuss the encoding of the proposition a γ for ghost ownership, which is not a built-in notion either. As we have seen, this proposition allows one to have multiple ghost locations γ, all of which can range over different RAs. As a primitive, Iris provides just a single global ghost location whose structure is described by a single global RA picked by the user. However, by picking an appropriate RA, one can define the proposition a γ in Iris and derive its rules as given in Figure 4 within the logic. Iris's primitive construct for ghost ownership is Own (a), whose rules are given in Figure 6. It is worth noting that the global RA must be unital, which means it should have a unit element ε (Figure 3). The reason for this is twofold. First of all, it allows us to have the rule OWN-UNIT, which is used to prove GHOST-ALLOC. Second of all, unital RAs enjoy the properties that the extension order is reflexive and that the | -| function is total, which simplify the model construction in §4 and §5. Note that one can always turn an RA into a unital RA by extending it with a unit element, preserving all frame-preserving updates.

In order to define the a γ connective, we need to instantiate the single global ghost state RA with a heap of ghost cells. To this end, we assume that we are given a family of RAs (M i ) i∈I for some index set I, and then we define the RA M of the global ghost state to be the indexed (dependent) product over "heaps of M i " as follows:

M ∏ i∈I N fin -M i
In this construction, we use the natural pointwise lifting of the RA operations from each M i through the finite maps and products all the way to M, so that M is an RA itself. In fact, it is a uRA, with the unit being the product of all empty maps:

ε λ j. / 0
It is straightforward to show that frame-preserving updates can be performed pointwise in products, and the following laws hold for frame-preserving updates in finite maps:

FMAP-UPDATE a B f [i ← a] { f [i ← b] | b ∈ B} FMAP-ALLOC a ∈ V f { f [i ← a] | i / ∈ dom( f )}
Notice that FMAP-ALLOC is a non-deterministic frame-preserving update: we cannot pick the index i of the new element in advance because it depends on the frame. Instead we are guaranteed to obtain a new singleton map with some fresh index i being mapped to a. The rule FMAP-UPDATE expresses pointwise lifting of updates to the finite map.

Using M as the uRA to instantiate Iris with allows us to (a) use all the M i in our proofs, and (b) treat ghost state as a heap, where we can allocate new instances of any of the M i at any time. We define the connective for ghost ownership of a single location as:

a : M i γ Own   λ j.    [γ → a] if i = j / 0 otherwise  
In other words, a : M i γ asserts ownership of the singleton heap [γ → a] at position i in the product. We typically leave the concrete M i implicit and write just a γ . The rules for --given in Figure 4 can now be derived from those for Own (-) shown in Figure 6.

Obtaining modular proofs. Even with multiple RAs at our disposal, it may still seem like we have a modularity problem: every proof is done in an instantiation of Iris with some Iris from the ground up 21 particular family of RAs. As a result, if two proofs make different choices about the RAs, they are carried out in entirely different logics and hence cannot be composed.

To solve this problem, we generalize our proofs over the family of RAs that Iris is instantiated with. All proofs are carried out in Iris instantiated with some unknown (M i ) i∈I . If the proof needs a particular RA, it further assumes that there exists some j such that M j is the desired RA. Composing two proofs is thus straightforward; the resulting proof works in any family of RAs that contains all the particular RAs needed by either proof. Finally, if we want to obtain a "closed form" of some particular proof in a concrete instance of Iris, we simply construct a family of RAs that contains only those that the proof needs.

Notice that the generalization over resource algebras happens at the meta-level here, so the set of available resource algebras must be fixed before the proof begins. If, for example, the type of the ghost state should depend on some program value determined only at runtime, that would involve a form of dependent type. We have not yet explored to what extent our approach is compatible with this situation, as it does not seem to arise in practice.

Naive higher-order ghost state paradox

As we have seen, the setup of Iris is that the user provides a resource algebra M by which the logic is parameterized. This gives a lot of flexibility, as the user is free to decide what kind of ghost state to use. We now discuss whether we could stretch this flexibility even further, by allowing the construction of the resource algebra M to depend on the type of Iris propositions iProp. In [START_REF] Jung | Higher-order ghost state[END_REF], we dubbed this phenomenon higherorder ghost state and showed that it has useful applications in program verification. In Krebbers et al. (2017a), we then showed that higher-order ghost state has applications beyond program verification: it can be used to encode invariants in terms of plain ghost state.

The way we model higher-order ghost state puts certain restrictions on the way iProp can be used in the construction of the user-supplied resource algebra. In [START_REF] Jung | Higher-order ghost state[END_REF], these restrictions appeared as a semantic artifact of modeling the logic. However, in this section we show that restrictions of some kind are in fact necessary: allowing higher-order ghost state in a naive or unrestricted manner leads to a paradox (i.e., an unsound logic). In order to demonstrate this paradox, we use the simplest instance of higher-order ghost state, named propositions (also called "stored" or "saved" propositions) [START_REF] Dodds | Verifying custom synchronization constructs using higher-order separation logic[END_REF].

In order to explain named propositions, let us have another look at the agreement resource algebra AG 0 (X). As part of the decomposition of the oneshot RA into small, reusable pieces, we also generalized the set of values that the proof can pick from (i.e., the parameter of AG 0 ) from Z to any given set X. Instead of choosing X to be a "simple" set like Z or ℘(N), we could try choosing it to be iProp, the type of Iris propositions. The RA AG 0 (iProp) would then allow us to associate a ghost location (or "name") γ with a proposition P. However, this ability to "name" propositions leads to a paradox.

Theorem 1 (Higher-order ghost state paradox). Assume we can instantiate the construction from §3.2 with AG 0 (iProp) being in the family of RAs. Then we have: Before proving this paradox, let us take a look at the proof rules of Iris that are crucial to prove the paradox:

True E False
True ∃γ. ag 0 (A(γ)) γ (SPROP0-ALLOC) ag 0 (P 1 ) γ * ag 0 (P 2 ) γ ⇒ P 1 = P 2 (SPROP0-AGREE)
The rule SPROP0-AGREE, which states that named propositions with the same name are the same, follows easily from AG0-AGREE. The rule SPROP0-ALLOC, which allows one to allocate a name for a proposition, looks a lot like an instance of GHOST-ALLOC, except that the initial state of the new ghost location may depend on the fresh γ (here, A is a function mapping ghost names to iProp, which lets us express said dependency). In fact, the rule SPROP0-ALLOC follows from the following generalized rule for ghost state allocation:

∀γ. V(g(γ)
)

True E ∃γ. g(γ) γ (GHOST-ALLOC-DEP)
This rule allows the initial state of the freshly allocated ghost location (described by the function g) to depend on the location γ that has been picked. The rule GHOST-ALLOC-DEP can be proved from the basic rules for the Own (-) connective as shown in Figure 6 and the following frame-preserving update on finite maps:

∀i. V(g(i)) f { f [i ← g(i)] | i / ∈ dom( f )} (FMAP-ALLOC-DEP)
This completes the proof of SPROP0-ALLOC.

We now turn towards the proof of Theorem 1. First, we define

A(γ) ∃P. (P ⇒ False) * ag 0 (P) γ Q(γ) ag 0 (A(γ)) γ
Intuitively, A(γ) asserts that ghost location γ names some proposition P that does not hold. (Since our ghost locations are described by the RA AG 0 (iProp), we know that everybody agrees on which proposition is named by a ghost location γ, so it makes sense to talk about "the proposition with name γ".)

The proposition Q(γ) says that the proposition with name γ is A(γ). After unfolding the definition of A, we can see that Q(γ) means that the proposition with name γ says that the proposition with name γ does not hold-i.e., the proposition with name γ states its own opposite. This is an instance of the classic "liar's paradox", so it should not be surprising that it leads to a contradiction. Concretely, we show the following lemma, from which Theorem 1 is a trivial consequence.

Lemma 1. We have the following properties:

1. Q(γ) ⇒ (A(γ) ⇒ False), 2. Q(γ) ⇒ A(γ), and, 3. True E ∃γ. Q(γ).
Proof. Notice that all propositions we are using for this paradox, except the existentially quantified P in the definition of A(γ), are persistent, so we can mostly ignore the substructural aspects of Iris in this proof. Iris from the ground up

23

For 1, we can assume Q(γ) and A(γ) and we have to show False. After unfolding both of them, we obtain some P such that:

ag 0 (A(γ)) γ * (P ⇒ False) * ag 0 (P) γ
From SPROP0-AGREE, we can now obtain that A(γ) = P. Since we have a proof of A(γ), and we have (P ⇒ False), this yields the desired contradiction.

For 2, we can assume Q(γ). Our goal is A(γ), so we start by picking P A(γ). We now have to show (A(γ) ⇒ False), which easily follows from (a) and our Q(γ). We further have to show ag 0 (A(γ))

γ , which is exactly Q(γ). Hence we are done.

Finally, for 3, we use SPROP0-ALLOC.

So, what went wrong here? Where does this contradiction originate from? The problem is that higher-order ghost state, and named propositions in particular, allow us to express concepts like "the proposition with name γ". This allows us to define a proposition as being its own negation. Usually, such nonsense is prevented because the definition of a proposition cannot refer back to this proposition in a cyclic way; the fact that higher-order ghost state lets us give names to propositions lets us circumvent this restriction and close the cycle. In some sense, this is very similar to Landin's knot, where a recursive function is created by indirecting recursive references of the function to itself through a location on the (higher-order) heap.

The paradox shows that one has to be very careful if one wishes to support higher-order ghost state. In particular, one cannot allow resource algebras like AG 0 (iProp) to be used to instantiate the Iris logic. In the subsequent sections of this paper we show that higher-order ghost state is sound if the recursive occurrences of iProp are suitably guarded. To model named propositions, one should then use AG( iProp). Here, AG is a refined definition of the agreement RA, and the "later" plays the role of a guard. As a result of the guard (whose constructor is next), we only get a weaker version of the rule SPROP0-AGREE:

ag(next(P 1 )) γ * ag(next(P 2 )) γ ⇒ (P 1 = P 2 )
This rule gives us the equality one step of execution "later", which prohibits the paradox in this section. In the next sections we show how to extend the logic and its model with the operators and .

A model of Iris

In the previous sections, we gave a high-level tour of Iris. In the remainder of the paper, we will make the definition of Iris more precise and prove that it is sound.

As in much prior work on separation logic, we prove soundness of Iris by giving a semantic model. Fundamentally, such a proof proceeds in three steps:

1. Define a semantic domain of propositions with an appropriate notion of entailment. 2. Define an interpretation function that maps all statements in the logic to elements of the semantic domain. 3. For every proof rule of the logic, show that the semantic interpretation of the premise entails the semantic interpretation of the conclusion. In this section, we tackle (1): defining the semantic domain of Iris propositions. This may seem somewhat difficult since we have not yet nailed down exactly what Iris's propositions are. However, in §2, we have already given an intuition for the kind of statements we want to make in Iris. This is sufficient to discuss the construction of the semantic domain of Iris propositions. We will then proceed bottom-up, first in §5 building the Iris base logic on top of this semantic domain, then in §6 and §7 building the higher-level program logic connectives that we saw in §2 on top of the base logic.

As demonstrated in §2, one of the main features of Iris is resource ownership, which, for a separation logic, should not be very surprising. The usual way [START_REF] O'hearn | Local reasoning about programs that alter data structures[END_REF] to model the propositions of a separation logic is as predicates over resources. Whereas traditionally "resource" meant a fragment of the heap, in our case, "resource" is something the user can define by an RA of their choice. Furthermore, since Iris is an affine separation logic (see also §9.5), these predicates have to be monotone: If a predicate holds of a resource, it should hold of any larger resource. 8This leads us to the following attempt at a semantic domain of Iris propositions:

iProp Res mon --→ Prop
Here, Res is the global resource algebra, chosen by the user, and Prop is the domain of propositions of the ambient meta-logic. However, recall that we mentioned in §3.3 that we will need higher-order ghost state to encode advanced features of Iris like invariants. That means we want the resources Res to depend on iProp, while at the same time iProp depends on Res!

In order to formally capture this dependency, we replace the fixed choice of Res by a function F that can depend on iProp. The user can then pick F (rather than picking Res), and Res is defined as F(iProp). Thus, the definition changes as follows:

iProp = Res mon --→ Prop where Res F(iProp) (PRE-IRIS)
Unfortunately, this is no longer just a definition-the existence of a solution iProp to this equation is not at all obvious. In fact, for certain choices of F, for example by taking F(X) AG 0 (X), we end up with iProp = AG 0 (iProp) mon --→ Prop, which can be shown to have no solutions using a cardinality argument. This should not be surprising; after all, in §3.3 we showed that having AG 0 (iProp) as our type of resources leads to a contradiction in the logic, which implies that no model with AG 0 (iProp) as the type of resources can exist!

In order to circumvent this problem, we use step-indexing [START_REF] Appel | An indexed model of recursive types for foundational proof-carrying code[END_REF]. Roughly, the idea of step-indexing is to make elements of iProp a sequence of propositions: the n-th proposition in such a sequence holds as far as only the n following steps of computation are concerned. This step-index can be used for stratifying the recursion in PRE-IRIS. In the following, we give a conceptual overview of the model and the use of stepindexing in §4.1. Subsequently, in §4.2- §4.4, we introduce the actual infrastructure to treat step-indexing in a modular way. Finally, in §4.5- §4.7, we use this infrastructure to obtain a semantic domain of Iris propositions. Since [START_REF] Birkedal | First steps in synthetic guarded domain theory: step-indexing in the topos of trees[END_REF] it is known that step-indexing can be treated in an abstract, categorical way. Roughly speaking, this means that we can apply the following translation of terminology to go from the usual set-theoretical setting to a step-indexed setting:

Set-theoretic setting

Step-indexed setting Sets OFEs (Ordered families of equivalences)

Prop (Propositions) SProp (Downwards-closed sets of step-indices) Functions Non-expansive functions
The notions on the right-hand side of the table will be explained in §4.2. For now, it is important to know that the usual operations on Prop (e.g., equality, conjunction, implication and quantification) and constructions on sets (e.g., products and sums) are also available on SProp and OFEs, respectively. These operations and constructions furthermore enjoy many of the same laws we know from set theory, which means that most of the time, one can entirely ignore in which of these two settings we are working.

Notice that not just the notions of propositions and sets change, but also the function space: In the step-indexed setting, we are restricted to the non-expansive functions. These are functions that are "compatible" with the structure that OFEs impose on their domains and codomains. By restricting the function space, we get that the proposition ∀x, y. x = y ⇒ f (x) = f (y) in SProp (i.e., after translation to the step-indexed setting) always holds-in fact, this is the very definition of non-expansiveness of f .

If set theory and the step-indexed setting behave much the same, what is the benefit of working in the step-indexed setting? It turns out that unlike in the set-theoretic setting, in the step-indexed setting one can obtain a solution (up to isomorphism) to the equation PRE-IRIS that we have seen before:

iProp ∼ = Res mon --→ SProp where Res F(iProp)
The catch to solving this equation in the step-indexed setting is that F has to be "guarded" in some sense (by a "later" as we will see in this section).

While the Iris model presented in this section may seem arbitrary and the reader may have the impression that everything just "luckily" works out in the end, this is not the case. The definitions arise by translating a standard set-theoretic model of the logic of bunched implications [START_REF] O'hearn | The logic of bunched implications[END_REF] to the step-indexing world. This translation works out in the expected way due to the close correspondence between the set-theoretic and the stepindexed setting. However, showing that one can work in the step-indexed setting without even noticing that one is doing so requires a significant body of category theory. In this paper, we will avoid diving into category theory, and thus present the model in elementary terms instead of categorically. Essentially, we "unfold" the step-indexed definitions so that we can stay in set theory as our ambient framework.

= ⊆ T × T ) n∈N ) satisfying: ∀n. ( n =) is an equivalence relation (OFE-EQUIV) ∀n, m. n ≥ m ⇒ ( n =) ⊆ ( m =) (OFE-MONO) ∀x, y. x = y ⇔ (∀n. x n = y) (OFE-LIMIT)
A complete OFE (COFE) is an OFE T where: Presenting the model in elementary terms not only makes the construction more accessible, but it also eases the formalization in Coq. The formalization of the Iris model in Coq is thus very close to the presentation in this section.

∀(c n ) n∈N . (∀n, m. n ≤ m ⇒ c m n = c n ) ⇒ ∃c ∞ . ∀n. c n n = c ∞ (OFE-COMPL) A function f : T → U is non-expansive if: ∀n, x, y. x n = y ⇒ f (x) n = f (y) (OFE-NONEXP) A function f : T → U is contractive if: ∀n, x, y. (∀m < n. x m = y) ⇒ f (x) n = f (y) (OFE-CONTR)

Ordered families of equivalences

Ordered families of equivalences (OFEs), proposed by Di [START_REF] Gianantonio | A unifying approach to recursive and corecursive definitions[END_REF] and defined in Figure 7, are sets with some basic algebraic structure to equip them with a form of step-indexing. The key intuition behind OFEs is that elements x and y are nequivalent, written x n = y, if they are equivalent for n steps of computation, i.e., if they cannot be distinguished by a program running for no more than n steps. It follows that, as n increases, n = becomes more and more refined (OFE-MONO). In the limit, it agrees with plain equality (OFE-LIMIT).

A function f : T ne -→ U between two OFEs is non-expansive if it preserves the structure defined by ( n =) (OFE-NONEXP). In other words, applying such a function to some data will not suddenly introduce differences between seemingly equal data. Elements that cannot be distinguished by programs within n steps remain indistinguishable after applying f .

In the step-indexed world, we will always work with equality up to some n. For these equalities to be of any use, it is hence crucial that all functions respect the equivalence relation n =. This is why we require all functions to be non-expansive. If f does not just preserve n-equality, but actually makes things "more equal", then we say that f is contractive (OFE-CONTR). Notice that the definition given in Figure 7 is equivalent to demanding f (x)

0 = f (y) and x n = y ⇒ f (x) n+1 = f (y).
The OFEs that are essential to the Iris model are defined in Figure 8. The discrete OFE ∆X turns any set X into an OFE by assigning the degenerate step-indexed equivalence. The later OFE T moves the step-indexed equivalence of an OFE T up by one, thus making everything "one level more equal" than in T .

The OFE of step-indexed propositions SProp represents propositions ("truth values") that are true for some number of steps, or forever. (SProp corresponds to the set of ordinals up to and including ω.) Crucially, elements of SProp are downwards-closed, which means Iris from the ground up 27

Step-indexed propositions:

SProp {X ∈ ℘(N) | ∀n ≥ m. n ∈ X ⇒ m ∈ X} X n = Y ∀m ≤ n. m ∈ X ⇔ m ∈ Y X n ⊆ Y ∀m ≤ n. m ∈ X ⇒ m ∈ Y Discrete: ∆X X x n = y x = y Later: T {next(x) | x ∈ T } next(x) n = next(y) n = 0 ∨ x n-1 = y
Fig. 8. The OFEs used in constructing the Iris model.

that if a statement is valid for n steps, it is also valid for m ≤ n steps. Instead of just working with absolute ideas of things being either true or not, SProp provides a more approximate approach. Fully valid statements are represented by N, false statements are represented by / 0; but there are also infinitely many "shades of truth" between these two extremes. Two step-indexed propositions are equal at step-index n if they agree for all step-indices up until and including n. Similarly, we define step-indexed inclusion ( n ⊆) as inclusion ignoring elements larger than n.

An important class of step-indexed propositions are equalities between elements of an OFE. Indeed, the set n x n = y of step-indices at which x and y are equal is an SProp:

From OFE-MONO, it follows that the closure requirement of SProp is satisfied. Now, if f is a non-expansive function, then n x n = y ⊆ n f (x) n = f (y)
, which means that nonexpansiveness of f allows us to perform rewriting of a step-indexed equality under f . Chains and complete OFEs. The step-indexed equality of an OFE gives rise to the notion of a chain, which is a sequence (c n ) n∈N of elements that become "more and more equal":

∀n, m. n ≤ m ⇒ c m n = c n
A chain has a limit if there exists an element c ∞ that is "more and more equal" to c n as n increases:

∀n. c n n = c ∞ An OFE T is said to be complete (it is a COFE) if every chain in T has a limit (OFE-COMPL).
Chains with limits are interesting because they can be used to define an object as the limit of a sequence of approximations, rather than defining the object directly. For example, we will see in §5.5 how this can be used to take the fixed-point of a function. All of the OFEs discussed so far are complete.

Higher-order agreement

With the framework of the model-in particular the notion of an OFE-in hand, let us now look again at the agreement RA AG 0 defined in §3.1. In §3.3, we explained that we would like to obtain an agreement construction on iProp in order to encode higher-order ghost state, and we explained in the beginning of the present section that doing this naively leads to a circularity. Let us however ignore that problem for now, and instead have a closer look at AG 0 (T ) for any OFE T . Our goal is to make this "work" as an OFE, i.e., AG 0 (T ) should itself be a well-behaved OFE.

Equipping AG 0 (T ) with a step-indexed equality is straightforward (we just lift the equality from T ). However, there is a problem: the composition operator (•) is not non-expansive. That is, given P and Q that are equal up to n steps, but not equal for all steps, i.e., P n = Q for some n while P = Q, we have:

ag(P) • ag(Q) = n = ag(P) = ag(P) • ag(P)
For (•) to be non-expansive, we should have ag(P) • ag(P) n = ag(P) • ag(Q), which is clearly not the case. This is not entirely surprising: Agreement composition was defined in terms of plain (absolute) equality rather than step-indexed equality.

In order to maintain the correspondence laid out in §4.1, we want composition to be non-expansive. This means we have to find another construction. Given any OFE T , we are looking for an OFE and RA AG(T ) with a non-expansive composition. Just like the previous construction, AG has to provide a non-expansive function ag ∈ T ne -→ AG(T ) and they should enjoy the following properties:

∀a ∈ AG(T ). |a| = a (AG-CORE) ∀x. V(ag(x)) (AG-VALID) ∀x, y. V(ag(x) • ag(y)) ⇒ x = y (AG-AGREE)
These properties imply a • a = a and injectivity of ag. Any construction satisfying these laws is sufficient to perform the proof in §2.4. However, that is not enough. Our goal is to have a construction that interacts well with stepindexing. As already mentioned, in a step-indexed world, we are dropping absolute terms like the elements being (exactly) equal or statements being (completely) valid. Instead we use step-indexed versions of those terms. So what we are looking for is a step-indexed version of AG-CORE, AG-VALID and AG-AGREE. To this end, we first turn validity into a stepindexed predicate V of type AG(T ) → SProp. (This also explains why we used V up to now: We kept the identifier V free for the more general step-indexed validity.) Now we can formulate the following properties:

∀a ∈ AG(T ), n. |a| n = a (AG-CORE-STEP) ∀x, n. n ∈ V(ag(x)) (AG-VALID-STEP) ∀x, y, n. n ∈ V(ag(x) • ag(y)) ⇒ x n = y (AG-AGREE-STEP)
The new V and these properties arise by applying the translation outlined in §4.1 to AG-CORE, AG-VALID and AG-AGREE. First, we mention some key observations:

• The rule AG-CORE-STEP is equivalent to AG-CORE (by OFE-LIMIT).

• If we define V(a) ∀n. n ∈ V(a), then AG-VALID and AG-VALID-STEP are equivalent. This is a natural definition because it says that an element is (fully) valid if it is valid at all step-indices. Iris from the ground up

29

• The rule AG-AGREE-STEP implies AG-AGREE, but the converse is not true. In fact, AG-AGREE-STEP is significantly stronger because it lets us derive "approximate" equality of x and y from "approximate" validity.

In order to fulfill these requirements, we can no longer define the composition as a case distinction on whether or not the two operands are fully equal. This case distinction completely ignores the case where the operands are approximately equal, introducing a kind of "non-continuity", which is what breaks non-expansiveness of composition in AG 0 .

So instead, we use a completely different idea: An element of AG(T ) is a non-empty finite set of elements of T . Composition is just set union. We can think of this set as recording which elements have been composed so far. The "evaluation" of whether that composition makes any sense is "delayed" until validity checking: An element of AG(T ) is valid for n steps if all elements in that set are equal up to ( n =), i.e., if the set is a singleton when viewed for just n steps. More formally, we define AG(T ) for any OFE T :

AG(T ) a ∈ ℘ fin (T ) a = / 0 |a| a a n = b (∀x ∈ a. ∃y ∈ b. x n = y) ∧ (∀y ∈ b. ∃x ∈ a. x n = y) a • b a ∪ b V(a) n ∀x, y ∈ a. x n = y ag(x) {x}
The step-indexed equality on AG(T ) is defined so that we have a

Cameras

The AG(T ) construction as defined in the previous section is no longer just an RA-we had to define V as a step-indexed predicate, and we had to prove non-expansiveness of the operations. In this section we introduce the algebraic structure of cameras to abstractly characterize such "step-indexed RAs". 9 Essentially, cameras arise by applying the translation given in §4.1 to RAs. You can find its definition in Figure 9. The differences from RAs are as follows:

• The carrier needs to be an OFE, and the core and composition operations have to be non-expansive. 9 The reader may wonder why on earth we call them "cameras". The reason, which may not be entirely convincing, is that "camera" was originally just used as a comfortable pronunciation of "CMRA", the name used in earlier Iris papers. CMRA was originally supposed to be an acronym for "complete metric resource algebras" (or something like that), but we were never very satisfied with it and thus ended up never spelling it out. To make matters worse, the "complete" part of CMRA is now downright misleading, for whereas previously the carrier of a CMRA was required to be a COFE (complete OFE), in the present paper we relax that restriction and permit it to be an (incomplete) OFE ( §8.1 goes into more detail about this change). For these reasons, we have decided to stick with the name "camera", for purposes of continuity, but to drop any pretense that it stands for something.

A camera is a tuple (M : OFE, V : M ne -→ SProp, |-| : M ne -→ M ? , (•) : M × M ne -→ M) satisfying: ∀a, b, c. (a • b) • c = a • (b • c) (CAMERA-ASSOC) ∀a, b. a • b = b • a (CAMERA-COMM) ∀a. |a| ∈ M ⇒ |a| • a = a (CAMERA-CORE-ID) ∀a. |a| ∈ M ⇒ ||a|| = |a| (CAMERA-CORE-IDEM) ∀a, b. |a| ∈ M ∧ a b ⇒ |b| ∈ M ∧ |a| |b| (CAMERA-CORE-MONO) ∀a, b. V(a • b) ⊆ V(a) (CAMERA-VALID-OP) ∀n, a, b 1 , b 2 . n ∈ V(a) ∧ a n = b 1 • b 2 ⇒ ∃c 1 , c 2 . a = c 1 • c 2 ∧ c 1 n = b 1 ∧ c 2 n = b 2 (CAMERA-EXTEND)
where • The predicate V that defines valid elements is replaced by the step-indexed variant V.

a b ∃c. b = a • c (CAMERA-INCL) a n b ∃c. b n = a • c (CAMERA-INCLN) a B ∀n, c ? ∈ M ? . n ∈ V(a • c ? ) ⇒ ∃b ∈ B. n ∈ V(b • c ? ) a b a {b}
The predicate V must be non-expansive (so at step-index n, it may not distinguish nequal elements of M). Furthermore, V(a) being an SProp enforces that at smaller step-indices, only more elements can become valid. The rule CAMERA-VALID-OP expresses that we need decomposition to preserve validity at every step-index. We can recover the simpler RA validity via V(a) ∀n. n ∈ V(a). RA-VALID-OP then follows from CAMERA-VALID-OP. The definition of a frame-preserving update a B is also affected by this change, and again, the camera version of a frame-preserving update implies the RA version. Just as the validity predicate V of an RA encodes the fact that composition is partial, the step-indexed validity predicate V of a camera encodes "step-indexed partiality". This is more general than using a partial function, which can only be "fully defined" or "fully undefined". • The notion of inclusion comes in two versions: The step-indexed version, ( n ), and the equality-based one, ( ). It is worth noting that, in general, these relations are reflexive only when the core |-| is a total function. However, the main use of these relations as order relations appears in the definition of uniform predicates in §4.5, where we assume that the core is total10 , so this is not a problem in practice. • The extension axiom CAMERA-EXTEND roughly states that decomposition must commute with step-indexed equivalence. This property is needed for proving the validity of the rule (P * Q) P * Q. The issue is discussed in more depth in our technical appendix [START_REF] Team | The Iris documentation and Coq development[END_REF].

Notice that every RA can be trivially turned into a camera by equipping it with the discrete OFE ∆ and letting V(a)

n V(a) , i.e., a is either valid at all step-indices or at Iris from the ground up 31 no step-index. Furthermore, functions between discrete OFEs are trivially non-expansive. This means that a user who does not need higher-order ghost state can safely ignore this new construction and need not prove that their functions are non-expansive. We can easily generalize existing RA constructions to camera constructions by lifting the step-indexed validity. For example, for + from §3.1, this is done as follows:

V(a) {n | (∃a 1 ∈ M 1 . a = inl(a 1 ) ∧ n ∈ V 1 (a 1 )) ∨ (∃a 2 ∈ M 2 . a = inr(a 2 ) ∧ n ∈ V 2 (a 2 ))}

Uniform predicates as a model of separation logic

We have now established enough infrastructure to come back to our original goal of modeling propositions of separation logic in a step-indexed fashion. In this setting, a proposition will be a step-indexed predicate over any unital camera (i.e., a camera with a unit element). We call such predicates uniform predicates. The formal definition of the OFE of uniform predicates over some camera M is based on monotone, non-expansive SProp predicates:

M mon,ne ----→ SProp Φ : M ne -→ SProp ∀n, a, b. a n b ⇒ Φ(a) n ⊆ Φ(b) Φ n = Ψ ∀a. Φ(a) n = Ψ(a)
That is, M mon,ne ----→ SProp is the OFE of non-expansive step-indexed predicates that are monotone with respect to camera inclusion n and SProp inclusion n ⊆ at every step-index. This monotonicity requirement is common in models of affine separation logic.

To obtain uniform predicates, we take a quotient over the monotone step-indexed predicates defined above. The goal of the quotient is to identify predicates that only differ on invalid elements. For the purpose of the logic, we only care about valid elements-we want to equate predicates when they agree on all valid elements. Since validity is a step-indexed notion, obtaining this quotient requires not only a quotient on the carrier set but also a modified step-indexed equality ( n =):

UPred(M) M mon,ne ----→ SProp ≡ Φ ≡ Ψ ∀m, a. m ∈ V(a) ⇒ (m ∈ Φ(a) ⇐⇒ m ∈ Ψ(a)) Φ n = Ψ ∀m ≤ n, a. m ∈ V(a) ⇒ (m ∈ Φ(a) ⇐⇒ m ∈ Ψ(a))
An important property of UPred(M) is that it is complete. Completeness is crucial to solve the recursive domain equation ( §4.6) and to model a fixed-point operator in the logic ( §5.6). Notice that UPred(M) is complete even if M is not complete. In fact, as we will discuss further in §8.1, UPred(M) turns out to be the only OFE that we need to be complete.

Having defined UPred as a semantic domain of propositions, we can now define semantic entailment between two elements of UPred(M): Φ entails Ψ if Ψ holds "more often" on valid elements, or formally speaking:

Φ Ψ ∀n, a. n ∈ V(a) ∧ n ∈ Φ(a) ⇒ n ∈ Ψ(a)

The Iris model

We now have everything set up to construct a model of Iris that supports higher-order ghost state. In order to obtain such a model, we will solve the following equation:

iProp ∼ = UPred(Res) where Res F(iProp) (IRIS)
This looks very much like PRE-IRIS from the beginning of this section, except that we are now using uniform predicates UPred(Res) (i.e., predicates involving a step-index) instead of ordinary monotone predicates Res mon --→ Prop. We can rewrite this equation into a fixed-point problem over some G as follows:

G(T ) UPred(F(T ))
It is easy to verify that a fixed-point iProp of G is sufficient to satisfy IRIS.

We solve this fixed-point using America and Rutten's theorem [START_REF] America | Solving reflexive domain equations in a category of complete metric spaces[END_REF][START_REF] Birkedal | The category-theoretic solution of recursive metric-space equations[END_REF]. In order to state this theorem, we cannot entirely avoid category theory. Readers not familiar with category theory can simply skip the remainder of this section and jump to §4.7. Concretely, G has a fixed-point if it is a locally contractive bifunctor from the category of COFEs to itself. In order to understand what that means, we will define the category of COFEs and then define what it means for G to be a locally contractive bifunctor.

Categorical prerequisites of America and Rutten's theorem. The first thing that we need to define is the categories OFE of OFEs (respectively COFE of COFEs): The objects of this category are simply the OFEs (respectively COFEs), and the arrows are the nonexpansive functions.

Next we need the notion of bifunctors: A bifunctor from the category A to the category B is a functor from A op × A to B. Both functors and contravariant functors (i.e., functors from A op to B) can be considered as particular cases of bifunctors, by ignoring either the first or second argument. As a result, many OFE and COFE constructions we have seen so far can be considered as bifunctors between the corresponding categories: As examples, this is the case of UPred, of the non-expansive arrow • ne -→ •, and of , the later OFE. Bifunctors can be composed: if F is a bifunctor from B to C, and G is a bifunctor from A to B, then the composition

F • G defined by (F • G)(X op , X) = F(G op (X, X op ), G(X op , X))
is also a bifunctor. For simplicity's sake, we abuse notation and do not repeat the covariant and contravariant parameters. Hence, for defining F • G, we would simply have written

(F • G)(X) = F(G(X)).
We now lift the notions of non-expansiveness and contractiveness from functions between OFEs to bifunctors. A bifunctor G from COFE to COFE is said to be locally nonexpansive if its action on arrows G :

(T 2 ne -→ T 1 ) × (T 1 ne -→ T 2 ) → (G(T 1 , T 1 ) ne -→ G(T 2 , T 2 )) is itself a non-expansive map.
Here, non-expansive functions are equipped with an OFE with the usual step-indexed equality:

f n = g ∀u. f (u) n = g(u)
In a similar spirit, a bifunctor G is locally contractive if its action on arrows is contractive. The functor (see Figure 8) is the archetypical example of a locally contractive bifunctor. Iris from the ground up

33

Now we can state America and Rutten's theorem:

Theorem 2 (America and Rutten). Let 1 be the discrete COFE on the unit type: 1 ∆{()}.

Given a locally contractive bifunctor G : COFE op × COFE → COFE, and provided that G(1, 1) is inhabited, then there exists a unique COFE T such that G(T op , T ) ∼ = T (i.e., the two are isomorphic in COFE).

Using America and Rutten's theorem. To apply America and Rutten's theorem, we have to show that G(T ) UPred(F(T )) is in fact a locally contractive bifunctor. G is composed from F and UPred, and similarly, we will decompose the proof of local contractiveness.

To achieve that, we define the category Camera of cameras: We say that f is an arrow in the category if it is a non-expansive function and further satisfies the following properties of structure preservation (making f a camera homomorphism):

∀a. |a| ∈ M ⇒ | f (a)| = f (|a|) ∀a. |a| = ⊥ ⇒ | f (a)| = ⊥ ∀a, b. f (a • b) = f (a) • f (b) ∀n, a. n ∈ V(a) ⇒ n ∈ V( f (a))
Now that we have defined these categories, it is easy to show that the various constructions on these categories like UPred, AG, etc. are locally non-expansive, that is locally contractive, and that composing locally non-expansive and contractive functors preserves local contractiveness.

Instantiating the Iris model

As we have seen, the model of propositions iProp is a solution of the equation IRIS:

iProp ∼ = UPred(Res)
where Res F(iProp)

In this equation, F is a user-selected function from OFEs to unital cameras, which describes the type of ghost state one wishes to instantiate Iris with. In order to solve this equation, the function F has to satisfy some restrictions (it has to be a locally contractive bifunctor, as discussed in §4.6). However, it is important to note that the user of Iris does not have to understand what exactly these restrictions are; it is sufficient to ensure that:

1. F is composed of the usual type constructors (like functions and products), arbitrary but fixed OFEs and cameras, and OFE and camera constructions we have given in this section (like and AG), and 2. the recursive occurrences of X in F(X) are guarded by a .

In case the user needs multiple different cameras described by different functions F, we can apply the construction already described in §3.2: Given an entire family of functions (F i ) i∈I , we can instantiate Iris with the "global function" F displayed below:

F(X) ∏ i∈I N fin -F i (X)
It is easy to show that this construction preserves the restrictions from §4.6. Now, if one wants to obtain a version of Iris with named propositions, one could pick F(T ) AG( (T )). This way, we obtain ghost state of type AG( iProp)-i.e., we can use any logical proposition and put it into a resource. The crucial reason why this is sound, contrary to the naive version of named propositions that we have seen in §3.3, is that validity only gives an equality at a lower step-index, i.e., given n > 0, we have:

n ∈ V(ag(next(x)) • ag(next(y))) ⇒ x n-1 = y
As we will show in the next section, this equality will be internalized in the logic as:

V(ag(next(x)) • ag(next(y))) ⇒ (x = y) (SPROP-AGREE)
Contrary to the inconsistent rule SPROP0-AGREE from §3.3, the equality on the right-hand side of this rule is now guarded by the later modality , which reflects that it holds only at the next step-index. This is a weaker conclusion than we might wish for, but it has the benefit of restoring consistency, and as we shall see, it is still quite powerful.

The Iris base logic

Now that we have the semantic domain iProp of propositions in hand, we can define the primitive connectives of the logic as elements of that domain. As already mentioned, Iris attempts to keep the base logic as small as possible. In particular, the base logic has no concept of programs and does not provide any primitive connective to reason about themmany of the connectives presented in §2 are missing in the base logic. In §6 and §7, we will show how these missing connectives can be defined-inside the logic-in terms of just the propositions of the base logic.

As is common for grammars of higher-order logic, the grammar of Iris is essentially the simply-typed lambda calculus equipped with some primitive types (of which, of course, the type iProp of logical propositions is the most interesting one) and with primitive terms operating on these types. 11 The following grammar defines both the valid types of the logic as well as its terms. 12 We use P, Q, . . . as meta-variables for terms that are propositions, and t, u, . . . as meta-variables for general terms of any type.

τ ::= M | iProp | 0 | 1 | τ | τ × τ | τ + τ | τ → τ | . . . t, u, P, Q ::= x | next(t) | λ x : τ. t | t(u) | a | |t| | t • u | µx : τ. t | False | True | t = τ u | P ⇒ Q | P ∧ Q | P ∨ Q | P * Q | P - * Q | ∃x : τ. P | ∀x : τ. P | Own (t) | V(t) | P | P | | P | . . .
We omit the usual terms for introduction (if applicable) and elimination of sums, products, unit and the empty type. We also do not show the definition of the well-typedness judgment Γ t : τ, where Γ assigns types to free variables. It is mostly standard, the only exception being a restriction on recursive predicates: they have to be guarded. In µx. t, the variable x can only appear in t under the later ( ) modality.

The type M is the type of the elements of the unital camera by which Iris is parameterized, and the term a lets us use any unital camera element inside the logic. Iris from the ground up

35

The ellipses above hint at the fact that the logic may be extended with additional types and terms by the user. Formally speaking, the user picks a signature of types, terms and proof rules that they want to add to the logic, e.g., to be able to talk about lists and reason about their properties. This is formally spelled out in the technical appendix. In the following, we assume that standard types like sets, lists, etc. together with their operations and properties are appropriately reflected into the logic.

The semantic interpretation functionfor types and -: τ ρ for terms of type τ in variable assignment ρ is given in Figure 10. Notice that the interpretation of every logical connective comes with a proof obligation: namely, to verify the side conditions of UPred, as spelled out in §4.5.

It is important to note that functions τ → τ are interpreted as non-expansive functions τ ne -→ τ . In particular, this means that the predicate Φ in the rule EQ-LEIBNIZ, which lets us substitute equals by equals, is interpreted as a non-expansive function τ ne -→ iProp. Non-expansiveness is crucial to justify this rule. In addition, since functions are interpreted as non-expansive functions, it is necessary to establish that the map P : iProp ρ is nonexpansive in the variable assignment ρ.

The rules for the logical provability judgment13 P Q are displayed in Figure 11. Here, P Q is shorthand for having both P Q and Q P. The connection between the definition of the model and the logic is made by the following key theorem:

Theorem 3 (Soundness of the semantic model).

(P Q) ⇒ P Q
This theorem says that if a judgment can be derived using the rules, then the semantic interpretations of the involved propositions are related by entailment in the semantic model (as defined in §4.5).

We will now go over the proof rules and model of the basic connectives in more detail. We do not discuss the ordinary connectives of intuitionistic higher-order logic, which are standard both in terms of their model (given in Figure 10) and their rules (displayed in the technical appendix). The remaining connectives and proof principles fall into two broad categories: those dealing with ownership of resources ( §5.1- §5.4) and those related to stepindexing ( §5.5- §5.7).

Separation logic

The connectives * and - * of bunched implications (O'Hearn & Pym, 1999) make our base logic a separation logic: they let us reason about ownership of resources. The key point is that P * Q describes ownership of a resource that can be separated into two disjoint pieces, one satisfying P and one satisfying Q. This is in contrast to P ∧ Q, which describes ownership of a resource satisfying both P and Q.

For example, consider the resources owned by different threads in a concurrent program. Because these threads operate concurrently, it is crucial that their ownership is disjoint. As Iris from the ground up 37

iProp iProp τ τ M Res τ 1 × τ 2 τ 1 × τ 2 1 ∆{()} τ → σ τ ne -→ σ False : iProp ρ λ . / 0
True : iProp ρ λ . N t = τ u : iProp ρ λ . n t : τ ρ n = u : τ ρ P ∧ Q : iProp ρ λ a. P : iProp ρ (a) ∩ Q : iProp ρ (a) P ∨ Q : iProp ρ λ a. P : iProp ρ (a) ∪ Q : iProp ρ (a) P ⇒ Q : iProp ρ λ a. n ∀m ≤ n, b. a b ∧ m ∈ V(b) ⇒ m ∈ P : iProp ρ (b) ⇒ m ∈ Q : iProp ρ (b) ∀x : τ. P : iProp ρ λ a. v∈ τ P : iProp ρ[x ← v] (a) ∃x : τ. P : iProp ρ λ a. v∈ τ P : iProp ρ[x ← v] (a) P * Q : iProp ρ λ a. n ∃b 1 , b 2 . a n = b 1 • b 2 ∧ n ∈ P : iProp ρ (b 1 ) ∧ n ∈ Q : iProp ρ (b 2 ) P - * Q : iProp ρ λ a. n ∀m ≤ n, b. m ∈ V(a • b) ⇒ m ∈ P : iProp ρ (b) ⇒ m ∈ Q : iProp ρ (a • b) Own (t) : iProp ρ λ b. n t : M ρ n b V(t) : iProp ρ λ . V( t : M ρ ) P : iProp ρ λ a. P : iProp ρ (|a|) | P : iProp ρ λ a. n ∀m ≤ n, c. m ∈ V(a • c) ⇒ ∃b. m ∈ V(b • c) ∧ m ∈ P : iProp ρ (b) µx. t : iProp ρ fix τ (λ u. t : iProp ρ[x ← u] ) P : iProp ρ λ a. n n = 0 ∨ n -1 ∈ P : iProp ρ (a) x : τ ρ ρ(x) (t 1 ,t 2 ) : τ 1 × τ 2 ρ ( t 1 : τ 1 ρ , t 2 : τ 2 ρ ) λ x : τ. t : τ → σ ρ λ u. t : σ ρ[x ← u] π i (t) : τ i ρ π i ( t : τ 1 × τ 2 ρ ) t(u) : σ ρ t : τ → σ ρ ( u : τ ρ ) a : M ρ a () : 1 ρ () |t| : M ρ | t : M ρ | next(t) : τ ρ next( t : τ ρ ) t • u : M ρ t : M ρ • u : M ρ
Laws for equality.

EQ-REFL

True x = τ x EQ-LEIBNIZ x = τ y Φ(x) ⇒ Φ(y) EQ-NEXT next(x) = τ next(y) (x = τ y)
Laws of (affine) bunched implications.

True * P P

P * Q Q * P (P * Q) * R P * (Q * R) * -MONO P 1 Q 1 P 2 Q 2 P 1 * P 2 Q 1 * Q 2 - * -INTRO P * Q R P Q - * R - * -ELIM P Q - * R P * Q R
Laws for resources and validity.

OWN-OP

Own (a) * Own (b) Own (a • b) OWN-UNIT True Own (ε) OWN-CORE Own (a) Own (|a|) OWN-VALID Own (a) V(a) VALID-OP V(a • b) V(a) VALID-PERSISTENT V(a) V(a)
Laws for the basic update modality.

UPD-MONO P Q | P | Q UPD-INTRO P | P UPD-TRANS | | P | P UPD-FRAME Q * | P | (Q * P) UPD-UPDATE a B Own (a) | ∃b ∈ B. Own (b)
Laws for the persistence modality. a consequence, separating conjunction is the natural operator to combine the ownership of concurrent threads. Notice, however, that the base logic does not have a notion of "threads" or "programs", but only of separation. Ultimately, the logic does not care about "who" owns the resources under consideration; it is only concerned with reasoning about consequences of ownership and how it can be merged and split disjointly. The connection to threads will be made when we encode Hoare triples in the base logic (see §6.3 and §7.3).

Together with separating conjunction, we have a second form of implication: the magic wand P - * Q. It describes ownership of "Q minus P", i.e., it describes resources such that, if you (disjointly) add resources satisfying P, you obtain resources satisfying Q. This is in contrast to implication P ⇒ Q, where instead of adding a disjoint resource that satisfies P, it is demanded that the same resource additionally (in the sense of a "normal" conjunction) satisfies P. In other words, to eliminate an implication, we use the rule P ∧ (P ⇒ Q) Q, but to eliminate a magic wand, we need to demand the separating conjunction, as in P * (P - * Q) Q.

The model of separating conjunction is the usual one for intuitionistic separation logic [START_REF] Reynolds | Intuitionistic reasoning about shared mutable data structure[END_REF], just slightly adapted to the step-indexed setting. As usual in a Kripke semantics [START_REF] Kripke | Semantical analysis of intuitionistic logic I. Formal systems and recursive functions[END_REF], both implication and magic wand have to be closed under smaller step-indices and bigger resources.

Resource ownership

The purpose of the Own (a) connective is to assert ownership of the resource a : M. Recall that M is interpreted by a unital camera Res, the resources by which Iris is parameterized, so that the user can decide what kind of ghost ownership they wish to have.

Since Res is a unital camera, we can use the step-indexed inclusion n (CAMERA-INCLN)

to define the model of Own (a) as "the current resources are at least a":

Own (t) : iProp ρ λ b. n t : M ρ n b
Notice that the resources should be at least a and not exactly a; this is what makes our logic affine, see also §9.5.

The proposition Own (a) forms the "primitive" form of ownership in our logic, which can then be composed into more interesting propositions using the previously described connectives. The most important fact about ownership is that separating conjunction "reflects" the composition operator of RAs into the logic (OWN-OP).

Besides the Own (a) connective, we have the primitive connective V(a), which reflects the step-indexed validity of camera elements into the logic. Note that ownership is connected to validity: the rule OWN-VALID says that only valid elements can be owned.

The persistence modality and persistent resources

In §2.3, we explained that some propositions are persistent, meaning that they hold forever and that they can be arbitrarily duplicated. For example, validity, equality, and ownership of a core are all persistent propositions. This notion of being persistent is expressed in Iris by means of the persistence modality .

The purpose of the persistence modality P is to say that P holds without asserting any exclusive ownership. This is modeled by defining P to hold for some resource a if P holds for |a|, the core of a:14 P : iProp ρ λ a. P : iProp ρ (|a|)

As a consequence, the assumption P can be used arbitrarily often, i.e., cannot be "used up"-it is persistent. We can now define what it means for a proposition to be persistent: Iris from the ground up

39

Using just the rules in Figure 11, it is easy to verify that validity V(a), equality t = u, ownership of a core Own (|a|), and the persistence modality P itself are persistent. Furthermore, it is easy to verify that any persistent proposition P enjoys: P * P ⇔ P ⇔ P and P * Q ⇔ P ∧ Q.

The persistence modality gives more flexibility than just having a notion of persistent propositions: we make use of this flexibility to define view shifts ( §7.2) and Hoare triples ( §6) in such a way that they are persistent. For now, we will give a simple teaser.

Consider the proposition P - * Q. This proposition can generally only be applied (to produce the result Q) once, because its proof may depend on some exclusively owned resource. However, we can use the persistence modality to "tag" that the proof of P - * Q does not depend on any exclusive ownership. This gives rise to (P - * Q), which is a persistent "fact" that can be duplicated and applied repeatedly.

Obviously, this comes at the price that when proving (P - * Q), we cannot make use of any non-persistent hypotheses in the surrounding context. This is witnessed by the following derived rule for introduction, which only allows one to introduce a persistence modality whenever the context is persistent:

-INTRO P Q P Q
In fact, this is exactly the usual introduction rule for the ! ! ! of linear logic. (For Iris, we picked the equivalent formulation using -MONO and -IDEMP instead, but both are equivalent.) In other words, the persistence modality is a lot like the ! ! !, except that it supports some more operations ( -CONJ and commuting with quantifiers).

The basic update modality

So far, resources have been static: the base logic provides propositions to reason about resources you own, the consequences of that ownership, and how ownership can be disjointly separated. What has been missing is a way to change the current resources. As discussed in §2.1, the natural notion of a resource update is the frame-preserving update. The purpose of the (basic) update modality | P is to reflect frame-preserving updates into the logic. The proposition | P thus holds for some resource a, if from a we can do a frame-preserving update to some b that satisfies P:

| P : iProp ρ λ a. n ∀m ≤ n, c. m ∈ V(a • c) ⇒ ∃b. m ∈ V(b • c) ∧ m ∈ P : iProp ρ (b)
The update modality | P provides a way, inside the logic, to talk about the resources we could own after performing an update to what we do own.

The rule UPD-UPDATE witnesses the relationship between the | modality and framepreserving updates, while the remaining proof rules of | essentially say that | is a strong monad with respect to separating conjunction (Kock, 1970;[START_REF] Kock | Strong functors and monoidal monads[END_REF].

This gives rise to an alternative interpretation of the basic update modality: we can think of | P as a thunk (or "suspended computation") that captures some resources in its environment and that, when executed, will "return" resources satisfying P. The various proof rules then let us perform additional reasoning on the result of the thunk (UPD-MONO), create a thunk that does nothing (UPD-INTRO), compose two thunks into one (UPD-TRANS), and add resources to those captured by the thunk (UPD-FRAME).

In §2, we introduced view shifts P E Q as the "ghost moves" of Iris. View shifts and the update modality are clearly closely related: both describe updates of the ghost state. In fact, in §7.2, we will see how to encode view shifts in terms of the update modality. We

have not yet introduced enough machinery to give the full definition, but we give here a teaser with the following simplified definition, the basic view shift:

P ˙ Q (P - * | Q)
View shifts have a precondition P imposing some restrictions on when they can be applied; this is expressed using the magic wand. The update modality is inserted in the conclusion in order to allow updates in the ghost state to happen when the basic view shift is "executed". Finally, we use the persistence modality to make view shifts persistent. It is easy to show that the basic view shift enjoys most of the rules given in Figure 4, except those involving invariants.

The later modality

As we have seen in §4, Iris uses step-indexing to obtain a solution to the recursive domain equation defining the semantic domain of propositions. This step-indexing aspect of the model is internalized into the logic by adding the later modality, P [START_REF] Nakano | A modality for recursion[END_REF][START_REF] Appel | A very modal model of a modern, major, general type system[END_REF]. The modality is modeled as follows: P holds for some resource at some step-index if P holds for the same resource at the next (lower) step-index. In the definition of Hoare triples in §6, we will connect to steps of the program, allowing one to think of P as saying that P holds at the "next step of computation". One interesting aspect of working with a step-indexed model is that propositions like x = y and V(a) are implicitly referring to the current step index. For example, AG-AGREE-STEP could be expressed inside Iris as ∀x, y. V(ag(x) • ag(y)) ⇒ x = y. Combined with the rule EQ-NEXT in Figure 11, we can then derive SPROP-AGREE, as we already hinted at in the previous section:

V(ag(next(x)) • ag(next(y))) ⇒ (x = y)
It is important to understand that this statement does not just talk about the case where ag(next(x)) • ag(next(y)) is "fully" valid (i.e., valid at all step-indices); rather, it asserts that the validity of this camera element for n steps implies that x n-1 = y. Note that many of the usual rules for later, such as those displayed below, are derivable from the rules in Figure 11.

-INTRO

P P

-AND

(P ∧ Q) P ∧ Q -EXIST inhabited(x)
(∃x. P) ∃x. P

Guarded fixed-points

Step-indexing not only gives us a way to build a model of Iris propositions. It is also a powerful tool for defining propositions recursively: under some conditions, we can define Iris from the ground up 41 propositions as being the unique fixed-point of some functions over propositions. In the logic, this is internalized by the guarded fixed-point operator µx. t. This fixed-point operator can be used to define recursive predicates without any restriction on the variance of the recursive occurrences of x in t. Instead, all recursive occurrences of x must be guarded: they have to appear below a later modality .

In §6, we will show how guarded recursion is used for defining weakest preconditions. Moreover, as shown in [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF], guarded recursion is useful for defining specifications for higher-order concurrent data structures.

In the model, the existence of such a unique fixed-point is guaranteed by Banach's fixedpoint theorem:

Theorem 4 (Banach's fixed-point). Given an inhabited COFE T and a contractive function f : T → T , there exists a unique fixed-point fix T f such that f (fix T f ) = fix T f .

We have already seen in §4.5 that iProp = UPred(Res) is complete. The syntactic guardedness condition turns out to be sufficient to ensure the contractiveness of the predicate. The reason for this is that the interpretation of is contractive, while the interpretations of all the other logical connectives are non-expansive, as we have seen earlier in §5. One can easily verify that composing a contractive function with a non-expansive one preserves contractiveness.

A crucial proof rule for is L ÖB, which allows assuming that the current goal holds later. The proof of the L ÖB rule proceeds by performing induction over the step-index. L ÖB facilitates proving properties about fixed-points: we can essentially assume that the recursive occurrences (which are below a later) are already proven correct. We will see L ÖB induction in action in §6, where we define Hoare triples using a guarded fixed-point.

Timeless propositions

There are some occasions where we inevitably end up with hypotheses below a later. The prime example is the rule for accessing invariants (HOARE-INV in §2). Although one can always introduce a later ( -INTRO), one cannot just eliminate a later, so the later may make certain reasoning steps impossible.

As we will prove in §8.2, it is crucial for logical soundness that when opening impredicative invariants P, we get access to P below a later modality. However, if P does not refer to other invariants, it is generally safe to get P immediately, i.e., without a later modality. In the Iris program logic, this is facilitated using the following rule: VS-TIMELESS timeless(P)

P E P
The intuitive meaning of timeless(P) (pronounced "P is timeless") is that P is a first-order predicate that does not refer to other invariants. When we compose this rule with HOARE-INV and HOARE-VS, we can derive the following rule for opening invariants without a later: So, what does it mean for a proposition to be timeless? Semantically speaking, a proposition P is timeless if, whenever it holds at step-index 0, it also holds at all step-indices. More precisely, as usual, we restrict attention to step-indices at which the resource under consideration is valid:

HOARE-INV-TIMELESS {P * Q 1 } e {v. P * Q 2 } E \N timeless(P) atomic(e) N ⊆ E { P N * Q 1 } e {v. P N * Q 2 } E
∀n, a. n ∈ V(a) ∧ 0 ∈ P (a) ⇒ n ∈ P (a)
With this definition in hand, we could prove suitable rules for timeless propositions in the model of the logic. This is what we have done previously in Iris 1.0 and Iris 2.0.

In Iris 3.x we proceed in a different way: we wish to define what it means for a proposition to be timeless within the logic. Naively, one might expect a proposition P to be timeless if it satisfies P P. However, in the presence of L ÖB, this entailment would give us that P = True! The reason for this is that P is always true at step-index 0. To define timelessness, we thus have to exclude the step-index 0.

Excluding the step-index 0 is achieved using the "except 0" modality , as defined below, whose semantics is that the given proposition holds at all step-indices greater than 0. This makes use of the fact that False holds only at step-index 0. Using this modality, we then define timelessness in the logic as follows: P P ∨ False timeless(P) P P

Using just the rules in Figure 11, we can prove that most of the connectives of our logic preserve timelessness: when P and Q are timeless, the propositions P ∧ Q, P ∨ Q, P ⇒ Q, ∀x. P, ∃x. P, P * Q, P - * Q, and P are timeless. Most of these proofs are straightforward, but for P ⇒ Q, P - * Q, and ∀x. P we have to make use of the rule -TIMELESS. This somewhat strange-looking rule allows us to essentially do case analysis on whether the step-index is 0 or not. Indeed, the proposition False ∨ ( False ⇒ P) holds precisely when either the step-index is zero (the left disjunct), or P holds at step-index zero (the right disjunct). Thus for timeless propositions (which hold at all step-indices if they hold at zero), we can read the whole rule as saying that if P holds then either the step index is zero, or P holds.

Note that even if P is timeless, then P generally is not. In fact, Löb induction shows that if P is timeless then True P. Furthermore, we can show that if a camera M is discrete-i.e., it degenerates to a plain RA with a discrete equality and discrete validity-then the propositions for equality a = b and validity V(a) of elements in a, b : M are timeless. Then, using the rule -OWN we can derive that ownership a : M γ of elements of a discrete camera M is timeless.

These properties make timeless propositions a powerful notion. The cameras of many commonly appearing types of resources are discrete-for example, our oneshot RA and the traditional separation-logic RA of heaps with disjoint union. To this end, when we put just first-order propositions involving ownership of discrete resources in invariants, we can open said invariants without a later. Thus, using the notion of timelessness we can recover some of the convenience of logics which support only first-order, predicative invariants, while retaining the ability to form and use higher-order invariants when desired.

It is important to notice that the modality "except 0" is useful beyond defining what it means for a proposition to be timeless. Its most important property is that when we have Iris from the ground up 43 a goal that is behind the modality, then we can strip laters off of timeless hypotheses, as shown by the derived rule -TIMELESS below.

-MONO

P Q P Q -ELIM P P -ELIM P P -TIMELESS P Q timeless(P) P Q
In §7.2 we will see that the modality can be used to define the view shift in the base logic, and it will allow us to derive the rule VS-TIMELESS from the primitive rules of the base logic.

Consistency

Logical consistency is usually stated as True False: from a closed context one cannot prove a contradiction. This theorem holds for Iris, but we will strengthen it in two ways.

• If we unfold this statement slightly, we arrive at (True False) ⇒ False where the implication and the second False are on the meta-level. In some sense, this theorem says that we can "move" proofs of False performed inside the logic in the empty context to the "outside world". If we want to use our logic for more than just stating provability (notably, we want to prove a Hoare triple and show that its postcondition actually reflects the program behavior, as in §7.4), we will need a stronger statement where we can move more interesting propositions to the "outside".

• Furthermore, we have to consider the fact that Iris has various modalities. Certainly, True False is covered by the above statement (because the persistence modality can just be eliminated, -ELIM), but what about True False or True | False? We certainly want these statements to hold, but they do not follow from the usual notion of consistency as shown above.

These considerations lead us to the following theorem:

Theorem 5 (Soundness of first-order interpretation). Given a first-order proposition φ (not involving ownership, higher-order quantification, or any of the modalities) and True ( | ) n φ , then the "standard" (meta-logic) interpretation of φ holds. Here, ( | ) n is notation for nesting n times the modalities | and .

The proposition φ should be a first-order predicate to ensure it can be used equivalently both inside our logic and at the meta-level (i.e., it enjoys φ ⇐⇒ ∀a, n. n ∈ φ (a)). Given that, Theorem 5 is a straightforward consequence of the soundness of the model (Theorem 3). The usual notion of consistency, i.e., True False, follows trivially from Theorem 5: just pick φ = False and n = 0. Furthermore, while our soundness theorem is restricted to alternating sequences of and | , it actually implies consistency under a combination of modalities: We already discussed that the persistence modality can just be removed. Beyond this, "unevenly" nested occurrences of and | can easily be brought into the shape ( | ) n by collapsing adjacent occurrences of | (UPD-TRANS) and inserting | between adjacent occurrences of (UPD-INTRO).

Weakest preconditions

As a warm-up for §7, where we will describe the encoding of the entire Iris program logic in terms of the Iris base logic, we first show in this section how to encode a stripped-down concurrent program logic. This stripped-down program logic features Hoare triples, but it

does not yet support (impredicative) invariants. For this reason, we will temporarily ignore the masks annotating Hoare triples (and the weakest precondition modality, described below). Furthermore, we will for simplicity use the ML-like programming language from §2, whereas the actual Iris program logic can be instantiated with an arbitrary programming language.

Usually, program logics are centered around Hoare triples. However, instead of directly defining Hoare triples in the Iris base logic, we first define the notion of a weakest precondition. Using weakest preconditions, Hoare triples can then be defined as:

{P} e {Φ} (P - * wp e {Φ}).

Notice that Hoare triples relate to weakest preconditions just as basic view shifts ( §5.4) relate to basic updates: The restriction enforced by the precondition P is expressed using the magic wand, and the persistence modality is used to make the Hoare triple persistent. Given a postcondition Φ : Val → iProp, the connective wp e {Φ} gives the weakest precondition under which all executions of e are safe and all return values v satisfy Φ(v).

For an execution to be safe, we demand that it does not get stuck. In the case of our MLlike programming language, this means, in particular, that the program must never access invalid locations and that, for all assert(e) statements, if e terminates, it should evaluate to true.

In §6.1, we first define the operational semantics of the programming language we introduced in §2. In §6.2, we then give some intuitions about weakest preconditions and how to work with them. After that, in §6.3, we present the encoding of weakest preconditions in three stages, gradually adding support for reasoning about state and concurrency. Finally, in §6.4 we prove adequacy of weakest preconditions.

Operational semantics

Figure 12 shows the operational semantics of the ML-like language that we introduced in §2 and will consider in this section. For clarity's sake, we repeat the syntax below:

v ∈ Val ::= () | z | true | false | | λ x.e | . . . (z ∈ Z) e ∈ Expr ::= v | x | e 1 (e 2 ) | fork {e} | assert(e) | ref(e) | ! e | e 1 ← e 2 | CAS(e, e 1 , e 2 ) | . . . K ∈ Ctx ::= • | K(e) | v(K) | assert(K) | ref(K) | ! K | K ← e | v ← K | CAS(K, e 1 , e 2 ) | CAS(v, K, e 2 ) | CAS(v, v 1 , K) | . . .
Head reduction (e, σ ) → h (e , σ , e f ) is defined on pairs (e, σ ) consisting of an expression e and a shared heap σ (a finite partial map from locations to values). Moreover, e f is a list of forked-off expressions, which is used to define the semantics of fork {e}. The headreduction is lifted to a per-thread reduction (e, σ ) → t (e , σ , e f ) using evaluation contexts. We define an expression e to be reducible in a shared heap σ , written red(e, σ ), if it can Iris from the ground up

45

Head reduction.

((λ x.e)(v), σ ) → h (e[v/x], σ , ε) (fork {e} , σ ) → h ((), σ , e) (assert(true), σ ) → h ((), σ , ε) (ref(v), σ ) → h ( , σ [ ← v] , ε) if σ ( ) = ⊥ (! , σ ) → h (v, σ , ε) if σ ( ) = v ( ← w, σ ) → h ((), σ [ ← w] , ε) if σ ( ) = v (CAS( , v, w), σ ) → h (true, σ [ ← w] , ε) if σ ( ) = v (CAS( , v, w), σ ) → h (false, σ , ε) if σ ( ) = ⊥ and σ ( ) = v
Thread-local and threadpool reduction. make a thread-local step. The threadpool reduction (T, σ ) → tp (T , σ ) is an interleaving semantics where the threadpool T denotes the existing threads as a list of expressions. Note that in this language, compare-and-set can be used with any type of values, including sums and products, which is unrealistic. This is for simplicity only: Other instantiations of Iris use a more realistic compare-and-set semantics [START_REF] Jung | RustBelt: Securing the foundations of the Rust programming language[END_REF].

Proof rules

In order to give some more intuition about weakest preconditions, we discuss some of the proof rules and indicate how these can be used to prove the Hoare rules in Figure 1. Those already familiar with weakest preconditions can proceed to §6.3.

Figure 13 shows the most important rules of the wp e {Φ} connective. These rules are inspired by the "backwards" style Hoare rules of [START_REF] Ishtiaq | BI as an assertion language for mutable data structures[END_REF], but presented in weakest-precondition style. As usual in a weakest-precondition style system [START_REF] Dijkstra | Guarded commands, nondeterminacy and formal derivation of programs[END_REF], the postcondition of the conclusion of each rule involves an arbitrary predicate Φ.

For example, imagine we are given P Q and want to prove:

→ v * P wp ( ← w) {r. r = () * → w * Q},
which is equivalent to the Hoare triple { → v * P} ← w {r. r = () * → w * Q}. The rule WP-STORE tells us which resources we have to supply and which resources we get in return. This becomes evident in the following derivation: Here, we use * -MONO to prove that we own the location -this should not be surprising; in a separation logic, we have to demonstrate ownership of a location to access it. Then, using our remaining resources P we have to prove -→ w - * () = () * → w * Q. We do this by introducing the wand, which gives us the location with its updated value.

P Q * -MONO P * - → w () = () * → w * Q - * -INTRO P - → w - * () = () * → w * Q * -MONO, -INTRO → v * P - → v * ( - → w - * () = () * → w * Q) WP-STORE → v * P
Notice the end-to-end effect of applying this little derivation: we gave up → v, and it got replaced in our context with → w. However, this was all expressed in the premise of WP-STORE (and similarly for the other rules), with the conclusion applying to an arbitrary postcondition Φ.

Keeping the above derivation in mind, it is straightforward to derive the rules for Hoare triples as shown in Figure 1. Take HOARE-STORE for example: By expanding the definition of the Hoare triple, we end up with ( -→ v - * wp ( ← w) { -→ w}), which can be proven using roughly the same derivation as shown above.

Note that the rules for weakest preconditions in Figure 13 are in fact stronger than those for Hoare triples in Figure 1 due to the presence of the later in the premise, which allows one to strip off a later of any hypothesis in the surrounding context. This functionality is crucial when one uses Löb induction to reason about fixed-point computations-it enables hypotheses introduced by Löb induction (under a later) to become usable (by having the later stripped off) after the program has taken some physical step of computation.

It may be surprising that there is no frame rule in Figure 13. It turns out that WP-WAND implies framing. In fact, WP-WAND is equivalent to the following two more conventional rules: It is useful to note that weakest preconditions are more convenient for performing interactive proofs with Iris [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF]. In order to use the rules for Hoare triples (e.g., those for primitive instructions, like HOARE-LOAD and HOARE-STORE) one first needs to bring the pre-and postcondition into a specific shape, which is done via framing and the rule of consequence. In contrast, the rules for weakest preconditions (e.g., WP-LOAD and WP-STORE) are applicable with any postcondition and implicitly have framing and consequence built in, which allows for much easier interactive proofs, as we have seen in the example proof above.

Definition of weakest preconditions

We now discuss how to define weakest preconditions using the Iris base logic, proceeding in three stages of increasing complexity. As part of defining weakest preconditions, we will also have to define the points-to connective.

First stage

To get started, let us assume the program we want to verify makes no use of fork or shared heap access. The idea of wp e {Φ} is to ensure that given any reduction (e, σ ) → t • • • → t (e n , σ n ), either (e n , σ n ) is reducible, or the program terminated, i.e., e n is a value v for which we have Φ(v). The natural candidate for encoding this is using the fixed-point operator Note that the recursive occurrence wp e 2 {Φ} appears under a -modality, so the above can indeed be defined using the fixed-point operator µ. In some sense, this "ties" the steps of the program to the step-indices implicit in the logic, by adding another for every program step.

(∀ . - → v - * Φ( )) wp ref(v) {Φ} (WP-ALLOC) - → v * ( - → v - * Φ(v)) wp ! {Φ} (WP-LOAD) - → v * ( - → w - * Φ()) wp ( ← w) {Φ} (WP-STORE) → v * ( → w - * Φ(true)) wp CAS( , v, w) {Φ} (WP-CAS-SUC) v = v ∧ → v * ( → v - * Φ(false)) wp CAS( , v ,
So, how useful is this definition? The rules WP-VAL and WP-λ are almost trivial, and using L ÖB induction we can prove WP-WAND and WP-BIND. We can thus reason about programs that do not fork or make use of the heap.

But unfortunately, this definition cannot be used to verify programs involving heap accesses: the states σ and σ 2 are universally quantified and not related to anything. The program must always be able to proceed under any heap, so we cannot possibly prove the rules of the load, store, and allocation constructs.

The usual way to proceed in constructing a separation logic is to define the pre-and postconditions as predicates over states, but that is not the direction we take. After all, our base logic already has a notion of "resources that can be updated"-i.e., a notion of state-built into its model of propositions. Of course we want to make use of this power in building our program logic.

→ v as [ ← ex(v)] γ HEAP ,
where γ HEAP is a fixed name of a ghost location. However, that leaves us with a problem: how do we tie the resources -→ v to the actual heap that the program executes on? We have to make sure that from owning -→ v we can actually deduce that the location is allocated in the heap σ and has value v.

To this end, we will actually have two heaps in our resources, both consisting of elements of Loc fin -EX(Val ). The authoritative heap • σ is managed by the weakest precondition and tied to the physical state occurring in the program reduction. There will only ever be one authoritative heap resource, i.e., we want • σ • • σ to be invalid. At the same time, the heap fragments • σ will be owned by the program itself and used to give meaning to -→ v. Specifically, → v will be encoded as

• [ ← v]
γ HEAP , i.e., as ownership of the singleton heap fragment governing . These fragments can be composed the usual way, i.e.,

• σ • • σ = • (σ σ ).
Finally, we need to tie these two pieces together, making sure that the fragments are always a "part" of the authoritative state: if • σ • • σ is valid, then σ σ should hold. The above reasoning pattern can be formalized as an instance, AUTH(Loc fin -EX(Val)), of a more general Iris construction, called the authoritative camera, which we present in §6.3.3. Before we explain how to define the authoritative camera, let us see why it is useful in the definition of weakest preconditions. The new definition is (changes are in blue):

wp e {Φ} (e ∈ Val ∧ | Φ(e)) ∨ e / ∈ Val ∧ ∀σ . • σ γ HEAP - * | red(e, σ ) ∧ ∀e 2 , σ 2 . (e, σ ) → t (e 2 , σ 2 , ε) - * | • σ 2 γ HEAP * wp e 2 {Φ} - → v • [ ← v] γ HEAP
The difference from the first definition is that the second disjunct (the one covering the case of a program that can still reduce) requires proving safety and preservation under the assumption that the authoritative heap • σ matches the physical one. Moreover, when the program makes a step to some new state σ 2 , the proof must be able to produce a matching authoritative heap. Finally, the basic update modality permits the proof to perform framepreserving updates.

To see why this is useful, consider proving WP-LOAD, the weakest precondition of the operation ! . After picking the right disjunct of the definition of the weakest precondition and introducing all assumptions, we can combine the assumptions made by the rule, → v, with the assumptions provided by the definition of weakest preconditions to obtain

• σ • • [ ← v]
γ HEAP . By OWN-VALID, we learn that this camera element is valid, which (as

discussed above) implies [ ← v] σ , so σ ( ) = v.
In other words, because the camera ties Iris from the ground up 49 the authoritative heap and the heap fragments together, and weakest precondition ties the authoritative heap and the physical heap used in program reduction together, we can make a connection between → v and the physical heap. This is yet another example of "fictional separation": Weakest precondition treats the heap as a single unsplittable entity, but through the use of the authoritative camera we can nevertheless hand out logical ownership of pieces of the state to different parts of a program.

Completing the proof of safety and preservation is now straightforward. Since all possible reductions of ! do not change the heap, we can produce the authoritative heap • σ 2 by just "forwarding" the one we got earlier in the proof. In this case, we did not even make use of the fact that we are allowed to perform frame-preserving updates. Such updates are necessary, however, to prove weakest preconditions of operations that actually change the state (like allocation ref(e) or storing e 1 ← e 2 ), because in those cases the authoritative heap needs to be changed likewise.

Authoritative camera

To complete the definition, we need to define the authoritative camera [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF], and we will do so in a general way, because there is nothing about this construction that is specific to ownership of heaps vs. any other kind of resource. Assume we are given some unital camera M and let:

AUTH(M) EX(M) ? × M V(x, b) n (x = ⊥ ∧ n ∈ V(b)) ∨ (∃a. x = ex(a) ∧ n ∈ V(a) ∧ b n a) (x 1 , b 1 ) • (x 2 , b 2 ) (x 1 • x 2 , b 1 • b 2 ) |(x, b)| (⊥, |b|)
With a ∈ M, we write • a for (ex(a), ε) to denote authoritative ownership of a, and • a for (⊥, a) to denote fragmentary ownership of a.

It can be easily verified that this camera has the three key properties discussed above: ownership of • a is exclusive, ownership of • a composes like that of a, and the two are tied together in the sense that validity of • a • • b implies b a. In addition, as we will show in §7.1.1, we can use the authoritative camera Auth(M) with different choices of M, and depending on the choice of M we will get different frame-preserving updates for Auth(M).

In particular, choosing M to be Loc fin -EX(Val), as we have done above, we can show the following frame-preserving updates that are needed for WP-STORE and WP-ALLOC:

• σ • • [ ← v] • σ [ ← w] • • [ ← w] • σ • σ [ ← w] • • [ ← w] if / ∈ dom(σ )
In our appendix (Iris Team, 2017) and the Coq formalization, we provide a generic mechanism for deriving such updates in a composable fashion.

Third stage: Adding fork

Our previous definition of wp e {Φ} only talked about reductions (e, σ ) → t (e 2 , σ 2 , ε) which do not fork off threads, and hence one could not prove WP-FORK. The following new

wp e {Φ} (e ∈ Val ∧ | Φ(e)) ∨ e / ∈ Val ∧ ∀σ . • σ γ HEAP - * | red(e, σ ) ∧ ∀e 2 , σ 2 , e f . (e, σ ) → t (e 2 , σ 2 , e f ) - * | • σ 2 γ HEAP * wp e 2 {Φ} * * e ∈ e f wp e {v.True} - → v • [ ← v] γ HEAP
Instead of just demanding a proof of the weakest precondition of the thread e under consideration, we also demand proofs that all the forked-off threads e f are safe, by using the iterated separated conjunction * . We do not care about their return values, so the postcondition is trivial. This encoding shows how much mileage we get out of building on top of the Iris base logic. Because said logic supports ownership and step-indexing, we can get around explicitly managing resources and step-indices in the weakest precondition definition. We do not have to explicitly account for the way resources are subdivided between the current thread and the forked-off thread. Instead, all we have to do is surgically place some update modalities, a single , and some standard separation logic connectives. This keeps the definition of, and the reasoning about, weakest preconditions nice and compact.

Eliminating basic view shifts

An important property of Hoare triples in Iris is that we can perform updates to ghost state around them. In the Iris program logic, this is realized via the rule HOARE-VS, which allows one to eliminate view shifts in the pre-and postcondition. For the definition of Hoare triples and weakest preconditions in this section, we cannot prove that rule yet. However, we can already prove a similar rule for basic view shifts P ˙ Q (P - * | Q) (see §5.4). This rule and the corresponding rule for weakest preconditions are as follows:

P ˙ P {P } e {v. Q } ∀v. Q ˙ Q {P} e {v. Q} | wp e v. | Φ(v)
wp e {Φ} Recall that we have defined {P} e {Φ} (P - * wp e {Φ}). Hence, the rule on the left trivially follows from the rule on the right. Given the definitions of weakest preconditions in §6.3.2 and §6.3.4, the rule on the right can be proven using L ÖB. Note that for these rules to hold, it is crucial to put an update modality | in the value case of the definition of weakest preconditions.

Adequacy

To demonstrate that our weakest preconditions actually make the expected statements about program executions, we prove the following adequacy theorem.

Theorem 6 (Adequacy of weakest preconditions). Let φ be a first-order predicate. If True wp e {φ } and (e, σ ) → * tp (e 1 . . . e n , σ ), then: Iris from the ground up 51 1. For any i with 1 ≤ i ≤ n we have that either e i is a value, or red(e i , σ ); 2. If e 1 (the main thread) is a value v, then φ (v).

Proof. In order to prove this theorem, we first show that reduction steps preserve weakest preconditions. Since our language has a threadpool semantics, this means we have to show that reduction steps preserve the weakest preconditions of all threads. So, for any reduction sequence (e 1 , e 2 . . . e n , σ ) → k tp (e 1 , e 2 . . . e m , σ ) of k steps, we prove:

• σ γ HEAP * wp e 1 {φ } * * 2≤i≤n wp e i {True} ( | ) k • σ γ HEAP * wp e 1 {φ } * * 2≤ j≤m wp e j {True}
This result is proven entirely in the Iris base logic. The proof proceeds by induction on k and unfolding the definition of weakest preconditions in each step of the induction proof.

With the above result in hand, it is now easy to show that given any reduction sequence (e, σ ) → k tp (e 1 . . . e n , σ ) of k steps, we have:

• σ γ HEAP * wp e {φ } ( | ) k+1 e i ∈ Val ∨ red(e i , σ ) for 1 ≤ i ≤ n • σ γ HEAP * wp e {φ } ( | ) k+1 φ (e 1 ) if e 1 ∈ Val
Both proofs are done within the base logic, again, and proceed by unfolding the definition of weakest preconditions once more. Now, if we combine these results with the hypothesis True wp e {φ } and with the rule GHOST-ALLOC for allocating the ghost variable • σ γ HEAP , we get True ( | ) k+1 (e i ∈ Val ∨ red(e i , σ )) and True ( | ) k+1 (e 1 ∈ Val ⇒ φ (e 1 )).

Since (e i ∈ Val ∨ red(e i , σ )) and (e 1 ∈ Val ⇒ φ (e 1 )) are first-order propositions, and thus can be used both inside the base logic and at the meta level, we can apply Theorem 5, the soundness result of the base logic. This concludes the proof.

Recovering the Iris program logic

In this section, we will show how to encode the reasoning principles of the full Iris program logic [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF][START_REF] Jung | Higher-order ghost state[END_REF] within the base logic. Most importantly, we will derive the core proof rule for accessing invariants, HOARE-INV ( §2.2), which allows opening an invariant around an atomic expression:

{ P * Q 1 } e {v. P * Q 2 } E \N atomic(e) N ⊆ E { P N * Q 1 } e {v. P N * Q 2 } E
One can also present this rule directly for weakest preconditions. The following weakest precondition rule can easily be shown to imply the above rule:

WP-INV P wp E \N e {v. P * Φ(v)} atomic(e) N ⊆ E P N wp E e {Φ}
Before proving these rules, we begin in §7.1 by establishing the required infrastructure to encode invariants. In §7.2, we then define the fancy update modality, which enriches the basic update modality with support for accessing invariants and removing laters ( ) from timeless propositions. The fancy update modality is used to recover the notion of

γ INV * * ι∈dom(I) ( I(ι) * {ι} γ DIS ) ∨ {ι} γ EN
(Above, ag and next are implicitly mapped pointwise over I). view shifts. In §7.3, we proceed by giving the full definition of weakest preconditions, which extends the definition in §6.3.4 with support for opening invariants and is moreover parameterized by the language of program expressions. In particular, we prove WP-INV. We conclude this section with a generic adequacy result for the full Iris program logic in §7.4. All definitions of the Iris program logic are shown in Figure 14. We explain these in detail in the following sections.

P ι • [ι ← ag(next(P))] γ INV P N ∃ι ∈ N ↑ . P ι | E 1 E 2 P W * E 1 γ EN - * | (W * E 2 γ EN * P) P E 1 E 2 Q (P - * | E 1 E 2 Q) wp S E e {Φ} (e ∈ Val ∧ | E Φ(e)) ∨ e / ∈ Val ∧ ∀σ . S(σ ) - * | E / 0 red(e, σ ) ∧ ∀e 2 , σ 2 , e f . (e, σ ) → t (e 2 , σ 2 , e f ) - * | / 0 E S(σ 2 ) * wp S

Invariants

This section describes the protocol governing the allocation of and access to invariants, which we call world satisfaction. The name "world satisfaction" originates in earlier work on Kripke logical-relations models of higher-order stateful languages, e.g., [START_REF] Dreyer | A relational modal logic for higher-order stateful ADTs[END_REF]; there, it denoted the global invariant that the physical state (i.e., the heap) should, after every step of computation, respect whatever invariants had thus far been established in a logical-relations proof, which were codified in a "possible world" parameter of the logical relation. In the context of Iris, world satisfaction means something similar, but it is generalized to a property over arbitrary resources (not just physical ones) and expressed directly as a formula in the Iris base logic, which we will end up enforcing as a global invariant simply by threading it through the definition of view shifts and weakest preconditions (see §7.2). Iris from the ground up

53

In §7.1.1, we define world satisfaction. However, instead of using namespaces N to identify invariants (as WP-INV does), world satisfaction uses a simpler mechanism: Each invariant has a dynamically chosen name ι ∈ N. In §7.1.2, we show the limitations of dynamically chosen names and introduce the concept of namespaces to overcome these limitations.

World satisfaction

The key idea for defining invariants in the Iris base logic is to define a single global invariant W , called world satisfaction, which keeps track of all existing invariants. The definition of world satisfaction is given below:

W ∃I : N → iProp. • ag(next(I)) γ INV * * ι∈dom(I) ( I(ι) * {ι} γ DIS ) ∨ {ι} γ EN P ι • [ι ← ag(next(P))] γ INV
Here, I : N → iProp is a registry of all existing invariants. The iterated separating conjunction * makes sure that invariants that have not been opened do in fact hold. We use two tokens ( {ι} γ DIS and {ι} γ EN ) to keep track of whether invariants have been opened or not; this mechanism will be explained in more detail below. World satisfaction not only ensures that the existing invariants hold, but it also ensures that these invariants are tied to the occurrences of the connective P ι , which witness that P has been registered as an invariant named ι in I. We achieve this using a mechanism similar to the one tying the heap σ to the points-to connective -→ v in §6.3.2: namely, the authoritative camera. For the heap we used the camera AUTH(Loc fin -EX(Val)), but for invariants we will be using AUTH(N fin -AG( iProp)). These cameras differ in the following key respects:

• Rather than storing values, the map we are maintaining here is storing propositions (of type iProp). This is achieved using higher-order ghost state, which means that we have to guard the occurrence of iProp by a later . Formally, we instantiate Iris with the following function:

F INV (X) AUTH(N fin -AG( X))
• We are not interested in expressing exclusive ownership of invariants, like we did for heap locations. Instead, the entire point of invariants is sharing, so we need to make sure that everybody agrees on what the invariant with a given name is. To that end, we use the agreement camera AG instead of the exclusive camera EX.

In the definition of world satisfaction W and the invariant connective P ι , we use the elements of the camera AUTH(N fin -AG( iProp)) in the following ways:

• Ownership of the fragment • [ι ← ag(next(P))] states that P is registered as an invariant named ι, and • ownership of the authoritative element • ag(next(I)) states that I is the full map of all registered invariants. The camera enjoys the following properties:

• [ι ← ag(next(P))] = • [ι ← ag(next(P))] V • ag(next(I)) • • [ι ← ag(next(P))] ⇒ ( I(ι) ⇔ P) if ι / ∈ dom(I) • ag(next(I)) • ag(next(I [ι ← P])) • • [ι ← ag(next(P))])
These properties, in turn, give rise to the following rules:

P ι P ι (INVREG-PERSIST) • ag(next(I)) γ INV * P ι I(ι) ⇔ P (INVREG-AGREE) ι / ∈ dom(I) ∧ • ag(next(I)) γ INV | • ag(next(I [ι ← P])) γ INV * P ι (INVREG-ALLOC)
The rule INVREG-PERSIST follows from the definition of the core | -| of fragments. The rule INVREG-AGREE witnesses that the registry and the fragments agree on the proposition managed at a particular name. Note that we only get the bi-implication with a later because the occurrence of iProp in AUTH(N fin -AG( iProp)) is necessarily guarded with a later . This rule follows from the property of validity of composing authoritative and fragmentary elements. Finally, INVREG-ALLOC can be used to create a new invariant, provided the new name is not already used. This rule follows from the frame-preserving update.

Naively, we may think that world satisfaction should always require all invariants to hold. However, this does not work: after all, within a single atomic step of execution, threads are allowed to temporarily break invariants so long as they restore them by the end of the step. To support this, world satisfaction keeps invariants in one of two states: either they are enabled (currently enforced), or they are disabled (currently broken by some thread). The definition of the weakest precondition connective will then ensure that invariants are never disabled for more than an atomic step. That is, no invariant is left disabled between physical computation steps.

The protocol for opening (i.e., disabling) and closing (i.e., re-enabling) an invariant employs two exclusive tokens: an enabled token {ι} γ EN , which witnesses that the invariant is currently enabled and gives the right to disable it; and dually, a disabled token {ι} γ DIS .

These tokens are controlled by the following two simple RAs:

EN ℘(N) | DIS ℘ fin (N) |
The composition for both RAs is disjoint union. The invalid elements account for composition of overlapping sets. Let us take a look at the definition of world satisfaction W again to see how these tokens are being used. For each invariant ι we either have that it is enabled and maintained-in which case world satisfaction owns I(ι)-or the invariant is disabled. Additionally, in the reverse of what one might naively expect, world satisfaction owns the disabled token {ι} γ DIS when ι is enabled, and it owns the enabled token {ι} γ EN when ι is disabled. We will see shortly why this achieves the desired semantics for enabled/disabled tokens. It is important to note that P ι just means that the registry maps ι to P; it does not say anything about whether ι is enabled or not. own the enabled token {ι} γ EN , we can exclude the right disjunct and deduce that the invariant is currently enabled. So we take out the P and the disabled token, and instead put the enabled token into W , disabling the invariant. This concludes the proof.

The proof of WSAT-ALLOC is slightly more subtle. In particular, we have to be careful in picking the new invariant name such that: (a) it is in E , (b) it is not used in I yet, and (c) we can create a disabled token for that name and put it into W alongside P. Since disabled tokens are modeled by finite sets, only finitely many of them can ever be allocated, so it is always possible to pick an appropriate fresh name.

Invariant namespaces

We have defined the proposition P ι expressing knowledge that P is maintained as an invariant with name ι. This way of naming invariants has two limitations:

1. Invariant names are unstructured. In a proof that uses n invariants, we will have n names. If we wish to open any combination of these invariants at the same time, we have to keep track of n 2 mutual inequalities to ensure that all names are different. 2. To make the situation worse, the concrete names of invariants are picked when invariants are allocated, so they cannot possibly be statically known (see the existential quantifier in WSAT-ALLOC).

To solve these issues, we organize invariants in namespaces N ∈ List(N). Namespaces can be thought of as fully qualified class names, and as such have a tree-like structure. For example, org.iris has sub-namespaces like org.iris.lock and org.iris.barrier.

The crux to solving (2) is that each namespace N contains infinitely many names, as given by its closure N ↑ ⊆ N:

N ↑ f (N ) N is a sub-namespace of N
Here, f is an injection from List(N) to N, which exists because List(N) is countably infinite. We now define:

P N ∃ι ∈ N ↑ . P ι
Let us see how this definition solves the aforementioned issues:

1. Whenever namespaces N 1 and N 2 are not sub-namespaces of one another, we know that N ↑ 1 ∩ N ↑ 2 = / 0, and as such, they can be opened at the same time. For example, the namespaces N .lock and N .barrier represent disjoint sets of invariant names no matter the choice of N . In particular, the existential quantifier is gone (or rather, it is hidden). From now on, we will write N instead of N ↑ when there is no ambiguity-in particular, when N is used where a mask E is expected.

Cancellable invariants

Invariants as described above are persistent: Once allocated, they must be satisfied forever.

On first sight, it may seem as if this would lead to problems when reasoning about code that performs memory deallocation. For example, a lock that can be deallocated must "tear down" its invariant when the lock is destroyed. However, Iris's ghost state is powerful enough to encode such cancellable invariants-i.e., invariants that come with an additional mechanism to tear down the invariant and re-gain full ownership of the resources protected by the invariant. Indeed, cancellable invariants have already proven crucial in work on Iris that deals with languages supporting deallocation [START_REF] Jung | RustBelt: Securing the foundations of the Rust programming language[END_REF][START_REF] Kaiser | Strong logic for weak memory: Reasoning about release-acquire consistency in Iris[END_REF]. Their encoding is formally described in our appendix (Iris Team, 2017).

View shifts and the fancy update modality

Before we define weakest preconditions and prove the rules for invariants, there is one other piece of the Iris program logic we need to cover: view shifts ( ). These serve three roles:

1. They permit frame-preserving updates, as realized by the rule GHOST-UPDATE:

a B a γ E ∃b ∈ B. b γ 2.
They allow stripping away a later modality off a timeless proposition, as realized by the rule VS-TIMELESS:

timeless(P)

P E P
3. They allow accessing invariants, as realized by the following rule, which we have not shown before:

P * Q 1 E \N P * Q 2 N ⊆ E P N * Q 1 E P N * Q 2
We have already seen two connectives that can be used for the first or second role of view shifts, respectively: the basic update modality | , which can be used to perform framepreserving updates (UPD-UPDATE), and the except-0 modality , which can be used to strip laters of timeless propositions ( -TIMELESS). We will now define a new modality that subsumes the basic update modality | and the except-0 modality and can also take care of the last role: accessing invariants. This modality, called the fancy update modality | E 1 E 2 , is defined as follows:

P Q | E 1 E 2 P | E 1 E 2 Q FUP-INTRO-MASK E 2 ⊆ E 1 True | E 1 E 2 | E 2 E 1 True FUP-TRANS | E 1 E 2 | E 2 E 3 P | E 1 E 3 P FUP-FRAME Q * | E 1 E 2 P | E 1 E f E 2 E f (Q * P)
| E 1 E 2 P W * E 1 γ EN - * | (W * E 2 γ EN * P) | E P | E E P
Before going into details about the definition or the proof rules (see Figure 15) of this modality, let us mention that in the same way that Hoare triples are defined in terms of weakest preconditions, view shifts are defined in terms of fancy updates:

P E 1 E 2 Q (P - * | E 1 E 2 Q) P E Q P E E Q
Using the rules of the fancy update modality in Figure 15, it is easy to establish the rules for view shifts that we have seen in this paper. The intuition behind | E 1 E 2 P is to express ownership of resources such that, if we further assume that the invariants in E 1 are enabled, we can perform a frame-preserving update such that we end up owning P and the invariants in E 2 are enabled. By looking at the definition of | E 1 E 2 P, we can see how it supports all the fancy roles of view shifts:

1. At the heart of the fancy update modality is a basic update modality, which permits doing frame-preserving updates (see the rule FUP-UPD in Figure 15). 2. The modality is able to remove laters from timeless propositions by incorporating the "except 0" modality (see §5.7 and FUP-TIMELESS). 3. Finally, the modality "threads through" world satisfaction, in the sense that a proof of | E 1 E 2 P can use W but also has to reestablish it. Furthermore, controlled by the two masks E 1 and E 2 , the modality provides and consumes "enabled" tokens. The first mask controls which invariants are available to the modality, while the second mask controls which invariants remain available afterwards (see INV-ACCESS). It is also possible to allocate new invariants (INV-ALLOC). Notice that this is mostly a matter of convenience; treating the tokens as masks instead of the underlying ghost resources keeps them out of the way for the majority of the proof that does not care about them. It is important to notice that there is no rule like P | E 1 E 2 P that lets us introduce an arbitrary mask-changing fancy update. With a goal of the form | E 1 E 2 , the fact that the masks are different actually presents an obligation that we have to discharge-typically by eliminating fancy update modalities until the masks do match. The reason this works is that elimination of a mask-changing fancy update changes only the first of the two masks in our goal:

FUP-CHANGE-MASK R * P | E 2 E 3 Q R * | E 1 E 2 P | E 1 E 3 Q
As you can see, eliminating an update with masks E 1 and E 2 has the effect of changing the first mask in the conclusion from E 1 to E 2 , while the second mask (E 3 ) stays unaffected. FUP-CHANGE-MASK follows from FUP-FRAME, FUP-MONO and FUP-TRANS. As a more concrete example for how to work with mask-changing updates, we will prove the rule INV-ACCESS in Figure 15. The rule may look fairly cryptic; we will see in the next section how it can be used to derive WP-INV. The proof of this rule relies on the following two mask-changing updates, both centering around 7. The goal now immediately follows from INV-CLOSE and our remaining assumptions.

Weakest preconditions

We will now define weakest preconditions that support not only the rules in Figure 13, but also the ones in Figure 17. We will also show how, from WP-ATOMIC and INV-ACCESS, we can derive the rule motivating this entire section, WP-INV.

Compared to the definition developed in §6, there are two key differences: First of all, we use the fancy update modality instead of the basic update modality. Secondly, we do not want to tie the definition of weakest preconditions to a particular language, and instead we operate generically over any notion of expressions and state, and any reduction relation.

The definitions in this section are thus parameterized by a language, which consists of:

S(σ ) • σ : Loc fin -EX(Val) γ HEAP
More sophisticated forms of separation like fractional permissions [START_REF] Boyland | Checking interference with fractional permissions[END_REF][START_REF] Bornat | Permission accounting in separation logic[END_REF] can be obtained by using an appropriate camera and defining S accordingly.

Given a state interpretation S : State → iProp, our definition of weakest precondition looks as follows (changes from §6.3.4 are colored red): The state interpretation S is generally clear from the context, so we often leave it implicit and write just wp E e {Φ}. The mask E of wp E e {Φ} is used for the "outside" of the fancy update modalities, providing them with access to these invariants. The "inner" masks are / 0, indicating that the reasoning about safety and progress can temporarily open/disable all invariants. The forked-off threads e f have access to the full mask as they will only start running in the next instruction, so they are not constrained by whatever invariants are available right now. Note that the definition requires all invariants in E to be enabled again after every physical step: This corresponds to the fact that an invariant can only be opened atomically.

In addition to the rules already presented in §6, this version of the weakest precondition connective lets us prove (among others) the new rules in Figure 17. The rule WP-VUP witnesses that the entire connective as well as its postcondition are living below the fancy update modality, so we can freely add/remove that modality.

Finally, we come to WP-ATOMIC to open an invariant around an atomic expression. The rule is similar to WP-VUP, with the key difference being that it can change the mask. On the left-hand side of the turnstile, we are allowed to first open some invariants, then reason about e, and then close invariants again. This is sound because e is atomic. tp (e 1 . . . e n , σ ), then: 1. For any e i we have that either e i is a value or red(e i , σ ); 2. If e 1 (the main thread) is a value v, then φ (v).

The proof of this theorem is similar to the proof of Theorem 6. A notable difference is that now, the user of the theorem has to pick the state interpretation predicate S, which is existentially quantified below an update modality in the theorem. The reason for that is that one typically has to allocate some ghost variables before the predicate can be defined. For example, one can only define the predicate below-the one we use for our simple ML-like language-after a ghost variable with name γ HEAP has been allocated.

S(σ

) • σ : Loc fin -EX(Val) γ HEAP

Discussion

Past development of Iris

In this section we describe some notable differences between the presentation of Iris in the present paper and the presentation in earlier papers [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF][START_REF] Jung | Higher-order ghost state[END_REF]Krebbers et al., 2017a). This section does not give a comprehensive list of differences between the Iris papers, just some that we think are worth discussing.

OFEs versus COFEs.

A key difference between the Iris model as described in this paper and in the Iris 2.0 paper [START_REF] Jung | Higher-order ghost state[END_REF] is the move from COFEs to OFEs. Previously, we worked exclusively with COFEs, i.e., all OFEs that we used were required to satisfy the completeness property (OFE-COMPL). However, more recently, we noticed that the completeness property is only needed in two places:

• America and Rutten's theorem (Theorem 2), which we use in the model of Iris ( §4.6) to construct a solution to the recursive domain equation IRIS. To use the first theorem, the only OFE that needs to be complete is UPred(M), and to use the second theorem, the only OFE that needs to be complete is T 1 → . . . → T n → UPred(M). These OFEs are complete regardless of whether the camera M or the OFEs T i are complete. More precisely, UPred(M) is complete even if M is not complete, and T ne -→ U is complete even if T is not (because the limit is just taken pointwise in U).

A consequence of the move from COFEs to OFEs is that we no longer require cameras to be complete, which in turn allows us to use a much simpler construction for the higherorder agreement camera AG than we used previously. 16 This new definition of cameras furthermore enjoys a useful property that the old definition did not. It is now the case that if T is discrete (i.e., the step-indexed equivalence is just equality: ∀x, y, n. x n = y ⇐⇒ x = y), then so is AG(T ). As a consequence of this, ownership of ghost elements like ag(0) (in AG(Z)) is timeless: a property we need in many of our proofs. The old higherorder agreement construction did not have this property, so we actually had two agreement constructions: AG 0 for timeless, first-order ghost state and the complicated version of AG for higher-order ghost state. The new AG presented in this paper unifies both constructions.

View shifts versus fancy updates. Iris 1.0 and 2.0 [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF][START_REF] Jung | Higher-order ghost state[END_REF] use the (binary) view shift connective P E 1 E 2 Q as the main vehicle for performing ghost updates, whereas Iris 3.0 (Krebbers et al., 2017a) uses the (unary) fancy update modality | E 1 E 2 P. It is worth noting that this is just a matter of presentation and does not affect the expressive power of the logic. As demonstrated in §7.2, view shifts P E 1 E 2 Q can be encoded in terms of the fancy update modality as P - * | E 1 E 2 Q. Perhaps more surprisingly, the fancy update modality can also be encoded in terms of view shifts:

| E 1 E 2 P ∃R. R * (R E 1 E 2 P)
However, ignoring this choice of presentation, there are actually some small semantic differences between view shifts in Iris 1.0/2.0 and 3.x. Firstly, in Iris 1.0/2.0, the rule FUP-TRANS had a side condition restricting the masks it could be instantiated with, whereas now it does not. Secondly, in Iris 1.0/2.0, instead of FUP-INTRO-MASK, only mask-invariant view shifts could be introduced (i.e., there was only FUP-INTRO: P | E P). The reason we can now support FUP-INTRO-MASK is that masks are actually just sugar for owning or providing particular resources (namely, the enabled tokens). This is in contrast to previous versions of Iris, where masks were entirely separate from resources and treated in a rather ad-hoc manner.

Our more principled treatment of masks significantly simplifies building abstractions involving invariants. In particular, recall the proof of INV-ACCESS in §7.2: The invariants in N \ {ι} do, in fact, remain enabled even though they disappear from the mask. This is possible because FUP-INTRO-MASK lets us remove invariants from the mask and add them back later without actually opening them-effectively "hiding" enabled invariants. In Iris Iris from the ground up 63 1.0/2.0, it would not have been possible to hide invariants like this. In fact, it was not possible to prove a concise abstract rule like INV-ACCESS for invariants with namespaces; we had to resort to a much more complicated lemma.

It is worth noting that as a consequence of defining weakest preconditions inside the base logic instead of directly in the model, we not only obtained a conceptual benefit, but also a very practical one: enabling projects that build on top of Iris to define their own variants of weakest preconditions. For example, Timany et al. (2018) use a version of weakest preconditions in big-step instead of small-step style, the Iris Coq development [START_REF] Team | The Iris documentation and Coq development[END_REF]) also includes a version of weakest preconditions for total program correctness, Timany & Birkedal (2018) use a version of weakest preconditions for a language with continuations, and [START_REF] Tassarotti | A separation logic for concurrent randomized programs[END_REF] use a version of weakest preconditions for randomized algorithms.

Differences from Iris 3.0. The differences between Iris 3.0 (Krebbers et al., 2017a) and Iris 3.1 (this paper) are minor: The "always modality" has been renamed as the "persistence modality" in the interest of adopting a more consistent nomenclature. Moreover, we managed to derive two previously primitive proof rules about the persistence modality, namely True

True and (P ∧ Q) (P * Q), from the remaining rules. Finally, we slightly changed the definition of UPred, making it a quotient over monotone predicates instead of the previous, more ad-hoc (but isomorphic) definition.

Naive impredicative invariants paradox

In §3.3, we have seen that higher-order ghost state without the later modality (which is modeled using step-indexing) leads to a paradox. One may argue that the burden of having the later modality in the logic is too high and it is not worth having higher-order ghost state at that cost. However, we will see in this section that the later modality plays an essential role even without higher-order ghost state: It is needed for impredicative invariants. Indeed, we show here that omitting the later in the rule to access invariants (INV-ACCESS) leads to a contradiction, even in the absence of general higher-order ghost state.

This answers an open question: The later modality has been present in all concurrent separation logics with impredicative invariants [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF][START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF][START_REF] Jung | Higher-order ghost state[END_REF], but it was unclear whether this was a mere artifact of the model, or whether it is in some sense required. We show that it is indeed required in some sense.

More precisely, we prove the following theorem, without using higher-order ghost state:

Theorem 8. Assume we add the following proof rule to Iris:

ι ∈ E P ι | E E \{ι} P * (P - * | E \{ι} E True)
Then, if we pick an appropriate RA, True | False.

Notice that the above rule is the same as INV-ACCESS in Figure 15, except that it does not add a in front of P.

Of course, this does not prove that we absolutely must have a modality, but it does show that the stronger rule one would prefer to have for invariants is unsound. The later The proof of this theorem does not use the fact that fancy updates are defined in any particular way, but just uses the proof rules for the fancy update modality. In fact, the paradox can be generalized so that it applies to all versions of Iris [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF][START_REF] Jung | Higher-order ghost state[END_REF]Krebbers et al., 2017a), as is shown by the following theorem: Theorem 9. Assume a higher-order separation logic with and an update modality with a binary mask | {0,1} (think: empty mask and full mask) satisfying strong monad rules with respect to separating conjunction and such that: In other words, the theorem requires three ingredients to be present in the logic in order to derive a contradiction:

• An update modality that satisfies the laws of Iris's basic update modality (Figure 11).

The modality needs a mask for the same reason that Iris's fancy update modality has a mask: to prevent opening the same invariant twice. • Invariants that can be opened around the update modality without a later.

• A two-state protocol whose only transition is from the first to the last state. This is what S

• and F • encode. The proof does not actually depend on how that protocol is made available to the logic. For example, to apply this proof to iCAP [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF], one could use iCAP's built-in support for state-transition systems to achieve the same result. However, for the purpose of this theorem, we had to pick some way of expressing protocols. We picked the token-based approach common in Iris.

All versions of Iris easily satisfy the first and third requirement, by using fancy updates (Iris 3) or view shifts (Iris 1 and 2) for the update modality | {0,1} , and by constructing an appropriate RA (Iris 2 and 3) or PCM (Iris 1) for the two-state protocol. Of course, INV-OPEN-NOLATER is the one assumption of the theorem that no version of Iris satisfies, which is the entire point.

The proof works by constructing a proposition that is equivalent (in some rather loose sense) to the stored propositions we used for the paradox in §3.3, and then deriving a Iris from the ground up 65 contradiction in much the same way. The full details of this construction are spelled out in the appendix (Iris Team, 2017).

Formalization in Coq

All results in this paper, including adequacy and soundness of Iris as well as the derived constructions like weakest preconditions and invariants, have been formalized using Coq. Additionally, we have formalized a large body of derived reasoning principles for concurrency in Iris. The Iris Coq formalization has been used by a variety of projects, e.g., [START_REF] Kaiser | Strong logic for weak memory: Reasoning about release-acquire consistency in Iris[END_REF][START_REF] Jung | Robust and compositional verification of object capability patterns[END_REF][START_REF] Jung | RustBelt: Securing the foundations of the Rust programming language[END_REF]Timany et al., 2018;Timany & Birkedal, 2018;[START_REF] Tassarotti | A separation logic for concurrent randomized programs[END_REF][START_REF] Tassarotti | A higher-order logic for concurrent termination-preserving refinement[END_REF][START_REF] Frumin | ReLoC: A mechanised relational logic for fine-grained concurrency[END_REF]. The Coq formalization of Iris is a so-called shallow embedding [START_REF] Wildmoser | Certifying machine code safety: Shallow versus deep embedding[END_REF]. That is, the logical connectives are defined directly as functions on the semantic model of propositions iProp. This approach differs from a deep embedding-of the sort we have used in this paper-where one first defines an explicit syntax of the logic and then interprets that syntax into the semantic model. When working in Coq, avoiding the indirection of first defining a syntax has a couple of technical advantages:

• It allows us to use Coq functions to represent binding in the object logic (e.g., in the logical quantifiers and postconditions of weakest preconditions) instead of having to use an explicit encoding of binders, like De Bruijn indices. • It avoids the need to extend the signature of the object logic with types like lists, natural numbers, finite sets, finite maps, etc. A shallow embedding allows one to just reuse the data types of the meta-logic, i.e., Coq.

Since Iris uses a step-indexed model instead of a set-theoretic model, the shallow embedding does not use plain Coq types and plain Coq functions, but rather OFEs and nonexpansive functions. In order to make a shallowly embedded step-indexed logic usable in practice, it is crucial to have Coq automatically infer that given Coq types are OFEs, and that given Coq functions are non-expansive. For this, we use the approach that we outlined in the Iris 2.0 paper [START_REF] Jung | Higher-order ghost state[END_REF]): Coq's canonical structures mechanism [START_REF] Garillot | Packaging mathematical structures[END_REF] is used to automate dealing with OFEs, and Coq's setoid mechanism [START_REF] Sozeau | A new look at generalized rewriting in type theory[END_REF] is used to automate dealing with non-expansive functions.

All proofs of the derived constructions (in particular, everything related to invariants and weakest preconditions) are carried out by solely using the rules of the Iris base logic, i.e., without making use of the interpretation of Iris in the model. To enforce that this is indeed the case, we have made sure that Coq does not unfold the logical connectives into their definitions in the model (i.e., we made sure that the model is opaque).

In order to formalize the proofs in the Iris base logic, we have used the Iris Proof Mode (IPM) [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF], which provides tactic support for reasoning with interesting combinations of separation-logic propositions and our modalities , | , and (for example, as they arise in the definition of weakest preconditions and fancy updates).

Related work

In §9.1 we will discuss work that is related to the pillars of Iris 1.0: user-defined ghost state and invariants. Sections §9.2 and §9.3 cover work that is related to the new features of Iris 2.0: higher-order ghost state and the generalization of PCMs to RAs/cameras. In §9.4, we will compare with work that has similar aims to our Iris 3.x base logic. Finally, in §9.5, we discuss the point of why Iris is an affine/intuitionistic (as opposed to a linear/classical) separation logic.

User-defined ghost state and invariants

Iris's main vehicle to provide user-defined ghost state is the parameterization of the logic by a user-chosen partial commutative monoid (PCM) (which we generalized to a user-chosen camera in Iris 2.0). The use of PCMs for this purpose is not entirely surprising, after all: They have been used since the earliest models of separation logic. As such, already before Iris 1.0, a number of different models and logics started to employ PCMs as a way of characterizing more fine-grained notions of interference [START_REF] Krishnaswami | Superficially substructural types[END_REF][START_REF] Dinsdale-Young | Views: Compositional reasoning for concurrent programs[END_REF][START_REF] Ley-Wild | Subjective auxiliary state for coarse-grained concurrency[END_REF]. The Views framework [START_REF] Dinsdale-Young | Views: Compositional reasoning for concurrent programs[END_REF], in particular, enables the user to tie ghost resources-which are represented by a user-defined PCM-to physical resources. However, the Views framework is limited in that it effectively requires the user to bake in a fixed invariant that ties ghost resources to physical ones, with no logical support for layering further invariants on top of those ghost resources. Iris, in contrast, provides built-in logical support for user-defined invariants, which allows one to relate (ghost and physical) resources to each other. Relatedly, in the Verified Software Toolchain (VST) [START_REF] Beringer | Verified compilation for shared-memory C[END_REF] one can pick a fixed PCM (in fact, a "separation algebra") for ghost state, from which a higher-order separation logic is constructed. However, primitive rules for the chosen ghost state have to be proven in the model of the logic, whereas those can be derived from a few fundamental proof rules in Iris.

The idea of relating (ghost and physical) resources through invariants was already present in works that appeared before Iris 1.0-for example, in Pilkiewicz and Pottier's work on "monotonic state" [START_REF] Pilkiewicz | The essence of monotonic state[END_REF], Jensen and Birkedal's work on "fictional separation logic" [START_REF] Jensen | Fictional separation logic[END_REF], and Krishnaswami et al.'s work on "superficially substructural types" [START_REF] Krishnaswami | Superficially substructural types[END_REF]. All of these earlier works were restricted to the sequential setting, however.

In the concurrent setting, work prior to Iris 1.0, such as CaReSL [START_REF] Turon | Unifying refinement and Hoare-style reasoning in a logic for higher-order concurrency[END_REF], iCAP (Svendsen &[START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF]TaDA (da Rocha Pinto et al., 2014), has typically used a particular PCM construction to fix its ghost state. Iris's combination of user-defined ghost state and invariants can be used to encode the patterns of reasoning found in these logics, but with a simpler set of primitive mechanisms and proof rules.

Around the time Iris 1.0 was developed, several other logics integrated support for userdefined ghost state. Notably, GPS [START_REF] Turon | GPS: Navigating weak memory with ghosts, protocols, and separation[END_REF] also used PCMs and FCSL used an abstraction called "concurroids" [START_REF] Nanevski | Communicating state transition systems for fine-grained concurrent resources[END_REF].

An interesting difference in expressiveness between Iris and FCSL is the support for "hiding" of invariants. FCSL supports a certain kind of hiding, namely the ability to transfer some local state into an invariant (actually a "concurroid"), which is enforced during the execution of a single expression e, but after which the state governed by the invariant is returned to local control. Iris can support such hiding as well, via cancellable invariants ( §7.1.3). Additionally, Iris allows a different kind of hiding, namely the ability to Iris from the ground up 67 hide invariants used by (nested) Hoare-triple specifications. For example, the higher-order function mk oneshot from §2 returns two functions (tryset and check), whose Hoaretriple specification is only correct under the invariant I (which was established during execution of mk oneshot). Since invariants in Iris are persistent propositions, I can be hidden, i.e., it need not infect the specification of mk oneshot, tryset, or check. To our knowledge, FCSL does not support hiding of this form. Like Iris, [START_REF] Cohen | Invariants, modularity, and rights[END_REF] strive to provide a minimal basis for concurrent reasoning, but theirs is based on a ghost heap and two-state invariants, whereas ours is based on arbitrary ghost PCMs and one-state invariants. The two approaches are optimizing for different goals. Our logic is substructural and supports more expressive-e.g., higher-orderspecifications. Theirs is not, making it better suited to use with automated verification tools like SMT solvers.

Higher-order ghost state

As we have just seen, some logics prior to Iris 2.0, such as GPS [START_REF] Turon | GPS: Navigating weak memory with ghosts, protocols, and separation[END_REF] and FCSL [START_REF] Nanevski | Communicating state transition systems for fine-grained concurrent resources[END_REF], supported user-defined ghost state like Iris 1.0. However, these logics are all restricted to first-order ghost state, since the structure of the ghost state is defined and fixed before the logic is instantiated.

Other prior work employs a model that can be considered to involve second-order ghost state, e.g., to justify their use of dynamically allocated locks [START_REF] Gotsman | Local reasoning about storable locks and threads[END_REF][START_REF] Hobor | Oracle semantics for concurrent separation logic[END_REF][START_REF] Buisse | Step-indexed Kripke model of separation logic for storable locks[END_REF], regions [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF], or invariants [START_REF] Jung | Iris: Monoids and invariants as an orthogonal basis for concurrent reasoning[END_REF]. However, this ghost state has a fixed structure, rather than being an instance of a generic algebraic structure like cameras.

In some of the logics prior to Iris 2.0, even though the structure of ghost state is fixed, it can sometimes be adapted cleverly to support a range of proof patterns. For example, [START_REF] Dodds | Verifying custom synchronization constructs using higher-order separation logic[END_REF] have shown that the built-in "protocol" mechanism of the iCAP logic [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF] can be repurposed to encode named propositions ( §3.3), a functionality for which it was not originally intended. However, the encoding is somewhat artificial, must be verified by direct appeal to the model of iCAP, and does not scale to support general higher-order ghost state as introduced in Iris 2.0 [START_REF] Jung | Higher-order ghost state[END_REF].

The Verified Software Toolchain (VST) [START_REF] Beringer | Verified compilation for shared-memory C[END_REF] is another logic that is generic in the kind of resources. It applies "indirection theory" [START_REF] Hobor | A theory of indirection via approximation[END_REF] to solve recursive domain equations that are very much like the equation IRIS in §4. Although VST has been demonstrated to support particular forms of second-order ghost state, it is not yet clear how precisely indirection theory compares with the categorical COFE-based approach. We believe that VST can potentially be generalized to support more general higher-order ghost state in the manner of Iris 2.0. Notably, VST has a notion of PCM-like structures that are compatible with aging (step-indexing) and seem to loosely correspond to cameras.

Generalizations of PCMs

In prior work, there have been several presentations of generalizations of PCMs that have "multiple units" or include a notion of a "duplicable core". [START_REF] Dockins | A fresh look at separation algebras and share accounting[END_REF] introduced "multi-unit separation algebras", which, unlike PCMs, only demand the existence of a possibly different unit u a with u a • a = a for any element a. A notable difference between multi-unit separation algebras and RAs is that we present the monoidal operation • and core | -| as functions, whereas they represent these operations as relations. Treating these operations as functions, as we do, allows one to reason equationally, which makes it more convenient to carry out proofs.

The terminology of a (duplicable) core has been adapted from Pottier, who introduced it in the context of "monotonic separation algebras" [START_REF] Pottier | Syntactic soundness proof of a type-and-capability system with hidden state[END_REF]. However, the axioms of Pottier's cores, as well as those of related notions in other work [START_REF] Turon | GPS: Navigating weak memory with ghosts, protocols, and separation[END_REF][START_REF] Beringer | Verified compilation for shared-memory C[END_REF], are somewhat different from the axioms of our notion of RAs. Some common properties appear in most of these works (either as axioms or as admissible rules):

The core must produce a unit (RA-CORE-ID), be idempotent (RA-CORE-IDEM), and be a homomorphism, i.e., be compatible with composition: |a • b| = |a| • |b|. The last property is stronger than our monotonicity axiom (RA-CORE-MONO), and as such, the axioms of RAs are weaker than those demanded by prior work. In fact, RAs are strictly weaker: One of our most important RA constructions, the state-transition system (STS),17 has a core that is not a homomorphism. This shows that demanding the core to be a homomorphism, as prior work has done, rules out useful instances.

Another difference is the fact that our core may be partial, whereas in prior work it was always a total function. As discussed in §3.1, partial cores make it easier to compose RAs out of simpler constructions like sums.

Finally, [START_REF] Bizjak | On models of higher-order separation logic[END_REF] have shown that our notion of the core is precisely what is needed to get a well-behaved notion of persistent propositions.

Minimal base logic

The most notable attempt at mitigating the difficulty of the model construction of a stateof-the-art separation logic is Svendsen and Birkedal's work on the iCAP logic (2014). They defined the model of the iCAP logic in the internal logic of the topos of trees, which includes a later connective to reason about step-indexing in an abstract way, i.e., without step-indexes appearing explicitly in the interpretations of the connectives of the logic in the model. However, their model of Hoare triples still involves explicit resource management, which ours does not.

On the other end of the spectrum, there has been some work on encoding binary logical relations in concurrent separation logics [START_REF] Dreyer | A relational modal logic for higher-order stateful ADTs[END_REF][START_REF] Turon | Unifying refinement and Hoare-style reasoning in a logic for higher-order concurrency[END_REF][START_REF] Krogh-Jespersen | A relational model of typesand-effects in higher-order concurrent separation logic[END_REF][START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF]. These encodings of binary logical relations are relying on an ambient separation logic that already includes a plethora of high-level concepts, such as weakest preconditions and view shifts. Our goal, in contrast, is precisely to define these concepts in simpler terms.

FCSL [START_REF] Sergey | Mechanized verification of finegrained concurrent programs[END_REF] takes an opposite approach to our base logic. To ease reasoning about programs in a proof assistant, they avoid reasoning in separation logic as much as possible, and reason mostly in the ambient logic (in their case, the Coq logic). This requires Iris from the ground up 69 the model to stay as simple as possible; in particular, FCSL does not make use of stepindexing. As a consequence, they do not support impredicative invariants, which we believe are an important feature of Iris. For example, they are needed to model impredicative type systems [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF] or to model a reentrant event loop library [START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF]. Furthermore, as we have shown in recent work [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF], one can actually reason conveniently in a separation logic in Coq, so the additional complexity of our model is hardly visible to users of our logic. The Verified Software Toolchain (VST) [START_REF] Beringer | Verified compilation for shared-memory C[END_REF]) is a framework that provides machinery for constructing sophisticated higher-order separation logics with support for impredicative invariants in Coq. However, VST is not a logic and, as such, does not abstract over step-indices and resources. Defining a program logic in VST thus still requires significant manual management of such details, which are abstracted away when defining a program logic in the Iris base logic. Logics built using the VST typically fix a particular ghost state for their purpose and provide primitive rules for this particular ghost state, whereas Iris is designed for such proof rules to be derived inside a single logic from a few fundamental proof rules. As a result, Iris proofs using different ghost state can be safely composed ( §3.2), whereas proofs carried out in different VST logics do not necessarily interoperate. Furthermore, VST has so far only been demonstrated in the context of sequential reasoning and coarse-grained (lock-based) concurrency [START_REF] Beringer | Verified compilation for shared-memory C[END_REF], whereas the focus of Iris is on fine-grained concurrency.

Affine vs. linear separation logic

As we pointed out in §2, Iris is an affine separation logic in the sense that we can always throw away resources-i.e., all resources may be used at most once. Formally speaking, this means that for every proposition P and Q we have:18 P * Q P (SEP-AFFINE)

Separation logics that enjoy the above rule are often called "intuitionistic", because they do not admit the rule of excluded middle (True P ∨ ¬P): From the rule of excluded middle and SEP-AFFINE one can show that separating conjunction is degenerate, i.e., P ∧ Q ⇔ P * Q for any P and Q. Conversely, separation logics that do not enjoy the rule SEP-AFFINE are often called "classical" or "linear" because resources should be used exactly once.

The reader may of course wonder why Iris is affine/intuitionistic instead of linear/classical. There are various reasons for this:

• Logics that incorporate the later modality do not admit the law of excluded middle despite their linearity: From the rule of excluded middle and L ÖB-induction one can prove that is degenerate, i.e., P ⇔ True for any P. • The traditional advantage of linear separation logic is the more precise tracking of resources: Not being able to throw resources away enables one to reason about the absence of resource leaks. For example, in traditional classical separation logic it is guaranteed that allocated memory has been freed since one cannot throw away

  Fig. 1. Basic rules for Hoare triples.

Fig. 3 .

 3 Fig. 3. Resource algebras.

  ref(inl(0)) in x → inl(0) * pending γ I N where I (x → inl(0) * pending γ ) ∨ (∃n. x → inr(n) * shot(n) ! x in {I * P}where P y = inl(0) ∨ (∃n. y = inr(n) * shot(n) let z = ! x in I * y = inl(0) ∨ (∃n. y = z = inr(n)) I N * y = inl(0) ∨ (∃n. y = z = inr(n)) match y, z with inl( ) ,⇒() | inr(n), inl( ) ⇒ assert(false) | inr(n), inr(m) ⇒ assert(n = m) end } Fig. 5. Example proof outline.

  Fig. 6. Primitive rules for ghost state.

  and conceptual overview of the model construction The precise definitions in the following sections are somewhat technical and may somewhat obscure the conceptual simplicity at the heart of the model. Hence, we first give an informal overview of how the model can be understood and how the technical definitions used in the following sections are really systematic and natural generalizations of concepts we have already seen.

Fig. 7 .

 7 Fig. 7. (C)OFEs and non-expansive/contractive functions.

n=

  b if a and b contain the same elements up to the step-indexed equality ( n =) on T . This definition makes composition (defined as union) a non-expansive operation: For a 1 n = a 2 , it is easy to see that a 1 • b n = a 2 • b and b • a 1 n = b • a 2 . (Note: Composition would not be non-expansive if a n = b were instead defined as ∀x ∈ a. ∀y ∈ b. x n = y.) The other properties (AG-CORE-STEP, AG-VALID-STEP and AG-AGREE-STEP) are also easily verified.

Fig. 9 .

 9 Fig. 9. Camera operations and axioms.

Fig. 10 .

 10 Fig. 10. Semantic interpretation of the Iris base logic.

  Fig. 11. Proof rules of the Iris base logic.

  Fig. 12. Operational semantics.

  wp ( ← w) {r. r = () * → w * Q}

  WP-MONO ∀v. Φ(v) Ψ(v) wp e {Φ} wp e {Ψ} WP-FRAME P * wp e {Φ} wp e {P * Φ}

  Φ(v) - * Ψ(v)) * wp e {Φ} wp e {Ψ} (WP-WAND) Φ(v) wp v {Φ} (WP-VAL) wp e {v. wp K[ v ] {Φ}} wp K[ e ] {Φ} (WP-BIND) Φ() * wp e {v. True} wp fork {e} {Φ} (WP-FORK) Φ() wp assert(true) {Φ} (WP-ASSERT) wp e[v/x] {Φ} wp (λ x.e)v {Φ} (WP-λ )

E e 2

 2 {Φ} * * e ∈ e f wp S e {v.True} {P} e {Φ} S E (P - * wp S E e {Φ}) Fig. 14. The Iris program logic.

  each namespace N represents an infinite set N ↑ , once we allocate an invariant, we can use any ι ∈ N ↑ . This gives rise to the following rule: WSAT-ALLOC-NAMESP W * P - * | W * P N

Fig. 15 .

 15 Fig. 15. Rules for the fancy update modality and invariants.

  The rules FUP-MONO, FUP-TRANS, and FUP-FRAME correspond to the related rules of the basic update modality in Figure11. We can also derive a rule corresponding to UPD-INTRO: FUP-INTRO P | E P This follows from FUP-INTRO-MASK, FUP-TRANS and FUP-FRAME (or, alternatively, from FUP-UPD).

  wp S E e {Φ} (e ∈ Val ∧ | E Φ(e)) ∨ e / ∈ Val ∧ ∀σ . S(σ ) - * | E / 0 red(e, σ ) ∧ ∀e 2 , σ 2 , e f . (e, σ ) → t (e 2 , σ 2 , e f ) - * | / 0 E S(σ 2 ) * wp S E e 2 {Φ} * * e ∈ e f wp S e {v.True}

  WEAKEN-MASK | 0 P | 1 P Assume a type I and a proposition • • : I → iProp → iProp satisfying: the existence of a type G and two tokens S • : G → iProp and F • : G → iProp parameterized by G and satisfying the following properties:

  The main connective for manipulating these ghost assertions is called a view shift (or ghost move): P E Q says that, given resources satisfying P, we can change the ghost state and end up with resources satisfying Q. (We will come back to the mask annotation E in §2.2.) Intuitively, view shifts are like Hoare triples, but without any code-there is just a precondition

	paper	5 December 2018	11:34

  We now consider programs that access the shared heap but still do not fork. To use the resources provided by the Iris base logic, we have to start by thinking about the right camera to encode the points-to connective -→ v and to connect it to the current shared heap. An obvious candidate would be to use Loc

	ZU064-05-FPR	paper	5 December 2018	11:34
		48		R. Jung et al.
				6.3.2 Second stage: Adding state

fin

-EX(Val) 15 and define -

  OPENCLOSE in more detail. We start by using INVREG-AGREE to learn that the authoritative registry I maintained by world satisfaction contains our invariant P at index ι. We thus obtain from the big separating conjunction that P * {ι} γ DIS ∨ {ι} γ EN . Since we moreover

	ZU064-05-FPR	paper	5 December 2018	11:34
			Iris from the ground up	55
		Properties of world satisfaction. With this encoding, we can prove the following key
		properties modeling the allocation, opening, and closing of invariants:
		WSAT-ALLOC E is infinite W * P - * | W * ∃ι ∈ E . P	ι		WSAT-OPENCLOSE P ι W * {ι} γ EN ⇔ W * P * {ι}	γ DIS
		Let us look at the proof of the direction P	ι	W * {ι}	γ EN ⇒ W * P * {ι}	γ DIS of WSAT-

  We discuss the proof of INV-OPEN in some more detail; the proof of INV-CLOSE proceeds in a very similar way.• To prove INV-OPEN, we start by unfolding the definition of the fancy update modality.We can thus assume P ι , W , and the enabled token E γ EN . From these assumptions we now have to show | W * E \ ι γ EN * P * {ι} γ DIS .• We split the enabled token E γ EN into {ι} γ EN and E \ {ι} γ EN .• We do not actually need to do any frame-preserving updates, so we just apply UPD-INTRO. Furthermore, we have the assumptions to use WSAT-OPENCLOSE, which gives us W , the disabled token {ι} γ DIS , and P. We can thus easily discharge our goal.Now we can prove INV-ACCESS using just the rules INV-OPEN, INV-CLOSE, and some rules shown in Figure15. Below we give detailed notes to accompany the derivation in Figure16. Fig. 16. Derivation of INV-ACCESS.2. Now we applyFUP-CHANGE-MASK, which means the update modality is removed from our new assumption and the goal changes to | E \{ι} E \N . . .. Observe how, by applying view shift INV-OPEN, which has two different masks, the first mask in our goal changes from E to E \ {ι}. 3. We do not have any more invariants to open, but our goal is still a mask-changing update. This is where we use FUP-INTRO-MASK. We proceed in much the same way as when we applied INV-OPEN in the previous step: First, we obtain a new assumption |

	ZU064-05-FPR	paper	5 December 2018	11:34
						Iris from the ground up	59
	P	ι * P * {ι}	| E \{ι} E True ι * P * {ι} γ DIS * True γ DIS * | E \N E \{ι} True γ DIS * | E \N E \{ι} True E \{ι} E \N P ι * P * {ι} ι * P * {ι} γ DIS * P P | | E \N E \{ι} True	| E \{ι} E True | E \{ι} E True | E \N E True P * ( P - * E \N E True) (7) (6) | | E \{ι} E \N P * ( P - * E \N E True) (5) |	(4)
					P	P E E\{ι} P * {ι} ι * P * {ι} ι * | γ DIS γ DIS	| E \{ι} E \N P * ( P - * | E E \N P * ( P - * E \N E True) | E \N E True) |	(3) (2)
						P	N	| E E \N P * ( P - *	| E \N E True)	(1)
		E \{ι} E \N	| E \N E \{ι} True.
		4. Next, we apply FUP-CHANGE-MASK, which eliminates the outer of the two update
		WSAT-OPENCLOSE: modalities from our new assumption, and turns our goal into | E \N E \N . . .. Now we
		INV-OPEN can use FUP-INTRO to get rid of the fancy update in the goal, because the two masks ι ∈ E INV-CLOSE ι ∈ E are the same.
		P 5. Since we have P as an assumption, we can now discharge the left-hand side of ι E E \{ι} P * {ι} γ DIS P ι * P * {ι} γ DIS E \{ι} E True our goal. For the right-hand side, we immediately assume P again, and our goal
		becomes	| E \N E True.
		6. Using FUP-CHANGE-MASK with our assumption	| E \N E \{ι} True (which we obtained
		from the update we got via FUP-INTRO-MASK), the goal changes to	| E \{ι} E True.
		1. We begin by assuming P	N , from which we have to show | E E \N P * ( P - *
			| E \N E True). By definition of P	N this means there exists ι ∈ N s.t. P	ι . So using
			INV-OPEN and P	ι , we can add | E E\{ι} P * {ι}	γ DIS to our assumptions. We can
			keep P	ι around because it is persistent.

  A set of states State, and• A per-thread step relation (→ t ) ⊆ (Expr × State) × (Expr × State × List(Expr)).These should satisfy: if (e, σ ) → t (e , σ , e f ) then e / ∈ Val. As a consequence of parameterizing by a language, we can no longer assume that our physical state is a heap of values with disjoint union as composition. Therefore, instead of using the authoritative heap defined in §6.3, we further parameterize weakest preconditions by a predicate S : State → iProp called the state interpretation. In case State = Loc Val, we can recover the definition and rules from §6.3 by taking:

	ZU064-05-FPR	paper	5 December 2018	11:34
		60			R. Jung et al.
		WP-VUP | E wp E e {v. | E Φ(v)} wp E e {Φ}	WP-ATOMIC | E 1 E 2 wp E 2 e v. | atomic(e) E 2 E 1 Φ(v)	wp E 1 e {Φ}
			Fig. 17. New rules for weakest precondition with invariants.
		• A set of expressions Expr,
		• A set of values Val ⊆ Expr,

• fin -

  The rule WP-ATOMIC is needed to derive WP-INV:To demonstrate that wp e {φ } actually makes the expected statements about program executions, we prove the following adequacy theorem. Theorem 7 (Adequacy of weakest preconditions). Let φ be a first-order predicate. If True | ∃S : State → iProp. S(σ ) * wp S e {φ }, where S is the state interpretation predicate, and (e, σ ) → *

	ZU064-05-FPR	paper	5 December 2018	11:34
						Iris from the ground up	61
					P wp E \N e {v. P * Φ(v)}
		P * ( P - *	| E \N E True) wp E \N e v. P * Φ(v) * ( P - *	| E \N E True)	WP-FRAME
		P * ( P - *	| E \N E True) wp E \N e v. Φ(v) *	| E \N E True	WP-MONO
		P * ( P - *	| E \N E True) wp E \N e v.	| E \N E Φ(v)	FUP-FRAME
				P	N	| E E\N wp E \N e x.	| E \N E Φ(v)	INV-ACCESS
				P	N	wp E e {Φ}	WP-ATOMIC
						7.4 Adequacy

As in, e.g., Appel et al.'s work on foundational proof-carrying code(Appel,

2001;[START_REF] Appel | An indexed model of recursive types for foundational proof-carrying code[END_REF] and later on the Verified Software Toolchain[START_REF] Beringer | Verified compilation for shared-memory C[END_REF]. This is in contrast to tools like Chalice[START_REF] Leino | Verification of concurrent programs with Chalice[END_REF], Dafny[START_REF] Leino | Dafny: An automatic program verifier for functional correctness[END_REF], or Viper[START_REF] Müller | Viper: A verification infrastructure for permission-based reasoning[END_REF], which have much larger trusted computing bases because they assume the soundness of non-trivial extensions of Hoare logic and do not produce independently checkable proof terms.

Our semantics here is that assert(e) gets stuck if e evaluates to false. To that end, we have the rule HOARE-ASSERT for assert(true), but no corresponding rule for assert(false).

Note the difference between Prop, which denotes the type of propositions of the meta-logic (e.g., Coq), and iProp, which denotes the type of propositions of Iris.

Note that the notion of frame-preserving updates is defined for RAs in general, and not just uRAs. To that end, we cannot rely on the presence of a global unit ε to account for the absence of a frame (or the absence of another thread with ownership of the ghost location).

By convention, the absence of a mask means we are using the full mask containing all the namespaces.

RAs are ordered by the extension order (defined in Figure3), and Prop is ordered by entailment.

This follows from the fact that there exists a unit.

Note that we write iProp for the type of propositions in Iris, versus iProp for the semantic domain used to interpret them.

Note that we use similar notations λ x : τ. t and λ x. e for a term of the Iris logic and a lambdaabstraction in our programming language, respectively. It should always be clear from context which of these notions is used.

The full judgment is of the shape Γ | P Q. However, since Γ only plays a role in the rules for quantifiers, we omit it.

Remember that Res has a unit, and hence the | -| is a total function.

This camera is isomorphic to finite partial functions with composition being disjoint union.

The conference paper[START_REF] Jung | Higher-order ghost state[END_REF] did not even have the space for the full previous definition, which was only given in the appendix.

The RA of STSs, as well as the example demonstrating that the core is not a homomorphism, is described in our technical appendix[START_REF] Team | The Iris documentation and Coq development[END_REF].

This follows from the fact that True is a unit of the separating conjunction.

Acknowledgments

This research was supported in part by a European Research Council (ERC) Consolidator Grant for the project "RustBelt", funded under the European Union's Horizon 2020 Framework Programme (grant agreement no. 683289); and by the ModuRes Sapere Aude Advanced Grant from The Danish Council for Independent Research for the Natural Sciences (FNU).

We thank Philippa Gardner for helpful feedback, and Rose Hoberman for editing advice. We also give many thanks to all those who have contributed to the development, implementation, and applications of the Iris framework. ZU064-05-FPR paper 5 December 2018 11:34

Iris from the ground up

V(pending • a) ⇒ False (PENDING

points-to predicates → v. However, to our knowledge, this property has never been proven for any existing logic for fine-grained concurrency [START_REF] Nanevski | Communicating state transition systems for fine-grained concurrent resources[END_REF][START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF][START_REF] Da Rocha Pinto | TaDA: A logic for time and data abstraction[END_REF]. Besides, for logics with impredicative invariants [START_REF] Hobor | Oracle semantics for concurrent separation logic[END_REF][START_REF] Svendsen | Impredicative concurrent abstract predicates[END_REF], despite linearity, it is possible to store an invariant "inside itself", making it in turn possible to leak invariants and all the resources they contain. Hence, in said logics, one does not formally gain anything from linearity in terms of tracking resource leaks.

Nevertheless, linearity can be useful even when reasoning in impredicative concurrent logics, e.g., to establish refinement properties [START_REF] Tassarotti | A higher-order logic for concurrent termination-preserving refinement[END_REF] or to manage obligations using resources [START_REF] Bizjak | Iron: Managing obligations in higher-order concurrent separation logic[END_REF].

Ongoing and future development of Iris

In this paper, we have tried to paint a comprehensive picture of present-day Iris from the ground up, but this is certainly not the last word on the subject. The Iris framework is continuously under development, and although we do not expect major changes to the foundations of Iris as we have presented them, we have been actively exploring several extensions that would broaden the generality and expressive power of Iris even further.

First of all, we are exploring ways to incorporate linear resources ( §9.5) into Iris. We would like to find a suitable generalization of Iris such that the user of Iris has control over which resources are affine or linear, along the lines of recent work by [START_REF] Cao | Bringing order to the separation logic jungle[END_REF]. Furthermore, we would like to determine which restrictions are needed on impredicative invariants so that Iris can be used to reason about the absence of resource leaks.

Beyond linearity, we have experimented with adding a new "plainness modality" for assertions that do not depend on any resources, even duplicable ones. Such "plain" assertions enjoy stronger proof rules in conjunction with the update modalities (Timany et al., 2018). We have worked on generalizing our Coq infrastructure, the interactive proof mode [START_REF] Krebbers | Interactive proofs in higher-order concurrent separation logic[END_REF], to handle a larger variety of logics [START_REF] Krebbers | MoSeL: A general, extensible modal framework for interactive proofs in separation logic[END_REF], which led to a more modular definition of the Iris model. Finally, we have extended weakest preconditions in two ways: to support reasoning about programs that may get stuck, which is useful in applications that interact with untrusted code [START_REF] Jung | Robust and compositional verification of object capability patterns[END_REF], and to support reasoning about total program correctness.