• Aucun résultat trouvé

3. TRANSLATING SURFACE MEZZO DOWN TO CORE MEZZO

3.2. Well-kindedness

The combined surface and core syntaxes of Mezzo are presented in Fig.15. The surface syntax has the function typeT1 T2, which is exposed to the user under the ASCII

κ ::= value | type | perm | . . . (Kinds)

T , P ::= (Types and permissions)

| x (Variable)

| =x (Singleton type)

| T T (Function type (core))

| (T |P) (Type/permission conjunction)

| (T , T) (Pair type)

| x@T (Atomic permission)

| empty (Empty permission)

| PP (Permission conjunction)

| duplicableT (Duplicability assertion)

| ∀(x:κ)T (Universal quantification)

| ∃(x:κ)T (Existential quantification)

| T T (Function type (surface))

| x:T (Name introduction (surface))

| consumesT (Consumes annotation (surface))

Fig. 15. Types and permissions: combined core and surface syntaxes

names(x:T) = {(x,value)} ]names(T) (Name introduction)

names(T1, T2) = names(T1)]names(T2) (Pair type)

names(T|P) = names(T) (Type/permission conjunction)

names(consumesT) = names(T) (Consumes annotation)

names(T) = (Any other type)

Fig. 16. Name collection function

K-OPENNEWSCOPE Γ;names(T)`T :κ

Γ`#T:κ

Fig. 17. Types and permissions: well-kindedness (auxiliary judgement)

formT1 -> T2. The core syntax has the function typeT1→T2instead. The constructs x:T andconsumesT appear only in the surface syntax. All of the other constructs are shared between the two levels.

The well-kindedness judgement checks (among other things) that every name is properly bound. Thus, in a slightly indirect way, it defines the scope of every name.

In addition to the universal and existential quantifiers, which are perfectly standard, Mezzo offers the name introduction constructx:T, which is nonstandard, sincexis in scope not just in the typeT, but also “higher up”, so to speak. For instance, in the type (x1:T1, x2:T2), bothx1andx2are in scope in bothT1andT2.

In order to reflect this convention, in the well-kindedness rules, one must at cer-tain well-defined points go down and collectthe names that are introduced by some name introduction form, so as toextendthe environment with assumptions about these names.

The auxiliary functionnames(T), which collects the namesintroduced bythe typeT, is defined in Fig. 16. In short, it descends into tuples, looking for name introduction forms, and collects the names that they introduce.

The well-kindedness judgement Γ `T :κmeans that under the kind assumptions inΓ, the typeT has kindκ. Its definition (Fig. 18) relies on an auxiliary judgement, Γ ` #T : κ, which by definition means Γ;names(T)`T : κ(Fig. 17). Intuitively,#is a

“beginning-of-scope” mark: it means that, at this point, the names collected by the aux-iliary functionnamesare in scope. We stress that the#symbol is not part of the syntax

K-VAR

Fig. 19. Types and permissions: first translation phase (auxiliary judgement)

T1-VAR Fig. 20. Types and permissions: first translation phase

of types, and is not exposed to the user. It is purely a technical means of formulating our rules in a convenient manner.

There are additional restrictions that the well-kindedness rules should impose: for instance, theconsumeskeyword should appear only in the left-hand side of an arrow, and should not appear under another consumes keyword. This can be expressed by extending the well-kindedness judgement with a Boolean parameter, which indicates whether consumesis allowed or disallowed. In order to reduce clutter, we omit this aspect.

3.3. Translation

We now define the translation of (well-kinded) types and permissions from the surface syntax into the core syntax. For greater clarity, we present it as the composition of two phases. In the first phase, we eliminate the name introduction construct. In the

T2-EXTERNALARROW

T1BT10 T2BT20

T10in= [T /consumesT]T10 T10out= [?/consumesT]T10 T1 T2B∀(x:value) (=x|x@T10in)(T20|x@T10out)

Fig. 21. Types and permissions: second translation phase (only one rule shown)

second phase, we transform the surface function type into its core counterpart, and at the same time eliminate theconsumesconstruct.

Phase 1.The first phase is described by the translation judgement T I T0, whose definition (Fig.20) relies on the auxiliary judgement#T IT0(Fig.19).

