From 7f548a31c9c839520b422088cdae977ae8ae0292 Mon Sep 17 00:00:00 2001 From: Ralf Jung <jung@mpi-sws.org> Date: Wed, 13 Mar 2019 19:55:18 +0100 Subject: [PATCH] add documentation of the global RA pattern --- Naming.md | 84 ------------ ProofGuide.md | 188 +++++++++++++++++++++++++++ README.md | 21 ++- StyleGuide.md | 118 +++++++++++++++++ tests/one_shot.v | 3 +- theories/base_logic/lib/saved_prop.v | 6 +- 6 files changed, 326 insertions(+), 94 deletions(-) delete mode 100644 Naming.md create mode 100644 ProofGuide.md create mode 100644 StyleGuide.md diff --git a/Naming.md b/Naming.md deleted file mode 100644 index b330260a8..000000000 --- a/Naming.md +++ /dev/null @@ -1,84 +0,0 @@ -# Naming conventions for variables/arguments/hypotheses in the Coq development - -## small letters - -* a : A = cmraT or cofeT -* b : B = cmraT or cofeT -* c -* d -* e : expr = expressions -* f = some generic function -* g = some generic function -* h : heap -* i -* j -* k -* l -* m : iGst = ghost state -* m* = prefix for option -* n -* o -* p -* q -* r : iRes = resources -* s = state (STSs) -* s = stuckness bits -* t -* u -* v : val = values of language -* w -* x -* y -* z - -## capital letters - -* A : Type, cmraT or cofeT -* B : Type, cmraT or cofeT -* C -* D -* E : coPset = Viewshift masks -* F = a functor -* G -* H = hypotheses -* I = indexing sets -* J -* K : ectx = evaluation contexts -* K = keys of a map -* L -* M = maps / global CMRA -* N : namespace -* O -* P : uPred, iProp or Prop -* Q : uPred, iProp or Prop -* R : uPred, iProp or Prop -* S : set state = state sets in STSs -* T : set token = token sets in STSs -* U -* V : abstraction of value type in frame shift assertions -* W -* X : sets -* Y : sets -* Z : sets - -## small greek letters - -* γ : gname -* σ : state = state of language -* φ : interpretation of STS/Auth - -## capital greek letters - -* Φ : general predicate (over uPred, iProp or Prop) -* Ψ : general predicate (over uPred, iProp or Prop) - -# Naming conventions for algebraic classes in the Coq development - -## Suffixes - -* C: OFEs -* R: cameras -* UR: unital cameras or resources algebras -* F: functors (can be combined with all of the above, e.g. CF is an OFE functor) -* G: global camera functor management -* T: canonical structurs for algebraic classes (for example ofeT for OFEs, cmraT for cameras) diff --git a/ProofGuide.md b/ProofGuide.md new file mode 100644 index 000000000..df476850f --- /dev/null +++ b/ProofGuide.md @@ -0,0 +1,188 @@ +# Iris Proof Guide + +This work-in-progress document serves to explain how Iris proofs are typically +carried out in Coq: what are the common patterns, what are the common pitfalls. +This complements the tactic documentation for the [proof mode](ProofMode.md) and +[HeapLang](HeapLang.md) as well as the documentation of syntactic conventions in +the [style guide](StyleGuide.md). + +## Resource algebra management + +When using ghost state in Iris, you have to make sure that the resource algebras +you need are actually available. Every Iris proof is carried out using a +universally quantified list `Σ: gFunctors` defining which resource algebras are +available. You can think of this as a list of resource algebras, though in +reality it is a list of functors from OFEs to Cameras (where Cameras are a +step-indexed generalization of resource algebras). This is the *global* list of +resources that the entire proof can use. We keep it universally quantified to +enable composition of proofs. The formal side of this is described in §7.4 of +[The Iris Documentation](http://plv.mpi-sws.org/iris/appendix-3.1.pdf); here we +describe the Coq aspects of this approach. + +The assumptions that an Iris proof makes are collected in a type class called +`somethingG`. The most common kind of assumptions is `inG`, which says that a +particular resource algebra is available for ghost state. For example, in the +[one-shot example](tests/one_shot.v): +``` +Class one_shotG Σ := { one_shot_inG :> inG Σ one_shotR }. +``` +The `:>` means that the projection `one_shot_inG` is automatically registered as +an instance for type-class resolution. If you need several resource algebras, +just add more `inG` fields. If you are using another module as part of yours, +add a field like `one_shot_other :> otherG Σ`. + +Having defined the type class, we need to provide a way to instantiate it. This +is an important step, as not every resource algebra can actually be used: if +your resource algebra refers to `Σ`, the definition becomes recursive. That is +actually legal under some conditions (which is why the global list `Σ` contains +functors and not just resource algebras), but for the purpose of this guide we +will ignore that case. We have to define a list that contains all the resource +algebras we need: +``` +Definition one_shotΣ : gFunctors := #[GFunctor one_shotR]. +``` +This time, there is no `Σ` in the context, so we cannot accidentally introduce a +bad dependency. If you are using another module as part of yours, add their +`somethingΣ` to yours, as in `#[GFunctor one_shotR; somethingΣ]`. (The +`#[F1; F2; ...]` syntax *appends* the functor lists `F1`, `F2`, ... to each +other; together with a coercion from a single functor to a singleton list, this +means lists can be nested arbitrarily.) + +Now we can define the one and only instance that our type class will ever need: +``` +Instance subG_one_shotΣ {Σ} : subG one_shotΣ Σ → one_shotG Σ. +Proof. solve_inG. Qed. +``` +The `subG` assumption here says that the list `one_shotΣ` is a sublist of the +global list `Σ`. Typically, this should be the only assumption your instance +needs, showing that the assumptions of the module (and all the modules it +uses internally) can trivially be satisfied by picking the right `Σ`. + +Now you can add `one_shotG` as an assumption to all your module definitions and +proofs. We typically use a section for this: +``` +Section proof. +Context `{!heapG Σ, !one_shotG Σ}. +``` +Notice that besides our own assumptions `one_shotG`, we also assume `heapG`, +which are assumptions that every HeapLang proof makes (they are related to +defining the `↦` connective as well as the basic Iris infrastructure for +invariants and WP). For this purpose, `heapG` contains not only assumptions +about `Σ`, it also contains some ghost names to refer to particular ghost state +(see "global ghost state instances" below). + +The backtic (`` ` ``) is used to make anonymous assumptions and to automatically +generalize the `Σ`. When adding assumptions with backtic, you should most of +the time also add a `!` in front of every assumption. If you do not then Coq +will also automatically generalize all indices of type-classes that you are +assuming. This can easily lead to making more assumptions than you are aware +of, and often it leads to duplicate assumptions which breaks type class +resolutions. + +### Obtaining a closed proof + +To obtain a closed Iris proof, i.e., a proof that does not make assumptions like +`inG`, you have to assemble a list of functors of all the involved modules, +and if your proof relies on some singleton (most do, at least indirectly; also +see the next section), you have to call the respective initialization or +adequacy lemma. [For example](tests/one_shot.v): +``` +Section client. + Context `{!heapG Σ, !one_shotG Σ, !spawnG Σ}. + + Lemma client_safe : WP client {{ _, True }}%I. + (* ... *) +End client. + +(** Assemble all functors needed by the [client_safe] proof. *) +Definition clientΣ : gFunctors := #[ heapΣ; one_shotΣ; spawnΣ ]. + +(** Apply [heap_adequacy] with this list of all functors. *) +Lemma client_adequate σ : adequate NotStuck client σ (λ _ _, True). +Proof. apply (heap_adequacy clientΣ)=> ?. apply client_safe. Qed. +``` + +### Advanced topic: Ghost state singletons + +Some Iris modules involve a form of "global state". For example, defining the +`↦` for HeapLang involves a piece of ghost state that matches the current +physical heap. The `gname` of that ghost state must be picked once when the +proof starts, and then globally known everywhere. Hence it is added to +`gen_heapG`, the type class for the generalized heap module: +``` +Class gen_heapG (L V : Type) (Σ : gFunctors) `{Countable L} := { + gen_heap_inG :> inG Σ (authR (gen_heapUR L V)); + gen_heap_name : gname +}. +``` + +Such modules always need some kind of "initialization" to create an instance +of their type class. For example, the initialization for `heapG` is happening +as part of [`heap_adequacy`](theories/heap_lang/adequacy.v); this in turn uses +the initialization lemma for `gen_heapG` from +[`gen_heap_init`](theories/base_logic/lib/gen_heap.v): +``` +Lemma gen_heap_init `{gen_heapPreG L V Σ} σ : + (|==> ∃ _ : gen_heapG L V Σ, gen_heap_ctx σ)%I. +``` +These lemmas themselves only make assumptions the way normal modules (those +without global state) do, which are typically collected in a `somethingPreG` +type class (such as `gen_heapPreG`): +``` +Class gen_heapPreG (L V : Type) (Σ : gFunctors) `{Countable L} := { + gen_heap_preG_inG :> inG Σ (authR (gen_heapUR L V)) +}. +``` +Just like in the normal case, `somethingPreG` type classes have an `Instance` +showing that a `subG` is enough to instantiate them: +``` +Instance subG_gen_heapPreG {Σ L V} `{Countable L} : + subG (gen_heapΣ L V) Σ → gen_heapPreG L V Σ. +Proof. solve_inG. Qed. +``` +The initialization lemma then shows that the `somethingPreG` type class is +enough to create an instance of the main `somethingG` class *below a view +shift*. This is written with an existential quantifier in the lemma because the +statement after the view shift (`gen_heap_ctx σ` in this case) depends on having +an instance of `gen_heapG` in the context. + +Given that these global ghost state instances are singletons, they must be +assumed explicitly everywhere. Bundling `heapG` in a module type class like +`one_shotG` would lose track of the fact that there exists just one `heapG` +instance that is shared by everyone. + +### Advanced topic: Additional module assumptions + +Some modules need additional assumptions. For example, the STS module is +parameterized by an STS and assumes that the STS state space is inhabited: +``` +Class stsG Σ (sts : stsT) := { + sts_inG :> inG Σ (stsR sts); + sts_inhabited :> Inhabited (sts.state sts); +}. +``` +In this rather exceptional case, the `Instance` for this class has more than +just a `subG` assumption: +``` +Instance subG_stsΣ Σ sts : + subG (stsΣ sts) Σ → Inhabited (sts.state sts) → stsG Σ sts. +``` +If users of this module follow the pattern described above, their own type class +instance will check these additional assumption. But this is one more reason +why it is important for every module to have an instance for its `somethingG`: +to make sure that it does not accidentally make more assumptions than it intends +to. + +Another subtle detail here is that the `subG` assumption comes first in +`subG_stsΣ`, i.e., it appears before the `Inhabited`. This is important because +otherwise, `sts_inhabited` and `subG_stsΣ` form an instance cycle that makes +type class search diverge. + +## Canonical structures and type classes + +In Iris, we use both canonical structures and type classes, and some careful +tweaking is necessary to make the two work together properly. The details of +this still need to be written up properly, but here is some background material: + +* [Type Classes for Mathematics in Type Theory](http://www.eelis.net/research/math-classes/mscs.pdf) +* [Canonical Structures for the working Coq user](https://hal.inria.fr/hal-00816703v1/document) diff --git a/README.md b/README.md index 1d934d454..4312f7b6f 100644 --- a/README.md +++ b/README.md @@ -111,13 +111,17 @@ that should be compatible with this version: ## Further Resources -* Information on how to set up your editor for unicode input and output is - collected in [Editor.md](Editor.md). -* The Iris Proof Mode (IPM) / MoSeL is documented at [ProofMode.md](ProofMode.md). -* HeapLang (the Iris example language) and its tactics are documented at - [HeapLang.md](HeapLang.md). -* Naming conventions are documented at [Naming.md](Naming.md). +Getting along with Iris in Coq: + +* Iris proof patterns are documented in the [proof guide](ProofGuide.md). +* Syntactic conventions are described in the [style guide](StyleGuide.md). +* The Iris tactics are described in the + [the Iris Proof Mode (IPM) / MoSeL documentation](ProofMode.md) as well as the + [HeapLang documentation](HeapLang.md). * The generated coqdoc is [available online](https://plv.mpi-sws.org/coqdoc/iris/). + +Contacting the developers: + * Discussion about the Iris Coq development happens on the mailing list [iris-club@lists.mpi-sws.org](https://lists.mpi-sws.org/listinfo/iris-club) and in the [Iris Chat](https://mattermost.mpi-sws.org/iris). This is also the @@ -128,6 +132,11 @@ that should be compatible with this version: [issue tracker](https://gitlab.mpi-sws.org/iris/iris/issues), which also requires an MPI-SWS GitLab account. * To contribute to Iris itself, see the [contribution guide](CONTRIBUTING.md). + +Miscellaneous: + +* Information on how to set up your editor for unicode input and output is + collected in [Editor.md](Editor.md). * If you are writing a paper that uses Iris in one way or another, you could use the [Iris LaTeX macros](docs/iris.sty) for typesetting the various Iris connectives. diff --git a/StyleGuide.md b/StyleGuide.md new file mode 100644 index 000000000..56d55f39e --- /dev/null +++ b/StyleGuide.md @@ -0,0 +1,118 @@ +# Iris Style Guide + +This document lays down syntactic conventions about how we usually write our +Iris proofs in Coq. It is a work-in-progress. This complements the tactic +documentation for the [proof mode](ProofMode.md) and [HeapLang](HeapLang.md) as +well as the [proof guide](ProofGuide.md). + +## Implicit generalization + +We often use the implicit generalization feature of Coq, triggered by a backtic: +`` `{!term A B}`` means that an implicit argument of type `term A B` is added, +and if any of the identifiers that are used here is not yet bound, it gets added +as well. Usually, `term` will be some existing type class or similar, and we +use this syntax to also generalize over `A` and `B`; then the above is +equivalent to `{A B} {Hterm: term A B}`. The `!` in front of the term disables +an even stronger form of generalization, where if `term` is a type class then +all missing arguments get implicitly generalized as well. This is sometimes +useful, e.g. we can write `` `{Countable C}`` instead of `` `{!EqDecision C, +!Countable C}``. However, generally it is more important to be aware of the +assumptions you are making, so implicit generalization without `!` should be +avoided. + +## Type class resolution control + +When you are writing a module that exports some Iris term for others to use +(e.g., `join_handle` in the [spawn module](theories/heap_lang/lib/spawn.v)), be +sure to mark these terms as opaque for type class search at the *end* of your +module (and outside any section): +``` +Typeclasses Opaque join_handle. +``` +This makes sure that the proof mode does not "look into" your definition when it +is used by clients. + +## Naming conventions for variables/arguments/hypotheses + +### small letters + +* a : A = cmraT or cofeT +* b : B = cmraT or cofeT +* c +* d +* e : expr = expressions +* f = some generic function +* g = some generic function +* h : heap +* i +* j +* k +* l +* m : iGst = ghost state +* m* = prefix for option +* n +* o +* p +* q +* r : iRes = resources +* s = state (STSs) +* s = stuckness bits +* t +* u +* v : val = values of language +* w +* x +* y +* z + +### capital letters + +* A : Type, cmraT or cofeT +* B : Type, cmraT or cofeT +* C +* D +* E : coPset = Viewshift masks +* F = a functor +* G +* H = hypotheses +* I = indexing sets +* J +* K : ectx = evaluation contexts +* K = keys of a map +* L +* M = maps / global CMRA +* N : namespace +* O +* P : uPred, iProp or Prop +* Q : uPred, iProp or Prop +* R : uPred, iProp or Prop +* S : set state = state sets in STSs +* T : set token = token sets in STSs +* U +* V : abstraction of value type in frame shift assertions +* W +* X : sets +* Y : sets +* Z : sets + +### small greek letters + +* γ : gname +* σ : state = state of language +* φ : interpretation of STS/Auth + +### capital greek letters + +* Φ : general predicate (over uPred, iProp or Prop) +* Ψ : general predicate (over uPred, iProp or Prop) + +## Naming conventions for algebraic classes + +### Suffixes + +* C: OFEs +* R: cameras +* UR: unital cameras or resources algebras +* F: functors (can be combined with all of the above, e.g. CF is an OFE functor) +* G: global camera functor management +* T: canonical structurs for algebraic classes (for example ofeT for OFEs, cmraT for cameras) diff --git a/tests/one_shot.v b/tests/one_shot.v index a71159b6a..1b5ff38cf 100644 --- a/tests/one_shot.v +++ b/tests/one_shot.v @@ -103,11 +103,10 @@ Definition client : expr := (Fst "ff" #5 ||| let: "check" := Snd "ff" #() in "check" #()). Section client. - Local Set Default Proof Using "Type*". Context `{!heapG Σ, !one_shotG Σ, !spawnG Σ}. Lemma client_safe : WP client {{ _, True }}%I. - Proof. + Proof using Type*. rewrite /client. wp_apply wp_one_shot. iIntros (f1 f2) "[#Hf1 #Hf2]". wp_let. wp_apply wp_par. - wp_apply "Hf1". diff --git a/theories/base_logic/lib/saved_prop.v b/theories/base_logic/lib/saved_prop.v index cb956f556..7e3401fd3 100644 --- a/theories/base_logic/lib/saved_prop.v +++ b/theories/base_logic/lib/saved_prop.v @@ -8,8 +8,10 @@ Import uPred. (* "Saved anything" -- this can give you saved propositions, saved predicates, saved whatever-you-like. *) -Class savedAnythingG (Σ : gFunctors) (F : cFunctor) := - saved_anything_inG :> inG Σ (agreeR (F (iPreProp Σ))). +Class savedAnythingG (Σ : gFunctors) (F : cFunctor) := SavedAnythingG { + saved_anything_inG :> inG Σ (agreeR (F (iPreProp Σ))); + saved_anything_contractive : cFunctorContractive F (* NOT an instance to avoid cycles with [subG_savedAnythingΣ]. *) +}. Definition savedAnythingΣ (F : cFunctor) `{!cFunctorContractive F} : gFunctors := #[ GFunctor (agreeRF F) ]. -- GitLab