ISSN 02496399 apport de recherche THME 1 INSTITUT NATIONAL DE RECHERCHE EN INFORMATIQUE ET EN AUTOMATIQUE Global abstractionsafe marshalling with hash types James J. Leifer + Gilles Peskine + Peter Sewell # Keith Wansbrough # + INRIA Rocquencourt # University of Cambridge {First.Last}@inria.fr {First.Last}@cl.cam.ac.uk N 4851 25 June 2003 Unit de recherche INRIA Rocquencourt Domaine de Voluceau, Rocquencourt, BP 105, 78153 Le Chesnay Cedex (France) Tlphone : +33 1 39 63 55 11 --- Tlcopie : +33 1 39 63 53 30 Global abstractionsafe marshalling with hash types James J. Leifer + Gilles Peskine + Peter Sewell # Keith Wansbrough # + INRIA Rocquencourt # University of Cambridge {First.Last}@inria.fr {First.Last}@cl.cam.ac.uk Th eme 1 --- R eseaux et syst emes Projet Moscova Rapport de recherche n 4851 --- 25 June 2003 --- 86 pages Abstract: Type abstraction is a key feature of MLlike languages for writing large programs. Marshalling is necessary for writing distributed programs, exchanging values via network bytestreams or persistent stores. In this report we combine the two, developing compiletime and runtime semantics for marshalling, that guarantee abstractionsafety between separatelybuilt programs. We obtain a namespace for abstract types that is global, i.e. meaningful between programs, by hashing module dec larations. We examine the scenarios in which values of abstract types are communicated from one program to another, and ensure, by constructing hashes appropriately, that the dynamic and static notions of type equality mirror each other. We use singleton kinds to express abstraction in the static semantics; abstraction is tracked in the dynamic semantics by coloured brackets. These allow us to prove preservation, erasure, and coincidence results. We argue that our proposal is a good basis for extensions to existing MLlike languages, pragmatically straightforward for language users and for implementors. Keywords: programming languages, ML, type theory, abstract types, marshalling, serialisation, modules, singleton kinds, hashing, distributed programming, lambda calculus S erialisation avec s uret e globale des abstractions : utilisation des types de hachage R esum e : L'abstraction de types est un trait essentiel des langages de la famille ML pour ecrire les logiciels de taille importante. La s erialisation est un ingr edient indispensable des programmes distribu es, de la transmission de valeurs sur un r eseau, et du stockage persistant de donn ees. Dans ce rapport, nous combinons les deux, en d eveloppant des s emantiques statique et dynamique de la s erialisation qui garantissent la s uret e des abstractions entre des programmes d evelopp es s epar ement. Nous obtenons un espace de nommage pour les types abstraits qui est global, c'est adire commun a tous les pro grammes, par hachage des d eclarations de modules. Nous examinons les sc enarios dans lesquels des valeurs de types abs traits sont transmises entre les programmes ; nous garantissons que les notions statique et dynamique d' egalit e de types co ncident gr ace a la construction de la fonction de hachage. Nous utilisons les singleton kinds pour exprimer les abs tractions dans la s emantique statique ; ces abstractions sont suivies a la trace par des crochets color es dans la s emantique dynamique. Cette m ethode nous permet de prouver les th eor emes de pr eservation, d'effacement, et de correspondance. Nous proposons cette technique comme une base pour etendre les langages a la ML, car elle est simple et pratique pour l'utilisateur comme pour l'impl ementeur. Motscl es : langages de programmation, ML, th eorie des types, types abstraits, marshalling, s erialisation, modules, singleton kinds, hachage, programmation distribu ee, lambda calcul Global abstractionsafe marshalling with hash types 3 RR n 4851 4 Leifer, Peskine, Sewell, Wansbrough Contents 1 Introduction 6 2 Abstraction and interaction: the desired behaviour 7 2.1 Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Respecting types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.3 Respecting abstractions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.4 Communication between completelyshared sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.5 Communication between partiallyshared sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.6 Guaranteeing compatible invariants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.7 Respecting names (when necessary) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.8 Module dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.9 Mirroring local type sharing: manifest types, functors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.10 Breaking abstractions (simple bidirectional case) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.11 Breaking abstractions (directed case) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.12 Forcing generativity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.13 Effectful module initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.14 Marshalling functions and rebinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3 Solution: hash types as global names 14 3.1 Simple examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 3.2 Module dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.3 Abstractionpreserving reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.4 Modest implementation demands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.5 Lowlevel details of hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 4 Formal system 18 4.1 Relation to the informal discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.2 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.3 Static and dynamic semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 4.3.1 Singleton kinds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 4.3.2 Hash formation, type equality of hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 4.3.3 Compiletime reduction and coloured brackets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 4.3.4 Expression reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 4.3.5 Marshalling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 4.3.6 Programs and networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4.4 Results . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 5 Related work 23 6 Conclusions and future work 24 6.1 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 6.2 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 A Introduction to the complete definitions and proofs 26 A.1 Differences between the main body and the appendices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 A.2 Correspondence between the theorems in the main body and in the appendices . . . . . . . . . . . . . . . . . . . . . 26 B Syntax 26 C Static judgements 29 C.1 # / # domE nonclash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 C.2 # hm ok hash correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.3 E #hm ok environment correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.4 E #hm K ok kind correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.5 E #hm K == K # kind equality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.6 E #hm K <: K # subkinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.7 E #hm T :K kind of a type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 C.8 E #hm T == T # type equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 C.9 E #hm S ok signature correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 INRIA Global abstractionsafe marshalling with hash types 5 C.10 E #hm S == S # signature equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 C.11 E #hm S <: S # subsignaturing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 C.12 E #hm e:T type of an expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 C.13 E #hm M :S signature of a module expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 C.14 E #hm U :S signature of a module variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 C.15 E #hm m:T type of a machine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 C.16 # n ok network correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 D Reduction rules and structural congruence 33 D.1 m -# c m # compiletime reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 D.2 e -#hm e # expression reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 D.3 n # n # network structural congruence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 D.4 n -# n # network reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 E General definitions and lemmas 34 E.1 Proofs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 E.2 Correctness of parts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 E.3 Variables and colours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 F Type preservation by substitution 39 G Type decomposition and type preservation for reduction 52 H Progress 60 H.1 Classical progress theorems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 H.2 Determinism of reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 I Compilation 65 I.1 Decidability of type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 I.2 Bracket elimination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 J Coincidence of undyntime type checking and static type checking 76 K Table of theorems and definitions 82 References 85 RR n 4851 6 Leifer, Peskine, Sewell, Wansbrough This technical report is a long version of the paper [LPSW03]: James J. Leifer, Gilles Peskine, Peter Sewell, Keith Wansbrough. ``Global abstractionsafe marshalling with hash types''. Proc. 8th ICFP. 2003. (Available from http://pauillac.inria.fr/~leifer/research.html.) 1 Introduction Problem Type abstraction is a basic tool for modular programming, allowing the programmer to separate the interface and the implementation of an abstract data type, and to limit the scope in which the implementation details are visible. Work on MLstyle module systems, including [Mac84, MTH90, HL94, Ler94], has led to expressive language constructs for controlling abstraction, with modules (structures) that can export abstract types, and also parameterised modules (functors); they have rich notions of type equality to deal with generativity and sharing. This work has largely been in the nondistributed context, concerned only with isolated executions of single programs. There, buildtime type checking suffices to guarantee both typesafety and abstractionsafety --- the property that values of an abstract type can only be inspected or constructed by the code of its definition, and hence that any invariants of this code hold of all values. At runtime, type information can be erased. In the distributed setting, abstractionsafety is more subtle. One may need to exchange values between multiple executions of the same build, between executions of different builds of the same sources, and between executions of builds of different sources (sharing some modules, perhaps, but not all). This interaction might be by network communication or via a persistent store; in either case, some runtime check is clearly needed to guarantee safety. For abstractionsafety, it does not suffice to check only the underlying representation type; intuitively, we need also that the sender and receiver have compatible invariants. This can be enforced by requiring that they have the same code, but in general, where there is only partial sharing, we shall see that the design of an appropriate check is delicate. We focus in particular on language support for marshalling a value to a byte string and unmarshalling such strings back to values. With these, one can implement a variety of useful mechanisms above the standard (bytestring) primitives for network communication and persistence. For example: (1) In the existing distributed languages JoCaml [JoC] and Nomadic Pict [SWP99] a single program can dynamically distribute computations, which can then interact via typed channels, but unsafe ``name servers'' are required to bootstrap connections between programs. Type and abstraction safe marshalling would enable such name servers to be expressed in a safe way. (2) More generally, safe marshalling would enable one to code up a variety of communication abstractions, such as typed channels with differing behaviour (asynchronous, unicast, multicast,. . . ), within a highlevel language; they would then be automatically guaranteed to be safe. Contribution We present a type system and semantics (both compiletime and runtime) for marshalling and unmar shalling values between separate programs. Our solution: . covers modules that declare abstract types, and ancillary typesharing constraints; . involves a dynamic typecheck at unmarshal time that guarantees both typesafety and abstractionsafety; . ensures the resulting dynamic notion of type equality coincides with the usual static notion, so that distributed pro gramming is a smooth extension of local programming; . ``just works'' in standard cases for interaction between programs that share some modules, without requiring any shared data beyond the source code for these modules; . supports controlled abstractionbreaking, where required; and . is efficiently implementable. It is therefore a good basis for extensions to existing MLlike languages, pragmatically straightforward for language users and for implementors. Approach The basic idea of our solution is to construct a global namespace for abstract types, meaningful across all programs, by hashing module declarations. Hash types do not appear in source programs, but are constructed at compile time. For example, consider a module called N with the body struct type t=Trep let x=... end and the published interface sig type t val x:... end. The hash h = hash(module N=struct type t=Trep let x= ... end : sig type t val x: ... end, t) INRIA Global abstractionsafe marshalling with hash types 7 would be constructed to give a runtime analogue of the compiletime abstract type name N.t. By constructing hashes carefully, we ensure that a simple runtime syntactic type equality check, at unmarshaltime, corresponds to the compile time notion of type equivalence used in typechecking. The standard operational semantics for existentials forgets abstraction. In contrast, we give a runtime semantics that records which subterms can see through which abstractions using coloured brackets, adapting a device of [GMZ00]. For example, within the code of N.x, even after it has been substituted into its usage sites, the type equality h ==Trep can be used. This enables us to prove type and abstractionpreservation, progress, and the coincidence result mentioned above. We prove also that an implementation may safely erase all coloured brackets outside hashes at runtime. Nongoals Our focus in this report is on what mechanisms are required to guarantee abstractionsafety. We do not address the full ML language; instead, we focus on a core language based on simplytyped #calculus with abstract and manifest modules, although we argue that our formal system may be cleanly extended. Dynamic rebinding of identifiers within marshalled values is considered in [BHS + 03]. Moreover, we are not here concerned with lowlevel representations of marshalled values; we assume some fixed scheme for marshalling simplytyped values. Finally, we protect against confusion, not malice. Outline We begin in Section 2 by examining scenarios in which values of abstract types are communicated between programs, identifying the desired constructs and behaviour from the programmer's point of view. Section 3 outlines our solution informally, shows why it provides the desired behaviour, and shows it can be implemented efficiently. In Section 4 we present a formal calculus, # hash , that covers the novel aspects of our solution. It describes networks of interacting separatelybuilt modular programs. In Section 5 and Section 6 we discuss related and future work and conclude. 2 Abstraction and interaction: the desired behaviour In this section we discuss the desired behaviour of marshalling in a distributed setting, with a series of informal examples in an MLlike language. Our solution, in the following section, shows how this ideal can be achieved. We consider an MLlike language in which a program consists of two parts: first a sequence of module declarations, each of which can introduce abstract types; then an expression (the main body of the program). We are concerned with interaction between whole programs, usually built separately. This interaction is via network communication, though it could equally be via a persistent store; in either case, the underlying mechanism simply transmits byte strings. For concreteness, most of our examples involve networks consisting of two machines, pauillac and glia, running programs, say Pa and Pb . These network configurations are written pauillac[Pa ] | glia[Pb ]. It then suffices to consider a single communication channel (such as a TCP connection between fixed ports); the language has communication primitives send : string>unit receive : unit>string We can now explore the desired behaviour of marshal(e :T ) and unmarshal(e :T ), which marshal to and from string. 2.1 Communication The simplest example is that of sending a value of a nonabstract type between separatelybuilt programs. Consider the two programs P1a = send (marshal (5 : int)) P1b = print_int (unmarshal (receive ():int)) If these are built and then executed on the two machines the communication and unmarshal should succeed: pauillac[P1a ] | glia[P1b ] # 2.2 Respecting types On the other hand, if one machine sends a string that the other attempts to unmarshal as an int there should obviously be a runtime failure. RR n 4851 8 Leifer, Peskine, Sewell, Wansbrough P2a = send (marshal ("five":string)) P2b = print_int (unmarshal (receive ():int)) pauillac[P2a ] | glia[P2b ] To ease debugging, it is desirable for that failure to occur as early as possible (at unmarshaltime rather than when the string is used later) and to be trapped cleanly, raising an exception rather than giving unpredictable behaviour. The implementation must therefore send some form of type representation. The following examples explore the constraints on what this must be. 2.3 Respecting abstractions Now consider an example with an abstract type. Here the EvenCounter module declares a type EvenCounter.t which has a representation type of int but externally is abstract, as declared in its signature. The operations of EvenCounter enforce the invariant that values of EvenCounter.t are always represented by even integers. If we allowed an arbitrary integer to be unmarshalled as an EvenCounter.t then the abstraction, and this invariant, would be broken; the unmarshal should therefore fail. P3a = send (marshal(5:int)) P3b = module EvenCounter = struct sig type t=int type t let start=0 : val start:t let get x = x val get:t>int let up x = x+2 val up:t>t end end print_int (EvenCounter.get (unmarshal (receive ()):EvenCounter.t)) pauillac[P3a ] | glia[P3b ] Marshalling from a different abstract type --- say a TripleCounter.t --- to EvenCounter.t should fail similarly. 2.4 Communication between completelyshared sources For communication between two instances of the same build, which therefore have identical source code, the problem is relatively simple. Below, P4 declares an abstract type IntSet.t of sets of integers, representing them as binary search trees. It makes a runtime determination of which machine it is on and then sends or receives an IntSet.t; the unmarshal should succeed. We will develop this example later --- suppose this first implementation orders subtrees by <, and has a union operation that does not remove duplicate entries. INRIA Global abstractionsafe marshalling with hash types 9 P4 = module IntSet = struct type t = int tree let singleton = singletoncode 9 > > > > > = > > > > > ; IntSetStruct let mem = memcode ... end : sig type t val singleton : int > t val mem : int > t > bool 9 > > > > > > > > > = > > > > > > > > > ; IntSetSig val empty : t val add : int > t > t val union : t > t > t end if ...onmachinepauillac... then send (marshal (IntSet.singleton 17 : IntSet.t)) else if IntSet.mem 17 (unmarshal(receive():IntSet.t)) then print "y" else print "n" pauillac[P4 ] | glia[P4 ] # By default this should still succeed even if the two machines execute different builds of the same source. 2.5 Communication between partiallyshared sources More generally, one may need communication between programs which share only some modules (perhaps ubiquitous standard libraries, or applicationspecific libraries). Here P5a and P5b share the IntSet module from before, but other wise have different module declarations and main body expressions; their communication of an IntSet.t should succeed. P5a = module IntSet = IntSetStruct :IntSetSig send (marshal (IntSet.singleton 17 : IntSet.t)) P5b = module IntSet = IntSetStruct :IntSetSig module M = struct let haszero x = IntSet.mem 0 x end : sig val haszero : IntSet.t > bool end if M.haszero (unmarshal (receive () : IntSet.t)) then print "y" else print "n" pauillac[P5a ] | glia[P5b ] # 2.6 Guaranteeing compatible invariants In the previous example the two programs had syntactically identical IntSet implementations. Since IntSet does not depend on any other modules, this is a sufficient condition to guarantee that the two abstract types have compatible invariants, i.e. that any value of either will be correctly acted upon by the operations of the other. Moreover, it can be automatically checked, whereas compatibility of invariants cannot even be stated without specifing the behaviour of the two modules, and would then require general theoremproving to verify. Note that it would not be sufficient to require that the two implementations use the same representation type, or even to require that the implementations are (in the absence of marshalling) observationally equivalent. For example, suppose that IntSetStructGt is similar to IntSetStruct but orders subtrees with > rather than <. RR n 4851 10 Leifer, Peskine, Sewell, Wansbrough P6a = module IntSet = IntSetStructGt :IntSetSig send (marshal (IntSet.add 0 (IntSet.add 1 (IntSet.add 2 IntSet.empty)) : IntSet.t)) When communicating with P5b , which contains the original IntSetStruct , the unmarshal should fail, as otherwise an erroneous result could be produced. pauillac[P6a ] | glia[P5b ] Later we shall see that a mechanism for intentionally circumventing this restriction, in a controlled way, is sometimes desirable. 2.7 Respecting names (when necessary) In some cases one has modules with identical implementations that nonetheless provide conceptually different abstract types, for example in the Euro and Pound modules below. Unmarshalling should respect this difference, so the example should fail (just as, within a single ML program, types Euro.t and Pound.t would be incompatible). P7a = module Euro = struct type t=int let of_int x = x ... end : sig type t val of_int : int > t ... end send (marshal (Euro.of_int 17 : Euro.t)) P7b = module Pound = struct type t=int let of_int x = x ... end : sig type t val of_int : int > t ... end unmarshal (receive (): Pound.t) pauillac[P7a ] | glia[P7b ] This restriction is not always useful (e.g. whether an integer set module is called IntSet or Set Int is likely irrelevant), so the language should support some syntactic way of indicating whether a module name is significant or not. 2.8 Module dependencies Consider now modules that depend on abstract types declared by other modules. In P8a below there is a module IntSet, providing an abstract type IntSet.t, followed by a module SummedIntSet, providing an abstract type of sets of integers augmented with a running sum. The expression part constructs, marshals and sends a value of the SummedIntSet.t abstract type. This SummedIntSet depends on IntSet in three ways: (1) IntSet.t occurs in its representation type IntSet.t # int; (2) IntSet.t occurs in the type of an operation in its signature; and (3) operations from IntSet occur in the definitions of its operations. Any such dependency means that substantive changes to the definition of IntSet must propagate through to give distinct SummedIntSet.t types. On the other hand, any module declarations that are not (transitively) depended upon should have no effect on SummedIntSet.t. For example, consider also P8b below. It has exactly the same text as SummedIntSet but a different implementation of IntSet --- suppose IntSetStruct' has a different representation type from IntSetStruct , or the same representation but incompatible invariants, or different externallyobservable behaviour. The P8a and P8b SummedIntSet.t types should be incompatible, so the unmarshal should fail. INRIA Global abstractionsafe marshalling with hash types 11 P8a = module IntSet = IntSetStruct :IntSetSig module SummedIntSet = struct type t = IntSet.t * int let empty = (IntSet.empty,0) let sum (x,y) = y ... end : sig type t val empty : t val singleton : int > t val sum : t > int val to_intset : t > IntSet.t ... end send(marshal((SummedIntSet.singleton 2) : SummedIntSet.t )) P8b = module IntSet = IntSetStruct ':IntSetSig module SummedIntSet = ...same text as above... SummedIntSet.sum (unmarshal (receive () : SummedIntSet.t)) pauillac[P8a ] | glia[P8b ] 2.9 Mirroring local type sharing: manifest types, functors The examples in this and subsequent subsections are not covered by the formal calculus of Section 4. Nonetheless we argue informally in Section 6.2 how they can be treated by straightforward extensions of our main techniques and earlier work. ML module systems include parametric modules, known as functors, for largescale software structuring and code reuse. In the singleprogram world there are a number of subtle typeequality issues, related to how generative functors are, and how one can express type sharing constraints [MTH90, Ler94, HL94, SH00]. Our marshalling primitives should correctly reflect these subtleties in interprogram communication. For example, the module SummedIntSet above, which explicitly references IntSet, might be reexpressed in terms of a functor F which takes any argument structure U with interface IntSetSig and builds a SummedIntSet: module IntSet = IntSetStruct :IntSetSig module F = functor (U:IntSetSig ) > struct type t=U.t*int ... end : sig type t ... end module SummedIntSet = F(IntSet) The functor F generates an abstract type, so we must consider when that type should be compatible with others. If two separate programs contain this preamble, they should be able to exchange values of their respective SummedIntSet.t types. This mirrors the behaviour of OCaml's applicative functors [Ler95], in which another instance of the application F(IntSet) within the same program would have a type compatible with SummedIntSet.t. Should the functorised and nonfunctorised (P8a ) SummedIntSet.t be compatible? Again following existing module systems, we should make them incompatible, as otherwise static type equality would depend on module substitution. Type sharing allows functors to express type equalities between their argument and result; unmarshalling should respect these static type equalities. The example F' below constructs a type t but, in contrast to F, does not make that type abstract; instead it makes it manifestly equal to the product of its argument type U.t and int. RR n 4851 12 Leifer, Peskine, Sewell, Wansbrough module F' = functor (U:IntSetSig ) > struct type t=U.t*int ... end : sig type t=U.t*int ... end The application of F' to a module IntSet creates a static type equality F'(IntSet).t==IntSet.t*int, which should also be admitted at runtime. 2.10 Breaking abstractions (simple bidirectional case) In ongoing software evolution, implementations of an abstract type may need to be changed, to fix bugs or add function ality, while values of that type exist on other machines or in a persistent store. It is often impractical to simultaneously upgrade all machines to a new implementation version. A simple case is that in which the representation of the abstract type is unchanged and where the programmer asserts that the two versions have compatible invariants, so it is legitimate to exchange values in both directions. This may be the case even if the two are not identical, e.g. for an efficiency improvement or bug fix. Here there should be some mechanism for forcing the old and new types to be identical, breaking the Section 2.6 restriction. For example, consider the improved IntSetStructDeDup implementation below, in which the operations are sim ilar to IntSetStruct , the only difference being that union removes duplicates. The compiler cannot verify that IntSetStructDeDup has all the semantic properties that the programmer requires of IntSetStruct . Hence we provide a way of explicitly declaring that these modules provide compatible types. In P10a below, IntSet'.t is made equal to IntSet.t by the strong coercion ...with t =! IntSet.t. The compiler checks only that the old and new types have compatible representations (here int tree), but should respect further abstractions within those representation types. This is based on our earlier work of [Sew01]. P10a = module IntSet = IntSetStruct :IntSetSig module IntSet' = struct type t = int tree 9 > > = > > ; IntSetStructDeDup ...improved operations... end : IntSetSig with t =! IntSet.t send (marshal (IntSet'.singleton 17 : IntSet'.t)) P10b = module IntSet = IntSetStruct :IntSetSig if IntSet.mem 0 (unmarshal(receive():IntSet.t)) then print "y" else print "n" pauillac[P10a ] | glia[P10b ] # 2.11 Breaking abstractions (directed case) In the more complex case where the old and new invariants are not compatible, or where the two representation types differ, the programmer will have to write an upgrade function. The same strong coercion can be used to make this possible. For example, suppose we have a program that uses stored values of IntSetStruct and we wish to upgrade both the implementation and the stored values, changing the representation type from binary search trees to redblack trees. The new implementation would have a module declaration: INRIA Global abstractionsafe marshalling with hash types 13 module IntSet2 = struct type t = int rbtree 9 > > = > > ; IntSetStructRBT ... end : IntSetSig A program to upgrade the stored values can be expressed as below, with an Upgrade module that has both types, coerced respectively to be equal to the old and new abstract types. (We are not proposing machinery to automatically apply the upgrade function.) module IntSet = IntSetStruct :IntSetSig module IntSet2 = IntSetStructRBT : IntSetSig module Upgrade = struct type t1 = int tree type t2 = int rbtree let upgrade = ... end : sig type t1 type t2 val upgrade : t1 > t2 end with t1 =! Intset.t and t2 =! Intset2.t ...map Upgrade.upgrade over the stored values... Note that the coercion does not require the signature of Upgrade to coincide with those of IntSet and IntSet2. The compiler only checks that IntSet.t is represented by int tree and IntSet2.t by int rbtree. 2.12 Forcing generativity Dually, sometimes it is desirable to force a type change between builds even when the code remains identical, to prevent confusion between old and new communicated values. For example, one may have several distributed deployments of the same application which should be kept logically isolated. 2.13 Effectful module initialisation In our previous examples the components of modules are all values. Generalising this to arbitrary expressions (as ML does), an abstract type definition can be dependent on some computation with side effects. For example, consider an NCounter module that reads its step value from standard input when initialised; the invariant of any instance is then that any value of its NCounter.t is a multiple of this step. Two instances of the module can obviously have different invariants, and so marshalling from one to another should fail. Thus each run of a program containing NCounter should have an incompatible type NCounter.t. 2.14 Marshalling functions and rebinding In this report we deal only with marshalling of closed values; the semantics ensures that all module and expression declarations are substituted in before a marshal operation takes place. Marshalling of functions is therefore semantically straightforward. A full language should, however, provide some form of dynamic rebinding of identifiers when they are unmarshalled, both to achieve the desired semantics where local resources have different behaviour in different contexts, and for per formance reasons where much code is shared (and so should not be communicated). The paper [BHS + 03] addresses dynamic rebinding, in the absence of type abstraction. RR n 4851 14 Leifer, Peskine, Sewell, Wansbrough 3 Solution: hash types as global names This section introduces our solution informally, from both implementation and semantic viewpoints. As we have seen, typesafe and abstractionsafe unmarshalling requires some runtime type representation in mar shalled values, to permit a dynamic type comparison. Our solution is based on the observation that hashing module definitions provides a global namespace for abstract types: if an identical module is hashed during builds of two different programs at different sites, the same hash will be obtained. Thus the programs share names for any abstract type provided they share the source code of the module that declares the type (and of its dependencies); no communication (e.g. of GUIDs) is needed at build time. We regard hashes literally as types --- hashes appear as a clause in the type grammar. They do not appear in source programs, but are inserted during compilation; as we shall see in more detail, the compiler replaces occurrences of an abstract type such as IntSet.t by the hash of the definition of IntSet that is in scope. Semantically, we work with ideal hashing, with a formal syntactic construction hash(...). Implementations would realise this with an actual hash function; we discuss the lowlevel properties of hashes in Section 3.5. At runtime, after this compiletime type substitution, the types in marshal(e :T ) and unmarshal(e :T' ) are closed, without free module identifiers or type variables. They can therefore be easily represented as byte strings, com municated across the network or stored in a persistent store, and can be compared with simple string equality. We ensure that this dynamic equality precisely mirrors the static notion of provable type equality by carefully tuning the way in which hashes are generated and used; we show below that our system achieves this. Unmarshalling is therefore not only typesafe, but also abstractionsafe. The standard operational semantics for abstract types forgets about abstraction as computation proceeds, substituting in representation types and operations. Here, in contrast, we need a runtime semantics that maintains abstraction through out, both (1) so that our type preservation theorems tell us that abstractions are not broken; and (2) to support the proof that static and dynamic type equality coincide. After a module is reduced away, module code (which may see through the abstract type of that module) is intermixed with body code (which must treat the type as abstract). We therefore use a syntactic construct, coloured brackets, adapted from the work of [GMZ00], to delimit the regions in which different type equivalences hold. This is not purely a proof technique, however: in some subtle cases the coloured brackets within hashes are needed in compiletime hash generation to correctly distinguish abstract types that would otherwise be aliased. We show that implementations can erase coloured brackets outside hashes after compilation. 3.1 Simple examples We illustrate the use of hashes in a simple case by referring back to the example of Section 2.4, in which a single program, P4 , was built and run on the two machines. The build process is modelled in our semantics by typechecking, as usual, followed by reductions that substitute out module definitions, inserting hashes as required. Hash generation is deterministic, and hence the result of building P4 on the two machines is identical. The program has a single module definition. It has a compiletime reduction as below, to an `executable' P4' . (Note that for clarity of exposition, we omit coloured brackets from all reductions until Section 3.3; the example reductions as stated are not all typepreserving without them.) P4 = module IntSet = IntSetStruct :IntSetSig if ...onmachinepauillac... then send (marshal (IntSet.singleton 17 : IntSet.t)) else if IntSet.mem 17 (unmarshal(receive():IntSet.t)) then print "y" else print "n" -# c (compilation) if ...onmachinepauillac... then send (marshal (singletoncode 17 : h )) else if memcode 17 (unmarshal (receive () : h )) then print "y" else print "n" = P4' INRIA Global abstractionsafe marshalling with hash types 15 where h = hash(module IntSet=IntSetStruct :IntSetSig,t). Here the definitions of IntSet.singleton and IntSet.mem have been substituted for their occurrences, and the global name h has been substituted for type IntSet.t. Notice that h is constructed from the entire definition of IntSet, including the textual name IntSet, the implementation structure IntSetStruct , the interface IntSetSig , and the type field name t. In this simple example IntSet has no dependencies, so one can think of hashing its source text; we will discuss later the more interesting case of modules with dependencies, and also the question of exactly what form the hash function takes. Our liberal use of substitution is, of course, a semantic device --- in practice compilation would use other representations. At runtime, the two machines pauillac and glia execute their independentlycompiled copies of P4' . Their shared knowledge of the hash h acts as a certificate that they may safely share values of their respective abstract types IntSet.t and IntSet.t. pauillac[P4' ] | glia[P4' ] -# # (local computation on pauillac and glia) pauillac[send(marshal(singletoncode 17:h ))] | glia[if memcode 17 (unmarshal(receive():h )) then print "y" else print "n"] -# # (local computation on pauillac, to get v ) pauillac[send(marshalled( v :h ))] | glia[if memcode 17 (unmarshal(receive():h )) then print "y" else print "n"] -# (communication) pauillac[ () ] | glia[if memcode 17 (unmarshal(marshalled(v :h ):h )) then print "y" else print "n"] -# (on glia: dynamic type check h =h , succeeds) pauillac[ () ] | glia[if memcode 17 v then print "y" else print "n"] -# # (on glia: computation, prints "y") pauillac[ () ] | glia[ () ] Ultimately, only strings may be communicated across a network. The notation marshalled (v :T ) denotes a string literal containing representations of value v and its type T . This is only meaningful, and only used, where v and T are both closed. Notice that the dynamic check is simple: just that the type h sent from pauillac is identical to the type h written into the unmarshal on glia at compile time. Yet, by virtue of the construction of these hashes, this is sufficient to guarantee both typesafety and abstractionsafety. Consider now the programs of Section 2.1--2.7. How do hashes of modules provide the desired behaviour? In the case of concrete types, the comparison is obvious. For P1 , int=int; for P2 , string #=int; for P3 , for no hash h do we have int=h . As we have already seen, in the P4 case the two programs share an identical hash h . For P5 , in P5a and P5b the computed hash h for IntSet.t is identical (in fact the same h as above). Thus the h substituted for IntSet.t in P5a 's call to marshal will be identical to that in P5b 's call to unmarshal, and the communication will again succeed, exactly as we desire. Although P6a (Section 2.6) contains a type IntSet.t, it is clear that the hash h' of the modified module IntSet differs from the hash h of the original module, correctly reflecting the difference in the modules' behaviour. The two programs will, correctly, be unable to communicate; an exception will be raised at the point of the unmarshal. The example of Section 2.7 shows why one might wish the textual name (in general, the path) of a module to be included in its hash, along with the module body. The two modules Euro and Pound are identical in all but name, and so a hash that did not include the name would treat them as interchangeable, clearly leading to dangerous economic confusion, and furthermore differing from the usual semantics of MLlike languages. On the other hand, the programmer should also be able to specify that a name is not to be considered part of the module's identity. This can be done simply by having an additional form of module declaration, module* N = ... , for which hashing uses a canonical name *, not admissible RR n 4851 16 Leifer, Peskine, Sewell, Wansbrough in source programs, instead of the actual name N. In this simple scheme both sender and receiver must use the *'d form, of course. 3.2 Module dependencies The example of Section 2.8 shows that the same module text defines a different abstract type if its dependencies change, which means that the hash of a module must depend on the hashes of its dependencies. In our substitutive reduction semantics, type dependencies are handled automatically: we have substituted hashes for any types of earlier modules before constructing the hash of a module that depends on them. We shall see how term dependencies are also automatically taken into account. Consider the following (a simplification of P8a ): module A=struct type t=bool let x=true end : sig type t val x:t end module B=struct type t=A.t*int let x=(A.x,3) end : sig type t val x:t end send (marshal (B.x : B.t)) -# c (compilation) module B=struct type t=h *int let x=(true,3) end : sig type t val x:t end send (marshal (B.x : B.t)) -# c (compilation) send (marshal ((true,3) : h' )) where h = hash ( module A=struct type t=bool let x=true end : sig type t val x:t end,t) h' = hash ( module B=struct type t=h *int let x=(true,3) end : sig type t val x:t end,t) Here the hash h' for B is constructed after the hash h for A has been substituted for A.t, and after the term part true has been substituted for A.x. It is clear that if A changed, h would change, and so h' would change. This would still be true in the (unlikely) case that B mentions A.t but not A.x. We must also ensure h' depends on h in the (common) case that B mentioned A.x but not A.t, i.e. where A is used in B only to implement an internal computation. The coloured brackets of the following section will conveniently suffice for this. 3.3 Abstractionpreserving reduction Some reductions in Section 3.1, 3.2 require nonstandard type equalities to make them typepreserving. For example, to type the intermediate state in Section 3.2 we must have (true,3) of type h *int, hence we need a type equality identifying h with its representation type bool. We could allow this type equality to be used anywhere, but instead prefer to delimit more precisely which subterms can see through any particular abstraction. We introduce coloured brackets, adapted from the work of [GMZ00], during module reduction. In the previous example, the first reduction will actually replace (A.x, 3) by ([true] h h ,3) instead of just (true,3). The brackets serve two purposes. First, the lower annotation (the colour) is a hash h , indicating that the additional type equivalence h == bool is available when typing the in side of the bracketed expression. This equivalence is drawn from the structure of h , viz. h =hash(module A= struct type t=bool...end:...,t). Thus, inside the brackets we have true:h . Second, the upper h annotation is the type of the bracketed expression as seen from the outside, thus reduction is type preserving. (One would often have a more complex type in the upper annotation, not just a hash, e.g. [(true,3)] h *int h .) The reduction semantics of our formal system moves brackets around as required to ensure that abstraction is preserved throughout reduction, and so our type preservation result (Theorem 4.1) covers abstraction. If we did not use brackets but INRIA Global abstractionsafe marshalling with hash types 17 allowed hash type equalities to be used freely, abstraction would become invisible after reduction. The use of brackets also simplifies the statement of our result relating static and dynamic type equality (Theorem 4.7). Moreover, when compiling a module that refers to a term field of a previous one, the presence of brackets ensures that the hash of the later module does indeed depend on the hash of the earlier module. 3.4 Modest implementation demands Few changes are required in an MLlike language to support the strategy outlined above. Theorem 4.5 shows that type checking is decidable and that hashes play no role in compiletime typechecking of source code. In particular, we can use traditional type checking and inference algorithms essentially unchanged. Compile time reduction only builds hashes, without ever looking inside one. Runtime reduction only ever compares hashes by string equality. Theorem 4.6 shows that almost all coloured brackets and type information can be erased before runtime, with the exception of course of marshal and unmarshal type annotations, and brackets within hashes. MLlike languages usually support separate compilation of modules. Typically, a compilation phase takes a mod ule and the signatures of the modules it imports and generates code parameterised by these dependencies. For # hash , the compilation phase would also generate a hash parameterised by the hashes of the imported modules, in other words a hashtohash function. An appropriate compositional implementation of hashing must be used to make these effi ciently representable. Typically, linking instantiates the parameterised code with jumps to the code of previous modules. For # hash , the linking phase would do two further things. First, it would patch the type annotations for marshal and unmarshal in the code by replacing references to module types by their hashes. Second, it would calculate the hash of the module by applying the hashtohash function (generated by compilation) to the hashes of previous modules. 3.5 Lowlevel details of hashes In our semantics, we work with ideal hashing, taking a free constructor hash(...) which can be applied to elements of the abstract syntax. We can think of hash as a function whose injectivity guarantees abstractionsafety. To avoid communicating large quantities of source code, an implementation would reify hash with a fixedlength hash function, giving a safety guarantee that is only as strong as the probability of the absense of collisions. This must be chosen so that (1) collisions are rare, and (2) hashes are not too costly to compute. Both MD5 (RFC1321, 128bit) and SHA1 (RFC3174, 160bit) are sufficiently cheap, and may be considered random functions for this application [Rob96]. Let us consider the likelihood of collisions. For n abstract types and N possible hash values, the probability of a collision is approximately n 2 /2N . Pessimistically assuming 10 10 programmers in the world, writing 300 lines of code per day with one abstract type per 100 loc, the probability of a collision in a century of abstract types (using MD5) would then be (10 15 ) 2 /2 129 # 10 -9 . This is much less than the probability of a cosmicray induced processor error in this period. It may be desirable to have an absolute guarantee of typesafety, while accepting probabilistic abstractionsafety. To achieve this, one could pair hashes with the corresponding underlying representation types. At the other extreme, one could accept a probabilistic guarantee even at simple types, by sending only a hash of the marshalled type. These choices must depend on a risk assessment. Note that our proposal is aiming to protect only against accidental errors during programming and software deploy ment, not against malicious attack, and so we are not concerned with deliberate searches for collisions. Protecting against spoofed messages requires largely orthogonal techniques, e.g. message signatures and/or encryption, that are not in the scope of this report. Moreover, we do not address the problem of communication between untrusting peers, where one must check not just that the type advertised by the peer is compatible with the local type, but also the validity of the byte string's claim to represent a value of the advertised type (see, e.g., [PS00]). We hash elements of the abstract syntax, not concrete syntax, for two reasons. Firstly, it ensures hashes are not dependent on, e.g., the choice of newline or newline/CR, or on comments. Secondly, it fits well with the rest of the semantics --- recall we must calculate hashes of modules that are the results of module substitutions. In practice optimised calculations would be possible, without requiring the explicit construction of canonical representatives of abstract syntax elements. Hashing abstract syntax, which we take up to alphaequivalence, has the (benign) consequence that abstract type equality is not dependent on the names of function parameters. We have both internal (alphaconvertible) and external module names in the semantics; external names must be meaningful between programs. RR n 4851 18 Leifer, Peskine, Sewell, Wansbrough 4 Formal system Our calculus describes networks of machines. Each machine executes a program; a program consists of a sequence of module declarations followed by an expression. The expression language consists of a simplytyped callbyvalue #calculus with module field references, marshalling, and communication of strings. Consider a program containing a module declaring an abstract type. There is an abstraction boundary between the module's body and the rest of the program. Inside the boundary, the type's representation is visible; thus the type is said to be transparent. Outside, the type's representation is not visible, thus the type is opaque. Our calculus tracks this abstraction boundary as reduction proceeds. Compilation replaces the abstract type by a hash h and wraps the code e that comes from inside the module definition with coloured brackets decorated by h , as in [e] T h . The distinction between opaque and transparent views is therefore witnessed by the brackets: inside the brackets, we view h as transparent; when outside h is opaque. In order to express this distinction in our inference rules, we decorate each judgement with a colour hm , as in E #hm e:T . The colour has one of two forms: it can be a hash h , in which case h is transparent and all other hashes are opaque; or it can be the empty colour ., in which case all hashes are opaque. The appendices present a systematic development of the formal definitions and theorems complete with proofs. 4.1 Relation to the informal discussion For brevity, we take a module language in which structures are type/term pairs, rather than general dependent records from the earlier informal development. The following table summarises the correspondence between the informal and formal module syntax. struct type t = T0 let y = v . end # [T0 , v . ] sig type t val y : T end # [X :Type, T ] sig type t = T1 val y : T end # [X :Eq(T1 ), T ] We split module names into two parts, an external name N and an alphaconvertible name U . We write module declara tions as moduleNU = M :S in m , where U binds in m and N neither binds nor is subject to binding. The user would write only one identifier, which would be used for both. External names play no role in the static type system; they are used in hash construction and hence in dynamic type checks. The formal system omits =! coercions and runtime generativity, which should be straightforward extensions. Func tors are also omitted, though we include most of the technical machinery they require, expressing abstract and manifest types in signatures using singleton kinds. In Section 6.2 we propose extensions for treating these omissions. 4.2 Syntax We let x , X and U range over expression, type and module variables. Networks: n ::= 0 | m | n|n Machines (whole programs): m ::= e | moduleNU = M :S in m (U binds in m) Modules: M ::= [T , v . ] structure (v . is a value) S ::= [X :K , T ] signature (X binds in T ) Types: T ::= UNIT | INT | STRING base types | X | T#T | T # ... # T variable, function, product | U .TYPE type part of a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | h hash Hashes: h ::= hash(N , M :[X :Type, T ]) hash hm ::= h | . colour (``hash maybe'') INRIA Global abstractionsafe marshalling with hash types 19 Kinds: K ::= Type kind of all types | Eq(T ) kind of types statically equal to T Expressions: e ::= () | n unit, integers | (e, ..., e) | proj i e tuple, projection | x | #x :T .e | e e lambda calculus (x binds in e) | U .term value part of a module | mar (e:T ) marshalling primitive | unmar e:T unmarshalling primitive | ! e | ? send and receive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | marshalled (e:T ) result of marshalling | UnmarFailure exception caused by unmar | [e] T hm coloured bracket User source programs are closed terms of the m grammar which do not contain any of the constructs below the dotted lines. Values v hm are indexed by a colour. They are defined formally below; they include usual #calculus values, marshalled (v . :T ) and ``necessary'' brackets around values. For closed v . , the value marshalled (v . :T ) is a string, the sequence of bits that represents the value v . and the type T . We work up to alphaconversion. We write substitutions as follows: {x#e}A replaces x by e in A; we also define substitutions on module components, as in {U .TYPE T , U .term e}A. 4.3 Static and dynamic semantics The static type system for programs has judgements for subkinding, type equality, and subsignaturing relations. Module structures M and names U have signatures S , expressions and machines have types T , and types have kinds K . The system also defines correctness of colours hm , environments E , kinds K , and signatures S . E #hm K <: K # E #hm T == T # E #hm S <: S # E #hm M :S E #hm U :S E #hm e:T E # . m:T E #hm T :K # hm ok E #hm ok E #hm K ok E #hm S ok The typing rules are largely standard; the novel rules will be explained below. Recall that judgements are annotated by a colour hm , i.e. an optional hash --- the idea being that derivations of judgements annotated by a hash h can make use of the equality between the abstract type h and its implementation. Type environments may contain bindings for module, type and expression variables. Earlier variables bind in later types, kinds and signatures. E ::= nil | E , x :T | E , X :K | E , U :S Static typing of networks, # n ok, simply means that all machines are wellformed. We define compiletime reductions m -# c m # of machines (performed after type checking), and runtime reductions e -#hm e # and n -# n # for expressions and networks. 4.3.1 Singleton kinds Following [Ler94, HL94, SH00], we use singleton kinds to handle abstract and concrete signatures in a uniform way. We have two families of kinds: Type is the kind of all types; and, for any type T , Eq(T ) is the singleton kind of all types that are provably equal to T . A module consists of a structure [T 0 , v . ] and a signature [X :K , T ]. The structure has a representation type T 0 and a value v . --- think of a tuple of operations. This v . must have the type {X T 0 }T , and the implementation type T 0 must have the kind K . This is made precise by the following rule: E #hm T 0 :K E , X :K #hm T :Type E #hm v . :T # E , X :Eq(T 0 ) #hm T # == T E #hm [T 0 , v . ]:[X :K , T ] (MS.struct) RR n 4851 20 Leifer, Peskine, Sewell, Wansbrough For an abstract module we have K = Type, revealing no information about the representation type, whereas for a concrete module, commonly K = Eq(T 0 ), revealing it. This is captured with the type equality relation: in the context of a module declaration moduleNU = M :[X :K , T ] in , one can use the path U .TYPE to refer to the type part of the module. If it is concrete, with K = Eq(T 0 ), one can further use the type equality U .TYPE == T 0 , whereas if it is abstract U .TYPE is typically not equal to any other type. The subkinding relation K <: K # places Type above all singleton kinds. This is used to define subsignaturing and hence, using subsumption, allows a concrete module can be used as if it had an abstract signature. 4.3.2 Hash formation, type equality of hashes At runtime, we need globally meaningful type names for abstract types, corresponding to the U .TYPE paths used in compiletime type checking. We construct these global names by hashing (welltyped) closed abstract modules, together with the associated external name. # . [T 0 , v . ]:[X :Type, T ] # hash(N , [T 0 , v . ]:[X :Type, T ]) ok (hmok.hash) As explained informally earlier, judgements annotated by a hash permit an additional type equality: under the colour h = hash(N , [T 0 , v . ]:[X :Type, T ]), h is equal to its implementation T 0 : E # h ok E # h h == T 0 (Teq.hash) These two rules examine the internal structure of hashes, which might be thought to be computationally problematic. However, while they are semantically necessary, they play no role in user program typechecking (Theorem 4.5) or in execution. 4.3.3 Compiletime reduction and coloured brackets Module reduction constructs the type representations that will be used at runtime in marshalling and unmarshalling. Reducing a concrete module is simple: we replace references to its type component by its manifest type, and references to its term component by the value inside the module. moduleNU = [T 0 , v . ]:[X :Eq(T 1 ), T ] in m -# c {U .TYPE T 1 , U .term v . }m (mred.Eq) When it comes to abstract types, things are more interesting. Given an abstract module declaration moduleNU = [T 0 , v . ]:[X :Type, T ], we normally have no way of referring to its type other than by name, i.e. U .TYPE. How ever U is not meaningful on other machines, which motivates the introduction of the hash of the module, i.e. h = hash(N , [T 0 , v . ]:[X :Type, T ]). Then module reduction replaces references to the type component by h . References to the term component are replaced by the value suitably protected by hcoloured brackets, which embody the abstraction boundary around the module's body as discussed above. moduleNU = [T 0 , v . ]:[X :Type, T ] in m -# c {U .TYPE h, U .term [v . ] {X h}T h }m (mred.Type) In general, in a bracket expression [e] T hm , the lower annotation hm is a colour that indicates what type equalities may be used to type e. If hm = hash(N , [T 0 , v . ]:[X :Type, T ]), then the equality hm == T 0 is available when typing e, through (Teq.hash) (Section 4.3.2). If hm = ., e is typable without any extra equalities. The upper annotation T is the externally visible type of e . The following rule (the only typing rule that mentions brackets) shows this colour change formally. E # hm # T :Type E #hm e:T E # hm # [e] T hm :T (eT.col) 4.3.4 Expression reduction Expression reduction is based on a standard callbyvalue #calculus semantics. In this subsection, we give the function application rule and bracketpushing rules. In later subsections, we show the rules for marshalling and communication. INRIA Global abstractionsafe marshalling with hash types 21 As we show in Theorem 4.6, brackets can be erased before runtime reduction. However, the brackets' presence is necessary for type preservation (Theorem 4.1). Given their presence, we need reduction rules to ``push'' them inwards so that the brackets do not interfere with computationally significant reductions (Theorem 4.3). To describe the bracket pushing rules, and to achieve type preservation, it is necessary to index the reduction relation, class of values, and reduction contexts by colours. We write v hm for a value of colour hm . Brackets may appear in a value when used to build a value of an abstract type out of a value of the corresponding implementation type, for example [3] h h , where the implementation type of h is INT. v hm ::= n | () | (v hm , ..., v hm ) | #x :T .e | marshalled (v . :T ) | [v h1 ] h1 h1 where h 1 #= hm The following bookkeeping rules push brackets with manifestly decomposable types inside expressions, and remove them where not necessary. [n] INT hm # -#hm n (ered.col.int) [()] UNIT hm # -#hm () (ered.col.unit) [(v hm 1 , ..., v hm j )] T1#...#T j hm # -#hm ([v hm 1 ] T1 hm # , ..., [v hm j ] T j hm # ) (ered.col.tuple) [#x :T .e] T # #T ## hm # -#hm #x :T # .[{x #[x ] T # hm }e] T ## hm # (ered.col.fun) [marshalled (v . :T )] STRING hm # -#hm marshalled (v . :T ) (ered.col.marshalled) [[v h1 ] h1 h1 ] h1 h2 -#hm [v h1 ] h1 h1 if h 1 #= h 2 # h 2 #= hm (ered.col.col) [v hm 1 ] h2 hm 1 -#hm v hm 1 if hm 1 = hm # hm 1 = . (ered.col.le) Function application introduces brackets to protect the argument, since the formal parameter may itself be used under a bracket in the body of the function. This is a variant of [GMZ00], where the formal parameter has to be used at the colour of the function itself. (#x :T .e) v hm -#hm {x#[v hm ] T hm }e (ered.ap) 4.3.5 Marshalling As in [ACPP91], mar (e:T ) ``tags'' the value of e with a type annotation T , producing a result of type STRING. The dual construct unmar e:T produces a value of type T , which the type tag in e must (dynamically) match. E #hm e:T E #hm mar (e:T ):STRING (eT.mar) E #hm T :Type E #hm e:STRING E #hm (unmar e:T ):T (eT.unmar) There is a subtlety here: in the conclusion of (eT.mar), the fact that e has the type T may require the extra type equality provided by hm . Hence we introduce marshalled (e # :T ), which requires the argument to be not only closed but typable in ., i.e. everywhere. Reduction transforms mar (v hm :T ) into marshalled ([v hm ] T hm :T ), where the brackets serve to ensure that any type equality provided by hm is always available to type v hm (even after sending the marshalled value to another machine). Note that before reducing mar (v hm :T ), both v hm and T will have been closed by substitution. mar (v hm :T ) -#hm marshalled ([v hm ] T hm :T ) (ered.mar) E #hm ok # . e:T E #hm marshalled (e:T ):STRING (eT.marshalled) The unmarshalling of a string first extracts the type tag T from the string and compares it with the tag for the expected type T # . Since T is a valid type for v . in ., it is also one in hm . The type tags T and T # are compared by syntactic equality: if the types match, the original value is extracted from the string; otherwise an exception is raised. This dynamic type equivalence is closely related to static equivalence (Theorem 4.7). unmar (marshalled (v . :T ):T # ) -#hm v . if T = T # -#hm UnmarFailure otherwise RR n 4851 22 Leifer, Peskine, Sewell, Wansbrough 4.3.6 Programs and networks A machine consists of a series of module declarations followed by an expression. Each module declaration may refer to the previous ones. E # . T :Type E # . M :S E , U :S # . m:T E # . (moduleNU = M :S in m):T (mT.let) A network is a parallel juxtaposition of machines. Note that each machine has its own environment: there is no explicit scope that encompasses more than one machine. # n 1 ok # n 2 ok # n 1 | n 2 ok (nok.par) # . m:UNIT # m ok (nok.mach) We assume that there is a single channel, which carries values of type STRING. The expression ! e sends the value of e over that channel, and ? reads a value from that channel. Communication is straightforward as all the work required to make values and types intercomprehensible is done by the marshalling apparatus; for suitable evaluation contexts CC . hm 1 and CC . hm 2 we have just the rule below, writing context application with a dot. CC . hm 1 .! v hm 1 | CC . hm 2 .? -# CC . hm 1 .() | CC . hm 2 .v hm 1 (nred.comm) 4.4 Results First, our calculus enjoys type preservation and progress properties. Theorem 4.1 (type preservation for compiletime, expression, and network reduction) (See Theorem G.15 (type preservation for expression reduction), Theorem G.18 (type preservation for machine reduction), Corollary G.17 (type preservation for network reduction).) . if m -# c m # and # . m:T then # . m # :T ; . if e -#hm e # and #hm e:T then #hm e # :T ; and . if n -# n # and # n ok then # n # ok. Theorem 4.2 (progress for compiletime reduction) (See Theorem H.8 (progress of machines).) If # . m:UNIT then either . m is an expression; or . m reduces, i.e. there exists m # such that m -# c m # . Moreover, compiletime reduction is terminating. Theorem 4.3 (progress for expressions) (See Theorem H.6 (progress of expressions).) If #hm e:T then one of the following holds: . e is a value, i.e. there exists v hm such that e = v hm ; . e reduces, i.e. there exists e # such that e -#hm e # ; . e is blocked waiting for I/O, i.e. there exists CC hm hm 2 and e # such that e = CC hm hm 2 .! e # or e = CC hm hm 2 .?; or . e has thrown an exception, i.e. there exists CC hm hm 2 such that e = CC hm hm 2 .UnmarFailure. In addition, we have proved a normalisation result for expressions, showing that the rules for coloured brackets do not introduce any divergencies. Both compiletime machine reduction and runtime expression reduction are deterministic (network reduction is not, of course): Theorem 4.4 (determinacy for compiletime and expression reduction) (See Theorem H.11 (determinism of expression reduction), Discussion H.12 (strength of determinism), Theorem H.9 (determinism of machine reduction).) If m -# c m # and m -# c m ## then m # = m ## and both reductions apply the same rule to the same redex. if e -#hm e # and e -#hm e ## then e # = e ## and both reductions apply the same rule to the same redex. For static type checking: INRIA Global abstractionsafe marshalling with hash types 23 Theorem 4.5 (decidability of type checking) (See Theorem I.14 (decidability of type checking), Discussion I.16 (decidability of type checking for user programs).) Type checking is decidable. Furthermore, user source programs can be typed by derivations involving no hashes or coloured brackets. At runtime, all type annotations except those on mar , marshalled , and unmar can be erased. Moreover, all coloured brackets can be erased except for those that occur within a hash within one of those remaining annotations. More precisely, we define erase(e) to be e with all type annotations and brackets erased except that the type annotations on mar , marshalled , and unmar are left unchanged. We define ---# erase to be like -#hm by taking the eraseimage of the left and righthand sides of each rule (and removing rules that would become e ---# erase e). Theorem 4.6 (erasure preserves reduction outcomes) (See Theorem I.30 (type erasure).) Assume # . e:T . We have that e -# . e # implies erase(e) ---# erase 61 erase(e # ). Conversely, erase(e) ---# erase e 0 implies that there exists e # such that erase(e # ) = e 0 and e -# >1 . e # . Note that brackets are needed in module reduction, to keep track of a module's ancestors as we build its hash. Finally we show that, under reasonable conditions, static and dynamic type equality coincide. Let D be a module declaration context: moduleN 0 U0 = M 0 :S 0 in ...module N j U j = M j :S j in in the user source language (with no brackets or hashes). Consider a machine D .C .e for some expression context C and an expression e = (unmar (mar (e 0 :T 0 ):T 1 )). One would like this dynamic type check to succeed if and only if T 0 and T 1 are statically provably equal, i.e. iff U 0 :S 0 , ..., U j :S j # . T 0 == T 1 . Write #D for the accumulated substitution defined by the module reduction rules for D (we omit an explicit definition for lack of space). The dynamic check is then #D T 0 = #D T 1 . We have: Theorem 4.7 (coincidence between dynamic and static type checking) (See Corollary J.21 (coincidence between undyntime and static type checking with nonrepeated external names).) Suppose that D .C .e is well formed (i.e. # . D .C .e:UNIT), that it contains no hashes, and that its external names N 0 , ..., N j are distinct. Let E = U 0 :S 0 , ..., U j :S j be the associated environment. Assume that T 0 and T 1 contain no hashes and E # . T i :Type for i = 0, 1. Then E # . T 0 == T 1 iff #D T 0 = #D T 1 . The requirement that the external names N 0 , ..., N j be distinct rules out the rather pathological programs in which there are two module definitions with the same name, one shadowing the other, which have identical structures, signatures, and dependencies. The exclusion of hashes is automatic for user source programs. One can imagine stronger theorems, relating type equality between two programs that share a common (DAG)prefix of module definitions, but their statements become rather elaborate. 5 Related work Modules and generativity There is an extensive literature on MLstyle modules, including [Mac84, MTH90, HL94, Ler94, SH00, DCH03], much of it discussing subtle questions of generativity versus applicativity. To our knowledge, however, none deals with the interprogram case. In [Sew01], fresh type names are generated during callbyvalue module reduction, with #binders that can extrude across distributed scope. This allows interprogram sharing, and also a with! coercion, but at the pragmaticallyawkward cost of requiring particular object files to be shared. Type dynamic Our marshal and unmarshal operations are essentially constructors and destructors for values of dynamic type; mar is just dynamic, and unmar is a restricted form of typecase. Our dynamic values have type STRING, emphasising that they may be communicated readily. Type Dynamic was first formalised by Abadi et al. [ACPP91, ACPR95], who also gives a historical survey. Intensional polymorphism [HM95, Wei00] permits runtime type analysis of all values. Marshalling abstract types The problem of marshalling values of abstract (existential [MP88]) type has not been satisfactorily addressed theoretically before. In several systems, abstract types are run or buildtime generative, so that two executions or builds of the same source will yield distinct types. While communication within such a program can be RR n 4851 24 Leifer, Peskine, Sewell, Wansbrough abstractionsafe, successful communication between builds can only be at the representation type, and hence abstraction unsafe. This is true, for instance, of [ACPR95], TMAL [Dug02], Modula3 [B + 94, BNOW95], Alice [Ali03], and the typedchannel languages listed below. Weirich [Wei02] exposes an existential's representation type to type analysis, permitting a typesafe polytypic mar shalling function to be written. As future work we hope to expose our global type names at term level (cf. [HWC00]), permitting an abstractionsafe polytypic marshalling function to be written. Furuse and Weis [FW00] argue for ignoring abstraction altogether, checking representation types only. A number of programming languages feature some form of builtin marshalling (pickling, serialisation, etc.): for example Modula3, Alice, Java, .NET, and OCaml. Most of these languages serialise the type along with the value in order to permit a check at unmarshal time, and represent the type by a hash. Languages differ, however, in exactly what is hashed --- i.e., in what is considered when deciding type equality. In Modula3, abstract types are made opaque by branding, which may be either by a literal string (analogous to an external name) or a compilergenerated unique identifier. The latter are unique within a program but not necessarily related between programs, so explicit brands must be used for interprogram communication; however, they do not guarantee abstractionsafety for that case. Revelation can be used to make an abstraction transparent. In Alice, abstract type creation is runtime generative, meaning that abstract types from different executions are always distinct. This vacuous abstractionsafety forces the use of representation types for pickling between different programs. In Java serialisation [Sun02], class equivalence is on fullyqualified class name, the representation type of all fields, and the types of all nonprivate methods; the implementation is not considered in type equality. A strong coercion (Section 2.10) is provided (although compatibility of representation types is not checked until unmarshal time). In .NET serialisation [Mic01], class equivalence is on the textual name along with the implementation of the entire assembly in which it is defined (a single DLL or EXE, which may comprise many source files). This guarantees data structure invariants are maintained, as in our approach; however, we work on the much finer scale of individual modules, and furthermore we require only source code to be shared, not object files. Java and .NET both support controlled relaxation of type equivalence checking, the use of which clearly voids any abstractionsafety guarantees made by the type system. OCaml [OCa] does no typechecking for marshalling at all, and hence is not even typesafe. When unmarshalling a function, it verifies (by a hash) that the communicating builds are identical, thus allowing the code pointers of all closures to be communicated literally. Coloured brackets Coloured brackets were introduced in [ZGM99, GMZ00]; we differ in that we permit a variable to occur in a colour other than the one where it is defined. Our proofs are harder, our #rule has to introduce extra brackets, but our brackets carry only a single optional hash, rather than a list of hashes. Rossberg [Ros02], like us, is concerned to preserve the opacity of abstract types under reduction due to the presence of typecase. His coercions serve the same purpose as our brackets, but his use of the closedscope open construct instead of dot notation [CL90] prevents any possibility of sharing values of abstract type between instances. Typed channels Several languages, e.g. JoCaml [JoC], Nomadic Pict [SWP99], Facile [TLK96, Kna95], implement typed channels. These permit type and abstractionsafe communication once the channel is established. Establishing a channel at an abstract type, however, requires the endpoints somehow to share the type already; in the case that the endpoints reside in different programs or instances, this requires an unsafe cast, usually performed (outside the language) by a name server. 6 Conclusions and future work 6.1 Summary We have proposed a novel and expressive design for guaranteeing type and abstractionsafe marshalling of data sent between distributed ML programs, that can uniformly treat manifest, abstract, and generative types. The key technical idea is to use hashes of module declarations as globallymeaningful type names, which are inserted at compiletime and then compared dynamically when unmarshalling. We add coloured brackets to delimit the ``abstraction boundary'' within which hashes are transparent, tracking these brackets through the reductions so as to achieve type and abstraction preservation. Our proposal is a smooth extension of existing MLlike languages: type checking is unchanged, most type information can be erased before runtime, and the dynamic type check closely mirrors static ML type equivalence. INRIA Global abstractionsafe marshalling with hash types 25 6.2 Future work In the future, we aim to broaden our solution to be applicable to fullscale languages. The following extensions will be required to cope with the examples in Section 2.9--2.13. The strong coercion (Sec tion 2.10 and 2.11) used for forcing an abstract type to have the same hash as an earlier module, has a simple compiletime implementation: check the representation types of the two are provably equal, then simply reuse the hash of the earlier module as the type name for the new. This requires the compiler to keep a mapping from hashes to representation types, which is straightforward. Programmerrequested generativity (Section 2.12) can be dealt with in an implementation by generating a fresh global name (say a random bit string of the same length as hashes) at compile time; its semantics can be modelled by #binding. Both this and the strong coercion are very similar to the constructs in our earlier work [Sew01]. Sideeffectinduced generativity (Section 2.13) requires a way to identify simple pure computations in structure bodies that the programmer can easily understand; abstract types of structures with pure computations should be hashes, whereas those of structures with effectful computations should have freshlygenerated names. Functors (Section 2.9) are a more substantial extension, but, at least for a restricted but useful class, should be straightforward. Consider firstorder applicative functors [Ler95] and module expressions that are either (i) an explicit structure, possibly multiplyabstracted, or (ii) pure, i.e. constructed from module identifiers, abstraction and application. These give rise to functions from hashes to hashes; applying these functions gives runtime representations of the compiletime pathbased type names. Other substantial extensions also need to be considered. Dependent record structures, i.e. module structures with multiple fields also appear in this report's informal examples; they should be conceptually straightforward. Parametric and substructuring polymorphism within the dynamic check would allow receivers to accept a more general type than that offered by the sender. This is a more substantial extension; it will be a challenge to minimise the transmitted type information required for these dynamic ``subtype'' checks. One may want to rebind (Section 2.14) identifiers within a transmitted value to avoid the overhead of sending code already available at the other end, or to obtain locationspecific behaviour; here we aim to integrate hash types with [BHS + 03]. Marshalling reference cells exhibits related problems: should the reference be rebound, made remote, or duplicated? More generally, one must consider values mentioning other machine resources: screens, files. . . We wish to integrate our work with existing systems for distributed programming which have statically typed channels for normal operation but no safe way of initiating communication, such as JoCaml [JoC] and Nomadic Pict [SWP99]. We also wish to test the expressiveness of our marshalling primitives by using them to write libraries for safe distributed communication and persistence. Acknowledgments We acknowledge support from a Royal Society University Research Fellowship (Sewell), EPSRC grant GRN24872 (Wansbrough), EC FETGC project IST200133234 PEPITO, and APPSEM 2. The authors thank Georges Gonthier, JeanJacques L evy, Luc Maranget, and the anonymous referees for their suggestions. RR n 4851 26 Leifer, Peskine, Sewell, Wansbrough A Introduction to the complete definitions and proofs A.1 Differences between the main body and the appendices The following appendices present the full formal system with all proofs. There are slight differences between these and the main body of the report: Type system Some admissible rules were omitted from the main body. They are (Ssub.refl) and (Ssub.tran) (section C.11). We now also formalise the / #dom relation (section C.1), and define a kind equality (section C.5) which is used in the rules for subkinding. For the sake of completeness, we define a signature equality relation (section C.10). Integer constants We now omit the rules for the INT type and integer constants. They (and other base types) can be trivially added to the system on the model given by UNIT and (). A.2 Correspondence between the theorems in the main body and in the appendices Main body Appendices Theorem 4.1 Theorem G.15 (type preservation for expression reduction), Theorem G.18 (type preservation for machine reduction), Corollary G.17 (type preservation for network reduction) Theorem 4.2 Theorem H.8 (progress of machines) Theorem 4.3 Theorem H.6 (progress of expressions) Theorem 4.4 Theorem H.11 (determinism of expression reduction), Discussion H.12 (strength of determinism), Theorem H.9 (determinism of machine reduction) Theorem 4.5 Theorem I.14 (decidability of type checking), Discussion I.16 (decidability of type checking for user programs) Theorem 4.6 Theorem I.30 (type erasure) Theorem 4.7 Corollary J.21 (coincidence between undyntime and static type checking with non repeated external names) B Syntax e ::= expression () unit (e 1 , ..., e j ) tuple (2 6 j ) proj i e projection x variable #x :T .e abstraction (x binds in e) e e application mar (e:T ) dynamic marshalled (e:T ) closed, colourindependent dynamic unmar e:T undynamic ! e send ? receive U .term termpart of a module [e] T hm type colouring UnmarFailure T undyn failure INRIA Global abstractionsafe marshalling with hash types 27 v hm 0 ::= hm 0 value, i.e. value in the colour hm 0 () unit (v hm 0 1 , ..., v hm 0 j ) tuple (2 6 j ) #x :T .e abstraction (x binds in e) marshalled (v . :T ) closed dynamic value [v h1 ] h1 h1 type colouring, where h 1 #= hm 0 We sometimes omit the colour annotation on values, when it is irrelevant. C hm 1 hm 2 ::= singlelevel evaluation context (v hm 1 1 , .., v hm 1 i-1 , , e i+1 , .., e j ) tuple (2 6 j and 1 6 i 6 j ), if hm 1 = hm 2 proj i projection, if hm 1 = hm 2 e application left, if hm 1 = hm 2 v hm 1 application right, if hm 1 = hm 2 mar ( :T ) dynamic, if hm 1 = hm 2 marshalled ( :T ) colourindependent dynamic, if hm 2 = 0 unmar :T undynamic, if hm 1 = hm 2 ! send, if hm 1 = hm 2 [ ] T hm 2 coloured bracket CC hm 1 hm 2 ::= coloured evaluation context CC hm 1 hm # .C hm # hm 2 extra level identity, if hm 1 = hm 2 T ::= type UNIT unit T 1 # ... # T j tuple (2 6 j ) X variable T#T function STRING dynamic U .TYPE typepart of a module h hash TC ::= firstlevel constructed type context UNIT unit STRING dynamic 1 # 2 function 1 # ... # j tuple (2 6 j ) hm ::= hash option . none h some hash h ::=hash(N , M :[X :Type, T ]) hash N external name RR n 4851 28 Leifer, Peskine, Sewell, Wansbrough K ::= kind Type opaque Eq(T ) singleton M ::=[T , v ] module structure (typepart, termpart) S ::=[X :K , T ] module signature (X binds in T ) m ::= machine e expression moduleNU = M :S in m module declaration (U binds in m) n ::= network 0 null n | n parallel composition e expression (on one machine) # ::= variable x expression variable X type variable U module variable # ::= substitutable entity X type variable U .TYPE typepart of a module x expression variable U .term termpart of a module U module variable ... X ::= type substitutable entity X type variable U .TYPE typepart of a module ... x ::= expression substitutable entity x expression variable U .term termpart of a module E ::= environment E , x :T expression variable binding E , X :K type variable binding E , U :S module variable binding nil empty INRIA Global abstractionsafe marshalling with hash types 29 J ::= colourable statement ok environment correctness K ok kind correctness K == K # kind equivalence K <: K # subkinding T :K kind of a type T == T # type equivalence S ok signature correctness S == S # signature equivalence S <: S # subsignaturing e:T type of an expression M :S signature of a module expression U :S signature of a module variable m:T type of a machine CJ ::=E #hm J couloured judgement MJ ::= monochrome judgement # hm ok hash correctness # n ok network correctness AJ ::= judgement # / # domE nonclash judgement E #hm J coloured judgement MJ monochrome judgement Additionally, we use the following notations: #:# ::= x :T | X :K | U :S variable has sort #:# ::= e:T | T :K | M :S term has sort # ok ::= T :Type | K ok | S ok sort is correct = syntactic equality #, substitutions # derivation (i.e. proof tree) {# #}# substitution: replace # by # in # fv free variables (U , X , x ) fse free substitutable entities (U , U .TYPE, U .term, X , x ) (if U .TYPE # fse # or U .term # fse # then U # fse #) (and more, introduced as they are defined...) Note that in particular external names (N ) are not variables or substitutable entities, so they are never ``free'' nor can substitution touch them: fv N = fse N = ? and #N = N . C Static judgements C.1 # / # domE nonclash # / # dom nil (clash.nil) # / # domE # #= # # # / # dom (E , # # :# ) (clash.cons) For any two distinct variables # and # # , # #= # # is an axiom. RR n 4851 30 Leifer, Peskine, Sewell, Wansbrough C.2 # hm ok hash correctness nil # . M :[X :Type, T ] # hash(N , M :[X :Type, T ]) ok (hmok.hash) # . ok (hmok.zero) Note that N may be any external name. Note that we demand that a module have an opaque type in order to take its hash. C.3 E # hm ok environment correctness # hm ok nil #hm ok (envok.nil) E #hm T :Type x / # domE E , x :T #hm ok (envok.x) E #hm K ok X / # domE E , X :K #hm ok (envok.X) E #hm S ok U / # domE E , U :S #hm ok (envok.U) An alternate way of stating (envok.?) could be: E #hm # ok # / # domE E , #:# #hm ok C.4 E # hm K ok kind correctness E #hm ok E #hm Type ok (Kok.Type) E #hm T :Type E #hm Eq(T ) ok (Kok.Eq) C.5 E # hm K == K # kind equality E #hm ok E #hm Type == Type (Keq.Type) E #hm T == T # E #hm Eq(T ) == Eq(T # ) (Keq.Eq) C.6 E # hm K <: K # subkinding E #hm T :Type E #hm Eq(T ) <: Type (Ksub.Eq) E #hm K == K # E #hm K <: K # (Ksub.refl) E #hm K <: K # E #hm K # <: K ## E #hm K <: K ## (Ksub.tran) Note that (Ksub.tran) is currently derivable. C.7 E # hm T :K kind of a type E #hm T :K E #hm K <: K # E #hm T :K # (TK.sub) E #hm T == T # E #hm T :Eq(T # ) (TK.Eq) # h ok E #hm ok E #hm h:Type (TK.hash) E #hm ok E #hm UNIT:Type (TK.unit) E #hm ok E #hm STRING:Type (TK.mar) E #hm T :Type E #hm T # :Type E #hm T#T # :Type (TK.fun) E #hm T i :Type #i .1 6 i 6 j E #hm T 1 # ... # T j :Type (TK.tuple) E , X :K , E # #hm ok E , X :K , E # #hm X :K (TK.var) E #hm U :[X :K , T ] E #hm U .TYPE:K (TK.mod) INRIA Global abstractionsafe marshalling with hash types 31 C.8 E # hm T == T # type equivalence E #hm T :Eq(T # ) E #hm T == T # (Teq.Eq) E # h ok E # h h == T where h = hash(N , [T , v hm ]:[X :Type, T # ]) (Teq.hash) The rule (Teq.hash) is the rule that introduces type equivalences (see Lemma G.11 (type decomposition)). Other rules serve to propagate equivalences. Of course another way to obtain a type equivalence is to have an explicit Eq(T ) in the judgement and use (Teq.Eq). E #hm T :Type E #hm T == T (Teq.refl) E #hm T == T # E #hm T # == T (Teq.sym) E #hm T == T # E #hm T # == T ## E #hm T == T ## (Teq.tran) E #hm T 0 == T # 0 E #hm T 1 == T # 1 E #hm T 0 #T 1 == T # 0 #T # 1 (Teq.cong.fun) E #hm T i == T # i 1 6 i 6 j E #hm T 1 # ... # T j == T # 1 # ... # T # j (Teq.cong.tuple) C.9 E # hm S ok signature correctness E , X :K #hm T :Type E #hm [X :K , T ] ok (Sok) C.10 E # hm S == S # signature equivalence Note that we never use signature equivalence. E #hm K == K # E , X :K #hm T == T # E #hm [X :K , T ] == [X :K # , T # ] (Seq.struct) Signature equivalence is an equivalence relation since kind equivalence and type equivalence are. C.11 E # hm S <: S # subsignaturing E #hm K <: K # E , X :K #hm T == T # E #hm [X :K , T ] <: [X :K # , T # ] (Ssub.struct) E #hm S ok E #hm S <: S (Ssub.refl) E #hm S <: S # E #hm S # <: S ## E #hm S <: S ## (Ssub.tran) Note that (Ssub.refl) and (Ssub.tran) are derivable. C.12 E # hm e:T type of an expression E #hm e:T E #hm T == T # E #hm e:T # (eT.eq) E , x :T , E # #hm ok E , x :T , E # #hm x :T (eT.var) E #hm U :[X :K , T ] E #hm T :Type E #hm U .term:T (eT.mod) Note that in (eT.mod), the condition E #hm T :Type guarantees that X / # fv T . E #hm e # :T#T # E #hm e:T E #hm e # e:T # (eT.ap) E , x :T #hm e:T # E #hm #x :T .e:T#T # (eT.fun) RR n 4851 32 Leifer, Peskine, Sewell, Wansbrough E #hm e i :T i #i .1 6 i 6 j E #hm (e 1 , .., e j ):T 1 # ... # T j (eT.tuple) E #hm e:T 1 # ... # T j 1 6 i 6 j E #hm proj i e:T i (eT.proj) E #hm ok E #hm ():UNIT (eT.unit) E #hm e:STRING E #hm ! e:UNIT (eT.send) E #hm ok E #hm ?:STRING (eT.recv) E #hm e:T E #hm mar (e:T ):STRING (eT.mar) E #hm ok nil # . e:T E #hm marshalled (e:T ):STRING (eT.marred) E #hm T :Type E #hm e:STRING E #hm (unmar e:T ):T (eT.unmar) E #hm T :Type E #hm UnmarFailure T :T (eT.Undynfailure) For (eT.unmar), the condition E #hm T :Type ensures T is wellformed. Of course, nothing forces T to be closed; as usual reduction will transform it into something closed before (ered.unmar) happens. E #hm T :Type E # hm # e:T E #hm [e] T hm # :T (eT.col) Note that, when reading the rule backwards, hm # replaces hm: the brackets denote a colour change. C.13 E # hm M :S signature of a module expression E #hm T :K E , X :K #hm T # :Type E , X :Eq(T ) #hm T ## == T # E #hm v hm :T ## E #hm [T , v hm ]:[X :K , T # ] (MS.struct) Remark: in (MS.struct), the type T ## and the premise E #hm v hm :T ## are there to ensure that X is not free in v hm , even though its interesting type T # may contain X . The premise E , X :K #hm T # :Type is implied by the premise E , X :Eq(T ) #hm T ## == T # , since the correctness of T # only requires that the environment provide X , not that it be given any particular kind. (See Lemma F.9 (types are ok provided their hashes are).) We might be worried that there should be a (MS.sub), but this would be doing subsignaturing at compile time, and we have subsignaturing at run time via (US.sub) which seems to subsume it. We require the expression component of the module to be a value. This is to dispel any concern over the time at which a module is initialised. Note that if an arbitrary expression e is desired, it can be encoded as #x :UNIT.e, with the type of the module changed from T # to UNIT#T # , and its uses changed from U .term to U .term (). C.14 E # hm U :S signature of a module variable E , U :S , E # #hm ok E , U :S , E # #hm U :S (US.var) E #hm U :S E #hm S <: S # E #hm U :S # (US.sub) E #hm U :[X :K , T ] E #hm U :[X :Eq(U .TYPE), T ] (US.self) The way we prove things about the type of U .term is quite interesting. Here is an example: what are the possible types T such that E #. U .term:T with E = (U :[X :Type, X ])? Let us look at all the possible derivations (arguing slightly informally, i.e. without verifying everything, on the ``the proof has to be of this form'' bit). # E #. T :Type # E #. U :[X :K 4 , T 3 ] E #. U :[X :K 3 , T 3 ] (US.self) # E #. K 3 <: K 2 # E , X :K 3 #. T 3 == T E #. [X :K 3 , T 3 ] <: [X :K 2 , T ] (Seq.struct) E #. U :[X :K 2 , T ] (US.sub) E #. U .term:T (eT.mod) INRIA Global abstractionsafe marshalling with hash types 33 We can check that any proof of E #. U .term:T can be shortened to a proof of this form. The only alternative is not to use (US.self), but we can verify that this does not lead to any proof of E #. U :[X :K3 , T3 ]. (If the signature of U was [X :Eq(T1 ), X ], we could usefully use (US.var) there, and (US.sub) would rewrite the X into T1 , so we would get T = T1 ). At this point, we still get to choose T , K2 , K3 , K4 and T3 , but the derivations of (#), (#), (#) and (#) constrain these choices. In # we see that fv T # {U }. In (#), all we have left to do (assuming as stated a minimal proof) is apply (US.var), so K4 = Type and T3 = X . In (#) we have that K3 is Eq(U .TYPE). In (#) we have the only constraint on K2 , and we might as well take K2 = Type. In (#), we need to prove that X == T , and since X does not appear in T , the equivalence must come from the environment, hence K3 = Eq(T ). Unifying the constraints on K3 yields T = U .TYPE as the one possible type for U .term. C.15 E # hm m:T type of a machine E # . e:T E # . e:T (mT.expr) E # . T :Type E # . M :S E , U :S # . m:T E # . (moduleNU = M :S in m):T (mT.let) In (mT.expr), the premise is a ``type of an expression'' judgement, while the conclusion is a ``type of a machine'' judgement. Note: in (mT.let), the first premise is saying that U is not free in T . C.16 # n ok network correctness # n i ok i = 1, 2 # n 1 | n 2 ok (nok.par) # 0 ok (nok.zero) nil # . e:UNIT # e ok (nok.expr) D Reduction rules and structural congruence D.1 m -# c m # compiletime reduction moduleNU = [T , v . ]:[X :Type, T # ] in m -# c {U .TYPE h, U .term [v . ] {X h}T # h }m (mred.Type) where h = hash(N , [T , v . ]:[X :Type, T # ]) moduleNU = [T , v hm ]:[X :Eq(T ## ), T # ] in m -# c {U .TYPE T ## , U .term v hm }m (mred.Eq) D.2 e -# hm e # expression reduction Note that we don't rewrite types at all (unlike Grossman et al., p13 rule 7). proj i (v hm 1 , .., v hm j ) -#hm v hm i if 1 6 i 6 j (ered.proj) (otherwise stuck) (#x :T .e) v hm -#hm {x#[v hm ] T hm }e (ered.ap) (Note that (as Grossman et al.) we introduce a bracket around the argument of the function. This is needed e.g., because if h = hash(N , [INT, 3]:[X :Type, X ]) then nil # h (#x :INT.[x ] INT . )[3] h h :INT is derivable, yet [[3] h h ] INT . is not welltyped.) mar (v hm :T ) -#hm marshalled ([v hm ] T hm :T ) (ered.mar) unmar (marshalled (v . :T ):T # ) -#hm ( v . if T = T # UnmarFailure T otherwise (ered.unmar) RR n 4851 34 Leifer, Peskine, Sewell, Wansbrough Note that we use syntactic equality (up to alphaconversion (inside hashes)) to compare the types (which are closed). In practice, we could use a md5style checksum of the alphanormalized type expression. [()] UNIT hm # -#hm () (ered.col.unit) (and all base types when we have them) [(v hm # 1 , ..., v hm # j )] T1#...#T j hm # -#hm ([v hm # 1 ] T1 hm # , ..., [v hm # j ] T j hm # ) (ered.col.tuple) [#x :T .e] T # #T ## hm # -#hm #x :T # .[{x #[x ] T # hm }e] T ## hm # (ered.col.fun) [marshalled (v . :T )] STRING hm # -#hm marshalled (v . :T ) (ered.col.marred) [[v h0 ] h0 h0 ] h0 h1 -#hm [v h0 ] h0 h0 (ered.col.col) for h 0 #= h 1 and h 1 #= hm . We used to have this rule for an arbitrary type T , but in fact it is only ever necessary for T = h 0 . [v hm 1 ] h2 hm 1 -#hm v hm 1 (ered.col.le) for hm 1 4 hm . (See Definition E.13 (partial order on colours).) We used to have this rule with an arbitrary type T rather than h 2 , but the other (ered.col.*) rules deal with the case when T is a constructed type. e -#hm e # C hm # hm .e -# hm # C hm # hm .e # (ered.cong) D.3 n # n # network structural congruence 0 | n # n (nsc.id) n 1 | n 2 # n 2 | n 1 (nsc.commut) n 1 | (n 2 | n 3 ) # (n 1 | n 2 ) | n 3 (nsc.assoc) Plus reflexivity, symmetry and transitivity of #. D.4 n -# n # network reduction e -# . e # e -# e # (nred.expr) n -# n # n | n ## -# n # | n ## (nred.par) n # n 0 -# n # 0 # n # n -# n # (nred.strcong) CC . hm .! v hm | CC . hm # .? -# CC . hm .() | CC . hm # .v hm (nred.comm) Note that the value that is sent is a value in the domain from which it is sent, but just by looking at this rule, it might not be a value in the domain where it is received. In fact, typing will ensure that v hm = marshalled (v . :T ) and this is a value in any domain. E General definitions and lemmas E.1 Proofs We use the words ``proof'' and ``derivation'' indifferently, to mean a natural deductionstyle tree of inference steps leading to a judgement. Definition E.1 (smaller proof) A proof # is smaller than a proof # # iff # contains at most as many inference steps as # # , i.e. the number of nodes in the tree # is smaller. A subproof is a particular case of a smaller proof. Note that any proof is a subproof of itself and is smaller than itself. We will use the wordings ``proper subproof'' and ``strictly smaller proof'' to exclude equality (respectively, equal size). INRIA Global abstractionsafe marshalling with hash types 35 E.2 Correctness of parts Definition E.2 (domain of an environment) The domain of an environment E , written domE , is a set of variables defined by induction as follows: . dom nil = ? . dom (E , #:#) = domE # {#} Lemma E.3 (nonmembership in domain is interpreted trivially) The judgement # / # domE is provable iff # is not a member of the domain of E . Proof. Induct on the derivation of # / # domE . The rules (clash.nil) and (clash.cons) trivially maintain this property. We will freely make use of this lemma in the remainder of this section. Lemma E.4 (colours have to be ok) If E #hm J then # hm ok by a proper subproof. Proof. Induct on the derivation of E #hm J . All rules whose conclusion is a coloured judgement have at least one premise that is a similarly coloured judgement, so induction applies. This leaves only the rule (envok.nil), which has # hm ok as a premise. Lemma E.5 (hashes have to be ok) If E #hm J or # hm ok is derivable by a proof # and h is a subterm of E #hm J or # hm ok then # h ok by a subproof of #. Proof. Induct on the structure of #. Most metavariables in the conclusion of rules whose conclusion is a coloured judge ment also appear in at least one premise that is a coloured judgement. If h is in the instantiation of such a metavariable then we have the desired result by induction. We list the remaining cases (including ``exposed'' hashes). Case subterm of hash(N , M :S ) in (TK.hash): One premise is # hash(N , M :S ) ok. If h = hash(N , M :S ) we have the desired result. Otherwise induction gives the desired result. Case hm in (envok.nil): Trivial by induction. Case N (in (mT.let)): Trivial (fv N = ?). Case (hmok.hash): The conclusion is # hash(N , M :S ) ok. If h = hash(N , M :S ) we have the desired result. Other wise induction gives the desired result. Case (hmok.zero): Trivial. Lemma E.6 (environments have to be ok) If E #hm J then E #hm ok by a subproof. Proof. Simultaneously with the following lemma. Lemma E.7 (prefixes of ok environments are ok) If E , b #hm ok then E #hm ok by a subproof. Proof. Induct simultaneously on the derivation of E #hm J and E , b #hm ok. Note that E , b #hm ok can only be derived from a judgement of the form E #hm J , hence the result of the second lemma follows by induction from the first lemma. We now turn to the first lemma. Most rules whose conclusion is a coloured judgement have at least one premise that is a coloured judgement with the same environment, and so we apply the induction hypothesis to that premise. We list the remaining cases. Cases (envok.nil), (envok.x), (envok.X), (envok.U): Trivial. Case (eT.fun): There exist x , T , T # , e such that J = #x :T .e:T#T # . The premise is E , x :T #hm e:T # . By induction, E , x :T #hm ok by a subproof, hence E #hm ok by the second lemma, as desired. Lemma E.8 (ok environments have no repetition in the domain) If E , E # #hm ok then domE # domE # = ?. RR n 4851 36 Leifer, Peskine, Sewell, Wansbrough Proof. Induct on the length of E # . If E # = nil, then domE # = ?, so domE # domE # = ?. Otherwise write E # = (E ## , #:#). Then E , E # #hm ok must have been derived by the appropriate (envok.*) rule, with the premises E , E ## #hm # ok and # / # dom (E , E ## ). From the first premise, by Lemma E.6 (environments have to be ok), we get E , E ## #hm ok by a subproof, whence domE # domE ## = ? by induction. Then domE # domE # = (domE # domE ## ) # (domE # {#}) = ? # ? = ? as desired. Lemma E.9 (free variables of a judgement come from the environment) If E #hm J then fv (J ) # dom (E ). For completeness's sake: if # hm ok then fv (hm) # ?; if # n ok then fv (n) # ?. Proof. We freely use Lemma E.3 (nonmembership in domain is interpreted trivially). Induct on the size of the derivation # of the judgement. Most rules whose conclusion is a coloured judgement have the following property: every metavariable in the right hand side of the conclusion E #hm J is present in the righthand side, not under a binder, of a premise that is a coloured judgement with the same environment as the conclusion. Then, by induction on the premise, every free variable in the subterm matched by that metavariable is present in the domain of the environment. Most rules whose conclusion is the correctness of a network have the following property: every metavariable in the conclusion is also present in one of the premises which is a network correctness judgement. Then, by induction, there is no free variable in the subterm matched by that metavariable. We list the remaining cases. Case (TK.hash): The conclusion is E #hm hash(N , M :S ):Type. One premise is # hash(N , M :S ). By induction we have fv (hash(N , M :S )) = ?, so fv (hash(N , M :S )) # domE . Case (Teq.hash): The conclusion is E # h h == T where h = hash(N , [T , v hm ]:S ). The premise is E # h ok. By Lemma E.4 (colours have to be ok), # h by a proper subproof. By induction, we have fv h = ? and in particular fv T = ?, so fv (h == T ) = ? # domE . Case (hmok.hash): The conclusion is # hash(N , M :S ) ok and the premise is nil # . M :S . By induction we have fv (hash(N , M :S )) = dom nil = ? as desired. Case (hmok.zero): Trivial. Cases T in (Sok); T and T # (Seq.struct) and (Ssub.struct); e in (eT.fun); m in (mT.let): These rules have a metavariable # in the conclusion that is under a binder for some variable #. In each case, there is a premise of the form E , #:# #hm J # with # appearing not under a binder in J # . By induction, we get that fv # # domE # {#}. Since # is bound in the occurence of # in the conclusion, this is the desired result. Cases K in (Sok); T in (eT.fun): In each case, there is a premise of the form E , #:# #hm J # where E and hm are the environment and the colour of the conclusion and # is the metavariable under consideration. By Lemma E.6 (envi ronments have to be ok) and reversing the appropriate (envok.*) rule, we have, by a proper subproof, respectively, E #hm K ok, E #hm T :Type, E #hm M :S . In each case, by induction, we get fv # # domE . Case T # in (eT.fun): By induction as in the case of e, we get that fv T # # domE # {x}. By Lemma E.5 (hashes have to be ok), for any hash h that is a subterm of T # , we have # h ok by a (proper) subproof of #. Thus, by induction, fv h = ? and in particular x / # fv h . Given the syntax of types, the only place where T # might have a free expression variable is inside a hash, so x / # fv T # . Hence fv T # # domE as desired. Cases (TK.var), (eT.var), (US.var): The variable (X , x or U respectively) that the similarly written metavariable instan tiates to is obviously present in the environment. Case (eT.col): The conclusion is E #hm [e] T hm # :T . All that remains to be shown is that fv hm # # domE . One premise of the rule is E # hm # e:T . By Lemma E.4 (colours have to be ok), we have # hm # ok by a proper subproof, so by induction fv hm # = ? whence the desired result. Case N (in (mT.let)): Trivial as fv N = ?. Case (nok.expr): The conclusion is # e ok and the premise is nil # . e:UNIT. By induction we have fv e # dom nil = ? as desired. INRIA Global abstractionsafe marshalling with hash types 37 Definition E.10 (correctness judgement) A correctness judgement is a coloured judgement whose righthand side is of one of the following forms: ok, K ok, T :Type, S ok. (1) Note that a derivation of a correctness judgement may involve other sorts of judgements. For example, in order to derive U :S #hm U .TYPE:Type, one has to use (TK.mod), with a premise of the form U :S #hm U :S # . Definition E.11 (type world judgement) A type world judgement is a coloured judgement whose righthand side is of one of the following forms: ok, K ok, K <: K # , K == K # , T == T # , T :K , S ok, S == S # , S <: S # , U :S . (2) Note that any derivation of a type world judgement contains only type world judgements and nonclash judgement, except in the proof of correctness of hashes. E.3 Variables and colours Definition E.12 (hashes in something) The hashes in a syntactic entity are the subterms that are hash(N , M :S ) and that are not themselves subterms of a hash. Definition E.13 (partial order on colours) We call topped colours the set formed by all colours (i.e. hm) plus a point # that is distinct from any colour. We define a partial order on topped colours as follows: hm 4 hm # iff hm = hm # or hm = . or hm # = #. Note that 4 defines a poset with all greatest lower bounds, with . as the minimum element and # as the maximum element. Definition E.14 (pvu) Let # be a proof of a coloured judgement E #hm J , and let # be any variable. The set of colours at which the variable # is used in the proof #, written pvu # (#), is defined as follows. Consider the last rule used in the proof. The environment of its conclusion is an environment pattern. Take all the metavariables # in this pattern whose instantiation in the last step of # contains # as a variable bound by the environment. Then pvu # (#) is the union over all # of the following sets: . If # occurs in the righthand side of the conclusion of the rule, anywhere but under an explicit bracket, then {hm}, else ?. . For every occurence of # in the righthand side of the conclusion of the rule under an explicit bracket, the colour that the subscript of the bracket instantiates to. . For every occurence of # in the environment part of a premise which is a coloured judgement, pvu # (# # ) where # # is the subproof of # that leads to said premise. Note that with our current rules: . Since (as we will prove) ok environments have no repetition in the domain, only one metavariable # is concerned. It may be either a variable metavariable ( #) or an environment metavariable ( E ). . If # / # domE then there is no #, thus pvu # (#) = ?. . The first case only arises if # is a variable metavariable, and the rule is one of (*.var) introducing that variable. . The second case never arises (the only rule with an explicit bracket is (eT.col), and it has nothing from the environ ment inside the bracket). Definition E.15 (alternate, informal definition of pvu) Consider a proof # of a coloured judgement of the form E 0 , #:#, E #hm J . The set of colours at which the variable # is pulled from the environment in #, written pvu # (#), is the set of colours hm such that the judgement E 0 , #:#, E # #hm #:# appears in the proof as a conclusion of a varrule ((eT.var), (TK.var) or (US.var)). If # is not in the domain of the conclusion of #, then we define pvu # (#) to be the empty set. Lemma E.16 (monotonicity of pvu) If # # is a subproof of # then pvu # (# # ) # pvu # (#). Hence min pvu # (#)  4 min pvu # (# # )  . RR n 4851 38 Leifer, Peskine, Sewell, Wansbrough Proof. Trivial from the first definition. Definition E.17 (substitution) Some potentially interesting cases: #(mar (e 0 :T 0 )) = mar (#e 0 :#T 0 ) #(marshalled (e 0 :T 0 )) = marshalled (#e 0 :#T 0 ) #(unmar e 0 :T 0 ) = unmar#e 0 :#T 0 #([e 0 ] T hm 0 ) = [#e 0 ] #T #hm 0 {U .TYPE T}U .TYPE = T {U .term e}U .term = e {U U # }U .TYPE = U # .TYPE {U#U # }U .term = U # .term Note that substitution performs all necessary alphaconversions. We generally leave alphaconversion implicit. Lemma E.18 (stability of values by substitution) Let # be any substitution, hm be a colour and v hm be any hmvalue with correct hashes. Then #v hm is an hmvalue. Proof. Induct on the structure of v hm . As per the syntax of values, the only places where a value may contain free variables are inside hashes or under a #. Since the hashes in v hm are assumed to be correct, they are closed by Lemma E.9 (free variables of a judgement come from the environment). As for #'s, they allow an arbitrary expression, hence they are stable by substitution. Lemma E.19 (computing the pvu of a type world judgement) Let # be a proof of a type world judgement E 0 , #:#, E #hm J . Then hm 4 min pvu # (#)  . Proof. Induct on the structure of #. We freely use Lemma E.6 (environments have to be ok) and Lemma E.8 (ok environments have no repetition in the domain). Consider first the rules whose conclusion is a type world judgement but for which J #= ok. Each premise has one of the following properties: 1. The premise is a type world judgement whose colour is the same as in the conclusion and whose environment contains the conclusion's. 2. The premise is either not a coloured judgement or a coloured judgement with an empty environment. Suppose that the conclusion of # is obtained by such a rule. Then consider any subproof # # of # leading to a premise of the aforementioned conclusion. One of the alternatives holds: Case 1: By induction, hm 4 min pvu # (# # )  . Case 2: Then pvu # (# # ) = ? whence hm 4 # = min pvu # (# # )  . The set pvu # (#) is the union over all the premises of the pvu # (# # )'s, plus hm if the rule under consideration is (TK.var) or (US.var) introducing #. Hence hm 4 min pvu # (#)  . Let us turn to the (envok.*) rules. We distinguish as to whether # is the variable being added to the environment. Case (envok.*) not adding #: There exist E # , # # , and # # such that the conclusion is E 0 , #:#, E # , # # :# # #hm ok, and the premises are # # / # dom (E 0 , #:#, E # ) and E 0 , #:#, E # #hm # ok. Let # # be the subproof leading to the latter judgement. Then by induction hm 4 min pvu # (# # )  , whence the desired result as pvu # (#) = pvu # (# # ). Case (envok.*) adding #: The conclusion is E 0 , #:# #hm ok. The only premise that is a coloured judgement is E 0 #hm # ok, which does not contain #. So pvu # (#) = ? whence hm 4 # = min pvu # (#)  . INRIA Global abstractionsafe marshalling with hash types 39 Note that this lemma does not hold for expression typing judgements. We suspect that it would hold for shortest proofs, for some notion of shortest proof. Here is a counter example, where the problem is the use of X in a ``useless'' fashion. . . . X :Eq(UNIT) # hm ok (envok.X) X :Eq(UNIT) # hm ():UNIT . . . X :Eq(UNIT) # hm X :Eq(UNIT) (TK.var) X :Eq(UNIT) # hm X == UNIT X :Eq(UNIT) # hm UNIT == X (Teq.sym) X :Eq(UNIT) # hm ():X . . . X :Eq(UNIT) # hm X :Eq(UNIT) (TK.var) X :Eq(UNIT) # hm X == UNIT X :Eq(UNIT) # hm ():UNIT Lemma E.20 (connection between fv and fse) Let # be anything in the syntax. Then fv # # fse #. If x # fse # then x # fv #. If X # fse # then X # fv #. If U # fse # or U .term # fse # or U .TYPE # fse # then U # fv #. We may use this lemma implicitly. Proof. Trivial from the definition of fv and fse . Lemma E.21 (types do not contain free expression variables) If E #hm T :Type then fse T does not contain any expression substitutable entity (i.e. ... x ). Also, if E #hm K ok (respectively E #hm S ok) then fse K (respectively fse S ) does not contain any expression substitutable entity. Note that fse # not containing any expression substitutable entity implies that fv # does not contain any free expression variable. Proof. Let us first prove this lemma for a type T . Induct on the structure of T . Most cases are either obvious (X , U .TYPE) or obvious by induction (constructed type). The only nontrivial case is a hash h . By Lemma E.5 (hashes have to be ok), # h ok. By Lemma E.9 (free variables of a judgement come from the environment), fv h = ?, whence by Lemma E.20 (connection between fv and fse) fse h = ?. If E #hm Eq(T ) ok, then by reversing (Kok.Eq) we get E #hm T :Type, whence by the first part of this lemma fse K = fse T has the desired property. The case E #hm Type ok is trivial (fse Type = ?). If E #hm [X :K , T ] ok, then by reversing (Sok) we get E , X :K #hm T :Type. By the previous two paragraphs, neither fse K nor fse T contains any expression substitutable entity, so the same holds for fse S = fse K # (fse T \ X ). Lemma E.22 (environments do not contain free expression variables) If E 0 , E 1 #hm ok then fse E 1 does not contain any expression substitutable entity. Note that in particular fv E 1 does not contain any expression variable. Proof. Induct on the length of E 1 . If E 1 = nil the conclusion is obvious. Otherwise there exist E # 1 , # and # such that E 1 = E # 1 , #:# . By reversing the appropriate (envok.*) rule, we get E 0 , E # 1 #hm # ok. By Lemma E.6 (environments have to be ok), we have E 0 , E # 1 #hm ok. By induction, we have ... x / # fse E # 1 . Also, by Lemma E.21 (types do not contain free expression variables), ... x / # fse # (whether # is a type, kind or signature). Hence ... x / # fse E 1 . Lemma E.23 (expression substitution in environment) If E 0 , E 1 #hm ok and ... x is an expression substitutable entity (i.e. an expression variable or U .term for some U ) then { ... x##}E 1 = E 1 . Proof. Trivial consequence of Lemma E.22 (environments do not contain free expression variables). F Type preservation by substitution Lemma F.1 (``type of a machine'' judgements are not used to prove other coloured judgements) Proof. No rule whose conclusion is a coloured judgement other than ``type of a machine'' has a premise that is a ``type of a machine'' judgement. Lemma F.2 (colour stripping judgements) If E #hm J and # hm # ok and hm 4 hm # then E # hm # J for all coloured statements J other than ``type of a machine''. RR n 4851 40 Leifer, Peskine, Sewell, Wansbrough Proof. Induct on the derivation of E #hm J . In most rules where the conclusion is a coloured judgement, all the premises either: . do not involve the colour of the conclusion; or . are a coloured judgement of the same colour, other than ``type of a machine'', so we can use induction to prove them. Note that by Lemma F.1 (``type of a machine'' judgements are not used to prove other coloured judgements), a premise that is a coloured judgement is never a ``type of a machine'' judgement. If every premise of the last rule used in the derivation enjoys one of these properties, and if furthermore the rule applies to arbitrary colours, (so that replacing hm by hm # does yield an instance of the rule again), we have E # hm # J . We list the remaining cases. Case (Teq.hash): Then hm #= ., so hm # = hm . Case (TK.hash): Trivial (# hm # ok is assumed in this lemma). Case (envok.nil): Trivial (# hm # ok is assumed in this lemma). Case (eT.marred): Trivial. Case (eT.col): Trivial. Lemma F.3 (weakening) If E , E # , E ## #hm ok and E , E ## #hm J then E , E # , E ## #hm J . Furthermore, if # # domE # and E , E # , E ## #hm ok is derived by a proof # such that pvu # (#) = ?, then there is a proof # # of E , E # , E ## #hm J such that pvu # (# # ) = ?. Proof. We freely use Lemma E.3 (nonmembership in domain is interpreted trivially). Consider the variables that appear in the derivation of E , E ## #hm J but not in the judgement E , E ## #hm J itself. We can alphaconvert them to variables that are not present in dom (E , E # , E ## ). Induct on the derivation of E , E ## #hm J . Note that the inductive rules that define derivable coloured judgements are all rewriting rules (in other words, there is no side condition). Most rules have the following properties: 1. There is a distinguished environment metavariable E such that the conclusion is a judgement with this metavariable at the leftmost position and no other. 2. For each premise, one of the following conditions holds: (a) The premise is a coloured judgement whose environment is either the same as the conclusion's or the one in the conclusion followed by exactly one more binding. (b) The premise is # / # dom E for some # that is in the domain of the conclusion. (c) The premise does not mention E . Suppose that E , E ## #hm J was derived by an instance # of such a rule. There are two cases, depending on whether the instantiation of E (as per condition 1) includes the whole of E or not. Case E is instantiated by E , E ### : Then there exists E #### such that E ## = E ### , E #### . Since we have a raw term rewriting system, we get an instance # of the same rule by instantiating E by E , E # , E ### and other variables as in #. Let us prove that all the premises of # hold. Consider a premise in #, depending on which case of condition 2 holds: INRIA Global abstractionsafe marshalling with hash types 41 Case 2a: The premise is of the form E , E ## , E i #hm i J i , where E i is of length at most one. By Lemma E.6 (environments have to be ok), E , E ## , E i #hm i ok by a subproof. If E i is empty, then we have E , E # , E ## , E i #hm i ok. Otherwise there exist # and # such that E i = #:# . The judgement E , E ## , #:# must have been derived by the appropriate (envok.*) rule from E , E ## #hm i # ok and # / # dom (E , E ## ). By induction, we have E , E # , E ## #hm i # ok, whence by the same (envok.*) rule as previously: E , E # , E ## , #:# #hm i ok (recalling that we performed alphaconversion on the proof so that # / # domE ## whence # / # dom (E , E # , E ## )). If furthermore # # domE # and pvu # (#) = ?, then the induction gives a proof # ## of E , E # , E ## , x :T #hm i ok such that pvu # (# ## ) = ?. In any case, E , E # , E ## , E i #hm i ok, so we can apply induction, getting E , E # , E ## , E i #hm i J i as desired. If # # domE # and pvu # (#) = ?, then we have obtained a proof # ## of E , E # , E ## , E i #hm i ok such that pvu # (# ## ) = ?, and the last induction gives a proof # # of E , E # , E ## , E i #hm i J i such that pvu # (# # ) = ?. Case 2b: The premise is # # / # dom (E , E ### ) with # # in dom (E , E ### , E #### ). Hence # # # domE #### . Since E , E # , E ### , E #### #hm ok, by Lemma E.8 (ok environments have no repetition in the domain), we have # / # dom (E , E # , E ### ) as desired. Case 2c: The premise in # is exactly the premise in #. We have a derivation of all the premises in #, so we get a proof # # of its conclusion. Note further that if # # domE # and pvu # (#) = ?, we do get that pvu # (# # ) = ?. (If # is an instance of a (*.var) rule, then # is not the variable being introduced by # # domE # and Lemma E.8 (ok environments have no repetition in the domain).) Case E is instantiated by a proper prefix E ### of E : Only the following cases are concerned: Case (envok.*): Trivial (take # # = #). Case (*.var): Then there exists E #### such that E = E ### , #:#, E #### . By assumption, we have a proof # of E ### , # # :#, E #### , E # , E ## #hm ok. By (*.var) we get a proof # # of E ### , # # :#, E #### , E # , E ## #hm # # :# as de sired. If # # domE # and pvu # (#) = ?, then pvu # (# # ) = ?, because # #= # # since # # domE # and Lemma E.8 (ok environments have no repetition in the domain). The only rule whose conclusion is a coloured judgement that does not match the conditions above is (envok.nil). If the last step of the derivation uses this rule, then its conclusion is nil #hm ok, and we desire a proof of E # #hm ok, which holds by assumption: take # # = #. Lemma F.4 (merging environments) If E , E # #hm ok and E , E ## #hm ok and domE # # domE ## = ? then E , E # , E ## #hm ok. Furthermore, if # # domE # and E , E # #hm ok is derived by a proof # such that pvu # (#) = ?, then there is a proof # # of E , E # , E ## #hm ok such that pvu # (# # ) = ?. Proof. We freely use Lemma E.3 (nonmembership in domain is interpreted trivially). We induct on the length of E ## . If E ## = ?, the results are trivial. Now let us assume the lemma holds for E ## , #:# , and we have E , E ## , #:# #hm ok and domE # # dom (E ## , #:#) = ?. By reversing the appropriate (envok.*) rule, we get E , E ## #hm # ok. By Lemma E.6 (environments have to be ok), we have E , E ## #hm ok. By induction, we get E , E # , E ## #hm ok. By Lemma F.3 (weakening), we have E , E # , E ## #hm # ok. Then by applying the appropriate (envok.*) rule we get E , E # , E ## , #:# #hm ok as desired. Suppose furthermore that # # domE # and pvu # (#) = ?. Then the proof of E , E # , E ## #hm ok obtained above by induction and that of E , E # , E ## #hm # ok obtained by Lemma F.3 (weakening) have an empty pvu for #, so the proof # # of E , E # , E ## , #:# that we build satisfies pvu # (# # ) = ?. Lemma F.5 (combined weakening) If E , E # #hm ok and E , E ## #hm J and domE # # domE ## = ? then E , E # , E ## #hm J . Furthermore, if # # domE # and E , E # #hm ok is derived by a proof # such that pvu # (#) = ?, then there is a proof # # of E , E # , E ## #hm J such that pvu # (# # ) = ?. Proof. Trivial combination of Lemma F.4 (merging environments) and Lemma F.3 (weakening). Lemma F.6 (kinds are smaller than Type) If E #hm K ok then E #hm K <: Type. RR n 4851 42 Leifer, Peskine, Sewell, Wansbrough Proof. If K = Type, then we get E #hm Type <: Type by reversing (Kok.Type) and applying (Keq.Type) and (Ksub.refl). Otherwise there exists T such that K = Eq(T ). Then by reversing (Kok.Eq) and applying (Ksub.Eq) we get E #hm Eq(T ) <: Type. Lemma F.7 (relating typeiskind and subkinding) If E #hm T :K then E #hm Eq(T ) <: K . Proof. If K = Type, then we get the desired result by (Ksub.Eq). Otherwise there exists T # such that K = Eq(T # ). Then by (Teq.Eq), (Keq.Eq) and (Ksub.refl) we get E #hm Eq(T ) <: Eq(T # ) as desired. Lemma F.8 (components of modules are ok) If E #hm [T , v hm ]:[X :K , T # ] then there exists T ## such that E #hm T :K and E , X :K #hm T # :Type and E , X :Eq(T ) #hm T ## == T # and E #hm v hm :T ## and E #hm K ok. Proof. Reverse (MS.struct) to get the first four judgements. As for the last one, since E , X :K #hm T # :Type, by Lemma E.6 (environments have to be ok) and reversing (envok.X), we get E #hm K ok. Lemma F.9 (types are ok provided their hashes are) E #hm T :Type iff fv T # domE and E #hm ok and all the hashes in T are ok. Proof. Suppose that fv T # E and all the hashes in T are ok. We prove that E #hm T :Type by induction on the syntax of T . Case T = UNIT or T = STRING: Trivial by (TK.unit) or (TK.mar). Case there exist T 1 , ..., T j such that T = T 1 # ... # T j : Note that fv T i # fv T # domE . By induction, E #hm T i for 1 6 i 6 j . By (TK.tuple), we have E #hm T 1 # ... # T j :Type. Case there exist T 1 , T 2 such that T = T 1 #T 2 : Note that fv T i # fv T # domE . By induction, E #hm T i for 1 6 i 6 2. By (TK.fun), we have E #hm T 1 #T 2 :Type. Case T is a hash h: The hash h is ok by assumption, so we get E #hm h:Type by (TK.hash). Case T is a type variable X : Since {X } = fv T # domE , there exist E 1 , K and E 2 such that E = E 1 , X :K , E 2 . By (TK.var), we get E #hm X :K . By Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.X), we have E 1 #hm K ok, whence by Lemma F.3 (weakening) E #hm K ok. By Lemma F.6 (kinds are smaller than Type), we have E #hm K <: Type, whence by (TK.sub) E #hm X :Type as desired. Case there exists U such that T = U .TYPE: Since {U } = fv T # domE , there exist E 1 , K , T # and E 2 such that E = E 1 , U :[X :K , T # ], E 2 . By (US.var) and (TK.mod), we get E #hm U .TYPE:K . By Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.U), we have E 1 #hm [X :K , T # ] ok, whence by Lemma F.3 (weakening) E #hm [X :K , T # ] ok. By reversing (Sok), we have E #hm K ok. By Lemma F.6 (kinds are smaller than Type), we have E #hm K <: Type, whence by (TK.sub) E #hm U .TYPE:Type as desired. Now suppose E #hm T :Type. Then fv T # domE by Lemma E.9 (free variables of a judgement come from the environment). Also all the hashes in T are ok by Lemma E.5 (hashes have to be ok). Lemma F.10 (colour change preserves type okedness) If nil #hm 0 T :Type and nil #hm 1 ok then nil #hm 1 T :Type. Proof. Trivial application of Lemma F.9 (types are ok provided their hashes are). Definition F.11 (unresolved free variables of an environment) The unresolved free variables of an environment, written ufv E , are defined as follows: ufv nil = ? ufv (#:#, E ) = (ufv (E ) \ {#}) # fv # It is immediate that ufv E # ufv (E , E # ). Lemma F.12 (computing unresolved free variables) ufv (E , E # ) = ufv E # (ufv E # \ domE ) INRIA Global abstractionsafe marshalling with hash types 43 Proof. Induct on the length of E . The result is trivial if E is empty. If E = #:#, E ## , then ufv (E , E # ) = (ufv (E ## , E # ) \ {#})#fv # . By induction, ufv (E ## , E # ) = ufv E ## #(ufv E # \domE ## ). So ufv (E , E # ) = (ufv E ## #(ufv E # \domE ## )\ {#}) # fv # = (ufv E ## \ {#}) # (ufv E # \ (domE ## # {#})) # fv # = ufv E # (ufv E # \ domE ) as desired. Lemma F.13 (ok environments have no unresolved free variables) If E #hm ok then ufv E = ?. Proof. We prove by induction on the derivation size that E #hm J implies ufv E = ?. Most rules whose conclusion is a coloured judgement E #hm J have at least one premise that is a coloured judgement whose environment is E , E # for some E # , whence the induction hypothesis gives the desired result. Also, rules that have a conclusion of the form nil #hm J are trivial. The remaining rules are (envok.*). If we write the conclusion as E , #:# #hm ok, one premise is E #hm # ok. We have ufv E = ? by induction. By Lemma E.9 (free variables of a judgement come from the environment), we have fv # # domE . By Lemma F.12 (computing unresolved free variables), we have ufv (E , #:#) = ufv E # (ufv (#:#) \ domE ) = ? # (fv # \ domE ) thus ufv (E , #:#) = ? as desired. Lemma F.14 (type preservation by substitution) If E 0 , #:#, E #hm J by a proof # such that hm # 4 min pvu # (#)  and E 0 # hm # #:# then E 0 , #E #hm #J where # = {###} and #:# is an expression or type binding. Proof. Induct on the derivation # of E 0 , #:#, E #hm J . Note that the inductive rules that define derivable coloured judgements are all rewriting rules (in other words, there is no side condition). Most rules have the following properties: 1. There is a distinguished environment metavariable E such that the conclusion is a judgement with this metavariable at the leftmost position and no other. 2. For each premise, one of the following conditions holds: (a) The premise is a judgement with E in the leftmost position, and in no other place. (b) The premise is # / # dom E for some #. (c) The premise is a judgement with an empty environment and does not mention E . Suppose that E 0 , #:#, E #hm J was derived by an instance # of such a rule. Without loss of generality, # is not in a binding position (including the domain of an environment) anywhere in E 0 , #:#, E #hm J except where shown; furthermore # is not in a binding position in any premise either, except in instances of E if and where this includes E 0 , #:# . By condition 1, there are two possibilities: General case: The instance # was obtained by instantiating the metavariable E with E 0 , #:#, E ## , where E ## is an envi ronment. Hence E is of the form E ## , E # , where E # is an environment. Since we have a raw term rewriting system, we also get an instance # of the same rule by instantiating E by E 0 , E ## and other metavariables as in #. Note that # does not appear in any binding position in #. Note that the only places in the syntax where an expression (respectively type) variable is required are: . in binders, which doesn't matter for # as it does not affect variables that are bound in #. (+) . in the lefthand side of a nonclash judgement. These only occur in (envok.*) rules, in the form # / # dom E . Let us write # # for the instantiation of #. Then # # #= # as # # / # dom (E 0 , #:#, E ## ) is derivable. Note furthermore that welltyped values are stable by substitution, as per Lemma E.18 (stability of values by substitution). 1 Hence # is a wellsorted raw term substitution on # or any subterm thereof, so applying # to # yields another instance # of the rule. Note that the conclusion of # is #E 0 , #E ## #hm #J , which is also E 0 , #E ## #hm J as # / # fv E 0 by Lemma E.6 (environments have to be ok), Lemma E.7 (prefixes of ok environments are ok) and Lemma F.13 (ok environments have no unresolved free variables). Consider the premises in #, depending on which case of condition 2 holds: Case 2a: The premise is of the form E 0 , #:#, E ## , E # i #hm i J i . Given Lemma E.16 (monotonicity of pvu), we can apply induction, getting E 0 , #E ## , #E # i #hm i #J i , which is the corresponding premise in # (recall that #E 0 = E 0 ). 1 This is needed for (MS.struct), which requires a value in one place. RR n 4851 44 Leifer, Peskine, Sewell, Wansbrough Case 2b: The premise is of the form # # / # dom (E 0 , #:#, E ## ), and # # is not # (see (+) above). We need to prove that # # / # dom (E 0 , #E ## ). This follows easily, given that dom (E 0 , #E ## ) = dom (E 0 , E ## ) # dom (E 0 , #:#, E ## ). Case 2c: The premise is a judgement AJ with an empty environment, so by Lemma E.9 (free variables of a judge ment come from the environment) # is not free in AJ. Hence #AJ = AJ. Furthermore the premise does not include any instantation of E , so it is in fact exactly the premise needed in #. As all the premises of # are derivable, its conclusion holds. It reads: E 0 , #E ## , #E # #hm #J , which is what we set out to prove. Special cases: The instance was obtained by instantiating the metavariable E to a prefix E 1 of E 0 : so there is E 2 such that E 0 = E 1 , E 2 . Only the following cases of the following rules are concerned. Cases (envok.*): Then E = nil and E 2 = nil. The proof obligation is E 1 #hm ok, i.e. E 0 #hm ok, which holds by Lemma E.7 (prefixes of ok environments are ok). Cases (eT.var), (TK.var): # is of the form E 0 , #:#, E # #hm ok E 0 , #:#, E # #hm #:# and # = {# #}, and we have E 0 # hm # #:# with hm # 4 min pvu # (#)  4 hm . By induction (which we can apply thanks to Lemma E.16 (monotonicity of pvu)), we have E 0 , #E # #hm ok. Furthermore, since # hm ok by Lemma E.4 (colours have to be ok), E 0 #hm #:# by Lemma F.2 (colour stripping judgements). By Lemma F.3 (weakening) we get E 0 , #E # #hm #:# as desired. Every remaining rule is inapplicable because the environment in the conclusion must be empty. Lemma F.15 (strengthening) If E 0 , #:#, E #hm J and # / # fv E # fv J and #:# is a type or expression variable binding then E 0 , E #hm J . Proof. By Lemma E.6 (environments have to be ok) and Lemma E.7 (prefixes of ok environments are ok), we have E 0 , #:# #hm ok. Let us then reverse the rule (envok.*) that was applied to obtain this latter judgement: Case #:# is X :K : Then we have E 0 #hm K ok. If K = Type, by (TK.unit), we have E 0 #hm UNIT:K ; let # be UNIT. Otherwise there exists a type # such that K = Eq(#); by reversing (Kok.Eq), we get E 0 #hm #:Type. By Lemma F.10 (colour change preserves type okedness), we have E 0 # . #:Type. By (Teq.refl) and (TK.Eq), we get E 0 #hm #:K . Case #:# is x :T : Then we have E 0 #hm T :Type, whence by Lemma F.10 (colour change preserves type okedness): E 0 # . T :Type. Let # be UnmarFailure T . By (eT.Undynfailure), we have E 0 # . #:T . In any case, we have E 0 # . #:# . By Lemma F.14 (type preservation by substitution), we have E 0 , #E #hm #J where # = {# #}. However, by assummption, # / # fv E # fv J . Hence #E = E and #J = J , so we get E 0 , E #hm J as desired. This proof of strengthening calls the main substitution lemma. This has two inconveniences: . It does not work for module variables. In fact, if we expanded the proof of the substitution lemma, we would see that the (envok.*) case dealing with # is never invoked, so it does not matter than the substitution lemma does not have such a case for module variables. . It requires that kinds and types not be empty, which is true for a convoluted reason (all our kinds happen to be nonempty, and each of our types T contains the exception UnmarFailure T ). Definition F.16 (visible typepart of a module) typepart (N , [T , v hm ]:[X :K , T # ]) = ( hash(N , [T , v hm ]:[X :K , T # ]) if K = Type T 0 if K = Eq(T 0 ) for some T 0 INRIA Global abstractionsafe marshalling with hash types 45 Definition F.17 (visible termpart of a module) termpart (N , [T , v hm ]:[X :K , T # ]) = ( [v hm ] {X#h}T # h if K = Type v hm if K = Eq(T 0 ) for some T 0 where h = hash(N , [T , v hm ]:[X :Type, T # ]). Definition F.18 (set of equations used to type a module) equations (N , [T , v hm ]:[X :K , T # ]) = ( hash(N , [T , v hm ]:[X :K , T # ]) if K = Type . if K = Eq(T 0 ) for some T 0 Lemma F.19 (reflexivity of kind equivalence) If E #hm K ok then E #hm K == K . Proof. If K = Type then this lemma holds by (Keq.Type). Otherwise there exists T such that K = Eq(T ). The judgement E #hm K ok must have been derived by (Kok.Eq), from E #hm T :Type. Then by (Teq.refl) followed by (Keq.Eq) we have E #hm Eq(T ) == Eq(T ) as desired. Lemma F.20 (weakening kind to ok kind in the environment) If E 0 #hm K <: K # and E 0 #hm K ok and E 0 , X :K # , E 1 #hm J and J is a type world judgement righthand side then E 0 , X :K , E 1 #hm J . Note that the hypothesis E 0 #hm K ok is in fact superfluous (see Lemma (weakening kind in the environment) below). Proof. Since E 0 #hm K ok, by (envok.X), E 0 , Y :K #hm ok where Y is fresh. By Lemma F.3 (weakening), from E 0 , Y :K #hm ok and E 0 #hm K # ok, we get E 0 , Y :K #hm K # ok. By (envok.X), E 0 , Y :K , X :K # #hm ok. By Lemma F.5 (combined weakening), E 0 , Y :K , X :K # , E 1 #hm J . From E 0 , Y :K #hm ok, by (TK.var), we get E 0 , Y :K #hm Y :K . By Lemma F.3 (weakening), we also get E 0 , Y :K #hm K <: K # . By (TK.sub), we get E 0 , Y :K #hm Y :K # . By Lemma F.14 (type preservation by substi tution), using Lemma E.19 (computing the pvu of a type world judgement) E 0 , Y :K # , {X Y }E 1 #hm {X Y }J . By alphaconversion, we have E 0 , X :K # , E 1 #hm J as desired. Lemma F.21 (things have to be ok) If E #hm T :K then E #hm K ok. If E #hm T == T # then E #hm T :Type and E #hm T # :Type. If E #hm K == K # or E #hm K <: K # then E #hm K ok and E #hm K # ok. If E #hm S == S # or E #hm S <: S # then E #hm S ok and E #hm S # ok. If E #hm e:T or E #hm m:T then E #hm T :Type. If E #hm M :S or E #hm U :S then E #hm S ok. Proof. Induct on the size of the derivation of the hypothesis. Consider the last rule used in said derivation. Case (TK.sub): The conclusion is E #hm T :K # and one of the premises is E #hm K <: K # . By induction, we get E #hm K # ok. Case (TK.Eq): The conclusion is E #hm T :Eq(T # ). The premise is E #hm T == T # . By induction we have E #hm T # :Type, hence E #hm Eq(T # ) ok by (Kok.Eq). Case (TK.mod): The conclusion is E #hm U .TYPE:K . The premise is E #hm U :[X :K , T ]. By induction we have E #hm [X :K , T ] ok. This must have been obtained by applying (Sok), with the premise E , X :K #hm T :Type. By Lemma E.6 (environments have to be ok), we have E , X :K #hm ok that is smaller. Hence, by reversing (envok.X), we get a proof of E #hm K ok. Cases (TK.unit), (TK.fun), (TK.mar), (TK.tuple), (TK.hash): The conclusion is of the form E #hm T :Type for some T . By Lemma E.6 (environments have to be ok), E #hm ok. By (Kok.Type), we have E #hm Type ok. Case (TK.var): The conclusion is of the form E , X :K , E # #hm X :K . The premise is E , X :K , E # #hm ok. By Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.X), we get E #hm K ok. By Lemma F.3 (weakening), we get E , X :K , E # #hm K ok as desired. Note that the proof may be bigger, since the part that proves K has to be duplicated... RR n 4851 46 Leifer, Peskine, Sewell, Wansbrough Case (Teq.Eq): The conclusion is E #hm T == T # . The premise is E #hm T :Eq(T # ). By induction we have E #hm Eq(T # ) ok, which must have been derived by (Kok.Eq) from E #hm T # :Type. Also, from this, we get E #hm Eq(T # ) <: Type by (Ksub.Eq). We can apply (TK.sub) to get E #hm T :Type. Note that this may well be the shortest way to obtain E #hm T :Type (take T = U .TYPE). Case (Teq.hash): The conclusion is E #hm hm == T , and hm = h = hash(N , [T , v hm 1 ]:S ). The premise is E #hm ok. By Lemma E.4 (colours have to be ok), we get # hash(N , [T , v hm 1 ]:S ) ok. By reversing (hmok.hash) and (MS.struct), we get nil # . T :Type. By Lemma F.3 (weakening), we get E #hm T :Type. We also have E # h h:Type by (TK.hash). Case (Teq.refl): Trivial. Cases (Teq.sym), (Teq.tran): Trivial by induction. Case (Teq.cong.fun): The conclusion is E #hm T 0 #T 1 == T # 0 #T # 1 . By induction on the premises, we get E #hm T j :Type and E #hm T # j :Type for j = 0, 1. By (TK.fun), we get E #hm T 0 #T 1 :Type and E #hm T # 0 #T # 1 :Type. Case (Teq.cong.tuple): Similar to case (Teq.cong.fun). Case (Ksub.Eq): The conclusion is E #hm Eq(T ) <: Type. The premise is E #hm T :Type. From this, by (Kok.Eq), we get E #hm Eq(T ) ok. By Lemma E.6 (environments have to be ok), we have E #hm ok. Thus, by (Kok.Type), we have E #hm Type ok. Cases (Ksub.refl), (Ksub.tran): Trivial by induction. Case (Keq.Type): The premise is E #hm ok. By (Kok.Type), we get E #hm Type ok. Case (Keq.Eq): The conclusion is E #hm Eq(T ) == Eq(T # ). The premise is E #hm T == T # . By induction we get E #hm T :Type and E #hm T # :Type, whence by (Kok.Eq), E #hm Eq(T ) ok and E #hm Eq(T # ) ok Case (Ssub.struct): The conclusion is E #hm [X :K , T ] <: [X :K # , T # ]. One premise is E , X :K #hm T == T # . By induction, we get E , X :K #hm T :Type and E #hm T # :Type, whence the desired results by (Sok). Case (Seq.struct): Similar to case (Ssub.struct). Case (Ssub.refl): Trivial. Case (Ssub.tran): Trivial. Case (eT.var): Similar to case (TK.var). Case (eT.eq): The conclusion is E #hm e:T # . One premise is E #hm T == T # . By induction we get E #hm T # :Type. Case (eT.mod): The conclusion is E #hm U .term:T . One premise is E #hm T :Type. Case (eT.ap): The conclusion is E #hm e e # :T # . One premise is E #hm e # :T#T # . By induction we get E #hm T#T # :Type. By Lemma F.9 (types are ok provided their hashes are) applied to E #hm T#T # then to E #hm T # , we get E #hm T # :Type as desired. Case (eT.fun): The conclusion is E #hm #x :T .e:T#T # . The premise is E , x :T #hm e:T # . By induction, we get E , x :T #hm T # :Type by a proof #. By Lemma E.21 (types do not contain free expression variables), x / # fv T # . By Lemma E.6 (environments have to be ok), we have E #hm ok. By Lemma F.9 (types are ok provided their hashes are) applied once in each direction, we get first that the hashes in T # are ok and fv T # # domE # {fv } (hence fv T # # domE ), then that E #hm T # ok. Also, by Lemma E.6 (environments have to be ok), we get E , x :T #hm ok, whence E #hm T :Type by reversing (envok.x). By (TK.fun), we get E #hm T#T # :Type. Cases (eT.send), (eT.recv), (eT.mar), (eT.marred), (eT.unit): By Lemma E.6 (environments have to be ok), we have E #hm ok. Then (TK.unit) or (TK.mar) gives the desired result. Cases (eT.unmar), (eT.Undynfailure), (eT.col): Trivial (one of the premises is what we need to prove). INRIA Global abstractionsafe marshalling with hash types 47 Case (eT.tuple): The conclusion is E #hm (e 1 , ..., e j ):T 1 # ... # T j . The premises are E #hm e i :T i for 1 6 i 6 j . By induction, we have E #hm T i :Type for all i , whence by (TK.tuple): E #hm T 1 # ... # T j :Type. Case (eT.proj): Similar to (eT.ap). Case (MS.struct): The conclusion is E #hm M :[X :K , T ]. One premise is E , X :K #hm T :Type, whence by (Sok): E #hm [X :K , T ] ok. Case (US.var): Similar to case (TK.var). Case (US.sub): The conclusion is E #hm U :S # . One premise is E #hm S <: S # . By induction, we have E #hm S # ok. Case (US.self): The conclusion is E #hm U :[X :Eq(U .TYPE), T ]. The premise is E #hm U :[X :K , T ]. By (TK.mod), we have E #hm U .TYPE:K . And by induction we get E #hm [X :K , T ] ok, whence by reversing (Sok): E , X :K #hm T :Type. By Lemma E.6 (environments have to be ok) and reversing (envok.X), we have E #hm K ok. By Lemma F.6 (kinds are smaller than Type), we get E #hm K <: Type. By (TK.sub), given that E #hm U .TYPE:K , we have E #hm U .TYPE:Type, whence by (Kok.Eq): E #hm Eq(U .TYPE) ok. If K = Type, by Lemma F.6 (kinds are smaller than Type), we get E #hm Eq(U .TYPE) <: Type. Otherwise there exists T 1 such that K = Eq(T 1 ). From E #hm U .TYPE:Eq(T 1 ), by (Teq.Eq), (Keq.Eq) and (Ksub.refl), we get E #hm Eq(U .TYPE) <: Eq(T 1 ). In either case we have E #hm Eq(U .TYPE) <: K . By Lemma F.20 (weakening kind to ok kind in the environment), E , X :Eq(U .TYPE) #hm T :Type. Hence by (Sok) we have E #hm [X :Eq(U .TYPE), T ] ok. Cases (mT.expr), (mT.let): Trivial by induction. Lemma F.22 (weakening kind in the environment) If E 0 #hm K <: K # and E 0 , X :K # , E 1 #hm J and J is a type world judgement righthand side then E 0 , X :K , E 1 #hm J . Proof. By Lemma F.21 (things have to be ok), E 0 #hm K ok. By Lemma F.20 (weakening kind to ok kind in the environment), we get the desired result. Lemma F.23 (type preservation by guarded expression variable substitution) If E 0 , x :T , E #hm J and E 0 # hm # e:T and E 0 # . ok then E 0 , #E #hm #J where # = {x#[e] T hm # }. Proof. E 0 # hm # T :Type by Lemma F.21 (things have to be ok). By Lemma F.9 (types are ok provided their hashes are) applied one in each direction, we get E 0 # . T :Type. Applying (eT.col) to this and E 0 # hm # e:T yields E 0 # . [e] T hm # :T . We can now apply Lemma F.14 (type preservation by substitution) to get the desired result. Lemma F.24 (type equivalence is a congruence) If E #hm T # == T ## and E , X :Type #hm T :Type then E #hm {X#T # }T == {X#T ## }T . Proof. Induct on the structure of T . Case T = UNIT or T = STRING or T = U .TYPE or T = Y #= X or T = h 1 : Then X / # fv T (if T = h 1 , this is be cause fv T = ? by Lemma E.5 (hashes have to be ok) and Lemma E.9 (free variables of a judgement come from the environment)). By Lemma F.9 (types are ok provided their hashes are), the hashes of T are ok and fv T # domE # {X }. By Lemma F.9 (types are ok provided their hashes are) in the other direction, since fv T # E , we have E #hm T :Type. By (Teq.refl), we get E #hm T == T which is the desired result. Case T = X : We have E #hm T ## == T ## as desired. Case T = T 1 #T 2 : By induction, we have E #hm {X#T # }T i == {X#T ## }T i for i = 1, 2. By (Teq.cong.fun), we get E #hm {X#T # }T == {X#T ## }T as desired. Case T = T 1 # ... # T j : By induction, we have E #hm {X#T # }T i == {X#T ## }T i for i = 1, ..., j . By (Teq.cong.tuple), we get E #hm {X#T # }T == {X#T ## }T as desired. RR n 4851 48 Leifer, Peskine, Sewell, Wansbrough Lemma F.25 (type substitution in equivalence) If E #hm {X T 0 }T == {X T 0 }T # and E = E 0 , X :Eq(T 0 ), E 1 then E #hm okT == T # . Proof. By (TK.var) and (Teq.Eq), we have E #hm X == T 0 . Let Y be a fresh variable, i.e. Y / # domE . By Lemma E.6 (environments have to be ok) and (Kok.Type) and (envok.X), we have E , Y :Type #hm ok. By Lemma F.9 (types are ok provided their hashes are) applied once in each direction, we get that E , Y :Type #hm {X Y }T :Type. Then, by Lemma F.24 (type equivalence is a congruence), we get E #hm {Y X }{X Y }T == {Y T 0 }{X Y }T , i.e. E #hm T == {X T 0 }T . Similarly, we have E #hm T # == {X T 0 }T # . By (Teq.sym), we get E #hm {X T 0 }T # == T # . Finally, by two applications of (Teq.tran), we get E #hm T == T # as desired. Lemma F.26 (reversing subsignaturing judgement) If E #hm [X :K , T ] <: [X :K # , T # ] then E #hm K <: K # and E , X :K #hm T == T # . Proof. Induct on the derivation of E #hm [X :K , T ] <: [X :K # , T # ]. Case (Ssub.refl): The premise is E #hm [X :K , T ] ok, which must have been derived by (Sok) from E , X :K #hm T :Type. By (Teq.refl), we have E , X :K #hm T == T . By Lemma E.6 (environments have to be ok), we have E , X :K #hm ok. By reversing (envok.X), we get E #hm K ok whence E #hm K <: K by Lemma F.19 (reflexivity of kind equivalence) and (Ksub.refl). Case (Ssub.tran): The premises are E #hm [X :K , T ] <: [X :K ## , T ## ] and E #hm [X :K ## , T ## ] <: [X :K # , T # ]. By induction twice, we have: E #hm K <: K ## , E , X :K #hm T == T ## , E #hm K ## <: K # and E , X :K ## #hm T ## == T # . By (Ksub.tran), we have E #hm K <: K # . By Lemma F.22 (weakening kind in the environment), we get E , X :K #hm T == T # as desired. Case (Ssub.struct): The premises are the desired judgements. Lemma F.27 (reversing module value variable typing judgement) If E #hm U :S then there exist E 0 , E 1 , K , T such that E = E 0 , U :[X :K , T ], E 1 and E #hm [X :Eq(U .TYPE), T ] <: S . Proof. Induct on the derivation of E #hm U :S . Case (US.var): Then there exist E 0 , E 1 , K , T such that E = E 0 , U :[X :K , T ], E 1 and S = [X :K , T ]. The premise is E #hm ok. By (US.self) and (TK.mod), we have E #hm U .TYPE:K . By Lemma F.7 (relating typeiskind and subkinding), we get E #hm Eq(U .TYPE) <: K . By Lemma F.21 (things have to be ok) and reversing (Sok), we get E , X :K #hm T :Type. By Lemma F.9 (types are ok provided their hashes are) applied once in each direction, given that E , X :Eq(U .TYPE) #hm ok by Lemma F.21 (things have to be ok) and (envok.X), we get E , X :Eq(U .TYPE) #hm T :Type. By (Ssub.struct), we get E #hm [X :Eq(U .TYPE), T ] <: S as desired. Case (US.sub): There exists S # such that the premises are E #hm U :S # and E #hm S # <: S . By induction, we have E = E 0 , U :[X :K , T ], E 1 and E #hm [X :Eq(U .TYPE), T ] <: S # . By (Ssub.tran) we get E #hm [X :Eq(U .TYPE), T ] <: S as desired. Case (US.self): There exist K # and T # such that S = [Eq(U .TYPE), T # ] and the premise is E #hm U :[X :K # , T # ]. By induction we have E = E 0 , U :[X :K , T ], E 1 and E #hm [X :Eq(U .TYPE), T ] <: [X :K # , T # ]. By Lemma F.26 (reversing subsignaturing judgement), we have E , X :Eq(U .TYPE) #hm T == T # . Given that we also have E #hm Eq(U .TYPE) <: Eq(U .TYPE) (by Lemma F.21 (things have to be ok), (Teq.refl), (Keq.Eq) and (Ksub.refl)), by (Ssub.struct), we get E #hm [X :Eq(U .TYPE), T ] <: S as desired. Lemma F.28 (obtaining module value variable typing judgement) If E 0 , U :[X :K , T ], E 1 #hm [X :Eq(U .TYPE), T ] <: S then E 0 , U :[X :K , T ], E 1 #hm U :S . INRIA Global abstractionsafe marshalling with hash types 49 Proof. Write E = E 0 , U :[X :K , T ], E 1 . By Lemma E.6 (environments have to be ok), (US.var) and (US.self), we have E #hm U :[X :Eq(U .TYPE), T ]. By (US.sub), we get E #hm U :S as desired. Lemma F.29 (type preservation by module substitution in coloured judgements) Suppose E 0 , U :[X :K , T ], E #hm J and z and Z are fresh and # = {U .TYPE Z , U .term z }. If J = U :S # for some S # then E 0 , Z :K , z :{X #Z}T , #E #hm [X :Eq(Z ), T ] <: #S # . Otherwise E 0 , Z :K , z :{X #Z}T , #E #hm #J Proof. Write S = [X :K , T ]. Without loss of generality, we assume that X / # domE . We induct on the derivation # of E 0 , U :S , E #hm J . Note that the inductive rules that define derivable coloured judgements are all rewriting rules (in other words, there is no side condition). Most rules have the following properties: 1. There is a distinguished environment metavariable E such that the conclusion is a judgement with this metavariable at the leftmost position and no other. 2. For each premise, one of the following conditions holds: (a) The premise is a judgement with E in the leftmost position, and in no other place. Also the righthand side of the premise is not U :S # for any S # . (b) The premise is # / # dom E for some #. (c) The premise is a judgement with an empty environment and does not mention E . Suppose that E 0 , U :S , E #hm J was derived by an instance # of such a rule. Without loss of generality, U is not in a binding position (including the domain of an environment) anywhere in # except as the first binding in an instance of E . We distinguish two possibilities, using 1: General case: The instance # was obtained by instantiating the metavariable E with E 0 , U :S , E ## , where E ## is an environment. Hence E is of the form E ## , E # , where E # is an environment. Also, in this part of the proof, we assume that J is not of the form U :S # , and that the rule is not one of (TK.mod) or (eT.mod) with U in the conclusion. Since we have a raw term rewriting system, we also get an instance # of the same rule by instantiating E by E 0 , Z :K , z :{X #Z}T , E ## and other metavariables as in #. Note that U does not appear in any binding position in #. Note that # is a wellsorted raw term substitution on # or any subterm thereof, so applying # to # yields another instance # of the rule. Note that #(Z :K , z :{X #Z}T , E ## ) = Z :K , z :{X #Z}T , #E ## . Consider the premises in #, depending on which case of condition 2 holds: Case 2a: The premise is of the form E 0 , U :S , E ## , E # i #hm i J i . Furthermore J i is not U :S # for any S # , be cause we have excluded the rules (TK.mod) and (eT.mod) in the problematic case. By induction, we get E 0 , Z :K , z :{X #Z}T , #E ## , #E # i #hm i #J i , which is the corresponding premise in #. Case 2b: The premise is of the form # # / # dom (U :S , E ## ) for some # # . Given that dom#E ## = domE ## # dom (U :S , E ## ), we have # # / # dom#E ## . Since Z and z are fresh, we have # # / # dom (Z :K , z :{X #Z}T , #E ## ). Case 2c: The premise is a judgement AJ with an empty environment, so by Lemma E.9 (free variables of a judge ment come from the environment) U is not free in AJ. Hence #AJ = AJ. Furthermore the premise does not include any instantation of E , so it is in fact exactly the premise needed in #. As all the premises of # are derivable, its conclusion holds. It reads: E 0 , Z :K , z :{X #Z}T , #E ## , #E # #hm #J , which is what we set out to prove. Case E is instantiated to a prefix of E 0 and J is not U :S # for any S # : Only the following cases of the following rules are concerned. Case (envok.U): We have E 0 , U :S #hm ok, and one of the premises is nil #hm S ok. By reversing (Sok), we have E 0 , X :K #hm T :Type. By alphaconversion, we get E 0 , Z :K #hm {X Z}T :Type. By (envok.x), we have E 0 , Z :K , z :{X Z}T #hm ok as desired. RR n 4851 50 Leifer, Peskine, Sewell, Wansbrough Case (US.var): Impossible: J would be U :S . Case (TK.mod) where J = U .TYPE:K # for some K # : Then there exists T # such that the premise is E 0 , U :S , E #hm U :[X :K # , T # ]. By induction, we get E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: #[X :K # , T # ]. By Lemma F.26 (reversing subsignaturing judgement), we get E 0 , Z :K , z :{X Z}T , #E #hm Eq(Z ) <: #K # . By Lemma E.6 (environments have to be ok), (TK.var) and (TK.sub), we get E 0 , Z :K , z :{X Z}T , #E #hm Z :Type. By (Teq.refl), and (TK.Eq), we get E 0 , Z :K , z :{X Z}T , #E #hm Z :Eq(Z ). By (TK.sub), we get E 0 , Z :K , z :{X Z}T , #E #hm Z :#K # . Case (eT.mod) where J = U .term:T # for some T # : Then there exists K # such that the premises are E 0 , U :S , E #hm U :[X :K # , T # ] and E 0 , U :S , E #hm T # :Type. By applying induction, we get that E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: #[X :K # , T # ]. By Lemma F.26 (reversing subsignatur ing judgement), we have E 0 , Z :K , z :{X Z}T , #E , X :Eq(Z ) #hm T == #T # . By Lemma E.6 (environments have to be ok), Lemma E.7 (prefixes of ok environments are ok) and (TK.var), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :K . By Lemma F.21 (things have to be ok), we have E 0 , Z :K , z :{X Z}T , #E #hm K ok. By Lemma F.6 (kinds are smaller than Type) and (TK.sub), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :Type. By (Teq.refl) and (TK.Eq), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :Eq(Z ). By Lemma F.14 (type preservation by substitution), given Lemma E.19 (computing the pvu of a type world judge ment), we get E 0 , Z :K , z :{X Z}T , #E #hm {X Z}T == {X Z}{U .TYPE Z , U .term z}T # . Since E 0 , U :S , E #hm T # :Type, by Lemma E.9 (free variables of a judgement come from the environment), we have X / # fv T # , so we have E 0 , Z :K , z :{X Z}T , #E #hm {X Z}T == #T # . By Lemma E.6 (environments have to be ok) and (eT.var), we have E 0 , Z :K , z :{X Z}T , #E #hm z :{X Z}T . By (eT.eq), we get E 0 , Z :K , z :{X Z}T , #E #hm z :#T # as desired. Case J = U :S # for some S # : We have E 0 , U :S , E #hm U :S # . Subcase (US.var): We have E 0 , U :S , E #hm U :S (thanks to Lemma E.8 (ok environments have no repetition in the domain)). The premise is E 0 , U :S , E #hm ok. By induction, we have E 0 , Z :K , z :{X Z}T , #E #hm ok. By (TK.var) E 0 , Z :K , z :{X Z}T , #E #hm Z :K . By Lemma F.7 (relating typeiskind and subkinding), we have E 0 , Z :K , z :{X Z}T , #E #hm Eq(Z ) <: K . By Lemma E.6 (environments have to be ok), Lemma E.7 (prefixes of ok environments are ok), reversing (envok.U) and Lemma F.21 (things have to be ok), we have nil #hm S ok. By reversing (Sok), we get X :K #hm T :Type. By Lemma E.6 (environments have to be ok), we have X :K #hm ok. By reversing (envok.X) and Lemma F.3 (weakening), we have X :K #hm K ok, so by (envok.X), we get E 0 , Z :K , X :K #hm ok. By Lemma F.3 (weakening), we have E 0 , Z :K , X :K #hm T :Type. By Lemma E.7 (prefixes of ok environments are ok), and by (TK.var) and Lemma F.7 (relating typeiskind and subkinding) as before, we have E 0 , Z :K #hm Eq(Z ) <: K . By Lemma F.22 (weakening kind in the environment), we have E 0 , Z :K , X :Eq(Z ) #hm T :Type. By (TK.var), Lemma F.21 (things have to be ok), Lemma F.6 (kinds are smaller than Type) and (TK.sub), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :Type. Thus, by (Kok.Eq) and (envok.X), we have E 0 , Z :K , z :{X Z}T , #E , X :Eq(Z ) #hm ok. By Lemma F.3 (weakening), we have E 0 , Z :K , z :{X Z}T , #E , X :Eq(Z ) #hm T :Type. By (Teq.refl), we get E 0 , Z :K , z :{X Z}T , #E , X :Eq(Z ) #hm T == T . By (Ssub.struct), we get E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: [X :K , T ]. By Lemma E.9 (free variables of a judgement come from the environment), U / # fv S . So we have E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: #S as desired. Subcase (US.sub): We have E 0 , U :S , E #hm U :S # , and there exists S ## such that the premises are E 0 , U :S , E #hm U :S ## and E 0 , U :S , E #hm S ## <: S # . By induction over each premise, we get E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: #S ## and E 0 , Z :K , z :{X Z}T , #E #hm #S ## <: #S # . By (Ssub.tran), we get E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: #S # as desired. INRIA Global abstractionsafe marshalling with hash types 51 Subcase (US.self): We have E 0 , U :S , E #hm U :[X :Eq(U .TYPE), T # ]. There exists K # such that the premise is E 0 , U :S , E #hm U :[X :K # , T # ]. By induction, we get E 0 , Z :K , z :{X #Z}T , #E #hm [X :Eq(Z ), T ] <: #[X :K # , T # ]. By Lemma F.26 (reversing subsignaturing judgement), we get E 0 , Z :K , z :{X #Z}T , #E , X :Eq(Z ) #hm T == #T # . By Lemma E.6 (environments have to be ok) and (TK.var), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :K . By Lemma F.21 (things have to be ok) and Lemma F.6 (kinds are smaller than Type), we have E 0 , Z :K , z :{X Z}T , #E #hm Z :Type. By (Teq.refl), (Keq.Eq) and (Ksub.refl), we have E 0 , Z :K , z :{X Z}T , #E #hm Eq(Z ) <: Eq(Z ). By (Ssub.struct), we get E 0 , Z :K , z :{X Z}T , #E #hm [X :Eq(Z ), T ] <: [X :Eq(Z ), #T # ] as desired. Every remaining rule is inapplicable because the environment in the conclusion must be empty. Lemma F.30 (type world judgements do not contain free expression variables) If E #hm J is a derivable type world judgement then fse E # fse J does not contain any free expression substitutable entity. Proof. Given Lemma F.21 (things have to be ok), every free substitutable entity in J is free in a type, kind or signature that is correct in E under hm . Thus, by Lemma E.21 (types do not contain free expression variables), no free substitutable entity in J is an expression substitutable entity. Also, by Lemma E.6 (environments have to be ok), E #hm ok, so by Lemma E.22 (environments do not contain free expression variables), E does not have any free substitutable entity. Lemma F.31 (type preservation by module substitution in coloured judgements for type world judgements) Sup pose E 0 , U :[X :K , T ], E #hm J is a derivable type world judgement and J is not of the form U :S # for any S # and Z is fresh and # = {U .TYPE Z}. Then E 0 , Z :K , #E #hm #J . Proof. By Lemma F.29 (type preservation by module substitution in coloured judgements), for Z and z fresh, we have E 0 , Z :K , z :{X Z}T , E # #hm J # where E # = {U .TYPE Z , U .term z}E and J # = {U .TYPE Z , U .term z}J . Given the syntax of environments and type world judgements, z may appear free in E # or J # only inside a hash. Given Lemma E.5 (hashes have to be ok), any hash in E # or J # is ok, and by Lemma E.9 (free variables of a judgement come from the environment), none of these hashes has a free occurence of z . Therefore z / # fv E # and z / # fv J # . Thus we can write the judgement in question as E 0 , Z :K , z :{X Z}T , {U .TYPE Z}E #hm {U .TYPE Z}J . Given that z / # fv {U .TYPE Z}E # fv {U .TYPE Z}J , by Lemma F.15 (strengthening), we get E 0 , Z :K , {U .TYPE Z}E #hm {U .TYPE Z}J . Lemma F.32 (simplified module and type equality substitution for type world judgements) Suppose U :[X :Eq(T ), T # ], E #hm J and # = U .TYPE, or X :Eq(T ), E #hm J and # = X , where both are type world judgements and J is not of the form U # :S # . Then {# T}E #hm {# T}J . Proof. We consider each case. U :[X :Eq(T ), T # ], E #hm J : By Lemma F.31 (type preservation by module substitution in coloured judgements for type world judgements), this case reduces to the other case. X :Eq(T ), E #hm J by some proof #: By Lemma E.6 (environments have to be ok), X :Eq(T ), E #hm ok. By Lemma E.7 (prefixes of ok environments are ok), X :Eq(T ) #hm ok. By (TK.var) and (Teq.Eq), nil #hm T :Eq(T ). By Lemma E.19 (computing the pvu of a type world judgement) applied to #, we have hm 4 min (pvu X (#)). By Lemma F.14 (type preservation by substitution), {X#T}E #hm {X#T}J , as desired. Lemma F.33 (type preservation by fully carried out module substitution) If U :[X :Type, T 1 ], E # . J and J is not U :S for any S and nil # . [T 0 , v . ]:[X :Type, T 1 ] then #E # . #J , where # = {U .TYPE#h, U .term#[v . ] {X#h}T1 h }, where h = hash(N , [T 0 , v . ]:[X :Type, T 1 ]), for any N . Proof. From nil # . [T 0 , v . ]:[X :Type, T 1 ], Lemma F.8 (components of modules are ok), there exists T 2 such that X :Eq(T 0 ) # . T 2 == T 1 and X :Type # . T 1 :Type and nil # . T 0 :Type and nil # . v . :T 2 . Also, by (hmok.hash), we get # h ok. From nil # . v . :T 2 , by Lemma F.2 (colour stripping judgements), we get nil # h v . :T 2 . By Lemma E.6 (environments have to be ok), X :Eq(T 0 ) # . ok, and note that any proof of this finishes with (envok.X) and therefore has an empty pvu RR n 4851 52 Leifer, Peskine, Sewell, Wansbrough for X . By Lemma F.2 (colour stripping judgements), we get X :Eq(T 0 ) # h ok, also by a proof with an empty pvu for X . From nil # h v . :T 2 , by Lemma F.3 (weakening), we get X :Eq(T 0 ) # h v . :T 2 by a proof # such that pvu X (#) = ?. From X :Eq(T 0 ) # . T 2 == T 1 , by Lemma F.2 (colour stripping judgements), we get X :Eq(T 0 ) # h T 2 == T 1 . By Lemma E.19 (computing the pvu of a type world judgement), any proof # # of this judgement is such that pvu X (# # ) # {h}. We can combine # and # # by (eT.eq) to get a proof # ## of X :Eq(T 0 ) # h v . :T 1 , such that h 4 min (pvu X (# ## )). By (Teq.hash) and (TK.Eq), we have nil # h h:Eq(T 0 ). By Lemma F.14 (type preservation by substitution), we get nil # h v . :{X #h}T 1 . Now, by applying Lemma F.29 (type preservation by module substitution in coloured judgements) to U :[X :Type, T 1 ], E # . J and performing alphaconversion, we have X :Type, x :T 1 , #U E # . #U J , where #U = {U .TYPE X , U .term x}. From the latter judgement and nil # . h:Type, apply Lemma F.14 (type preservation by substitution). We get x :{X h}T 1 , {X h}#U E # . {X h}#U J . Finally, we can apply Lemma F.23 (type preservation by guarded expression variable substitution) to the latter judgement combined with nil # h v . :{X h}T 1 . We obtain {x [v . ] {X h}T1 h }{X h}#U E # . {x [v . ] {X h}T1 h }{X h}#U J , i.e. #E # . #J , as desired. G Type decomposition and type preservation for reduction Lemma G.1 (shortening typing proof) If E #hm e:T then there exists T # such that E #hm e:T # by a subproof that does not have (eT.eq) as the last rule used and E #hm T # == T . Proof. Induct on the structure of the derivation # of E #hm e:T . If the proof E #hm e:T does not have an instance of (eT.eq) as the last step, then we have the desired result, given that E #hm T :Type by Lemma F.21 (things have to be ok), whereupon we can apply (Teq.refl). Otherwise there is T # such that E #hm e:T is derived from E #hm e:T # and E #hm T # == T . By applying induction to the (proper) subproof # # leading to E #hm e:T # , we get that there is T ## such that E #hm e:T ## by a subproof of # # that does not have (eT.eq) as the last step used, and E #hm T ## == T # . By (Teq.tran), we have E #hm T ## == T , which completes our proof obligation. Lemma G.2 (reversing typing proof through a context) If nil # hm # CC hm # hm .e:T # then there exists T such that nil #hm e:T . If furthermore nil #hm e 1 :T then nil # hm # CC hm # hm .e 1 :T # . Proof. Induct on the structure of CC hm # hm . By Lemma G.1 (shortening typing proof), there exists T 0 such that nil # hm # T 0 == T # , and nil # hm # CC hm # hm .e:T 0 by a proof # that does not end with (eT.eq). Case CC hm # hm = : Trivial. Case CC hm # hm = C hm # hm 1 .CC hm 1 hm : Then # ends with an application of (eT.tuple), (eT.proj), (eT.ap), (eT.mar), (eT.marred), (eT.unmar), (eT.send) or (eT.col) (depending on C hm # hm 1 ). In any case, one premise is nil #hm 1 CC hm 1 hm .e:T 1 for some T 1 . By induction we get nil #hm e:T for some T . If furthermore nil #hm e 1 :T , then we have nil #hm 1 CC hm 1 hm .e 1 :T 1 by induction. Each of the (eT.*) rules considered above is linear with respect to the expression metavariable instantiated by CC hm 1 hm .e, with exactly one occurence above the line and one below. Instantiating this metavariable by CC hm 1 hm .e 1 yields another instance of the rule. By replacing in # the derivation leading to nil #hm 1 CC hm 1 hm .e:T 1 by that leading to nil #hm 1 CC hm 1 hm .e 1 :T 1 , we get a derivation of nil # hm # CC hm # hm .e 1 :T 0 . By (eT.eq), since nil # hm # T 0 == T # , we have nil # hm # CC hm # hm .e 1 :T # as desired. Lemma G.3 (transitivity of kind equivalence) If E #hm K == K # and E #hm K # == K ## then E #hm K == K ## . Proof. Both hypotheses have to be derived by the same rule. Case (Keq.Type): Trivial. Case (Keq.Eq): Trivial by (Teq.tran) and (Keq.Eq). INRIA Global abstractionsafe marshalling with hash types 53 Lemma G.4 (discreteness of subkinding below Type) If E #hm K <: Eq(T # ) then E #hm K == Eq(T # ) by a subproof. Proof. Induct on the derivation of E #hm K <: Eq(T # ). If the last rule in the proof is (Ksub.tran), then the result holds by induction and Lemma G.3 (transitivity of kind equivalence). Otherwise the last rule is (Ksub.refl) and the premise is the desired result. Definition G.5 (bare bones environment) A bare bones environment is one that contains only bindings of the form X :Type. Definition G.6 (purely abstract environment) A purely abstract environment is one that contains only bindings of the form U :[X :Type, T ] or X :Type. Lemma G.7 (signature rewriting in a type world judgement) If E 0 , U :[X :K , T ], E 1 #hm J is a derivable type world judgement and J is not of the form U :S # for any S # and E 0 #hm [X :K , T # ] ok then E 0 , U :[X :K , T # ], E 1 #hm J . Proof. By (envok.U), E 0 , U # :[X :K , T # ] #hm ok where U # is fresh. By Lemma F.5 (combined weakening), E 0 , U # :[X :K , T # ], U :[X :K , T ], E 1 #hm J . By Lemma F.31 (type preservation by module substitution in coloured judgements for type world judgements), for Z fresh, we have E 0 , U # :[X :K , T # ], Z :K , {U .TYPE Z}E 1 #hm {U .TYPE Z}J . By (US.var) and (TK.mod), from E 0 , U # :[X :K , T # ] #hm ok, we get E 0 , U # :[X :K , T # ] #hm U # .TYPE:K . By Lemma E.19 (computing the pvu of a type world judgement) and Lemma F.14 (type preservation by substitution), we get E 0 , U # :[X :K , T # ], {Z U # .TYPE}{U .TYPE Z}E 1 #hm {Z U # .TYPE}{U .TYPE Z}J . Since Z is fresh, we have E 0 , U # :[X :K , T # ], {U .TYPE U # .TYPE}E 1 #hm {U .TYPE U # .TYPE}J . By Lemma F.30 (type world judgements do not contain free expression variables), U .term / # fse E 1 # fse J . Furthermore, J is not of the form U :S # for any S # , so U only appears in J as U .TYPE. Hence we have E 0 , U # :[X :K , T # ], {U U # }E 1 #hm {U U # }J . By alphaconversion, we get E 0 , U :[X :K , T # ], E 1 #hm J as desired. Lemma G.8 (type substitution in a purely abstract environment) If E 0 , E 1 #hm J is a derivable type world judgement and J is not of the form U :S for any U , S and E 0 , E 2 #hm ok and E 1 and E 2 are purely abstract and E 1 and E 2 bind the same variables in the same order then E 0 , E 2 #hm J . Proof. Induct on the length of E 1 . Case E 1 = nil: Trivial. Case E 1 = X :Type, E # 1 and E 2 = X :Type, E # 2 : Trivial by induction (incorporating X :Type at the tail of E 0 ). Case E 1 = U :[X :Type, T 1 ], E # 1 and E 2 = U :[X :Type, T 2 ], E # 2 : By Lemma E.6 (environments have to be ok) and Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.U), we get E 0 #hm [X :Type, T 2 ] ok. Then, by Lemma G.7 (signature rewriting in a type world judgement), we have E 0 , U :[X :Type, T 2 ], E # 1 #hm J . By induction (incorporating U :[X :Type, T 2 ] at the tail of E 0 ), we get E 0 , U :[X :Type, T 2 ], E # 2 #hm J as desired. Lemma G.9 (equality kinding in an uncontributing environment) If E #hm T :Eq(T # ) and E is a bare bones envi ronment then E #hm T == T # by a strictly smaller proof. Note that if E was allowed to contain module bindings, we might not be able to obtain a strictly smaller proof. For example, take T = T # = U .TYPE, with a proof whose last steps are (US.var), (US.self) and lastly (TK.mod). To prove that E #hm U .TYPE == U .TYPE, we can't do any better than starting at E #hm ok and using (US.var), (TK.mod) and (Teq.refl). This gives an equal size proof, not a strictly smaller proof. Proof. Induct on the structure of the proof. RR n 4851 54 Leifer, Peskine, Sewell, Wansbrough Case (TK.sub): The premises are nil #hm T :K and nil #hm K <: Eq(T # ). By Lemma G.4 (discreteness of sub kinding below Type), nil #hm K == Eq(T # ) by a subproof, whence by reversing (Keq.Eq) there exists T ## such that K = Eq(T ## ) and nil #hm T ## == T # , the latter being derived by a proper subproof of the original proof. By induction on nil #hm T :Eq(T ## ), we get nil #hm T == T ## by a smaller proof. By (Teq.tran), we get nil #hm T == T # , by a proof that is at least one step smaller than the original proof. Case (TK.Eq): Trivial. Case (TK.var): Impossible since E only contains type variable bindings with the kind Type. Case (TK.mod): Impossible by Lemma E.9 (free variables of a judgement come from the environment), since E contains no module variable binding. Lemma G.10 (equivalence of small types in an uncontributing environment) If E # h T 0 == T 1 and E is a bare bones environment and h = hash(N , [T , v ]:S ) and T is not a subterm of T 0 or T is not a subterm of T 1 then T 0 = T 1 . Proof. Induct on the derivation of E # h T 0 == T 1 . Case (Teq.Eq): Then by Lemma G.9 (equality kinding in an uncontributing environment), E # h T 0 == T 1 by a strictly smaller proof, so we get the desired result by induction. Case (Teq.hash): We have h = T 0 and T = T 1 . T is a subterm of h , so T is a subterm of both T 0 and T 1 , which is impossible. Case (Teq.refl): Trivial. Case (Teq.sym): Trivial by induction. Case (Teq.tran): There exists T 2 such that the premises are E # h T 0 == T 2 and E # h T 2 == T 1 . Apply induction to the premise containing T i , T i being the one of T 0 and T 1 that does not contain T : we get T 2 = T i . Then apply induction to the other premise: we get T 1-i = T 2 , hence T 0 = T 1 as desired. Case (Teq.cong.fun): Then there are T # 0 , T ## 0 , T # 1 and T ## 1 such that T i = T # i #T ## i for i = 0, 1. The premises are E # h T # 0 == T # 1 and E # h T ## 0 == T ## 1 . Suppose that T is not a subterm of T i (i = 0 or i = 1). Then T is not a subterm of T # i nor of T ## i , so by induction we get that T # 0 = T # 1 and T ## 0 = T ## 1 , hence T 0 = T 1 as desired. Case (Teq.cong.tuple): Similar to the (Teq.cong.fun) case. Lemma G.11 (type decomposition) Let E be a purely abstract environment. 1. If E #hm T 0 == TC (T 1 , ..., T j ) or E #hm TC (T 1 , ..., T j ) == T 0 then there exist T # 1 , ..., T # j such that E #hm T # i == T i for 1 6 i 6 j and either T 0 = TC (T # 1 , ..., T # j ) or there exist v and T ## and N such that T 0 = hm = hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ## ]). 2. If E #hm T 0 == h 1 or E #hm h 1 == T 0 then one of the following cases holds: (a) There are T # and v and N such that hm = h 1 = hash(N , [T 0 , v ]:[X :Type, T # ]). (b) There are T # and v and N such that T 0 = hm = hash(N , [h 1 , v ]:[X :Type, T # ]). (c) T 0 = h 1 . Proof. Let us first prove the case when E is a bare bones environment. We induct on the size of the derivation of the hypothesis. We write the hypothesis as E #hm T 0 == T or E #hm T == T 0 . Consider the last step of the proof of the hypothesis. Case (Teq.Eq): By Lemma G.9 (equality kinding in an uncontributing environment), E #hm T 0 == T (or the converse) by a strictly smaller proof, so we can apply induction. INRIA Global abstractionsafe marshalling with hash types 55 Case (Teq.hash): One of the following cases holds: Case E #hm T 0 == TC (T 1 , ..., T j ) and T 0 = hm: We almost have the second alternative of the conclusion of the lemma (with T # i = T i ); all that remains to be proved is E #hm T i == T i . Write hm = hash(N , [TC (T 1 , ..., T j ), v ]:S ). The premise of (Teq.hash) is E #hm ok. By Lemma E.4 (colours have to be ok) and reversing (hmok.hash) and Lemma F.8 (components of modules are ok), we get E # . TC (T 1 , ..., T j ):Type. By Lemma F.9 (types are ok provided their hashes are) applied to TC (T 1 , ..., T j ) then to each T i , we get that E # . T i :Type, whence E #hm T i == T i by Lemma F.2 (colour stripping judgements) and (Teq.refl). This completes the proof of this subcase. Case E #hm TC (T 1 , ..., T j ) == T 0 and TC (T 1 , ..., T j ) = hm: Contradictory. Case E #hm T 0 == h 1 and T 0 = hm: Also hm = hash(N , [h 1 , v ]:S ) for some v and S and N . We have case 2b of the conclusion of the lemma. Case E #hm h 1 == T 0 and h 1 = hm: Also hm = hash(N , [T 0 , v ]:S ) for some v and S and N . We have case 2a of the conclusion of the lemma. Case (Teq.refl): Then T = T 0 . This completes the proof for statements 2 and 1 if j = 0. If j #= 0, take T # i = T i . By Lemma F.21 (things have to be ok), E #hm T 0 :Type, i.e. E #hm TC (T # 1 , ..., T # j ):Type. By Lemma F.9 (types are ok provided their hashes are) applied to T 0 and then to each T # i , we get E #hm T # i :Type, whence E #hm T # i == T # i by (Teq.refl). Case (Teq.sym): Trivial by induction. Case (Teq.tran): There exists T ## such that the premises are E #hm T 0 == T ## and E #hm T ## == T , or they are E #hm T ## == T 0 and E #hm T == T ## . We call # # the subproof leading to the premise referring to T 0 . We apply induction to the subproof leading to E #hm T ## == T or its converse. One of the following cases holds: Case same constructor in 1: There exist T ## 1 , ..., T ## j such that E #hm T ## i == T i (or the converse) for 1 6 i 6 j and T ## = TC (T ## 1 , ..., T ## j ). Apply induction to # # . There exist T # 1 , ..., T # j such that E #hm T ## i == T # i (or the converse) for 1 6 i 6 j ; by (Teq.tran), we get E #hm T # i == T i (or the converse) for 1 6 i 6 j ; and one of the cases of the lemma holds. Case use of hash in 1: There exist T # 1 , ..., T # j , v , T ### and N such that E #hm T # i == T i (or the converse) for 1 6 i 6 j and T ## = hm = hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ### ]). We have E #hm T 0 == hm (or the converse). By induction, one of the following cases holds: Case hm = hash(N , [T 0 , v # ]:[X :Type, T #### ]): Then hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ### ]) = hm = hash(N , [T 0 , v # ]:[X :Type, T #### ]), therefore T 0 = TC (T # 1 , ..., T # j ), and we have the desired result. Case T 0 = hm: Then we have the desired result. Case use of hash (2a) in 2: Then hm = h 1 and they are implemented by T ## . Write hm = h 1 = hash(N 1 , [T ## , v ]:[X :Type, T ### ]). One of the following cases holds, We do case analysis on the structure of T ## . Case T ## = h 2 for some h 2 : Apply induction to # # . One of the following cases holds: Case hm = h 2 = hash(N 2 , [T 0 , v # ]:[X :Type, ]): Since hm = hash(N 1 , [T ## , v ]:[X :Type, T ### ]), we have T 0 = T ## = h 2 which is impossible. Case T 0 = hm = hash(N 2 , [h 2 , v # ]:[X :Type, ]): We have T 0 = h 1 , i.e. we have alternative 2c of the conclusion of the lemma. Case T 0 = h 2 : Then T ## = T 0 , and we have alternative 2a of the conclusion of the lemma. Case T ## is of the form TC ## (T ## 1 , ..., T ## j ## ): Apply induction to # # (using part 1 of the lemma). There exist T # 1 , ..., T # j ## such that E #hm T # i == T ## i for 1 6 i 6 j ## and one of the following cases holds: Case T 0 = TC ## (T # 1 , ..., T # j ## ): For any i , T ## i is a proper subterm of T ## which is a subterm of hm , therefore hm is not a subterm of T ## i , hence by Lemma G.10 (equivalence of small types in an uncontributing environment) T # i = T ## i . Thus T 0 = T ## , and we have the first alternative of the conclusion of the lemma. RR n 4851 56 Leifer, Peskine, Sewell, Wansbrough Case use of hash: Then T 0 = hm = h 1 and we have the second alternative of the conclusion of the lemma. Case T ## = hm = hash(N , [h 1 , v ]:S ) (2b) in 2: Apply induction to # # . One of the following cases holds: Case hm = hash(N , [T 0 , v # ]:S # ) (2a): We have T 0 = h 1 , i.e. alternative 2c of the conclusion of the lemma. Case T 0 = hm = hash(N , [hm, v # ]:S # ) (2b): Impossible. Case T 0 = hm (2c): Then T 0 = hm = hash(N , [h 1 , v ]:S ), i.e. we have alternative 2b of the lemma. Case T ## = h 1 (2c) in 2: Apply induction to # # . Case (Teq.cong.fun): We treat the case E #hm T 0 == T , the converse is similar. We must be in part 1 of the lemma, with TC = 1 # 2 , and there exist T # 0 and T ## 0 such that the conclusion of the rule is E #hm T # 1 #T # 2 == T 1 #T 2 and the premises are E #hm T # i == T i for i = 1, 2. This proves the first alternative of the conclusion of the lemma. Case (Teq.cong.tuple): Similar to the (Teq.cong.fun) case. We now address the possibility of E containing module bindings. We give the proof for the case when E #hm T 0 == T ; a symmetric proof applies to the symmetric case. We induct on the number of module bindings in E . The base case (no module binding) has already been proved. Consider now the case E = E 0 , U :[X :Type, T # ], E 1 where E 1 contains only type variable bindings. Let Z and z be fresh variables. By applying Lemma F.29 (type preservation by module substitution in coloured judge ments), given that U is not free in E 1 , we get E 0 , Z :Type, z :{X Z}T # , E 1 #hm {U .TYPE Z , U .term z}T 0 == {U .TYPE Z , U .term z }T . By Lemma E.21 (types do not contain free expression variables), z / # fv T 0 #fv T . Hence, by Lemma F.15 (strengthening), E 0 , Z :Type, E 1 #hm {U .TYPE Z}T 0 == {U .TYPE Z}T . Apply induction to that last judgement. Case proving 1: We get T # 1 , ..., T # j such that E 0 , Z :Type, E 1 #hm T # i == {U .TYPE Z}T i for all i . By Lemma E.6 (environments have to be ok) and Lemma E.7 (prefixes of ok environments are ok), we have E 0 , U :[X :Type, T # ] #hm ok. By Lemma F.5 (combined weakening), given that U / # fv E 0 # fv E 1 by Lemma E.8 (ok environments have no repetition in the domain), we have E 0 , U :[X :Type, T # ], Z :Type, E 1 #hm T # i == T i . Let # be a proof of this last judgement. By Lemma E.19 (computing the pvu of a type world judgement), we have hm 4 min pvu # (#)  . Since E 0 , U :[X :Type, T # ] #hm ok, by (TK.mod), we have E 0 , U :[X :Type, T # ] #hm U .TYPE:Type. Then by Lemma F.14 (type preservation by substitution), we have E #hm {Z#U .TYPE}T # i == T i (note that {Z#U .TYPE}{U .TYPE#Z}T i = T i since Z / # fv T i ). Furthermore, by the same induction, we get one of the following properties: {Z#U .TYPE}T 0 = TC (T # 1 , ..., T # j ) or {Z#U .TYPE}T 0 = hm = hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ## ]). Since Z was chosen fresh, {U .TYPE#Z} is injective. Therefore we have T 0 = TC ({Z#U .TYPE}T # 1 , ..., {Z#U .TYPE}T # j ) or T 0 = hm = hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ## ]) (using Lemma E.4 (colours have to be ok) and Lemma E.9 (free variables of a judgement come from the environment)). Case proving 2: Here T = h 1 . One of the following properties holds: hm = {U .TYPE#Z}h 1 = hash(N , [{U .TYPE#Z}T 0 , v ]:[X :Type, T # ]) or {U .TYPE#Z}T 0 = hm = hash(N , [{U .TYPE#Z}h 1 , v ]:[X :Type, T # ]) or {U .TYPE#Z}T 0 = {U .TYPE#Z}h 1 . In any case, given Lemma E.5 (hashes have to be ok) and Lemma E.9 (free variables of a judgement come from the environment), fv h 1 = ? and fv T 0 = ? (since fv {U .TYPE#Z}T 0 = ?). So in the properties above, {U .TYPE#Z} is the identity, and we have the desired disjunction. Lemma G.12 (decomposition of type equivalence) If nil #hm TC (T 1 , ..., T j ) == TC (T 1 , ..., T # j ) then nil #hm T i == T # i for 1 6 i 6 j . Proof. Trivial consequence of part 1 of Lemma G.11 (type decomposition). Lemma G.13 (structural dependence of values on their types) Suppose nil #hm v hm :T 0 and nil #hm T 0 == TC (T 1 , ..., T j ). Consider the possible forms of TC . INRIA Global abstractionsafe marshalling with hash types 57 1. If TC = 1 # 2 , i.e. we have nil #hm T 0 == T 1 #T 2 , then there exists e and T # 1 such that v hm = #x :T # 1 .e and nil #hm T # 1 == T 1 . 2. If TC = 1 # ... # j , i.e. we have nil #hm T 0 == T 1 # ... # T j then there exists v hm 1 , ..., v hm j such that v hm = (v hm 1 , ..., v hm j ). 3. If TC = (), i.e. we have nil #hm T 0 == UNIT then v hm = (). 4. If TC = STRING, i.e. we have nil #hm T 0 == STRING then there exist v . and T such that nil # . T :Type and nil # . v . :T and v hm = marshalled (v . :T ). Proof. Induct on the size of the derivation of nil #hm v hm :T 0 . Consider the last step of the derivation of nil #hm v hm :T 0 . The rules not mentionned here cannot have been used because v hm is a value. Case (eT.eq): There is T # 0 such that nil #hm T # 0 == T 0 , and nil #hm v hm :T # 0 by a smaller proof compared with nil #hm v hm :T 0 . Using (Teq.tran), we get nil #hm T # 0 == TC (T 1 , ..., T j ). By induction we get the desired result. Case (eT.fun): There exist e, T # 1 and T # 2 such that v hm = #x :T # 1 .e and T 0 = T # 1 #T # 2 . By Lemma G.11 (type decom position), we are proving case 1, and we have nil #hm T 1 == T # 1 . Case (eT.unit): Then v hm = () and T 0 = UNIT. By Lemma G.11 (type decomposition), we are proving case 3. Case (eT.tuple): There exist T # 1 , ..., T # j , v hm 1 , ..., v hm j such that v hm = (v hm 1 , ..., v hm j ) and T 0 = T # 1 # ... # T # 2 . By Lemma G.11 (type decomposition), we are proving case 2. Case (eT.marred): There exist v . and T such that v hm = (marshalled v . :T ) and T 0 = STRING. By Lemma G.11 (type decomposition), we are proving case 4. One premise of (eT.marred) is nil # . v . :T . By Lemma F.21 (things have to be ok), we have nil # . T :Type. Case (eT.col): Given that v hm is a value that is a bracket expression, T 0 can be but a hash h 0 . By Lemma G.11 (type decomposition), this can only happen if there are T # 1 , ..., T # j and v hm such that hm = h and T 0 = h , with h = hash(N , [TC (T # 1 , ..., T # j ), v hm ]:[X :Type, X ]). However T 0 = hm is impossible as per the definition of values. Lemma G.14 (triviality of type equivalence in a trivial environment) If nil # . T == T # then T = T # . Proof. Induct on the structure of T . Case T is of the form TC (T 1 , ..., T j ): By Lemma G.11 (type decomposition) and the trivial colour of the hypothesis, there exist T # 1 , ..., T # j such that nil # . T i == T # i for 1 6 i 6 j and T # = TC (T # 1 , ..., T # j ). (We're possibly making use of (Teq.sym) here.) By induction on nil # . T i == T # i for 1 6 i 6 j we have T i = T # i hence T = T # as desired. Case T is of the form h: By Lemma G.11 (type decomposition) and the trivial colour of the hypothesis, T = T # . Case T is of the form U .TYPE or X : Impossible by Lemma E.9 (free variables of a judgement come from the environ ment) Theorem G.15 (type preservation for expression reduction) If nil #hm e:T and e -#hm e # then nil #hm e # :T . Proof. Note that by Lemma G.1 (shortening typing proof), there exists T # such that nil #hm T # == T and nil #hm e:T # by a proof that does not end in (eT.eq). In the discussion below, we will often make use of the fact that apart from (eT.eq), typing of expressions is syntaxdirected. Also, note that by Lemma F.21 (things have to be ok), we have nil #hm T # :Type, and by Lemma E.6 (environments have to be ok), we have nil #hm ok. We induct on the derivation of the reduction. RR n 4851 58 Leifer, Peskine, Sewell, Wansbrough Case (ered.proj): There exist v hm 1 , ..., v hm j and i such that e = proj i (v hm 1 , ..., v hm j ). Then nil #hm e:T # must have been derived by (eT.proj), and the i th premise is nil #hm v hm i :T # , i.e. nil #hm e # :T # . Case (ered.ap): There exist e 1 , v hm 2 and T 2 such that e = (#x :T 2 .e 1 ) v hm 2 and e # = {x [v hm 2 ] T2 hm }e 1 . nil #hm e:T # must have been derived by (eT.ap), with the premises nil #hm v hm 2 :T 2 and nil #hm (#x :T 2 .e 1 ):T 2 #T # . By Lemma G.1 (shortening typing proof), there exists T ## such that nil #hm (#x :T 2 .e 1 ):T ## by a proof that does not end in (eT.eq), and nil #hm T ## == T 2 #T # . By reversing (eT.fun), there exists T 1 such that T ## = T 2 #T 1 and x :T 2 #hm e 1 :T 1 . By Lemma G.12 (decomposition of type equivalence), we have nil #hm T 1 == T # . By Lemma F.23 (type preservation by guarded expression variable substitution), we have nil #hm {x [v hm 2 ] T2 hm }e 1 :T 1 . By (eT.eq), we get nil #hm e # :T # . Case (ered.mar): There exist v hm 1 and T 1 such that e = mar (v hm 1 :T 1 ), and e # = marshalled ([v hm 1 ] T1 hm :T 1 ). nil #hm e:T # must have been derived by (eT.mar), with the premise nil #hm v hm 1 :T 1 ; also T # = STRING. By Lemma F.21 (things have to be ok) and Lemma F.10 (colour change preserves type okedness), nil # . T 1 :Type. By (eT.col), we get nil # . [v hm 1 ] T1 hm :T 1 . By Lemma E.6 (environments have to be ok), we have nil #hm ok. By (eT.marred), we get nil #hm e # :STRING. Case (ered.unmar): There exist v hm 1 , T 1 and T 2 such that e = unmar (marshalled (v hm 1 :T 1 )):T 2 . By reversing (eT.unmar), we get nil #hm T 2 :Type and nil #hm marshalled (v hm 1 :T 1 ):STRING; also T # = T 2 . There are two possible outcomes. Case e # is UnmarFailure T # : By Lemma F.21 (things have to be ok), nil #hm T # :Type, hence nil #hm UnmarFailure T # :T # by (eT.Undynfailure). Case e # is the value v hm 1 and T 1 = T # : By Lemma G.1 (shortening typing proof) and reversing (eT.marred), we get nil # . v hm 1 :T 1 . By Lemma F.2 (colour stripping judgements), this implies that nil #hm v hm 1 :T 1 , i.e. nil #hm v hm 1 :T # . Cases (ered.col.*): There exist hm # and e 0 such that e = [e 0 ] T # hm # . By reversing (eT.col), we get nil # hm # e 0 :T # . Note than # hm ok and # hm # ok by Lemma E.4 (colours have to be ok). Case (ered.col.unit): Then T # = UNIT and e 0 = () and e # = (). By (eT.unit), nil #hm e # :UNIT. Case (ered.col.tuple): Then there exist T 1 , ..., T j , e 1 , ..., e j such that T # = T 1 # ... # T j and e 0 = (e 1 , ..., e j ) and e # = ([e 1 ] T1 hm # , ..., [e j ] T j hm # ). Hence T # = T . By Lemma G.1 (shortening typing proof) and reversing (eT.tuple), there exist T # 1 , ..., T # j such that nil # hm # T # 1 # ... # T # j == T # and nil # hm # e i :T # i for 1 6 i 6 j . By Lemma G.12 (decomposition of type equivalence), we have nil # hm # T # i == T i for every i . Hence, by applying (eT.eq) j times, we get nil # hm # e i :T i for every i . By Lemma F.9 (types are ok provided their hashes are) applied to T # then to each T i , we get nil #hm T i :Type for every i , whence by (eT.col) and (eT.tuple): nil #hm e # :T # . Case (ered.col.fun): Then there exist T 0 , T 1 , T 2 and e 1 such that T # = T 2 #T 1 and e 0 = #x :T 0 .e 1 and e # = #x :T 2 .[{x #[x ] T2 hm }e 1 ] T1 hm # . By Lemma G.1 (shortening typing proof) and reversing (eT.fun), there exists T 3 such that x :T 0 # hm # e 1 :T 3 and nil # hm # T 0 #T 3 == T # . By Lemma G.12 (decomposition of type equivalence), we have nil # hm # T 0 == T 2 (possibly using (Teq.sym)) and nil # hm # T 3 == T 1 . By Lemma F.21 (things have to be ok), nil # hm # T 2 :Type. By Lemma F.10 (colour change preserves type okedness), we also have nil # . T 2 :Type and nil #hm T 2 :Type. By (envok.x), we get y :T 2 #hm ok and y :T 2 # . ok and y :T 2 #hm ok. By (eT.var), we get y :T 2 #hm y :T 2 . By Lemma F.21 (things have to be ok), we get nil # hm # T 1 :Type. By Lemma F.10 (colour change preserves type okedness), we have nil #hm T 1 :Type. By Lemma F.3 (weakening), we get y :T 2 #hm T 1 :Type. We have y :T 2 # . ok and y :T 2 #hm y :T 2 and y :T 2 , x :T 0 # hm # e 1 :T 3 . By Lemma F.23 (type preservation by guarded expression variable substitution), we get y :T 2 # hm # {x#[y ] T2 hm }e 1 :T 3 . y :T 2 #hm T 1 :Type y :T 2 # hm # T 3 == T 1 y :T 2 # hm # {x#[y ] T2 hm }e 1 :T 3 y :T 2 # hm # {x#[y ] T2 hm }e 1 :T 1 (eT.eq) y :T 2 #hm [{x#[y ] T2 hm }e 1 ] T1 hm # :T 1 (eT.col) nil #hm #y :T 2 .[{x #[y ] T2 hm }e 1 ] T1 hm # :T 2 #T 1 (eT.fun) INRIA Global abstractionsafe marshalling with hash types 59 By alphaconversion, we get nil #hm e # :T 2 #T 1 . Case (ered.col.marred): Then there exist v . 1 and T 1 such that T # = STRING and e 0 = marshalled (v . 1 :T 1 ) = e # . Since nil # hm # e 0 :T # , by Lemma G.1 (shortening typing proof) and reversing (eT.marred), we have nil # . v . 1 :T 1 . Since nil #hm ok, by (eT.marred), we have nil #hm e # :STRING. Case (ered.col.col): Then there exist h 0 , h 1 and e 1 such that T # = h 0 and hm # = h 1 and e 0 = [e 1 ] h0 h0 . By Lemma G.1 (shortening typing proof) and reversing (eT.col), we get nil # h0 e 1 :h 0 . By Lemma E.5 (hashes have to be ok), we have # h 0 ok, whence nil #hm h 0 :Type by (TK.hash). Then, by (eT.col), we get nil #hm [e 1 ] h0 h0 :T # . Case (ered.col.le): Then hm # 4 hm and e # = e 0 . Since nil # hm # e 0 :T # , i.e. nil # hm # e # :T # , by Lemma F.2 (colour stripping judgements), we have nil # hm # e # :T . Case (ered.cong): There exist hm 0 , C hm hm 0 , e 0 and e # 0 such that e = C hm hm 0 .e 0 and e # = C hm hm 0 .e # 0 and e 0 -#hm 0 e # 0 by a proper subproof. Recall that we have nil #hm e:T # . By the first part of Lemma G.2 (reversing typing proof through a context), there exists T 0 such that nil #hm 0 e 0 :T 0 . By induction, we get nil #hm 0 e # 0 :T 0 . By the second part of Lemma G.2 (reversing typing proof through a context), we get nil #hm e # :T # . In any case, we have nil #hm e # :T # , whence by (eT.eq), nil #hm e # :T . Lemma G.16 (type preservation for network structural congruence) If # n ok and n # n # then # n # ok. Proof. Induct on the derivation of n # n # . Case (nsc.id): We have n = 0 | n # . By reversing (nok.par), we get # n # ok. Case (nsc.commut): There exist n 1 and n 2 such that n = n 1 | n 2 and n # = n 2 | n 1 . By reversing (nok.par) and applying it with the premises swapped, from # n ok, we get # n # ok. Case (nsc.assoc): There exist n 1 , n 2 , n 3 such that n = n 1 | (n 2 | n 3 ) and n # = (n 1 | n 2 ) | n 3 . By reversing (nok.par) twice, from # n ok, we get # n i ok for 1 6 i 6 3, whence by (nok.par) twice # n # ok. Reflexivity, symmetry, transitivity: Trivial (the latter two, by induction). Corollary G.17 (type preservation for network reduction) If # n ok and n -# n # then # n # ok. Proof. Induct on the derivation of the reduction n -# n # . Case (nred.expr): Trivial by Theorem G.15 (type preservation for expression reduction). Case (nred.par): There exist n 0 , n 1 , n 2 such that n = n 0 | n 2 and n # = n 1 | n 2 . The premise is n 0 -# n 1 . By reversing (nok.par), we have # n 0 ok and # n 2 ok. By induction we have # n 1 ok. By (nok.par), we have # n # ok. Case (nred.strcong): There exist n 0 and n 1 such that n # n 0 -# n 1 # n # . By Lemma G.16 (type preservation for network structural congruence), we get # n 0 ok. By induction we get # n 1 ok. By Lemma G.16 (type preservation for network structural congruence), we get # n # ok. Case (nred.comm): There exist CC . hm , v hm , CC . hm # such that n = CC . hm .! v hm | CC . hm # .? and n # = CC . hm .() | CC . hm # .v hm . By reversing (nok.par), then (nok.expr) twice, we get nil # . CC . hm .! v hm :UNIT and nil # . CC . hm # .?:UNIT. From nil # . CC . hm .! v hm , by Lemma G.2 (reversing typing proof through a context), we get nil #hm ! v hm :T # for some T # . By Lemma G.1 (shortening typing proof) and reversing (eT.send), we get nil #hm v hm :mar , and we see that nil #hm ! v hm :UNIT and nil #hm UNIT == T # . By Lemma E.6 (environments have to be ok), (eT.unit) and (eT.eq), we get nil #hm ():T # , so by the second part of Lemma G.2 (reversing typing proof through a context), we get nil # . CC . hm .():UNIT. By Lemma G.13 (structural dependence of values on their types), there exists v . and T 0 such that v hm = marshalled (v . :T 0 ) and nil # . T 0 :Type and nil # . v . :T 0 . From nil # . CC . hm # .?:UNIT, by Lemma G.2 (reversing typing proof through a context), we get nil # hm # ?:T ## for some T ## . By Lemma G.1 (shortening typing proof) and reversing (eT.recv), we get nil # hm # ok and nil # hm # RR n 4851 60 Leifer, Peskine, Sewell, Wansbrough STRING == T ## . By (eT.marred), we get nil # hm # marshalled (v . :T 0 ):STRING, so by the second part of Lemma G.2 (reversing typing proof through a context), we get nil # . CC . hm # .v hm :UNIT. By (mT.expr) twice, then (nok.par), we get # CC . hm .() | CC . hm # .v hm ok as desired. Theorem G.18 (type preservation for machine reduction) If nil # . m:T and m -# c m # then nil # . m # :T . Proof. Consider each rule. Case (mred.Type): We have that m is of the form moduleNU = [T 0 , v . ]:[X :Type, T 1 ] in m 0 and m # is of the form #m 0 where # = {U .TYPE h, U .term [v . ] {X h}T1 h } and h = hash(N , [T 0 , v . ]:[X :Type, T 1 ]). The judgement nil # . m:T can only be derived by (mT.let) with the following premises: nil # . T :Type and nil # . [T 0 , v . ]:[X :Type, T 1 ] and U :[X :Type, T 1 ] # . m 0 :T . By Lemma F.33 (type preservation by fully carried out module substitution), we have nil # . #(m 0 :T ), as desired. Case (mred.Eq): We have that m is of the form Let U = [T 0 , v . ]:[X :Eq(T 3 ), T 1 ] in m 0 and m # is of the form #m 0 where # = {U .TYPE T 3 , U .term v . }. The judgement nil # . m:T can only be derived by (mT.let) with the following premises: nil # . T :Type and nil # . [T 0 , v . ]:[X :Eq(T 3 ), T 1 ] and U :[X :Eq(T 3 ), T 1 ] # . m 0 :T . From nil # . [T 0 , v . ]:[X :Eq(T 3 ), T 1 ], by Lemma F.8 (components of modules are ok), there exists T 2 such that X :Eq(T 0 ) # . T 2 == T 1 and X :Eq(T 3 ) # . T 1 :Type and nil # . T 0 :Eq(T 3 ) and nil # . v . :T 2 . By Lemma F.29 (type preservation by module substitution in coloured judgements) and alphaconversion we have X :Eq(T 3 ), x :T 1 # . #U (m 0 :T ), where #U = {U .TYPE#X , U .term#x}. By Lemma E.6 (environments have to be ok), X :Eq(T 3 ) # . ok, whence by reversing (envok.X) nil # . T 3 :Type, so nil # . T 3 :Eq(T 3 ) by (Teq.refl) and (TK.Eq). Therefore, by Lemma F.14 (type preservation by substitution), we have x :{X #T 3 }T 1 # . {U .TYPE#T 3 , U .term#x}(m 0 :T ). From nil # . v . :T 2 (given that X :Eq(T 0 ) # . ok by Lemma E.6 (environments have to be ok)) by Lemma F.3 (weakening), we get X :Eq(T 0 ) # . v . :T 2 . By (eT.eq), we get X :Eq(T 0 ) # . v . :T 1 . From nil # . T 0 :Eq(T 3 ), by (Teq.Eq), (Teq.sym) and (TK.Eq), we get nil # . T 3 :Eq(T 0 ). By Lemma F.14 (type preservation by substitution), we get nil # . v . :{X #T 3 }T 1 . By Lemma F.14 (type preservation by substitution), we get nil # . {U .TYPE#T 3 , U .term#v . }(m 0 :T ) as desired. H Progress H.1 Classical progress theorems Definition H.1 (waiting for communication) An expression e is waiting for communication iff one of the following cases holds: . e is ready to output, i.e. there exists CC hm # hm and v hm such that e = CC hm # hm .! v hm . e is ready to input, i.e. there exists CC hm # hm such that e = CC hm # hm .? Definition H.2 (dormant) An expression e is dormant iff one of the following cases holds: . e is waiting for communication . e is dead, i.e. there exists CC hm # hm and T such that e = CC hm # hm .UnmarFailure T . Lemma H.3 (dormancy in context) If e is dormant and CC hm # hm is a coloured evaluation context then CC hm # hm .e is dormant. Proof. Composing coloured evaluation contexts yields a coloured evaluation context. INRIA Global abstractionsafe marshalling with hash types 61 Lemma H.4 (reduction in context) If e -#hm and CC hm # hm is an evaluation context then CC hm # hm .e -# hm # . Proof. Apply (ered.cong) as many times as the size of CC hm # hm requires. Definition H.5 (legitimately stuck expressions) An expression e is legitimately stuck in hm iff one of the following cases holds: . e is a hmvalue . e is dormant. Theorem H.6 (progress of expressions) If nil #hm e:T then one of the following cases holds: . e is legitimately stuck in hm . . e can reduce, i.e. there exists e # such that e -#hm e # . Proof. Induct on the type derivation. Consider the rule used in the last step of the proof. Cases (eT.var) and (eT.mod): Impossible by Lemma E.9 (free variables of a judgement come from the environment) since the environment is empty. Case (eT.eq): The inductive hypothesis is the desired result. Case (eT.ap): There exists e 0 , e 1 , T 1 such that e = e 0 e 1 and nil #hm e 0 :T 1 #T and nil #hm e 1 :T 1 . Apply the inductive hypothesis to e 0 . Case e 0 can reduce: there exists e # 0 such that e 0 -#hm e # 0 . By (ered.cong), e -#hm e # 0 e 1 . Case e 0 is ready to output: there exist CC hm hm # and v hm # such that e 0 = CC hm hm # .! v hm # , thus e = ( e 1 ).CC hm hm # .! v hm # is ready to output. Case e 0 is ready to input: similar to the output case. Case e 0 is dead: then e is dead. Case e 0 is a hmvalue: Apply the inductive hypothesis to e 1 . Note that e 0 is an evaluation context. Case e 1 can reduce: there exists e # 1 such that e 1 -#hm e # 1 . By (ered.cong), e -#hm e 0 e # 1 . Case e 1 is ready to output: there exist CC hm hm # and v hm # such that e 1 = CC hm hm # .! v hm # , thus e = (e 0 ).CC hm hm # .! v hm # is ready to output. Case e 1 is ready to input: similar to the output case. Case e 1 is dead: then e is dead. Case e 1 is a hmvalue: By Lemma G.13 (structural dependence of values on their types), there exists e 2 such that e 0 = #x :T 1 .e 2 . Then e = (#x :T 1 .e 2 ) e 1 . By (ered.ap), e -#hm {x [e 1 ] T1 hm }e 2 . Case (eT.fun): e is a value. Case (eT.send): e is ready to output. Case (eT.recv): e is ready ot input. Case (eT.mar): There exist an e 0 and a T 0 such that e = mar (e 0 :T 0 ), and T = STRING. If e 0 is dormant or reduces then the same holds for e by Lemma H.4 (reduction in context) and Lemma H.3 (dormancy in context). Otherwise, by the inductive hypothesis on e 0 , e 0 is a hmvalue, so by (ered.mar), e -#hm marshalled ([e 0 ] T hm :T ). Case (eT.marred): There exist an e 0 and a T 0 such that e = marshalled (e 0 :T 0 ), and T = STRING. If e 0 is dormant or reduces then the same holds for e by Lemma H.4 (reduction in context) and Lemma H.3 (dormancy in context), since marshalled ( :T 0 ) is an evaluation context. Otherwise, by the inductive hypothesis on e 0 , e 0 is a hmvalue, so e is an hmvalue. Case (eT.unmar): There is an e 0 such that e = (unmar e 0 :T ). If e 0 is dormant or reduces then the same holds for e by Lemma H.4 (reduction in context) and Lemma H.3 (dormancy in context). Otherwise, by the inductive hypothesis on e 0 , e 0 is a value. Its type is STRING, so by Lemma G.13 (structural dependence of values on their types), there is a v . and a T 0 such that e 0 = marshalled (v . :T 0 ). Then e reduces by (ered.unmar). RR n 4851 62 Leifer, Peskine, Sewell, Wansbrough Case (eT.Undynfailure): e is dormant. Case (eT.unit): e is a value. Case (eT.tuple): There are e 1 , ..., e j such that e = (e 1 , ..., e j ). Let i be the smallest index k such that e 1 through e k-1 are values. If i = j + 1 then e is a value. Otherwise, apply the inductive hypothesis to e i . Since e i is not a value, it is dormant or reduces, and in either case, the same holds for e by Lemma H.3 (dormancy in context) and Lemma H.4 (reduction in context), as (e 1 , ..., e i-1 , , e i+1 , ..., e j ) is an evaluation context. Case (eT.proj): There is an e # such that e = proj i e # , and there are T 1 , ..., T j such that nil #hm e # :T 1 # ... # T j . If e # is dormant or reduces, then the same holds for e since proj i is an evaluation context. Otherwise, apply the inductive hypothesis to e # : it is a value. By Lemma G.13 (structural dependence of values on their types), there are v hm 1 , ..., v hm j such that e # = (v hm 1 , ..., v hm j ). Then e -#hm v hm i by (ered.proj). Case (eT.col): There is an e 0 and an hm 0 such that e = [e 0 ] T hm 0 . Apply the inductive hypothesis to e 0 ; if e 0 is a value, the discussion depends on its form and that of hm 0 . By Lemma G.1 (shortening typing proof), there is a type T # such that nil #hm 0 e 0 :T # by a smaller proof that does not use (eT.eq) as its last step and nil #hm 0 T # == T . Case e 0 is dormant: e is dormant. Case e 0 reduces: There is an e # 0 such that e 0 -#hm 0 e # 0 . By (ered.cong), e -#hm [e # 0 ] T hm 0 . Case e 0 is an hm 0 value that is not a bracket expression: Then, by reversing the appropriate rule amongst (eT.unit), (eT.tuple), (eT.fun) or (eT.marred), we have that T # is some constructed type TC (T # 1 , ..., T # j ). Since nil #hm 0 T # == T , by Lemma G.11 (type decomposition), one of the following cases holds: Case T is a constructed type: There exist T 1 , ..., T j such that nil #hm T # i == T i for all i and T = TC (T 1 , ..., T j ). Then e reduces by the appropriate rule amongst (ered.col.unit), (ered.col.tuple), (ered.col.fun) or (ered.col.marred). Case T = hm 0 : If hm 0 4 hm then e = [e 0 ] hm 0 hm 0 reduces by (ered.col.le). Otherwise e is a value. Case e 0 = [v h1 ] h1 h1 for some h 1 and hm 0 = .: We have e = [[v h1 ] h1 h1 ] T . . By Lemma G.1 (shortening typing proof) and reversing (eT.col), we have nil # . h 1 == T # . By (Teq.tran), we have nil # . h 1 == T . By Lemma G.14 (triviality of type equivalence in a trivial environment), h 1 = T . Then e -#hm e 0 by (ered.col.le). Case e 0 = [v h1 ] h1 h1 for some h 1 and hm 0 #= .: We have nil #hm 0 [v h1 ] h1 h1 :T . Then nil #hm 0 [v h1 ] h1 h1 :T # must have been obtained by an application of (eT.col), so T # = h 1 . Since nil #hm 0 h 1 == T , by Lemma G.11 (type decomposition) and some more pattern matching, one of the following cases holds: Case T = h 1 #= hm 0 and hm 0 #= hm: Then e = [[v h1 ] h1 h1 ] h1 hm 0 . By (ered.col.col), e -#hm [v h1 ] h1 h1 (recall that hm 0 #= .). Case T = h 1 #= hm 0 and hm 0 = hm: Then e = [[v h1 ] h1 h1 ] h1 hm . By (ered.col.le), e -#hm e 0 . Case hm 0 = h 1 : Then e = [[v h1 ] h1 h1 ] T h1 . By (ered.col.le), [v h1 ] h1 h1 -# h1 v h1 . Hence, by (ered.cong), e -#hm [v h1 ] T h1 . Corollary H.7 (progress of networks) If # n ok then one of the following cases holds: . n is stopped, i.e. there exists n () and n fail such that n # n () | n fail . . n is waiting to input, i.e. there exists n () and n fail and n ? such that n # n () | n fail | n ? . n is waiting to output, i.e. there exists n () and n fail and n ! such that n # n () | n fail | n ! . n can reduce, i.e. there exists n # such that n -# n # INRIA Global abstractionsafe marshalling with hash types 63 where n () ::=0 null n () | n () parallel composition () unit n fail ::=0 null n fail | n fail parallel composition CC . hm .UnmarFailure T dead n ? ::=n ? | n ? parallel composition CC . hm .? waiting to input n ! ::=n ! | n ! parallel composition CC . hm .! v waiting to output Proof. Induct on the derivation of # n ok. Case (nok.zero): Trivial. Case (nok.par): There exist n 0 and n 1 such that n = n 0 | n 1 . If either n 0 or n 1 reduces then n reduces. If n 0 is stopped then n has the same form as n 1 , and vice versa. If n 0 and n 1 are both waiting to input (or both to output) then so is n . Otherwise n 0 # n () | n fail | n ? and n 1 # n () | n fail | n ! (or the converse): then n reduces by (nred.comm). Case (nok.expr): By Theorem H.6 (progress of expressions), one of the following cases holds: Case n is a .value: By (nok.expr), the value n has type UNIT. By Lemma G.13 (structural dependence of values on their types), n is an n () . Case n is dead: n is an n fail . Case n is waiting for input: n is an n ? . Case n is waiting for output: n is an n ! . Case n reduces: n reduces. Theorem H.8 (progress of machines) If nil # . m:T then either m is an expression or it reduces under -# c . Proof. Induct on the type derivation. Case (mT.expr): Trivial. Case (mT.let): It is obvious that m reduces, by either (mred.Type) or (mred.Eq). H.2 Determinism of reduction Theorem H.9 (determinism of machine reduction) Reduction of machines is deterministic, i.e. if m -# c m 1 and m -# c m 2 then m 1 = m 2 and both reductions use the same rule on the same redex. Proof. Induct on the structure of m . Case m is an expression: Impossible (m does not reduce). Case m = moduleNU = M :[X :Type, T ] in m # : The only applicable rule is (mred.Type). RR n 4851 64 Leifer, Peskine, Sewell, Wansbrough Case m = moduleNU = M :[X :Eq(T # ), T ] in m # : The only applicable rule is (mred.Eq). Lemma H.10 (values do not reduce) If e -#hm e # then e is not an hmvalue. Proof. We prove that if e is an hmvalue then e does not reduce in hm . We induct on the structure of values. Case v hm = (): No reduction rule applies. Case v hm = (v hm 1 , ..., v hm j ): The only reduction rule that is not obviously inapplicable is (ered.cong). If that rule applies, then it is with a context of the form (v hm 1 , ..., v hm i-1 , , v hm i+1 , ..., v hm j ). But then v hm i -#hm , which is impossible by induction. Case v hm = (#x :T .e): No reduction rule applies. Case v hm = marshalled (v . :T ): The only reduction rule that is not obviously inapplicable is (ered.cong). If that rule applies, then it is with a context of the form marshalled ( :T ). But then v . -# . , which is impossible by induction. Case v hm = [v h1 ] h1 h1 where h 1 #= hm: The rule (ered.col.le) requires h 1 4 hm , which given the definition of 4 implies that h 1 = hm , a contradiction. The rule (ered.col.col) does not apply as it requires the type and colour annotation on the bracket to be different. The other (ered.col.*) rules do not apply as they require the type annotation on the bracket not to be a hash. If (ered.cong) applies, then it is with a context of the form [ ] h1 h1 . But then v h1 -# h1 , which is impossible by induction. Theorem H.11 (determinism of expression reduction) Reduction of expressions and machines is deterministic, i.e. if e -#hm e # and e -#hm e ## then e # = e ## and both reductions use the same rule on the same redex. Proof. Induct on the structure of e . Cases e = x , e = U .term, e = UnmarFailure T : No reduction is possible. Cases e = (), e = (v hm 1 , ..., v hm j ), e = #x :T .e 0 , e = marshalled (v . :T ): No reduction is possible, by Lemma H.10 (values do not reduce). Case e = (e 1 , ..., e j ): Let i be the smallest k such that e 1 through e k-1 are hmvalues. The case i = j + 1 has already been treated. Given Lemma H.10 (values do not reduce), the only possibility of reduction is (ered.cong) with the context (e 1 , ..., e k-1 , , e k+1 , ..., e j ). By induction, only one reduction is possible. Case e = proj i e 0 : If e 0 is a hmvalue, given Lemma H.10 (values do not reduce), the only possibility of reduction is (ered.proj). Otherwise, by induction, only one reduction of e 0 is possible, and the only possibility for e to reduce is using (ered.cong) with the context proj i . Case e = e 1 e 2 : If e 1 and e 2 are both hmvalues, given Lemma H.10 (values do not reduce), the only possibility of reduction is (ered.ap). If e 1 is an hmvalue and e 2 is not an hmvalue, then the only possibility for reduction is to use (ered.cong) with the context e 1 ; by induction, this yields at most one possible reduction. Similarly, if e 1 is not an hmvalue, then the only possibility of reduction is (ered.cong) with the context e 2 . Case e = mar (e 0 :T ): If e 0 is an hmvalue, then e 0 does not reduce by Lemma H.10 (values do not reduce), so (ered.mar) is the only possibility of reduction. Otherwise the only possibility of reduction is (ered.cong) with the context mar ( :T ), so by induction, only one reduction is possible. Case e = marshalled (e 0 :T ): The only possibility of reduction is (ered.cong) with the context marshalled ( :T ), so by induction, only one reduction is possible. Case e = unmar e 0 :T : The only possibility of reduction is (ered.unmar), which has only one possible outcome for any given e 0 and T . Cases e = ! e 0 , e = ?: No reduction is possible (communication happens at the network level). INRIA Global abstractionsafe marshalling with hash types 65 Case e = [v hm 1 ] T hm 1 where T is not a hash: The rules (ered.col.col) and (ered.col.le) do not apply since T is not a hash. Also (ered.cong) does not apply since v hm 1 is a value and we have Lemma H.10 (values do not reduce). The only rules that may apply are the rules to push brackets in ((ered.col.unit), (ered.col.tuple), (ered.col.fun), (ered.col.marred)) which are mutually exclusive. Case e = [v hm 1 ] h2 hm 1 : By Lemma H.10 (values do not reduce), no reduction can be derived from (ered.cong) since v hm 1 is a value. The only potentially applicable rules are (ered.col.col) and (ered.col.le). If h 2 = hm then (ered.col.col) does not apply. Otherwise we do not have h 2 4 hm so (ered.col.le) does not apply. In both cases, only one reduction is possible. Case e = [e 1 ] T hm 1 where e 1 is not a value: Then the only possibility of reduction is (ered.cong). Note that (ered.col.col) is out since its side conditions would force e 1 to be a value. By induction there is at most one way to reduce e 1 , so there is at most one way to reduce e . Discussion H.12 (strength of determinism) Recall the rule to remove a bracket around another bracket: [[v h0 ] h0 h0 ] h0 h1 -#hm [v h0 ] h0 h0 if h 0 #= h 1 and h 1 #= hm (ered.col.col) These side conditions do not in fact affect the reductions possible: we have [[v h0 ] h0 h0 ] h0 h0 -#hm [v h0 ] h0 h0 by (ered.col.le) and (ered.cong), and [[v h0 ] h0 h0 ] h0 h1 -# h1 [v h0 ] h0 h0 by (ered.col.le). We include the side conditions to make our determinism result, Theorem H.11 (determinism of expression reduction), stronger. Without these side conditions, we could state determinism in the following way: if e -#hm e 1 and e -#hm e 2 then e 1 = e 2 . Of course, network reduction is not deterministic, as befits a concurrent system. I Compilation I.1 Decidability of type checking Definition I.1 (revelation of the implementation of a hash) Let h = hash(N , [T 0 , v ]:[X :Type, T 1 ]). The type reveal h T is obtained by replacing any subterm of T that is equal to h and not itself inside a hash by the implementation type T 0 . Thus: . reveal h UNIT = UNIT . reveal h (T 1 # ... # T j ) = (reveal h T 1 ) # ... # (reveal h T j ) . reveal h (T 1 #T 2 ) = (reveal h T 1 )#(reveal h T 2 ) . reveal h X = X . reveal h STRING = STRING . reveal h U .TYPE = U .TYPE . reveal h h = T 0 . reveal h h # = h # if h # #= h Also let reveal . T = T . Definition I.2 (partial type substitution associated to an environment) partenvsub nil = id partenvsub E ,x :T = partenvsub E partenvsub E ,X :Type = partenvsub E partenvsub E ,X :Eq(T) = partenvsub E {X T} partenvsub E ,U :[X :Type,T # ] = partenvsub E partenvsub E ,U :[X :Eq(T),T # ] = partenvsub E {U .TYPE T} RR n 4851 66 Leifer, Peskine, Sewell, Wansbrough Recall Definition G.6 (purely abstract environment). Lemma I.3 (a purely abstract suffix does not change the substitution) If E 1 is a purely abstract environment then partenvsub E0 ,E1 = partenvsub E0 . Proof. Trivial from Definition I.2 (partial type substitution associated to an environment). Lemma I.4 (stability of types through revelation) If E #hm T :Type then E #hm reveal hm T :Type and E #hm T == reveal hm T and E #hm partenvsub E T :Type and E #hm T == partenvsub E T . A trivial consequence that we also use is that if E #hm T :Type then E #hm reveal hm partenvsub E T :Type and E #hm T == reveal hm partenvsub E T . Also, we freely use (Teq.sym) on the conclusion of this lemma. Proof. Let h = hash(N , [T 0 , v . ]:[X :Type, T 1 ]). The type reveal hm T can be seen as T with T 0 for hm . By similar reasoning to the proof of Lemma F.24 (type equivalence is a congruence), starting from E #hm h == T 0 obtained through (Teq.hash), we get E #hm T == reveal hm T . By applying Lemma E.6 (environments have to be ok), Lemma E.7 (prefixes of ok environments are ok) and Lemma F.24 (type equivalence is a congruence) to each concrete binding in E , and using various rules, we get that E #hm T == partenvsub E T . By Lemma F.21 (things have to be ok), we have E #hm partenvsub E T :Type. Lemma I.5 (distinction of fresh type variables) If E is purely abstract and E #hm X == T or E #hm T == X or E #hm X :Eq(T ) or E #hm T :Eq(X ) or E #hm [Y :Eq(T ), T # ] <: [Y :Eq(X ), T ## ] or E #hm [Y :Eq(X ), T ## ] <: [Y :Eq(T ), T # ] then T = X . Also, if E is purely abstract, then E #hm U :[Y :Eq(X ), T # ] cannot be derived. Proof. Induct on the derivation of the assumed judgement. Case (TK.sub), E #hm X :Eq(T ): There exists K such that the premises are E #hm X :K and E #hm K <: Eq(T ). By Lemma G.4 (discreteness of subkinding below Type) and reversing (Keq.Eq), there exists T 1 such that K = Eq(T 1 ) and E #hm T 1 == T by a subproof. By induction on the subproof of E #hm X :Eq(T 1 ), we get T 1 = X . Since E #hm X == T by a subproof, by induction, we get T = X . Case (TK.sub), E #hm T :Eq(X ): There exists K such that the premises are E #hm T :K and E #hm K <: Eq(X ). By Lemma G.4 (discreteness of subkinding below Type) and reversing (Keq.Eq), there exists T 1 such that K = Eq(T 1 ) and E #hm T 1 == X by a subproof. By induction, we get T 1 = X . Since E #hm T :Eq(X ) by a subproof, by induction, we get T = X . Case (TK.Eq): Trivial by induction. Case (TK.mod): There exists U and T # such that the rule derives E #hm U .TYPE:Eq(X ) from E #hm U :[Y :Eq(X ), T # ]. By induction this is impossible. Case (TK.var): Impossible since E is purely abstract. Case (Teq.Eq): Trivial by induction. Case (Teq.hash): Impossible by Lemma E.4 (colours have to be ok) and Lemma E.9 (free variables of a judgement come from the environment) applied to # hm ok. Case (Teq.refl): Then T = X as desired. Case (Teq.sym): Trivial by induction. Case (Teq.tran): Then by induction the ``middle type'' is X , whence by induction again T = X . Case (Ssub.struct): We have either E #hm Eq(T ) <: Eq(X ) or E #hm Eq(X ) <: Eq(T ) by a subproof. By Lemma G.4 (discreteness of subkinding below Type) and reversing (Keq.Eq), either E #hm T == X or E #hm X == T by a subproof. By induction we get T = X as desired. Case (Ssub.refl): Trivial. Case (Ssub.tran): Similar to (Teq.tran) above. Case (US.var): Impossible since E is purely abstract. INRIA Global abstractionsafe marshalling with hash types 67 Case (US.sub): The conclusion is E #hm U :[Y :Eq(X ), T # ]. The premises are E #hm U :[Y :K , T ## ] and E #hm [Y :K , T ## ] <: [Y :Eq(X ), T # ]. By Lemma F.26 (reversing subsignaturing judgement), E #hm K <: Eq(X ) by a subproof. By Lemma G.4 (discreteness of subkinding below Type) and reversing (Keq.Eq), there exists T 1 such that K = Eq(T 1 ) and E #hm T 1 == X by a subproof. By induction we get T 1 = X . The other premise of the bottommost rule is therefore E #hm U :[Y :Eq(X ), T ## ], which is impossible by induction. Case (US.self): Impossible. Lemma I.6 (open interpretation of type equivalence) E #hm T == T # iff E #hm T :Type and E #hm T # :Type and reveal hm partenvsub E T = reveal hm partenvsub E T # . Proof. Assume E #hm T == T # . By Lemma F.21 (things have to be ok), we have E #hm T :Type and E #hm T # :Type. We will now prove the last statement by induction on the lexically ordered pair (m, n) where m is the length of E and n is the number of module bindings in E . Let E 1 be the longest bare bones suffix of E (see Definition G.5 (bare bones environment)). Case E = E 1 : Then partenvsub E = id, so the proof obligation is reveal hm T = reveal hm T # . Induct on the structure of T . Case T = TC (T 1 , ..., T j ): By Lemma G.11 (type decomposition), there exist T # 1 , ..., T # j such that E #hm T # i == T i for each i . Then, by induction, for each i , we have reveal hm T # i = reveal hm T i . Furthermore, one of the following cases holds: Case T # = TC (T # 1 , ..., T # j ): Then we have reveal hm T # = reveal hm T . Case T # = hm = hash(N , [TC (T # 1 , ..., T # j ), v ]:[X :Type, T ## ]): Then reveal hm T # = TC (T # 1 , ..., T # j ) = reveal hm (TC (T # 1 , ..., T # j )) = T (note that for all i , reveal hm T # i = T # i since hm is not a subterm of T # i ). Case T = X : By Lemma I.5 (distinction of fresh type variables), we have T # = X = T . Case T = U .TYPE: Impossible by Lemma E.9 (free variables of a judgement come from the environment). Case T = h 1 : By Lemma G.11 (type decomposition), one of the following cases holds: Case hm = h 1 = hash(N , [T # , v ]:[X :Type, T ## ]): Then reveal hm h 1 = T # = reveal hm T # (note that hm is not a subterm of T # ). Case T # = hm = hash(N , [h 1 , v ]:[X :Type, T ## ]): Then reveal hm h 1 = h 1 = reveal hm T # . Case T # = h 1 : Trivial. Case E = E 0 , x :T 1 , E 1 : By Lemma E.6 (environments have to be ok) and Lemma E.22 (environments do not contain free expression variables), x / # fv E 1 . By Lemma E.21 (types do not contain free expression variables), x / # fv T # fv T # . Then, by Lemma F.15 (strengthening), we get E 0 , E 1 #hm T == T # . We can apply induction to E 0 , E 1 , getting reveal hm partenvsub E0 ,E1 T = reveal hm partenvsub E0 ,E1 T # . Since partenvsub E0 ,E1 = partenvsub E , this is the desired result. Case E = E 0 , X :Eq(T 1 ), E 1 : By Lemma E.19 (computing the pvu of a type world judgement), hm 4 min (pvu X (E 1 )). By Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.X), we have E 0 #hm T 1 :Type, whence by (Teq.refl) and (TK.Eq), E 0 #hm T 1 :Eq(T 1 ). We can use Lemma F.14 (type preser vation by substitution) to {X#T 1 }, getting E # #hm {X#T 1 }T == {X#T 1 }T # where E # = E 0 , {X#T 1 }E 1 . By induction, we get that reveal hm partenvsub E # {X#T 1 }T = reveal hm partenvsub E # {X#T 1 }T # . By Definition I.2 (partial type substitution associated to an environment), partenvsub E0 ,X :Eq(T1 ) = partenvsub E0 {X T 1 }. By Lemma I.3 (a purely abstract suffix does not change the substitution), partenvsub E = partenvsub E0 {X T 1 }. So we have reveal hm partenvsub E T = reveal hm partenvsub E T # , as desired. Case E = E 0 , U :[X :K , T 1 ], E 1 : Let Z and z be fresh. By Lemma F.29 (type preservation by module sub stitution in coloured judgements), we get E 0 , Z :Eq(T 1 ), z :{X Z}T 2 , {U .TYPE Z , U .term z}E 1 #hm {U .TYPE Z , U .term z }T == {U .TYPE Z , U .term z}T # . Since E 1 is purely abstract, we have {U .TYPE Z , U .term z }E 1 = E 1 . By Lemma E.21 (types do not contain free expression variables), z / # fv {U .TYPE Z}T , whence {U .TYPE Z , U .term z}T = {U .TYPE Z}T , and the same holds for T # . By Lemma F.15 (strengthening), we get E # #hm #T == #T # where E # = E 0 , Z :K , E 1 and # = {U .TYPE Z}. Now we do case analysis on the structure of K . RR n 4851 68 Leifer, Peskine, Sewell, Wansbrough Case K = Type: By induction on the last judgement above, we have reveal hm partenvsub E # #T = reveal hm partenvsub E # #T # . Now partenvsub E # # = partenvsub E , so we have the desired result. Case K = Eq(T 0 ): Apply the case E = E 0 , Z :Eq(T 0 ), E 1 above to E # #hm #T == #T # . We get reveal hm partenvsub E # #T = reveal hm partenvsub E # #T # . Now given Definition I.2 (partial type substitu tion associated to an environment) and Lemma I.3 (a purely abstract suffix does not change the substitution), we in fact have partenvsub E = partenvsub E #. So we have the desired result. We turn to the other half of the proof. Assume that E #hm T :Type and E #hm T # :Type and reveal hm partenvsub E T = reveal hm partenvsub E T # . We induct on the number of bindings of E that are not of the form U :[X :Type, ] nor X :Type. We write E 1 for the longest purely abstract suffix of E . Case E is purely abstract: Induct on the structure of T . Note that partenvsub E = id. Case T = TC (T 1 , ..., T j ): By definition of reveal hm , one of the following cases holds: Case T # = UNIT or T # = mar : Then T = T # , so E #hm T == T # by (Teq.refl). Case T # = TC (T # 1 , ..., T # j ) with j #= 0: Still by the definition of reveal hm , we have reveal hm T i = reveal hm T # i for all i . Then by induction we have E #hm T i == T # i . By the appropriate rule amongst (Teq.cong.fun) and (Teq.cong.tuple), we have E #hm T == T # . Case T # = hm = hash(N , [T , v ]:[X :Type, T ## ]): We have E #hm ok by Lemma E.6 (environments have to be ok). Then, by (Teq.hash), we get E #hm T # == T , whence E #hm T == T # by (Teq.sym). Case T = X or T = U .TYPE: Then reveal hm T = T = T # = reveal hm T # . By (Teq.refl), we have E #hm T == T # . Case T = hm: By definition of reveal hm , one of the following cases holds: Case T # = hm: Then E #hm T == T # by (Teq.refl). Case hm = hash(N , [T # , v ]:[X :Type, T ## ]): We have E #hm ok by Lemma E.6 (environments have to be ok). Then, by (Teq.hash), we get E #hm T == T # . Case T is a hash but T #= hm: Then reveal hm T = T . By definition of reveal hm , one of the following cases holds: Case T # = hm = hash(N , [T , v ]:[X :Type, T ## ]): We have E #hm ok by Lemma E.6 (environments have to be ok). Then, by (Teq.hash), we get E #hm T # == T , whence E #hm T == T # by (Teq.sym). Case T # = T : Then E #hm T == T # by (Teq.refl). Case E = E 0 , x :T 0 , E 1 : By Lemma E.21 (types do not contain free expression variables), Lemma E.22 (environments do not contain free expression variables) and Lemma F.15 (strengthening), we have E 0 , E 1 #hm T :Type and E 0 , E 1 #hm T # :Type. Since furthermore partenvsub E = partenvsub E0 ,E1 , by induction, we get E 0 , E 1 #hm T == T # . By Lemma E.6 (environments have to be ok) and Lemma E.7 (prefixes of ok environments are ok), we have E 0 , x :T 0 #hm ok, so E #hm T == T # by Lemma F.5 (combined weakening) (note that x / # fv E 1 by Lemma E.8 (ok environments have no repetition in the domain)). Case E = E 0 , X :Eq(T 0 ), E 1 : Then partenvsub E = partenvsub E0 {X T 0 }. By Lemma E.6 (environments have to be ok), Lemma E.7 (prefixes of ok environments are ok) and reversing (envok.X) and (Kok.Eq), we have E 0 #hm T 0 :Type. By Lemma E.19 (computing the pvu of a type world judgement), any proof # of E #hm T :Type satisfies hm 4 min (pvu X (#)). By Lemma F.14 (type preservation by substitution), we get E 0 , {X#T 0 }E 1 #hm {X#T 0 }T :Type. Similarily, we have E 0 , {X#T 0 }E 1 #hm {X#T 0 }T # :Type. By induction, we get E 0 , {X#T 0 }E 1 #hm {X#T 0 }T == {X#T 0 }T # . Since E 0 , X :Eq(T 0 ) #hm ok and X / # domE 1 by Lemma E.8 (ok environments have no repetition in the domain), by Lemma F.5 (combined weakening), we have E 0 , X :Eq(T 0 ), {X#T 0 }E 1 #hm {X#T 0 }T == {X#T 0 }T # . By Lemma G.8 (type substitution in a purely abstract environment), given the latest judgement and E 0 , X :Eq(T 0 ), E 1 #hm ok, we get E 0 , X :Eq(T 0 ), E 1 #hm {X#T 0 }T == {X#T 0 }T # . By Lemma F.25 (type substitution in equivalence), we get E #hm T == T # as desired. INRIA Global abstractionsafe marshalling with hash types 69 Case E = E 0 , U :[X :Eq(T 0 ), T 1 ], E 1 : Then partenvsub E = partenvsub E0 {U .TYPE T 0 }. Let Z be a fresh variable, and let E # = E 0 , Z :Eq(T 0 ), {U .TYPE#Z}E 1 . By the definition of partenvsub, we have partenvsub E # = partenvsub E0 {Z#T 0 }. Since Z is fresh, we have reveal hm partenvsub E # {U .TYPE#Z}T = reveal hm partenvsub E # {U .TYPE#Z}T # . By Lemma F.31 (type preservation by module substitution in coloured judgements for type world judgements), since E #hm T :Type, we get E # #hm {U .TYPE#Z}T :Type. Similarly we have E # #hm {U .TYPE#Z}T # :Type. By the previous case, E # #hm {U .TYPE#Z}T == {U .TYPE#Z}T # . By Lemma E.6 (environments have to be ok) and Lemma E.7 (prefixes of ok environments are ok), we have E 0 , U :[X :Eq(T 0 ), T 1 ] #hm ok. Using Lemma E.8 (ok environments have no repetition in the do main), we get U / # {Z} # dom ({U .TYPE#Z}E 1 ), so by Lemma F.5 (combined weakening), we have E 0 , U :[X :Eq(T 0 ), T 1 ], Z :Eq(T 0 ), {U .TYPE#Z}E 1 #hm {U .TYPE#Z}T == {U .TYPE#Z}T # . By (US.var) and (TK.mod), we have E 0 , U :[X :Eq(T 0 ), T 1 ] #hm U .TYPE:Eq(T 0 ). By Lemma E.19 (com puting the pvu of a type world judgement), we can apply Lemma F.14 (type preservation by substitution). We get E 0 , U :[X :Eq(T 0 ), T 1 ], {Z U .TYPE}{U .TYPE Z}E 1 #hm {Z U .TYPE}{U .TYPE Z}T == {Z U .TYPE}{U .TYPE Z}T # . Since Z was fresh, we have E #hm T == T # , as desired. Theorem I.7 (semantic interpretation of type world judgements) Let E #hm J be any type world judgement. It is derivable iff all of the following conditions are met: 1. E has no repetition in the domain and unresolved free variables. 2. All the free variables of J are in domE . 3. All the hashes in E #hm J are correct. 4. The following extra condition is met, depending on the form of J : Case E #hm ok or E #hm K ok or E #hm T :Type or T #hm S ok: no extra condition. Case E #hm T == T # or E #hm T :Eq(T # ): reveal hm partenvsub E T = reveal hm partenvsub E T # . Case E #hm K == K # : either K = K # = Type, or there exist T and T # such that K = Eq(T ) and K # = Eq(T # ) and E #hm T == T # . Case E #hm K <: K # : either K # = Type, or there exist T and T # such that K = Eq(T ) and K # = Eq(T # ) and E #hm T == T # . Case E #hm [X :K , T ] <: [X :K # , T # ]: E #hm K <: K # and E , X :K #hm T == T # . Case E #hm U :[X :K , T ]: E contains a binding of the form U :[X :K 1 , T 2 ] for some K 1 and T 2 , and E #hm [X :Eq(U .TYPE), T 2 ] <: [X :K , T ]. Proof. If E #hm J , then E has no repetition in the domain and no unresolved free variables by Lemma E.6 (environments have to be ok) and Lemma E.8 (ok environments have no repetition in the domain) and Lemma F.13 (ok environments have no unresolved free variables); all the hashes in J are correct by Lemma E.5 (hashes have to be ok), and all the free variables of J are in domE by Lemma E.9 (free variables of a judgement come from the environment). We now assume that conditions 1--3 are met, and prove that E #hm J is derivable iff condition 4 is met. Cases E #hm ok, E #hm K ok, E #hm T :Type, E #hm [X :K , T ] ok: Since there is no extra condition, we just have to prove that the judgement is derivable. We induct on the length of E . Case nil #hm ok: By condition 3 (if hm is a hash) or (hmok.zero) (if hm = .), we can derive # hm ok, whence we apply (envok.nil). Case E , #:# #hm ok: Given the (envok.*) rules, this judgement is derivable iff the judgement E #hm #:# is. By induction, these judgements are derivable. Case E #hm T :Type: By the previous cases, E #hm ok is derivable. By Lemma F.9 (types are ok provided their hashes are), E #hm T :Type is derivable. RR n 4851 70 Leifer, Peskine, Sewell, Wansbrough Case E #hm Type ok: By the previous cases, E #hm ok is derivable. Then we can derive E #hm Type ok by (Kok.Type). Case E #hm Eq(T ) ok: By the previous cases, E #hm ok is derivable. By Lemma F.9 (types are ok provided their hashes are), E #hm T :Type is derivable. Then we can derive E #hm Eq(T ) ok by (Kok.Eq). Case E #hm [X :K , T ] ok: By the previous cases, E #hm K ok is derivable. By alphaconversion, we can choose X not in domE . By (envok.X), we get a proof of E , X :K #hm ok. By Lemma F.9 (types are ok provided their hashes are), since fv T # fv [X :K , T ] # {X } # domE # {X }, E , X :K #hm T :Type is derivable. Then, by (Sok), we get E #hm [X :K , T ] ok. Case E #hm T == T # : Note that by Lemma F.9 (types are ok provided their hashes are), we have E #hm T :Type and E #hm T # :Type. Apply Lemma I.6 (open interpretation of type equivalence). Case E #hm T :Eq(T # ): Given (TK.Eq) and (Teq.Eq), this judgement is equivalent to E #hm T == T # . Apply the previous case. Case E #hm K == K # : Obvious from (Keq.*). Case E #hm K <: K # : Subcase K = K # = Type: Trivial. Subcase K = Eq(T ) and K # = Type: By Lemma F.9 (types are ok provided their hashes are) and (Kok.Eq), E #hm Eq(T ) ok is derivable. By Lemma E.6 (environments have to be ok) and (Kok.Type), we have E #hm Type. By (Ksub.Eq), we have E #hm Eq(T ) <: Type. Subcase K = Type and K # = Eq(T # ): By Lemma G.4 (discreteness of subkinding below Type) and the (Keq.*) rules, this is impossible. Subcase K = Eq(T ) and K # = Eq(T # ): If E #hm T == T # then E #hm Eq(T ) <: Eq(T # ) by (Keq.Eq) and (Ksub.refl). The converse follows from Lemma G.4 (discreteness of subkinding below Type). Case E #hm [X :K , T ] <: [X :K , T # ]: Apply Lemma F.26 (reversing subsignaturing judgement). Conversely, apply (Ssub.struct). Case E #hm U :[X :K , T ]: Apply Lemma F.27 (reversing module value variable typing judgement) and Lemma F.28 (obtaining module value variable typing judgement). Lemma I.8 (uniqueness of expression typing) If E #hm e:T then E #hm e:T # iff E #hm T == T # . Proof. If E #hm e:T and E #hm T == T # then E #hm e:T # by (eT.eq). Now we prove the converse by induction on the structure of e . The crux of the matter is that (1) apart from (eT.eq), the expression typing rules are syntaxdirected and (2) type equivalence is a congruence. We have E #hm e:T and E #hm e:T # . By Lemma G.1 (shortening typing proof), there exist T 1 and T # 1 such that E #hm e:T 1 and E #hm T # 1 by proofs # and # # that do not end with (eT.eq), and E #hm T == T 1 and E #hm T # == T # 1 . We now prove that E #hm T 1 == T # 1 ; by (Teq.tran) and (Teq.sym) this gives E #hm T == T # as desired. Note that it is enough to prove that T 1 = T # 1 . We do case analysis on the structure of e; in each case # and # # have to end with the same rule. Case e = x (eT.var): Then E contains a binding for x ; by Lemma E.6 (environments have to be ok) and Lemma E.8 (ok environments have no repetition in the domain), this binding is unique, and the type attributed to x is T 1 = T # 1 . Case e = U .term (eT.mod): Then there exist K and K # such that E #hm U :[X :K , T 1 ] and E #hm U :[X :K , T # 1 ]. By Theorem I.7 (semantic interpretation of type world judgements) and Lemma F.26 (reversing subsignaturing judgement), we see that there exists T 2 such that E , X :Eq(U .TYPE) #hm T 2 == T 1 and E , X :Eq(U .TYPE) #hm T 2 == T # 1 . By (Teq.tran) and (Teq.sym), we get E , X :Eq(U .TYPE) #hm T 1 == T # 1 . Since E #hm T 1 :Type and E #hm T # 1 :Type, by Lemma E.9 (free variables of a judgement come from the environment), X / # fv T 1 # fv T # 1 . Hence, by Lemma F.15 (strengthening), we get E #hm T 1 == T # 1 as desired. Case e = e 1 e 2 (eT.ap): Then there exist T 2 and T # 2 such that E #hm e 1 :T 2 #T 1 and E #hm e # 1 :T # 2 #T # 1 . By induction, we get E #hm T 2 #T 1 == T # 2 #T # 1 . By Lemma I.6 (open interpretation of type equivalence) applied once in each direction, we get E #hm T 1 == T # 1 as desired. INRIA Global abstractionsafe marshalling with hash types 71 Case e = #x :T 0 .e 0 (eT.fun): Then there exist T 2 and T # 2 such that T 1 = T 0 #T 2 and T 1 = T 0 #T # 2 and E , x :T 0 #hm e 0 :T 2 and E , x :T 0 #hm e 0 :T # 2 . By induction we get E , x :T 0 #hm T 2 == T # 2 . By Lemma E.21 (types do not contain free expression variables) and Lemma F.15 (strengthening), we get E #hm T 2 == T # 2 . By (Teq.refl) and (Teq.cong.fun), we get E #hm T 1 == T # 1 as desired. Cases e = ! e 0 , e = () (eT.send, eT.unit): Then T 1 = UNIT = T # 1 . Cases e = ?, e = mar (e 0 :T 0 ), e = marshalled (e 0 :T 0 ) (eT.recv, eT.mar, eT.marred): Then T 1 = STRING = T # 1 . Cases e = unmar e 0 :T 0 , e = UnmarFailure T0 , e = [e 0 ] T0 hm 0 (eT.unmar, eT.Undynfailure, eT.col): Then T 1 = T 0 = T # 1 . Case e = (e 1 , ..., e j ) (eT.tuple): Similar to the (eT.fun) case, using (Teq.cong.tuple). Case e = proj i e 0 (eT.proj): Similar to the (eT.ap) case. Definition I.9 (reconstructed type of an expression) We define the reconstructed type of an expression e in an environment E and a colour hm , written basictype E hm e , recursively as follows: basictype E hm (x ) = # E hm T where E = ..., x :T , ... basictype E hm (U .term) = # E hm {X#U .TYPE}T where E = ..., U :[X :K , T ], ... basictype E hm (#x :T 0 .e 0 ) = (# E hm T 0 )#(basictype E ,x :T0 hm e 0 ) basictype E hm (e 1 e 2 ) = T 1 where basictype E hm e 1 = T 2 #T 1 basictype E hm ((e 1 , ..., e j )) = basictype E hm e 1 # ... # basictype E hm e j basictype E hm (proj i e 0 ) = T i where basictype E hm e 0 = T 1 # ... # T j basictype E hm (mar (e 0 :T 0 )) = STRING basictype E hm (marshalled (e 0 :T 0 )) = STRING basictype E hm (unmar e 0 :T 0 ) = # E hm T 0 basictype E hm (! e 0 ) = UNIT basictype E hm (?) = STRING basictype E hm ([e 0 ] T0 hm 0 ) = # E hm T 0 basictype E hm (UnmarFailure T0 ) = # E hm T 0 where # E hm = reveal hm partenvsub E . Note that this definition basictype E hm is partial: it fails if a subexpression does not have a type of the required form (cases e 1 e 2 , (e 1 , ..., e j )) or if a variable is not bound by the environment (cases x , U .term). Lemma I.10 (simplicity of the reconstructed type) If basictype E hm e is welldefined, it contains no hm , nor does it contain any variable that is bound concretely in E . Proof. Trivial by induction. Lemma I.11 (assurance of correctness of the reconstructed type) If E #hm e:T then basictype E hm e is welldefined and E #hm e:basictype E hm e . Proof. Induct on the derivation of E #hm e:T . By Lemma G.1 (shortening typing proof), there exists T # such that E #hm e:T # by a subproof # that does not end in (eT.eq), and E #hm T # == T . Note that by Lemma I.6 (open interpretation of type equivalence), reveal hm partenvsub E T = reveal hm partenvsub E T # . We discriminate on the last rule of #. Case (eT.var): We have E = E 0 , x :T # , E 1 and e = x . By Lemma I.4 (stability of types through revelation), E #hm T # == reveal hm partenvsub E T # , so by (eT.eq) we get E #hm x:reveal hm partenvsub E T # as desired. Case (eT.mod): The premises are E #hm U :[X :K , T ] and E #hm T :Type. By Theorem I.7 (semantic interpretation of type world judgements) and Lemma F.26 (reversing subsignaturing judgement), there exist E 0 , E 1 , K # and T # such that E = E 0 , U :[X :K # , T # ], E 1 and E , X :Eq(U .TYPE) #hm T # == T . By Lemma E.9 (free variables of a judgement come from the environment), we have X / # fv T . By Lemma E.19 (computing the pvu of a type world judgement) and Lemma F.14 (type preservation by substitution), we have E #hm {X#U .TYPE}T # == RR n 4851 72 Leifer, Peskine, Sewell, Wansbrough T . By Lemma I.4 (stability of types through revelation) and (Teq.tran) and (Teq.sym), we have E #hm T == reveal hm partenvsub E {X U .TYPE}T # as desired. Case (eT.fun): There exist T 1 and T 2 such that e = #x :T 1 .e 1 and T # = T 1 #T 2 , and the premise is E , x :T 1 #hm e 1 :T 2 . By induction, E , x :T 1 #hm e 1 :basictype E ,x :T1 hm e 1 . Thus, by (eT.fun), we get E #hm #x :T 1 .e 1 :T 1 #basictype E ,x :T1 hm e 1 . By Lemma I.4 (stability of types through revelation), we have E #hm reveal hm partenvsub E T 1 == T 1 , whence the desired result by (Teq.cong.fun). Case (eT.ap): There exist e 1 , e 2 and T 2 such that e = e 1 e 2 the premises are E #hm e 1 :T 2 #T # and E #hm e 2 :T 2 . By induction, we have E #hm e 1 :basictype hm E e 1 . By Lemma I.8 (uniqueness of expression typ ing), we have E #hm basictype hm E e 1 == T 2 #T # . By Lemma I.10 (simplicity of the reconstructed type), partenvsub hm (basictype hm E e 1 ) = basictype hm E e 1 . By using Lemma F.14 (type preservation by substitution) and Lemma F.33 (type preservation by fully carried out module substitution) once for each concrete binding in E , we get that E # # basictype hm E e1 == (partenvsub E T 2 )#(partenvsub E T # ) with E # fully abstract. By Lemma G.11 (type decomposition), and given that basictype hm E e 1 #= hm by Lemma I.10 (simplicity of the reconstructed type), basictype hm E e 1 must have the form T # 2 #T # 1 , with E # #hm T # 1 == T 1 . By Lemma I.6 (open interpretation of type equivalence) applied once in each direction, we have E #hm T # 1 == T # . Thus E #hm (basictype E hm e) == T # , whence the desired result. Case (eT.tuple): Similar to the lambdaabstraction case. Case (eT.proj): Similar to the application case. Cases (eT.mar), (eT.marred), (eT.send), (eT.recv): Trivial. Case (eT.unmar), (eT.col), (eT.Undynfailure): Trivial. Lemma I.12 (validity of the reconstructed type) If basictype E hm e is welldefined and E #hm ok then E #hm basictype E hm e:Type and E #hm e:basictype E hm e . Proof. Induct on the structure of e . The rules to define basictype E hm were chosen on purpose. Some steps apply reveal hm partenvsub E , which produces an equivalent type by an easy application of Lemma I.6 (open interpretation of type equivalence). Corollary I.13 (deciding expression typing through type reconstruction) E #hm e:T is derivable iff basictype E hm e is welldefined and reveal hm partenvsub E T = basictype E hm e and E #hm T :Type. Proof. If E #hm e:T is derivable then, by Lemma I.11 (assurance of correctness of the reconstructed type), basictype E hm e is welldefined and E #hm e:basictype E hm e . By Lemma I.8 (uniqueness of expression typing), E #hm T == basictype E hm e . By Lemma I.10 (simplicity of the reconstructed type) and Lemma I.6 (open inter pretation of type equivalence), reveal hm partenvsub E T = basictype E hm e . Finally, E #hm T :Type by Lemma E.6 (environments have to be ok). Conversely, assume basictype E hm e is welldefined and reveal hm partenvsub E T = basictype E hm e and E #hm T :Type. By Lemma E.6 (environments have to be ok) and Lemma I.12 (validity of the reconstructed type), we have E #hm e:basictype E hm e . By Lemma I.10 (simplicity of the reconstructed type) and Lemma I.6 (open interpretation of type equivalence), E #hm (basictype E hm e) == T . Thus, by (eT.eq), we have E #hm e:T . Theorem I.14 (decidability of type checking) There is algorithm that decides whether any given judgement # / # domE , # hm ok, E #hm J or # n ok is derivable. Note that given Lemma I.12 (validity of the reconstructed type), type checking for expressions does not in fact need to be given a type, it can infer it. As in the simplytyped lambdacalculus, of course, we need to annotate function arguments; and we also have explicit type annotations on the dynamic typing constructs and coloured brackets. INRIA Global abstractionsafe marshalling with hash types 73 Proof. We explain how to reduce the decidability of a judgement to the decidability of a number of strictly smaller judgements, the size of a judgement being here the lexically ordered pair (a, b) where a is the number of expression syntax nodes (including those inside hashes) and b is the total size of the judgement. By Lemma E.3 (nonmembership in domain is interpreted trivially), we get a decision procedure for checking deriv ability of # / # domE judgements, provided we have a decision procedure for checking correctness of embedded hashes. For type world judgements, Theorem I.7 (semantic interpretation of type world judgements) easily translates into an algorithm. Given the rules (hmok.*), (MS.struct), (mT.*), and (nok.*), decidability of derivability for judgements of the form # hm ok, E #hm M :S , E #hm m:T , and # n ok follows from decidability of smaler type world and expression typing judgements. For expression typing, Corollary I.13 (deciding expression typing through type reconstruction) gives a decision pro cedure: to decide whether E #hm e:T is derivable, construct basictype E hm e (if this fails, then e has no type), and check some smaller type world jdugements. Definition I.15 (user source program) User source programs are programs that contain no hashes or coloured brackets. Discussion I.16 (decidability of type checking for user programs) User source programs can be type checked without any computation on hashes. Following the decision procedure given in the previous proofs, type checking does not introduce extra hashes, hence hashfree programs can be checked without ever encountering hashes. I.2 Bracket elimination Definition I.17 (bracket elimination subsystem) The bracket elimination subsystem is a reduction relation -# be hm on expressions consisting of (ered.cong) and the (ered.col.*) rules. Lemma I.18 (determinism of bracket elimination) Bracket elimination is deterministic. Proof. Trivial consequence of Theorem H.11 (determinism of expression reduction). Lemma I.19 (termination of bracket elimination) Bracket elimination is strongly normalising. Proof. Define the bracket elimination weight of an expression w be (e) structurally on the expression. w be (()) = 1 w be ((e 1 , ..., e j )) = w be (e 1 ) + + w be (e j ) + 1 w be (proj i e) = w be (e) + 1 w be (x ) = 1 w be (#x :T .e) = 1 w be (e 1 e 2 ) = w be (e 1 ) + w be (e 2 ) + 1 w be (mar (e:T )) = w be (e) + 1 w be (marshalled (e:T )) = w be (e) + 1 w be (unmar e:T ) = w be (e) + 1 w be (! e) = w be (e) + 1 w be (?) = 1 w be (U .term) = 1 w be ([e] T hm ) = 2 w be (e) w be (UnmarFailure T ) = 1 It is obvious that w be (e) is always a positive integer. We prove that if e -# be hm e # then w be (e) > w be (e # ). We induct on the derivation of the reduction. Case (ered.col.unit): Here e = [()] UNIT hm # and e # = (). We have w be (e) = 2 > 1 = w be (e # ). Case (ered.col.tuple): Here e = [(v hm # 1 , ..., v hm # j )] T1#...#T j hm # and e # = ([v hm # 1 ] T1 hm # , ..., [v hm # j ] T j hm # ). We have w be (e) = 2(1 + P i w be (v hm # i )) > P i 2w be (v hm # i ) = w be (e # ). Case (ered.col.fun): Here e = [#x :T .e 0 ] T # #T ## hm # and e # = #x :T # .[{x #[x ] T # hm }e 0 ] T ## hm # . We have w be (e) = 2 > 1 = w be (e # ) RR n 4851 74 Leifer, Peskine, Sewell, Wansbrough Case (ered.col.marred): Here e = [marshalled (e 0 :T )] STRING hm # and e # = marshalled (e 0 :T ). We have w be (e) = 2(1 + w be (e 0 )) > 1 + w be (e 0 ) = w be (e # ). Case (ered.col.col): Here e = [[v h0 ] h0 h0 ] h0 h1 and e # = [v h0 ] v h 0 v h 0 . We have w be (e) = 2w be (e # ) > w be (e # ). Case (ered.col.le): Here e = [v hm # ] h ## hm # and e # = v hm # . We have w be (e) = 2w be (e # ) > w be (e # ). Case (ered.cong): Here e = C hm hm 0 e 0 and e # = C hm hm 0 e # 0 and e 0 -# be hm 0 e # 0 . By induction, w be (e 0 ) > w be (e # 0 ). If C hm hm 0 = [ ] T0 hm 0 , then w be (e) = 2w be (e 0 ) > 2w be (e # 0 ) = w be (e # ). Otherwise, there exists an integer k such that w be (e) = k + w be (e 0 ) > k + w be (e # 0 ) = w be (e # ). Since the weight of an expression is a positive integer that decreases at each step of reduction, bracket elimination is strongly normalising (bracket elimination of e terminates in at most w be (e) steps). Definition I.20 (bracket erasure) If # is any syntactic entity, erase brackets(#) is # with all brackets outside hashes erased. In particular, erase brackets(h) = h , and erase brackets([e] T hm ) = erase brackets(e). Definition I.21 (colourless proof) A colourless proof is one that does not use (hmok.hash) nor (eT.col). A colourless expression, type, etc., is one whose validity can be derived by a colourless proof. It is left as an exercise to the reader to prove that colourless types are those that contain no hashes and colourless expressions are those that contain no brackets and whose type annotations only use colourless types. Definition I.22 (bracketless expressions) A bracketless expression is one that does not have any coloured brackets in it. Ditto for values, contexts, and networks. Definition I.23 (bracketless reductions) Define e -# nb e # on bracketless expressions as given by the (ered.*) rules other than (ered.col.*), with the righthand sides modified to not introduce brackets, namely: . (ered.ap) becomes (#x :T .e)v -# nb {x v}e; . (ered.mar) becomes mar (e:T ) -# nb marshalled (e:T ). Also define n -# nb n # for networks in the obvious way. Lemma I.24 (progress and determinism of bracketless expression reduction) If e is a bracketless expression then exactly one of the following cases holds: . e is a bracketless value; . e is dormant, i.e. is UnmarFailure T or a communication in a bracketless evalution context; . e reduces by -# nb ; moreover there is exactly one rule with one redex applicable. Proof. Use Theorem H.6 (progress of expressions) for the ``at least one'' part. Similar to the proof of Theorem H.11 (determinism of expression reduction) for the ``at most one'' part. Theorem I.25 (bracket erasure preserves expression reduction outcomes) If nil #hm e:T and e -#hm e # then erase brackets(e) -# nb ? erase brackets(e # ). Proof. (Sketch.) Induct on the derivation of the reduction. If e -#hm e # by one of the bracket pushing rules, then erase brackets(e) = erase brackets(e # ) (check each case). If e -#hm e # by another rule, then erase brackets(e) -# nb erase brackets(e # ) (check each case, noticing that if v hm 1 is a value then erase brackets(v hm 1 ) is a bracketless value). Finally, the case of (ered.cong) follows by induction. Theorem I.26 (bracket erasure preserves reduction outcomes) If # n ok and n -# n # then erase brackets(n) -# nb ? erase brackets(n # ). Proof. Follows from Theorem I.25 (bracket erasure preserves expression reduction outcomes). INRIA Global abstractionsafe marshalling with hash types 75 Theorem I.27 (bracket erasure does not add expression reduction outcomes) If nil #hm e:T and erase brackets(e) -# nb e then there exists e # such that erase brackets(e # ) = e and e -# + hm e # Proof. By Theorem H.6 (progress of expressions) and Lemma I.19 (termination of bracket elimination), there exists a sequence of expressions e = e 0 , e 1 , ..., e j such that e i-1 -# be hm e i for 1 6 i 6 j and e j does not reduce by any bracket pushing rule. By definition, erase brackets(e) = erase brackets(e j ). Thus by hypothesis, erase brackets(e j ) -# nb e. By Theorem H.6 (progress of expressions) one of the following cases holds: Case e j is an hmvalue: Then erase brackets(e j ) is a bracketless value, so by Lemma I.24 (progress and determinism of bracketless expression reduction) does not reduce by -# nb , a contradiction. Case e j is dormant: Then erase brackets(e j ) is a bracketless dormant expression, so by Lemma I.24 (progress and determinism of bracketless expression reduction) does not reduce by -# nb , a contradiction. Case e j reduces: Therefore there exists e # such that e j -#hm e # , a non bracket pushing reduction. Hence erase brackets(e j ) -# nb erase brackets(e # ). By Lemma I.24 (progress and determinism of bracketless expres sion reduction), erase brackets(e # ) = e. Theorem I.28 (bracket erasure does not add reduction outcomes) If # n ok and erase brackets(n) -# nb n 0 then there exists n # such that erase brackets(n # ) = n 0 and n -# + n # This means we can erase all brackets once all the modules have been reduced away. Clearly -# nb is not type preserving but it represents a possible implementation strategy. Proof. Follows from Theorem I.27 (bracket erasure does not add expression reduction outcomes). We might hope that: If # n ok then n -# # n # iff erase brackets(n) -# nb # erase brackets(n # ). This is not true! Counterexample: erase brackets(3) -# nb = erase brackets([3] INT h ) but not 3 -# # . [3] INT h Definition I.29 (type erasure) We define erase(e) to be e with all type annotations and brackets erased except that the type annotations on mar , marshalled , and unmar are left unchanged. Ditto for networks. We define ---# erase to be like -#hm by taking the eraseimage of the left and righthand sides of each rule (and removing rules that would become e ---# erase e). Ditto for networks. Note that erased terms do not form a subsyntax of our usual syntax since e.g. erase(#x :T .e) = (#x .e). We will not make explicit any formal reasoning about erased terms so do not construct this new syntax explicitly. Note that the only difference between erase(e) and erase brackets(e) is that the former removes type annotations from lambdaexpressions. Moreover, ---# erase is the same as -# nb except that the type annotations in the lambdaexpressions are erased. Theorem I.30 (type erasure) Assume # n ok. We have that n -# n # implies erase(n) ---# erase ? erase(n # ). Conversely, erase(n) ---# erase n 0 implies that there exists n # such that erase(n # ) = n 0 and n -# + n # . Proof. By Theorem I.26 (bracket erasure preserves reduction outcomes) and Theorem I.28 (bracket erasure does not add reduction outcomes), the theorem holds for erase brackets in place of erase. Note that the type annotation T in #x :T .e never influences -# nb reduction, as desired. RR n 4851 76 Leifer, Peskine, Sewell, Wansbrough J Coincidence of undyntime type checking and static type checking We prove a theorem relating the undyntyime type checking in a single machine to static typechecking. Definition J.1 (multiplelet context) CL ::= multiplelet context identity moduleNU = M :S in CL module declaration Definition J.2 (correct multiplelet context) A multiplelet context CL is correct in E if the following (recursive) properties hold: Case CL = : We require E # . ok. Case CL = moduleNU = M :S in CL # : We require E # . M :S and CL # is correct in E , U :S . We say CL is correct if CL is correct in nil. Lemma J.3 (correct multiplelet contexts yield ok environments) If CL is correct in E then E , envofcl CL # . ok. Proof. Induct on the length of CL. Case CL = : By the definition of correctness, E # . ok, as desired. Case CL = moduleNU = M :S in CL # : Then envofcl CL = U :S , envofcl CL # . By definition of correctness, E , U :S # . ok and CL # is correct in E , U :S . By induction, E , U :S , envofcl CL # # . ok, i.e. E , envofcl CL # . ok as desired. Lemma J.4 (machine judgement characterisation of correct multiplelet contexts) CL is a correct multiplelet context iff nil # . CL():UNIT. Proof. We prove that CL is correct in E iff E # . CL():UNIT. We induct on the length of CL. Case CL = : If CL is correct in E , then E # . ok whence by (eT.unit) E # . ():UNIT. Conversely, assume E # . ():UNIT: by Lemma E.6 (environments have to be ok), we get E # . ok, i.e. CL is correct in E . Case CL = moduleNU = M :S in CL # , forward direction: We have that E # . M :S and CL # is correct in E , U :S . By induction, E , U :S # . CL # ():UNIT. By (mT.let), we get E # . moduleNU = M :S in CL # ():UNIT, as desired. Case CL = moduleNU = M :S in CL # , backward direction: We have E # . CL():UNIT. By reversing (mT.let), we get E # . M :S and E , U :S # . CL # ():UNIT. By induction, CL # is correct in E , U :S . By definition of correctness, CL is correct in E . Lemma J.5 (peeling outer let from a multiplelet context preserves correctness) Let CL be a multiplelet context. 1. If CL = moduleNU = [T , v . ]:[X :Eq(T ## ), T # ] in CL # is correct then #CL # is correct, where # = {U .TYPE T ## , U .term v . }. 2. If CL = moduleNU = [T , v . ]:[X :Type, T # ] in CL # is correct then #CL # is correct where # = {U .TYPE h, U .term [v . ] {X h}T # h } and h = hash(N , [T , v . ]:[X :Type, T # ]). Proof. By Lemma J.4 (machine judgement characterisation of correct multiplelet contexts), the hypotheses are equiv alent to nil # . CL():UNIT and the conclusions are equivalent to nil # . #CL # ():UNIT. By the definition of machine reduction CL() -# . #CL # () in both cases. By Theorem G.18 (type preservation for machine reduction), we get nil # . #CL # ():UNIT, as desired. Now we define a metafunction hashlistofcl which maps a multiplelet context to a list of the corresponding hashes: INRIA Global abstractionsafe marshalling with hash types 77 Definition J.6 (hash list of a multiplelet context) hashlistofcl = () hashlistofcl (moduleNU = [T , v . ]:[X :Eq(T ## ), T # ] in CL) = hashlistofcl ({U .TYPE T ## , U .term v . }CL) hashlistofcl (moduleNU = [T , v . ]:[X :Type, T # ] in CL) = h, hashlistofcl (#CL) where h = hash(N , [T , v . ]:[X :Type, T # ]) and # = {U .TYPE h, U .term [v . ] {X h}T # h } Definition J.7 (no shadowing in a multiplelet context) There is no shadowing in the multiplelet context CL iff hashlistofcl CL contains no repetitions. Definition J.8 (environment corresponding to a multiplelet context) envofcl = nil envofcl (moduleNU = M :S in CL) = U :S , envofcl CL Definition J.9 (substitution corresponding to a multiplelet context) subofcl = id subofcl (moduleNU = [T , v . ]:[X :Eq(T ## ), T # ] in CL) = (subofcl #CL )# where # = {U .TYPE T ## , U .term v . } subofcl (moduleNU = [T , v . ]:[X :Type, T # ] in CL) = (subofcl #CL )# where # = {U .TYPE h, U .term [v . ] {X h}T # h } and h = hash(N , [T , v . ]:[X :Type, T # ]) Lemma J.10 (substituting through the partial type substitution associated to an environment) Let be any substitu tion of the form {# 1 # 1 , ..., # j ## j } where (D#R)#domE = ? where D = fv # 1 #...#fv # j and R = fv # 1 #...#fv # j . Then partenvsub E = partenvsub E . Proof. Induct on the length of E . The only nontrivial cases are as follows: Case E = E # , X :Eq(T ): Then partenvsub E = partenvsub E # {X#T}. Since domE # # domE , we can apply induction to get that the last composition is equal to partenvsub E # {X#T}. Since X / # D # R, we can further rewrite the composition as partenvsub E # {X#T} = partenvsub (E # ,X :Eq(T)) , as desired. Case E = E # , U :[X :Eq(T ), T # ]: similar to the previous. Note that in the previous result we refer to fv # 0 , not just # 0 , since # 0 could be of the form U .TYPE or U .term. Lemma J.11 (partial type substitution associated to an environment via left folding) Let partenvsub E be as in Definition I.2 (partial type substitution associated to an environment). Then partenvsub nil = id partenvsub x :T ,E = partenvsub E partenvsub X :Type,E = partenvsub E partenvsub X :Eq(T),E = {X T}partenvsub E partenvsub U :[X :Type,T # ],E = partenvsub E partenvsub U :[X :Eq(T),T # ],E = {U .TYPE T}partenvsub E Proof. Follows from the associativity of substitution composition. Definition J.12 (partially ok environment) An environment E is partially ok if it has no repetition in the domain and all decompositions E = E 0 , E 1 have the property ufv E 0 # domE 1 = ?. Lemma J.13 (ok environments are partially ok) If E #hm ok then E is partially ok. RR n 4851 78 Leifer, Peskine, Sewell, Wansbrough Proof. If E #hm ok then E has no repetition in the domain by Lemma E.8 (ok environments have no repetition in the domain). By Lemma E.7 (prefixes of ok environments are ok) and Lemma F.13 (ok environments have no unresolved free variables), ufv E 0 = ?, whence ufv E 0 # domE 1 = ?. Lemma J.14 (partial type substitution associated to an environment, alternative characterisation) Let E be a par tially ok environment. Let partenvsub E be as in Definition I.2 (partial type substitution associated to an environment). Let # # nil = id # # x :T ,E = # # E # # X :Type,E = # # E # # X :Eq(T),E = # # {X#T}E {X#T} # # U :[X :Type,T # ],E = # # E # # U :[X :Eq(T),T # ],E = # # {U .TYPE T}E {U .TYPE T} Then partenvsub E = # # E . Proof. Induct on the length of E . By Lemma J.11 (partial type substitution associated to an environment via left folding) most cases are trivial, except for the following: Case E = X :Eq(T ), E # : By Lemma J.11 (partial type substitution associated to an environment via left folding), partenvsub E = {X T}partenvsub E # . Since E is partially ok, ({X } # fv T ) # domE # = ?. Thus by Lemma J.10 (substituting through the partial type substitution associated to an environment), we have that the composition is equal to partenvsub {X T}E # {X T}. Since {X T}E # is also partially ok, by induction the composition is equal to # # {X T}E # {X T} = # # X :Eq(T),E # , as desired. Case E = E # , U :[X :Eq(T ), T # ]: similar. Lemma J.15 (correspondence between open and hashed interpretation of type equivalence) Let CL be a multiplelet context. Suppose 1. CL has no shadowing. 2. CL is correct. 3. None of the hashes in CL is in hashlistofcl CL. 4. For all i = 0, 1 we have that none of the hashes in T i is in hashlistofcl CL. 5. For all i = 0, 1 we have that envofcl CL # . T i :Type. Let partenvsub E be the substitution from Definition I.2 (partial type substitution associated to an environment). Then partenvsub envofcl CL T 0 = partenvsub envofcl CL T 1 iff subofcl CLT 0 = subofcl CLT 1 . Proof. We freely use Lemma J.14 (partial type substitution associated to an environment, alternative characterisation) with Lemma J.13 (ok environments are partially ok) to reason about partenvsub. We will prove some useful properties, then consider each of the mutual implications. Prelude Prelude of the prelude Suppose that CL = moduleNU = M :S in CL # . Write M :S = [T , v . ]:[X :K , T # ]. Let # = {U .TYPE typepart (N , M :S ), U .term termpart (N , M :S )} and CL ## = #CL # . By hypothesis 2 and Definition J.2 (correct multiplelet context), nil # . M :S . By Lemma E.9 (free variables of a judgement come from the environment), U / # fv M #fv S . Whether S is concrete or abstract, this implies that U / # fv typepart (N , M :S ) and U / # fv termpart (N , U :S ). Hence {U .term termpart (N , M :S )}{U .TYPE typepart (N , M :S )} = {U .TYPE typepart (N , M :S ), U .term termpart (N , M :S )} = {U .TYPE typepart (N , M :S )}{U .term termpart (N , M :S )}. INRIA Global abstractionsafe marshalling with hash types 79 We will use this fact freely throughout the proof. By Lemma E.21 (types do not contain free expression variables), {U .term termpart (N , M :S )}T i = T i , whence #T i = {U .TYPE typepart (N , M :S )}T i . By Lemma E.23 (expression substitution in environment) applied to envofcl CL = (U :S , envofcl CL # ) (which is correct by hypothesis 2 and Lemma J.3 (correct multiplelet contexts yield ok environments)), {U .term#termpart (N , M :S )}envofcl CL # = envofcl CL # , whence envofcl CL ## = #envofcl CL # = {U .TYPE#typepart (N , M :S )}envofcl CL # . We now show that the numbered hypotheses hold for CL ## in place of CL and #T i in place of T i . Prelude: concrete case: K = Eq(T ## ) 1. By definition, hashlistofcl CL ## = hashlistofcl CL, therefore, CL ## contains no shadowing since CL contains no shadowing (hypothesis 1). 2. Since CL is correct by hypothesis 2, by Lemma J.5 (peeling outer let from a multiplelet context preserves correctness), CL ## is correct. 3. Each hash in CL ## in an element of the union of the hashes in T ## and v . and CL # , which is disjoint from hashlistofcl CL = hashlistofcl CL ## by hypothesis 3. 4. Each hash in #T i = {U .TYPE#T ## }T i is an element of the union of the hashes in T ## and T i , which is disjoint from hashlistofcl CL = hashlistofcl CL ## by hypotheses 3 (for T ## ) and 4. 5. By hypothesis 5, for i = 0, 1 we have U :[X :Eq(T ## ), T # ], envofcl CL # # . T i :Type. By Lemma F.32 (simplified module and type equality substitution for type world judgements), {U .TYPE#T ## }(envofcl CL # ) # . {U .TYPE#T ## }T i :Type, i.e. envofcl CL ## # . #T i :Type. Prelude: abstract case: K = Type Let h = hash(N , M :[X :Type, T # ]). 1. By definition, (h, hashlistofcl CL ## ) = hashlistofcl CL, therefore, CL ## contains no shadowing since CL contains no shadowing (hypothesis 1). (+) Also, since CL contains no shadowing, h is not in hashlistofcl CL ## (used below). 2. Since CL is correct by hypothesis 2, by Lemma J.5 (peeling outer let from a multiplelet context preserves correctness), CL ## is correct. 3. Each hash in CL ## in an element of the union of {h} and the hashes in T ## and v . and CL # . By (+), h does not appear in hashlistofcl CL ## . By hypothesis 3, the hashes in T ## and v . and CL # do not appear in hashlistofcl CL, hence do not appear in hashlistofcl CL ## . 4. Each hash in #T i is an element of the union of {h} and the hashes in T ## and v . and T i , which is disjoint from hashlistofcl CL ## , by (+) (for h) and hypothesis 3 (for T ## and v . ) and hypothesis 4 (for T i ). 5. By hypothesis 5, for i = 0, 1 we have envofcl CL # . T i :Type, i.e. U :[X :Type, T # ], envofcl CL # # . T i :Type. Given nil # . [T , v . ]:[X :Type, T # ], we can apply Lemma F.33 (type preservation by fully carried out module substitution) to get #envofcl CL # # . #T i :Type, i.e. envofcl CL ## # . #T i :Type. We also prove that partenvsub envofcl CL ## # = #partenvsub envofcl CL # . Let D = {U } be the variables in the domain of #. Let R = fv h # fv ([v . ] {X h}T # h ) be the free variables in the range of #. By standard reasoning about free variables and substitutions, R = fv h #fv v . #(fv T # \{X }). By hypothesis 2 and Definition J.2 (correct multiplelet context), nil # . [T , v . ]:[X :Type, T # ]. By Lemma E.9 (free variables of a judgement come from the environment) and the definition of free variables, fv v . = ? and fv T # # {X }. Also, given nil # . [T , v . ]:[X :Type, T # ], we have # h ok by (hmok.hash), whence by Lemma E.9 (free variables of a judgement come from the environment) fv h = ?. By Lemma J.3 (correct multiplelet contexts yield ok environments), envofcl CL # . ok. By Lemma E.8 (ok environments have no repetition in the domain), U / # domCL # . In summary, (D # R) # dom (envofcl CL # ) = ?. By Lemma J.10 (substituting through the partial type substitution associated to an environment), partenvsub envofcl CL ## # = partenvsub #envofcl CL # # = #partenvsub envofcl CL # . Direction partenvsub envofcl CL T 0 = partenvsub envofcl CL T 1 implies subofcl CLT 0 = subofcl CLT 1 : Induct on the length of CL. Case CL = : By hypothesis, T 0 = T 1 , as desired. RR n 4851 80 Leifer, Peskine, Sewell, Wansbrough Concrete case: CL = (moduleNU = [T , v . ]:[X :Eq(T ## ), T # ] in CL # ): We can write the hypothesis as partenvsub {U .TYPE T ## }(envofcl CL # ) {U .TYPE T ## }T i is constant w.r.t. i # {0, 1}). By the prelude to the prelude, envofcl CL ## = {U .TYPE T ## }(envofcl CL # ) and #T i = {U .TYPE T ## }T i , so partenvsub (envofcl CL ## ) #T i is constant w.r.t. i # {0, 1}). By the prelude (concrete case), we can apply induction. We get that subofcl CL ## #T i is constant w.r.t. i # {0, 1}). By Definition J.9 (substitution corresponding to a multiplelet context), we have subofcl CLT i is constant w.r.t. i # {0, 1}) as desired. Abstract case: CL = (moduleNU = [T , v . ]:[X :Type, T # ] in CL # ): We can write the hypothesis as partenvsub envofcl CL # T i is constant w.r.t. i # {0, 1}). Thus, #partenvsub envofcl CL # T i is constant w.r.t. i # {0, 1}), where # = {U .TYPE h, U .term [v . ] {X h}T # h } and h = hash(N , [T , v . ]:[X :Type, T # ]). Let CL ## = #CL # . By the prelude (abstract case), #partenvsub envofcl CL # = partenvsub envofcl CL ## #. There fore partenvsub envofcl CL ## #T i is constant w.r.t. i # {0, 1}). Given the prelude (abstract case), we can apply induction. We get that subofcl (envofcl (CL ## )) #T i is constant w.r.t. i # {0, 1}). Hence by definition, subofcl (envofcl CL)T i is constant w.r.t. i # {0, 1}), as desired. Direction subofcl CLT 0 = subofcl CLT 1 implies partenvsub envofcl CL T 0 = partenvsub envofcl CL T 1 : Induct on the length of CL. Case CL = : By hypothesis, T 0 = T 1 , as desired. Concrete case: CL = (moduleNU = [T , v . ]:[X :Eq(T ## ), T # ] in CL # ): Recall that CL ## = #CL # and # = {U .TYPE T ## , U .term v . }. We have subofcl CL = subofcl CL ## #. Our hypothesis is therefore that subofcl CL ## #T i is constant w.r.t. i # {0, 1}). By the prelude (concrete case), we can apply induction. We get partenvsub envofcl CL ## #T i is constant w.r.t. i # {0, 1}). By the prelude to the prelude, partenvsub {U .TYPE T ## }envofcl CL # {U .TYPE T ## }T i is constant w.r.t. i # {0, 1}). By Lemma J.14 (partial type substitution associated to an environment, alternative characterisation), partenvsub envofcl CL = partenvsub {U .TYPE T ## }envofcl CL # {U .TYPE T ## }. Thus partenvsub envofcl CL T i is constant w.r.t. i # {0, 1}) as desired. Abstract case: CL = (moduleNU = [T , v . ]:[X :Type, T # ] in CL # ): By hypothesis, subofcl CL ## #T i is con stant w.r.t. i # {0, 1}). By the prelude (abstract case), we can apply induction, getting partenvsub envofcl CL ## #T i is constant w.r.t. i # {0, 1}). By the prelude to the prelude, partenvsub {U .TYPE T ## }envofcl CL # {U .TYPE T ## }T i is constant w.r.t. i # {0, 1}). By Lemma J.14 (partial type substitution associated to an environment, alternative characterisation), partenvsub envofcl CL = partenvsub {U .TYPE T ## }envofcl CL # {U .TYPE T ## }. Thus partenvsub envofcl CL T i is constant w.r.t. i # {0, 1}) as desired. Theorem J.16 (coincidence between undyntime and static type checking) Let CL be a multiplelet context. Suppose . CL has no shadowing. . CL is correct. . None of the hashes in CL is in hashlistofcl CL. . For all i = 0, 1 we have envofcl CL # . T i :Type and that none of the hashes in T i is in hashlistofcl CL. Then envofcl CL # . T 0 == T 1 iff subofcl CLT 0 = subofcl CLT 1 . In other words, dynamic type checking corresponds exactly to static type checking when two conditions hold: no two module have the same hash (i.e. are of identical structure after their ancestors have been substituted in); the hashes in the modules and in the types being compared are different from the hashes that will be generated by reduction (such as is the case with a user program that contains no hashes at all). INRIA Global abstractionsafe marshalling with hash types 81 Proof. Immediate from Lemma J.15 (correspondence between open and hashed interpretation of type equivalence) and Lemma I.6 (open interpretation of type equivalence). Definition J.17 (list of external names in a multiplelet context) Define the list of external names in a multiplelet context by structural induction: enlistofcl = () enlistofcl (moduleNU = M :S in CL) = (N , enlistofcl CL) Definition J.18 (external name shadowing) Let CL be a multiplelet context. We say that CL has external name shadowing iff enlistofcl CL contains duplicates. Lemma J.19 (nonshadowing through external names) If CL has no external name shadowing then CL has no shad owing. Proof. Trivial by induction on the length of CL. Lemma J.20 (generalised machine judgement characterisation of correct multiplelet contexts) If nil # 0 CL.e:UNIT then CL is correct. Proof. By reversing (mT.let) once for each binding in CL, substituting envofcl CL # 0 ():UNIT for envofcl CL # 0 e:UNIT and reapplying (mT.let)'s again, we get nil # 0 CL.():UNIT. By Lemma J.4 (machine judgement characterisation of correct multiplelet contexts), CL is correct. Corollary J.21 (coincidence between undyntime and static type checking with nonrepeated external names) Sup pose that nil # 0 CL.e:UNIT, that CL contains no hashes, and that CL has no external name shadowing. Assume that T 0 and T 1 contain no hashes and envofcl CL # . T i :Type for i = 0, 1. Then envofcl CL # . T 0 == T 1 iff subofcl CLT 0 = subofcl CLT 1 . Proof. By Lemma J.19 (nonshadowing through external names), CL has no shadowing. By Lemma J.20 (generalised machine judgement characterisation of correct multiplelet contexts), CL is correct. Thus we can apply Theorem J.16 (coincidence between undyntime and static type checking). RR n 4851 82 Leifer, Peskine, Sewell, Wansbrough K Table of theorems and definitions Th. 4.1 type preservation for compiletime, expression, and network reduction . . . . . . . . . . . . . . . 22 Th. 4.2 progress for compiletime reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Th. 4.3 progress for expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 Th. 4.4 determinacy for compiletime and expression reduction . . . . . . . . . . . . . . . . . . . . . . . 22 Th. 4.5 decidability of type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Th. 4.6 erasure preserves reduction outcomes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 Th. 4.7 coincidence between dynamic and static type checking . . . . . . . . . . . . . . . . . . . . . . . . 23 Def. E.1 smaller proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Def. E.2 domain of an environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.3 nonmembership in domain is interpreted trivially . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.4 colours have to be ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.5 hashes have to be ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.6 environments have to be ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.7 prefixes of ok environments are ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.8 ok environments have no repetition in the domain . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Lem. E.9 free variables of a judgement come from the environment . . . . . . . . . . . . . . . . . . . . . . 36 Def. E.10 correctness judgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.11 type world judgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.12 hashes in something . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.13 partial order on colours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.14 pvu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.15 alternate, informal definition of pvu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Lem. E.16 monotonicity of pvu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 Def. E.17 substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Lem. E.18 stability of values by substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Lem. E.19 computing the pvu of a type world judgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 Lem. E.20 connection between fv and fse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Lem. E.21 types do not contain free expression variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Lem. E.22 environments do not contain free expression variables . . . . . . . . . . . . . . . . . . . . . . . . 39 Lem. E.23 expression substitution in environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Lem. F.1 ``type of a machine'' judgements are not used to prove other coloured judgements . . . . . . . . . . 39 Lem. F.2 colour stripping judgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Lem. F.3 weakening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Lem. F.4 merging environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Lem. F.5 combined weakening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Lem. F.6 kinds are smaller than Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Lem. F.7 relating typeiskind and subkinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Lem. F.8 components of modules are ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Lem. F.9 types are ok provided their hashes are . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Lem. F.10 colour change preserves type okedness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Def. F.11 unresolved free variables of an environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Lem. F.12 computing unresolved free variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Lem. F.13 ok environments have no unresolved free variables . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Lem. F.14 type preservation by substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Lem. F.15 strengthening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Def. F.16 visible typepart of a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Def. F.17 visible termpart of a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Def. F.18 set of equations used to type a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Lem. F.19 reflexivity of kind equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Lem. F.20 weakening kind to ok kind in the environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Lem. F.21 things have to be ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Lem. F.22 weakening kind in the environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Lem. F.23 type preservation by guarded expression variable substitution . . . . . . . . . . . . . . . . . . . . 47 INRIA Global abstractionsafe marshalling with hash types 83 Lem. F.24 type equivalence is a congruence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Lem. F.25 type substitution in equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Lem. F.26 reversing subsignaturing judgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Lem. F.27 reversing module value variable typing judgement . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Lem. F.28 obtaining module value variable typing judgement . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Lem. F.29 type preservation by module substitution in coloured judgements . . . . . . . . . . . . . . . . . . 49 Lem. F.30 type world judgements do not contain free expression variables . . . . . . . . . . . . . . . . . . . 51 Lem. F.31 type preservation by module substitution in coloured judgements for type world judgements . . . . 51 Lem. F.32 simplified module and type equality substitution for type world judgements . . . . . . . . . . . . . 51 Lem. F.33 type preservation by fully carried out module substitution . . . . . . . . . . . . . . . . . . . . . . 51 Lem. G.1 shortening typing proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Lem. G.2 reversing typing proof through a context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Lem. G.3 transitivity of kind equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Lem. G.4 discreteness of subkinding below Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Def. G.5 bare bones environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Def. G.6 purely abstract environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Lem. G.7 signature rewriting in a type world judgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Lem. G.8 type substitution in a purely abstract environment . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Lem. G.9 equality kinding in an uncontributing environment . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Lem. G.10 equivalence of small types in an uncontributing environment . . . . . . . . . . . . . . . . . . . . . 54 Lem. G.11 type decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Lem. G.12 decomposition of type equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Lem. G.13 structural dependence of values on their types . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Lem. G.14 triviality of type equivalence in a trivial environment . . . . . . . . . . . . . . . . . . . . . . . . . 57 Th. G.15 type preservation for expression reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Lem. G.16 type preservation for network structural congruence . . . . . . . . . . . . . . . . . . . . . . . . . 59 Cor. G.17 type preservation for network reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 Th. G.18 type preservation for machine reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Def. H.1 waiting for communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Def. H.2 dormant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Lem. H.3 dormancy in context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Lem. H.4 reduction in context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Def. H.5 legitimately stuck expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Th. H.6 progress of expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 Cor. H.7 progress of networks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Th. H.8 progress of machines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Th. H.9 determinism of machine reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Lem. H.10 values do not reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Th. H.11 determinism of expression reduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Dis. H.12 strength of determinism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Def. I.1 revelation of the implementation of a hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Def. I.2 partial type substitution associated to an environment . . . . . . . . . . . . . . . . . . . . . . . . 65 Lem. I.3 a purely abstract suffix does not change the substitution . . . . . . . . . . . . . . . . . . . . . . . 66 Lem. I.4 stability of types through revelation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Lem. I.5 distinction of fresh type variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Lem. I.6 open interpretation of type equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Th. I.7 semantic interpretation of type world judgements . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Lem. I.8 uniqueness of expression typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Def. I.9 reconstructed type of an expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Lem. I.10 simplicity of the reconstructed type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Lem. I.11 assurance of correctness of the reconstructed type . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Lem. I.12 validity of the reconstructed type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Cor. I.13 deciding expression typing through type reconstruction . . . . . . . . . . . . . . . . . . . . . . . 72 Th. I.14 decidability of type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Def. I.15 user source program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 RR n 4851 84 Leifer, Peskine, Sewell, Wansbrough Dis. I.16 decidability of type checking for user programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Def. I.17 bracket elimination subsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Lem. I.18 determinism of bracket elimination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Lem. I.19 termination of bracket elimination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Def. I.20 bracket erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Def. I.21 colourless proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Def. I.22 bracketless expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Def. I.23 bracketless reductions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Lem. I.24 progress and determinism of bracketless expression reduction . . . . . . . . . . . . . . . . . . . . 74 Th. I.25 bracket erasure preserves expression reduction outcomes . . . . . . . . . . . . . . . . . . . . . . . 74 Th. I.26 bracket erasure preserves reduction outcomes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Th. I.27 bracket erasure does not add expression reduction outcomes . . . . . . . . . . . . . . . . . . . . . 75 Th. I.28 bracket erasure does not add reduction outcomes . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Def. I.29 type erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Th. I.30 type erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Def. J.1 multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Def. J.2 correct multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Lem. J.3 correct multiplelet contexts yield ok environments . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Lem. J.4 machine judgement characterisation of correct multiplelet contexts . . . . . . . . . . . . . . . . . 76 Lem. J.5 peeling outer let from a multiplelet context preserves correctness . . . . . . . . . . . . . . . . . . 76 Def. J.6 hash list of a multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Def. J.7 no shadowing in a multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Def. J.8 environment corresponding to a multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . 77 Def. J.9 substitution corresponding to a multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Lem. J.10 substituting through the partial type substitution associated to an environment . . . . . . . . . . . 77 Lem. J.11 partial type substitution associated to an environment via left folding . . . . . . . . . . . . . . . . 77 Def. J.12 partially ok environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Lem. J.13 ok environments are partially ok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Lem. J.14 partial type substitution associated to an environment, alternative characterisation . . . . . . . . . 78 Lem. J.15 correspondence between open and hashed interpretation of type equivalence . . . . . . . . . . . . 78 Th. J.16 coincidence between undyntime and static type checking . . . . . . . . . . . . . . . . . . . . . . 80 Def. J.17 list of external names in a multiplelet context . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Def. J.18 external name shadowing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Lem. J.19 nonshadowing through external names . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Lem. J.20 generalised machine judgement characterisation of correct multiplelet contexts . . . . . . . . . . 81 Cor. J.21 coincidence between undyntime and static type checking with nonrepeated external names . . . . 81 INRIA Global abstractionsafe marshalling with hash types 85 References [ACPP91] Mart n Abadi, Luca Cardelli, Benjamin Pierce, and Gordon Plotkin. Dynamic typing in a statically typed language. ACM TOPLAS, 13(2):237--268, 1991. [ACPR95] Mart n Abadi, Luca Cardelli, Benjamin Pierce, and Didier R emy. Dynamic typing in polymorphic languages. J. Functional Programming, 5(1):111--130, 1995. [Ali03] The Alice Project. Alice manual: Pickling. http://www.ps.unisb.de/alice/manual/pickling. html, 2003. [B + 94] Andrei Z. Broder et al. Fingerprint.i3. http://research.compaq.com/SRC/m3sources/html/ fingerprint/src/Fingerprint.i3.html, 1994. [BHS + 03] Gavin Bierman, Michael Hicks, Peter Sewell, Gareth Stoyle, and Keith Wansbrough. Dynamic rebinding for marshalling and update, with destructtime #. In Proc. ICFP 2003, 2003. Full version available as UCAMCLTR568. http://www.cl.cam.ac.uk/~pes20/. [BNOW95] Andrew Birrell, Greg Nelson, Susan Owicki, and Edward Wobber. Network objects. Software -- Practice & Experience, 25(S4):87--130, 1995. Available in slightly different form as SRC115 revised. [CL90] Luca Cardelli and Xavier Leroy. Abstract types and the dot notation. Technical Report 56, DEC SRC, March 10 1990. [DCH03] Derek Dreyer, Karl Crary, and Robert Harper. A type system for higherorder modules. In Proc. 30th POPL, New Orleans, pages 236--249, 2003. [Dug02] Dominic Duggan. Typesafe linking with recursive DLLs and shared libraries. ACM TOPLAS, 24(6):711-- 804, 2002. [FW00] Jun Furuse and Pierre Weis. Entr ees/sorties de valeurs en Caml. In J. Francophones des Langages Applicatifs, 2000. [GMZ00] Dan Grossman, Greg Morrisett, and Steve Zdancewic. Syntactic type abstraction. ACM TOPLAS, 22(6):1037--1080, 2000. [HL94] Robert Harper and Mark Lillibridge. A typetheoretic approach to higherorder modules with sharing. In Proc. 21st POPL, 1994. [HM95] Robert Harper and Greg Morrisett. Compiling polymorphism using intensional type analysis. In Proc. 22nd POPL, pages 130--141, 1995. [HWC00] Michael Hicks, Stephanie Weirich, and Karl Crary. Safe and flexible dynamic linking of native code. In Proc. 3rd Workshop on Types in Compilation, pages 147--176, 2000. [JoC] JoCaml. http://pauillac.inria.fr/jocaml/. [Kna95] F. Knabe. Language Support for Mobile Agents. PhD thesis, Carnegie Mellon University, December 1995. [Ler94] Xavier Leroy. Manifest types, modules, and separate compilation. In Proc. 21st POPL, pages 109--122, 1994. [Ler95] Xavier Leroy. Applicative functors and fully transparent higherorder modules. In Proc. 22nd POPL, pages 142--153, 1995. [LPSW03] James J. Leifer, Gilles Peskine, Peter Sewell, and Keith Wansbrough. Global abstractionsafe marshalling with hash types. In Proc. 8th ICFP, 2003. Available from http://pauillac.inria.fr/~leifer/ research.html. [Mac84] David MacQueen. Modules for Standard ML. In Proc. 1984 ACM Symp. LISP and Func. Prog., pages 198--207, 1984. RR n 4851 86 Leifer, Peskine, Sewell, Wansbrough [Mic01] Microsoft Corporation. .NET Framework developer's guide: Serializing objects. http://msdn. microsoft.com/library/enus/cpguide/html/cpovrserializingobjects.asp, 2001. [MP88] John C. Mitchell and Gordon D. Plotkin. Abstract types have existential type. ACM TOPLAS, 10(3):470--502, July 1988. [MTH90] R. Milner, M. Tofte, and R. Harper. The Definition of Standard ML. MIT Press, 1990. [OCa] Objective Caml. http://caml.inria.fr. [PS00] Benjamin Pierce and Eijiro Sumii. Relating cryptography and polymorphism. http://web.yl.is.s. utokyo.ac.jp/~sumii/pub/, July 16 2000. Substantially revised version to appear in J. Comp. Security. [Rob96] M. J. B. Robshaw. On recent results for MD2, MD4 and MD5. RSA Laboratories' Bulletin, (4), November 12 1996. [Ros02] Andreas Rossberg. Dynamic opacity for abstract types. Technical report, Programming Systems Lab, Uni versit at des Saarlandes, 2002. http://www.ps.unisb.de/Papers/abstracts/opaque.html. [Sew01] Peter Sewell. Modules, abstract types, and distributed versioning. In Proc. 28th POPL, pages 236--247, 2001. [SH00] Christopher A. Stone and Robert Harper. Deciding type equivalence in a language with singleton kinds. In Proc. 27th POPL, pages 214--227, 2000. [Sun02] Sun Microsystems. Java object serialization specification 1.4.4. http://java.sun.com/j2se/1.4.1/ docs/guide/serialization/, 2002. [SWP99] Peter Sewell, Pawel/ T. Wojciechowski, and Benjamin C. Pierce. Locationindependent communication for mobile agents: a twolevel architecture. In Internet Programming Languages, LNCS 1686, pages 1--31, 1999. [TLK96] Bent Thomsen, Lone Leth, and TsungMin Kuo. A Facile tutorial. In CONCUR'96, LNCS 1119, pages 278--298, 1996. [Wei00] Stephanie Weirich. Typesafe cast: Functional pearl. In Proc. ICFP, Montreal, pages 58--67, 2000. [Wei02] Stephanie Weirich. Higherorder intensional type analysis. In Proc. 11th ESOP, LNCS 2305, Grenoble, France, 2002. [ZGM99] Steve Zdancewic, Dan Grossman, and Greg Morrisett. Principals in programming languages: A syntactic proof technique. In Proc. ICFP, Paris, pages 197--207, Sep 1999. main body: $Revision: 1.211 $ $Date:#2003/06/25#14:47:25#$ appendices: $Revision:#1.2#$ $Date: 2003/06/25 09:42:26 $ INRIA Unit de recherche INRIA Rocquencourt Domaine de Voluceau Rocquencourt BP 105 78153 Le Chesnay Cedex (France) Unit de recherche INRIA Lorraine : LORIA, Technople de NancyBrabois Campus scientifique 615, rue du Jardin Botanique BP 101 54602 VillerslsNancy Cedex (France) Unit de recherche INRIA Rennes : IRISA, Campus universitaire de Beaulieu 35042 Rennes Cedex (France) Unit de recherche INRIA RhneAlpes : 655, avenue de l'Europe 38330 MontbonnotStMartin (France) Unit de recherche INRIA Sophia Antipolis : 2004, route des Lucioles BP 93 06902 Sophia Antipolis Cedex (France) diteur INRIA Domaine de Voluceau Rocquencourt, BP 105 78153 Le Chesnay Cedex (France) http://www.inria.fr ISSN 02496399