The main rules of interest areT1-OPENNEWSCOPE, which introduces explicit existen-tial quantifiers for the names whose scope begins at this point; T1-EXTERNALARROW, which introduces explicit universal quantifiers, above the function arrow, for the names introduced by the domain of the function; and T1-NAMEINTRO, which trans-lates a name introduction form to a conjunction of a singleton type =x and a per-mission x@T0. The two occurrences of xin this conjunction are free: they refer to a quantifier that has necessarily been introduced higher up by T1-OPENNEWSCOPE or T1-EXTERNALARROW.

CLAIM 3.1. Well-kindedness is preserved by the first translation phase:

—Γ`T :κandT IT0implyΓ`T0 :κ.

—Γ`#T :κand#T IT0implyΓ`T0 :κ.

CLAIM 3.2. IfT IT0or#T IT0holds, thenT0does not contain a name introduction construct.

Phase 2.The second phase is described by the translation judgementT BT0, whose definition appears in Fig. 21. Only one rule is shown, as the other rules (omitted) simply encode a recursive traversal.

The ruleT2-EXTERNALARROWdoes several things at once. First, it transforms a sur-face arrow into a core arrow→. Second, it introduces a fresh name,x, which refers to the argument of the function; this is imposed by the singleton type =x. Finally, in order to express the meaning of theconsumeskeywords that may appear in the typeT10, it constructs distinct pre- and postconditions, namelyx@T10inandx@T10out. These per-missions respectively represent the properties ofxthat the function requires (prior to the call) and ensures (after the call).

The typeT10in is defined as[T /consumesT]T10. By this informal notation, we mean “a copy ofT10 where every subterm of the formconsumesT is replaced with justT”, or in other words, “a copy ofT10 where everyconsumeskeyword is erased”.

The type T10out is defined as[?/consumesT]T10. By this informal notation, we mean

“a copy ofT10 where every subterm of the formconsumesT is replaced with∃(x:κ)x, where the kind κis eithertype orperm, as appropriate”. We note that∃(x: κ)xis a

“top” type or permission: it does not provide any useful information.

Thus, the permissionx@T10in represents the ownership of the argument, including the components marked with consumes, whereas the permissionx@T10out represents the ownership of the argument, deprived of these components.

CLAIM 3.3. Well-kindedness is preserved by the second translation phase: assuming thatT contains no name introduction forms,Γ`T :κandT BT0 implyΓ`T0:κ.

CLAIM 3.4. IfΓ`T :κandT BT0 hold, thenT0contains no surface arrow and noconsumeskeyword.

4. KERNEL

Translating types (and terms) in the manner described in the previous section (§3) yields a Core Mezzo program. The remainder of this paper is devoted to:

— defining what it means for a Core Mezzo program to be well-typed;

— proving that a well-typed program cannot go wrong and must be data-race free.

Another important question is: how does one effectively determine whether a program is well-typed? This question is not addressed here: we provide a declarative definition of well-typedness, not a type-checking algorithm. The type-checking algorithms that we have implemented are described in Protzenko’s dissertation [2014a].

A (pseudo-)modular approach. Instead of defining Core Mezzo in a monolithic way, we set it up in a modular manner. We begin with a kernel, on top of which sit several relatively independent layers. This approach makes our presentation more gentle and makes the maintenance of the Coq formalization easier.

In order to avoid any confusion, we must emphasize the fact that our Coq code is, strictly speaking, not modular. The kernel and its extensions are not independent arti-facts: they cannot be independently type-checked and later brought together. In reality, the Coq code is monolithic: there is a single inductive type of the syntax of Core Mezzo, which includes the kernel and its extensions. Still, the code is “pseudo-modular” in the sense that the presence or absence of one extension in principle has little to no impact on the code of the kernel or of the other extensions. We briefly come back to this issue when we discuss the related work (§10).

Organization. The kernel, described in this section (§4), is a call-by-valueλ-calculus, equipped with a construct for dynamic thread creation. The three layers that we de-scribe in this paper are heap-allocated references (§5), locks (§6), and adoption and abandon (§7). Yet more layers would be needed in order to account for all of the fea-tures of Mezzo, as implemented today; we describe them briefly in §8.

We now present the core elements of the formalization of Mezzo, that is, the kernel of the proof. First, we axiomatize a notion of machine state, a notion of instrumented machine state (also known as aresource), and a connection between the two (§4.1). We present the syntax (§4.2) and operational semantics (§4.3) of the untyped calculus. We equip the calculus with a type discipline (§4.4, §4.5, §4.6) and prove a type soundness theorem (§4.7).