Formalization of the BitC Type System

SRL Technical Report SRL2007-01

Swaroop Sridhar, Jonathan S. Shapiro, Ph.D., Scott F. Smith, Ph.D.
Dept. of Computer Science
Johns Hopkins University

Table of Contents

1 Abstract

2 Introduction

3 Mutability Model and Type Inference

3.1 BitC

3.2 Mutability Model in BitC

3.3 Copy Compatibility

3.4 Type Inference

3.4.1 Why Should We Infer Mutability?

3.4.2 Incompleteness of Inference

3.4.3 Inference Considerations

3.5 Type Inference in BitC

4 Formalization

4.1 The Language

4.2 Dynamic Semantics

4.3 Static Semantics

4.3.1 Copy Compatibility

4.3.2 Compatibility of Function Types

4.3.3 Copy Coercions

4.3.3.1 Problem with Subsumption-like Coercion Rule

4.3.4 Location Semantics

4.3.5 Declarative Type Rules

4.3.6 Generalization

4.3.6.1 DEFINITION: Free Type Variables

4.3.6.2 DEFINITION: Value Restriction

4.3.7 Soundness of Declarative system

4.3.7.1 DEFINITION: Stack and Heap Typing

4.3.7.2 LEMMA: Inversion of Typing Relation

4.3.7.3 LEMMA: Inversion of Copy Coercion

4.3.7.4 LEMMA: Canonical Forms

4.3.7.5 LEMMA: Progress

4.3.7.6 LEMMA: Weakening

4.3.7.7 LEMMA: Value Substitution

4.3.7.8 LEMMA: Location Substitution

4.3.7.9 LEMMA: Stack and Heap Assignment

4.3.7.10 LEMMA: Preservation

4.3.7.11 DEFINITION: Stuck State

4.3.7.12 THEOREM: Type Soundness

4.3.8 Equational Inference Algorithm

4.3.9 Unification

4.3.9.1 DEFINITION: Constraint Set Closure

4.3.9.2 Solving the Constraints

4.3.10 Soundness of Equational Inference

4.3.10.1 THEOREM: Soundness of Constraint Closure

4.3.10.2 THEOREM: Correctness of Constraint Solver

4.3.10.3 THEOREM: Correctness of Unification

4.3.10.4 THEOREM: Decidability of Unification

4.3.10.5 LEMMA: Substitution on Declarative Derivation

4.3.10.6 LEMMA: Weakening of Substitutions

4.3.10.7 LEMMA: Composition of Solutions and Substitutions

4.3.10.8 THEOREM: Soundness of Equational Inference

4.3.11 Inference with Eager Unification

4.3.12 Unification

4.3.13 Solving Copy Compatibility Constraints

4.3.14 Soundness of Eager Inference

4.3.14.1 DEFINITION: Normalization of Constrained Types

4.3.14.2 DEFINITION: Normalization of Constraint Sets

4.3.14.3 DEFINITION: Normalization of Contexts

4.3.14.4 DEFINITION: Solvable Entities

4.3.14.5 DEFINITION: Consistency of Solvable Entities

A (possibly) constrained type or context ω is said to be consistent, that is,  ⊧ ω iff Ν(ω) =  \ C and  ⊧ .

4.3.14.6 DEFINITION: Reachable Store

4.3.14.7 DEFINITION: MTVs

4.3.14.8 DEFINITION: FTVs (Extension)

4.3.14.9 DEFINITION: NTVs

4.3.14.10 DEFINITION: TVs

4.3.14.11 DEFINITION:  ⊢sol 

4.3.14.12 DEFINITION:  ⊧sol 

4.3.14.13 DEFINITION: Strong Consistency

4.3.14.14 DEFINITION: Compatible Solutions (Substitutions)

4.3.14.15 DEFINITION: Equivalent Substitutions

4.3.14.16 DEFINITION: Sub-Substitutions

4.3.14.17 THEOREM: Correctness of Eager Unification

4.3.14.18 THEOREM: Correctness of the Constraint Solver

4.3.14.19 THEOREM: Totality of the Constraint Solver

4.3.14.20 THEOREM: Decidability of Unification and Solver

4.3.14.21 LEMMA: Properties of  ⊧sol 

4.3.14.22 LEMMA: Properties of Equivalent Substitutions

4.3.14.23 LEMMA: Strong Consistency Implies Consistency

4.3.14.24 LEMMA: Properties of Compatible Constrained Types

4.3.14.25 LEMMA: Relationship of Solutions

4.3.14.26 LEMMA: Uniqueness of Solutions Produced by the Solver

4.3.14.27 LEMMA: Relationship between  ⊢solve  and  ⊢sol 

4.3.14.28 LEMMA: Commutativity of  +  over Solvable Entities

4.3.14.29 LEMMA: Weakening over Solvable Entities

4.3.14.30 LEMMA: Extension to Weakening Lemma 4.3.7.6.

4.3.14.31 LEMMA: Substitution on Strongly Compatible Entities

4.3.14.32 LEMMA: Substitution Preserves Compatibility of Solutions

4.3.14.33 LEMMA: Unification Preserves Strong Consistency

4.3.14.34 LEMMA: Corollary to Lemma 4.3.14.33.

4.3.14.35 LEMMA: Unification of Maybe Types

4.3.14.36 LEMMA: Corollary to Lemma 4.3.14.35

4.3.14.37 LEMMA: Inferred Substitutions are Consistent.

4.3.14.38 LEMMA: Inferred Substitutions are Cumulatively Consistent.

4.3.14.39 LEMMA: Substitution on Declarative Derivation [Extension to Lemma 4.3.10.5].

4.3.14.40 LEMMA: Corollary to Lemma 4.3.14.39.

4.3.14.41 LEMMA: Elimination of Irrelevant Substitutions.

4.3.14.42 LEMMA: Equivalent Substitutions Preserve Declarative Derivation.

4.3.14.43 LEMMA: Compatible Substitutions Preserve Declarative Derivation.

4.3.14.44 LEMMA: Corollary to Lemma 4.3.14.43.

4.3.14.45 LEMMA: Corollary to Lemma 4.3.14.44.

4.3.14.46 LEMMA: Altering Shallow Mutability.

If

4.3.14.47 LEMMA: Corollary to Lemma 4.3.14.46.

If

4.3.14.48 LEMMA: Corollary to Lemma 4.3.14.47.

If

4.3.14.49 LEMMA: Valid Substitutions Preserve Declarative Derivation.

4.3.14.50 THEOREM: Soundness of Eager Inference

4.3.14.51 LEMMA: Corollary–1 to Soundness of Eager Inference

4.3.14.52 THEOREM: Corollary–2 to Soundness of Eager Inference

5 Implementation

6 Related Work

7 Conclusion


Chapter 1: Abstract

There is a persistent gap between modern language designs and the requirements of systems programmers. Systems programs rely on fine-grain control of data representation and use of state to achieve performance, conformance to hardware specification, and temporal predictability. Modern type systems such as those used in ML and Haskell rely on boxed representation of composite types and restricted support for mutability to enable features such as polymorphism, type inference, and sound type systems. No current language fully supports both feature sets, partly because no mutability model has been proposed that adequately combines explicitly unboxed types with consistent typing of mutability. C's ``const'' type qualifier is unsound, while from a systems programming perspective ML's ``ref'' construct is insufficiently expressive.

This paper introduces a new type system in which deep mutability is a first-class component of type. The type system is provably sound, expresses unboxed composite types, supports polymorphism and type inference, and reduces the amount of state that must be handled by stateful verification methods. The resulting system integrates these features in a way that is subjectively natural to systems programmers — in particular supporting naïve programmer intuitions about locations. A key element of this success is the adoption of certain ``hinting'' mechanisms that guide the inference process to the programmer-expected result. A practical and efficient implementation of this type system and inference mechanism has been constructed as part of the BitC programming language.

Chapter 2: Introduction

Safe systems programming is a focal topic for researchers in systems as well as programming languages and verification communities in the recent years. Although there seems to be a consensus about the fact that thirty years of programming in high level assembly is long enough [5], the level of correctness guarantee sought in the proposed alternatives vary greatly — ranging from memory safety [ 17 ,27 ] to static analysis or model checking to validate certain safety properties [2] to full semantic correctness verification [12] We are currently working on the Coyotos project [39], which investigates the possibility of a fully verified implementation of a secure microkernel. In order to achieve this, there is a need for a language framework that has the the right combination of expressiveness, formally founded semantics, control over low-level representation, and integration with modern verification technology.

Modern programming languages such as ML [26] or Haskell [19] provide newer, stronger, and more expressive type systems than systems programming languages such as C [ 22 ,16 ] or Ada [15]. These features improve the robustness and safety of programs, and it is desirable to incorporate them into languages that can be used for high-performance systems codes. Polymorphism facilitates better code reuse and organization. Type inference achieves the consistency advantages of static typing with a lower burden on the programmer, facilitating more rapid prototyping and development.

A key property of these ML-like languages is that the mutability model is a part of the type system. A term subset language suitable for modern logical frameworks [ 29 ,28 ,21 ,6 ] can be achieved simply by removing the ``ref'' construct. ML Types are decisive about the mutability of all locations (memory cells): every location has one and only one type across all aliases. Tools that perform static analysis or model checking benefit from the ML mutability model because conclusions drawn about location immutability need never be conservative [1]. This also reduces the state space that must be examined by stateful reasoning techniques. Since mutable values must not be let-polymorphic [3650], automatic inference of polymorphism requires a mathematically well-founded notion of mutability.

Unfortunately, most modern programming language implementations do not support certain key features that are essential for systems programming — most notably support for low-level representation management, and support for first class mutability. The support for mutability must be first class in the sense that a value of any type (and not just references) can be mutable, and we should be able to specify mutability at field level granularity.

Efforts have been made to retrofit safety and other high level language features into C-like low level languages. Systems like CCured [27] and Cyclone [17] use a combination of sound static analysis techniques, dynamic checks, user annotations, pointer restrictions and conservative garbage collection to ensure memory safety of C (or C-like) programs. First, a fundamental problem with the safe C-like languages is the lack of a rigorous semantic specification. Second, these systems conform to the C model of mutability, where all data is mutable (that is, types do not authentically distinguish mutable and immutable values see section 3.2). This promiscuity of mutability presents a great challenge for integration into a verification framework — we will either have to perform complex and whole program alias analyses, or draw conservative and weak conclusions about the semantic behavior of programs. The mutability model also limits the ability to perform polymorphic type inference. For example, Cyclone supports first class polymorphism only for function definitions [1153] that are explicitly annotated with a polymorphic type.

In this paper, we propose a new language BitC [55] which integrates all of the desirable features mentioned above into a single, consistent language framework. BitC is a type safe, higher order programming language that exposes machine-level representation of types, supports polymorphic type inference and well-founded first class mutability. BitC is a call-by-value expression language. Support for unboxed mutability means that we can allow some freedom in the compatibility of types with respect to their mutability at copy boundaries. This kind of compatibility has interesting ramifications for type inference, because there is no longer a unique way to type an expression. In this paper, we discuss some of these issues, and present a solution based on a simple extension to the Hindley-Milner inference algorithm [25]. A partial sketch of this approach was given in an earlier workshop paper [38]; the current paper presents the first complete treatment, consisting of a formalization and proof of soundness, as well as an implementation.

Chapter 3: Mutability Model and Type Inference

3.1 BitC

In this section, we give a brief introduction to BitC and the facilities available in BitC to suit systems programming. BitC supports a rich set of primitive datatypes and bit-fields: int8, int64, (bitfield uint32 8), float, double, etc. It also supports type classes [18] to support operator overloading over these types. For example:1

(define (inc x) (+ x 1))
inc: (forall ((Arith 'a) (IntLit 'a)) (fn ('a) 'a))

Like C, BitC provides full control over data structure representation, which is necessary for high-performance systems programming. Composite types (structures and unions) may be explicitly declared as boxed (:ref) or unboxed (:val). The default representation is boxed. For example:

 (defstruct (pair 'a 'b):val fst: 'a snd: 'b)
 (defunion (list 'a) nil 
                    (cons car:'a cdr:(list 'a)))

BitC also has homogeneous aggregate types in the form of arrays (unboxed) and vectors (boxed).

BitC is a stateful language. Variables or individual fields may be given a mutable type. In the following example, rec defines an unboxed structure in which one of the fields is mutable, while xyz is a stack variable that is mutable.

(defstruct rec:val id:uint32 mVal:(mutable int64))
(let ((xyz:(mutable bool) #t)) ...)
xyz: (mutable bool)

The dup operator performs a heap copy and returns the corresponding heap location. For example:

(define bPtr (dup #t))
bPtr: (ref bool)

Unlike ML, heap copy does not entail mutability. The type of bPtr in the above example is (ref bool), which just states that bPtr is a reference (pointer) to a location containing an immutable value of type bool. Expressions that have a reference type can later be dereferenced through the deref (or ^) operator. BitC does not have an address-of (&) operator to obtain the address of stack locations. As in the case of most safe languages, heap locations are first class values, but stack locations are not. We use the unqualified term ``location'' wherever both stack and heap locations are applicable.

BitC supports let-polymorphism as in ML. Polymorphism is supported even over unboxed types. However, in some cases, we may want to restrict the polymorphism to reference types only, in order to ensure that the definition is not polyinstantiated (or otherwise adjusted to handle unboxed types). In BitC, there is a built in type classes called ref-type to which all boxed types belong. Now, we can write a polymorphic identity function that only works on reference types as:

 (define id:(forall ((ref-type 'a)) (fn ('a) 'a))
   (lambda (x) x))

There are several syntactic constructs in BitC to support a metalanguage, which allows application-specific safety properties to be embedded within a program. This meta-language will eventually be interfaced with a theorem prover in order to discharge the proof obligations expressed by the programmer.

3.2 Mutability Model in BitC

Traditionally, there are two models of mutability studied in the case of imperative languages. One of them is the ML model, where there is a clear separation between name bindings and updatable locations. All updatable (mutable) locations live in the heap within ``ref cells''. Fetching the value inside a ref cell requires an explicit dereferencing operation. The major advantage of this approach is that types are definitive about the mutability of every location, across all aliases. In this sense, we can say that the support for mutability is mathematically ``well-founded.''

The other well known model of mutability is the C model, wherein the support for mutability is ``first-class'' in the sense that mutation of stack variables and unboxed values are supported. There is a notion of lvalues which are expressions that can be the target of an assignment, and rvalues, that are otherwise used in computations. The extraction of the value from a (mutable) location is implicit, and does not require dereferencing. However, in this model, types cannot distinguish mutable values from immutable ones. For example, in C (and safe-C languages) it is legal to write:

const int *cpi = ...;
int *pi = cpi;   // Warning only.
*pi = 5;         // OK!

The alleged ``constness'' of the location pointed to by cpi is a local property with (only) respect to this alias (cpi) and not a statement of true immutability of the location referenced by it. The compiler or other analytical engines are not entitled to believe that certain locations or fields are constant even if so declared.

BitC supports well-founded first class mutability. Similar to ML, we impose the the ``one location, one type'' rule.

(let ((cpi:(ref int32) (dup 10)))
  (let ((pi:(ref (mutable int32)) cpi)) ;ERROR

In order to support unboxed mutability, we still need to have a notion of lvalues. It is necessary for both preserving the programmer's mental model of the relationship between locations and storage, as well as ensuring that compiler transformations are semantics preserving. In an assignment context, the following syntactic forms in BitC accept only lvalues at positions indicated as lval, and return lvalues (except set!, which returns unit):

id
(array-nth lval ndx)
(vector-nth e ndx)
(member lval ident)
(deref e)
(set! lval e)

C's const notion of immutability-by-alias offers localized checking of immutability properties, and encourages good programming practice by serving as documentation of programmers intentions. Other systems have proposed immutability-by-name [7] (a simplified form of const), referential immutability [35] (immutability-by-reference that can be enforced shallowly or transitively) etc., which have versatile applications. These techniques are orthogonal and complementary to the immutability-by-location property that we have in BitC. For example, we could have types like (const (mutable τ)) that can express both global and local usage properties of a location.

3.3 Copy Compatibility

Since BitC is a call-by-value language, it is desirable that we allow some freedom in the compatibility of types with respect to their mutability at argument passing, assignment, and binding boundaries. We will refer to this as copy compatibility, denoted by ≅. For example:

(define (plus1 x:(mutable int32)) 
  (set! x (+ x 1)) x)       
plus1: (fn (mutable int32) int32)

(define v1 (plus1 10:int32))
v1: int32

In the application (plus1 10:int32) above, the type of the actual parameter 10 is int32 and that of the formal parameter x is (mutable int32). Here, we allow int32 ≅ (mutable int32).

Copy compatibility need not be restricted to the outermost mutability compatibility, but must not extend past a reference boundary. This is necessary to enforce the the invariant that every location must have a unique type, since the target of the reference is not copied. We define copy compatibility as follows:

  • τ ≅ (mutable τ) for any type τ (direct compatibility).

  • (array τ) ≅ (array (mutable τ)). Arrays are unboxed types, the entire array is copied by value.

  • (vector τ) ≇ (vector (mutable τ))

  • (ref τ) ≇ (ref (mutable τ)).

  • Compatibility of composites: Composite types are copy compatible if and only if all of their fields have equal types in the case of boxed types and copy compatible types in the case of unboxed types. In order to enforce this rule, the following restriction must be imposed for unboxed parametric types: instantiations of any type variable used within another reference type must match exactly. For example in the following structure:

    (defstruct (St 'a 'b):val f1:'a f2:(ref 'b))

    instantiations of 'a are only required to be copy compatible, but instantiations of 'b must match exactly.

    (St char char) ≅ (St (mutable char) char)
    (St char char) ≇ (St char (mutable char))

In addition to argument passing, and new variable bindings, we can also permit copy compatibility at argument and return position of all expressions that do not return an lvalue. This is because we can think of them in terms of equivalent SSA forms, which introduce temporary bindings for all intermediate results. For example, we can permit the various branches of conditional expressions to have to have different but copy compatible types:

(if #t m:(mutable int32) 10:int32)

3.4 Type Inference

When an exact type compatibility requirement is replaced in the language design by copy compatibility, it is no longer possible to infer a unique type for the expression. For example, in the following expression:

(let ((p 10:int32)) ... )

we know that the type of the literal 10 is int32, but the type of p could either be int32, or (mutable int32). When we cannot ascertain the mutability status of a bound identifier, we give it the so-called ``maybe'' type (?mutable? int32), which is actually a shorthand for the constrained type α \ {α ≅ int32}. That is, it is undecided as to whether the actual type is int32, or (mutable int32), and can be resolved by later unification. If it is not, a choice must eventually be fixed by the language definition. For example, the let form:

(let ((p (pair 1:int32 #t))) ... )

introduces copy compatibility at both the pair constructor application, and the formation of a new binding for p. The types assigned by the compiler are:

p:(?mutable? (pair (?mutable? int32) 
                   (?mutable? bool))

3.4.1 Why Should We Infer Mutability?

It is natural to ask why mutability should be inferred at all. That is: why not require explicit annotation for all mutable values, and infer immutable types by default? In an expression language with copy compatibility, inferring immutable types by default will result in a proliferation of type annotations. Constructor applications, (polymorphic) type instantiations, accessor functions, etc. will have to be explicitly annotated with their types. For example, if fst is an accessor that returns the first element of a pair and m is a variable with type (mutable bool), we will have to write:

(define xyz 
  (vector (fst (pair m 10)
               :(pair (mutable bool) int32)))
  :(vector (mutable int32)))

Pierce and Turner have conducted a study on the impact of requiring explicit type annotations in higher order typed programming languages [31]. Their measurements on about 1,60,000 lines of Objective Caml [23] code revealed that polymorphic type instantiations happen every third line of code, anonymous function definitions happen once in 10-100 lines of code and local bindings occur about once every twelve lines. Therefore, in BitC, not inferring mutability would make type inference a liability rather than an asset in the case of stateful programs.

3.4.2 Incompleteness of Inference

The key idea of maybe types is to defer commitments about the mutability status of types, and thus infer most-general types wherever possible. BitC is a let-polymorphic language and enforces the value restriction [3650]. This means that the decision about the mutability of types cannot be deferred past their let bindings, since mutable types must not be generalized. For example, in the case of the expression:

(let ((p nil)) ... )

we cannot give p the type

(forall ('a) (?mutable? (list 'a)))

We must instead choose one of:

(forall ('a) (list 'a)) ; polymorphic
(?mutable? (list 'a))   ; monomorphic

That is, there is no principal type that we can infer for p. Given this, we must fix these maybe types to either mutable or immutable at a let-boundary. In the next section, we will identify various choices for how to fix these maybe types, and discuss their merits and limitations.

In contrast, ML is able to infer principal types since its inference rules are purely syntax directed. In BitC, we trade completeness of inference to obtain a more expressive language without making any major changes to the core type system.

3.4.3 Inference Considerations

In this section we outline certain design considerations for a type inference scheme in the presence of copy compatibility. An ideal scheme must not require excessive programmer annotations in the common case, and must be capable of inferring all sound types at least when guided by explicit annotation.

The problem with programmer annotations is pragmatic rather than ideological: we do not view programmer specification of types as bad per se (indeed, in certain places BitC requires annotations), but ease of prototyping requires that these annotations be minimized. As a matter of good programming ideology and interfacing with other static analysis or verification tools, the inferred types must not be promiscuous with respect to mutability.

First, we consider how to solve the copy compatibility constraints introduced by the maybe types. One possibility is to fix all unresolved maybe types to immutable versions. For example:

(let ((p (pair n:(mutable int32) 
               (lambda (x) x)))) ...)
p: (pair int32 (fn ('a) 'a))

This scheme will preserve all polymorphism possible, but will mandate a programmer annotation for every mutable location. The alternative would be to choose the mutable variants, in which case we will effectively have no polymorphism (by default). In the case of local definitions, we can collect more usage information and fix maybe-ness accordingly.

The previous section argued that we ``lose'' precision of inferred types (with respect to mutability) by the introduction of copy compatibility. We can think of this as a trade-off between freedom in type compatibility and precision of inference. Therefore, we can choose whether to (or not to) introduce copy compatibility at various constructs like new bindings, function application/return, constructors, conditional expressions, etc. Another dimension of trade-off is whether we permit copy compatibility to the maximum permissible limit (as defined in section 3.3), or restrict it to top-level shallow mutability compatibility only. A further option is to require that all polymorphism be contained within function types, since we can make function types polymorphic even if they abstract over mutable or maybe types.

Unless handled with care, full use of copy compatibility can result in the inferred types that are counter-intuitive to the programmer. For example:

(import ls bitc.list)
(define (list2vec lst)
  (make-vector (length lst) 
    (lambda (n) (ls.list-nth lst n))))

For a naïve reader, the type of list2vec appears to be (fn ((list 'a)) (vector 'a)), but is actually the more general type:

(forall ((copy-compat 'a 'b)) 
            (fn ((list 'a)) (vector 'b)))

copy-compat is a special type class that relates two copy compatible types. Now, if we default maybe types that are ultimately unresolved to immutable, in the following definition we obtain:

(define mVec (list2vec mLst:(list (mutable bool)))
mVec: (vector bool)  ;; !!!

which is a correct typing, but is most likely not what the programmer expects. In this case, even though the both the argument and return types of list2vec are reference types, they are only required to be copy compatible because list2vec copies the constituent elements, thereby using new locations.

3.5 Type Inference in BitC

Having identified the various issues and trade-offs involved in type inference, we now describe the particular design choices made in BitC for handling copy compatibility. This is by no means ``the'' solution to the problem, but reflects our judgment of the best way to capture the programmer's intuition about the flow of types in the language. It has been driven in part by our experience writing BitC programs. In BitC, we allow copy compatibility to the full extent, up to a reference boundary. We allow copy compatibility to be invoked at arguments and return positions of all expressions that do not expect a location.

Every time we form a ``maybe'' type due to a copy operation, we remember the original type as a hint to resolve the copy compatibility constraints in the resultant type. At a let boundary, we resolve any unresolved compatibility constraints by unifying with this hint. Intuitively, this means that we will default maybe types to the types of their original copies, unless overridden by an explicit annotation. Here, we are approximating the user's intent to the lexical ``flow'' of type information. For example:

(define mb:(mutable bool) #t))
mb: (mutable bool)

(define p (vector mb))
p: (vector (mutable bool))

(define q:(vector bool) (vector mb))
q: (vector bool)

The type of p shows how maybe types are defaulted based on hint information, and the type of q shows how this can be overridden by programmer annotation. Since we default unresolved maybe-types to original ones the list2vec example described in section 3.4.3 now gets the more intuitive type:

list2vec: (fn ((list 'a)) (vector 'a))

In the case of locally defined identifiers, the top-most mutability is inferred by studying the syntactic usage of the identifier. That is, if the identifier is used as the target of a set!, it is given a shallowly mutable type. This is an ad hoc rule that tries to reduce the need for explicit type qualifications by the programmer in the common case (ex: iterators). However, this rule must not be invoked for top-level (global) definitions. Otherwise, inferred types will no longer be deterministic, as the top level definitions have unlimited scope.

(define (fact x) (do ((ans 1 ans) 
		      (i x (- i 1)))
		     ((== i 0) ans)
		     (set! ans (* ans i))))

In the case of conflicting hints in the different branches of conditional expressions, we pick the most immutable of all hints. This ensures that inferred types are always deterministic. For example:

(define boolPair 
  (if #t 
    (pair #t #f):((mutable bool), bool) 
    (pair #f #t):(bool, (mutable bool))))
p: (bool, bool)

If there are any residual compatibility constraints even after unifying with hints, we resolve them to immutable variants.

Due to copy compatibility, two function types are equal regardless of the shallow mutability of the argument and return types. Therefore, we enforce a syntactic restriction that all function types must be written with immutable types at copy compatible positions. The intuition here is that type of a function must be described in the interface form (the external type), and must hide the ``internal'' mutability information.

(define (f x) (set! x x))
Internal Type f: (fn ((mutable 'a)) ())
External Type f: (fn ('a) ())

Even though function types must be written in external form, any type-qualifications on the arguments of a function within its body correspond to the internal types, and may contain mutable qualifications.

(define abc:(fn ((mutable bool)) 'a) ...) ;; ERROR
(define (abc x:(mutable bool)) ... )      ;; OK

This internal/external type notion is also important in the process of resolving copy compatibility constraints using hints. We should be sure that the internal types of a function do not influence the result type of applications, but the effect of arguments on the return types must be preserved. For example:

(define p:(mutable bool) #t)
(define (f x) p)  ;; f: (fn ('a) bool)
(define (g x) x)  ;; g: (fn ('a) 'a)

(define ff (f p)) ;; ff: bool
(define gg (g p)) ;; gg: (mutable bool)

Further, two instances of a type class can co-exist only if all methods have different external signatures. For example:

(deftypeclass (TCL 'a 'b)
  mtd: (fn ('a (vector 'b)) 'a)))

(definstance (TCL bool bool) ...)
(definstance (TCL (mutable bool) bool) ..) ;CONFLICT!
(definstance (TCL bool (mutable bool)) ..) ;OK.

Chapter 4: Formalization

4.1 The Language

In the interest of brevity, we will limit ourselves to the following core calculus:

Syntax   

Identifiers

_x_ ::=

_y_ | _z_ | ...

Stack Locations

l ::=

l1 | l2 | ...

Heap Locations

::=

1 | 2 | ...

Values

_v_ ::=

() | true | false |  | λ_x_._e_

lvalues

£ ::=

| ^

Expressions

_e_ ::=

_v_ | _e_ _e_ | _e_ : τ | _e_ := _e_ | letκ _x_[:τ] = _e_ in _e_

  

 

 | dup(_e_| _e_^ | if  _e_ then _e_ else _e_

Let-kinds

κ ::=

| α | ψ | ∀

All syntactic forms introduced in this document can be parenthesized without change in meaning. An optional qualification is provided on the identifier defined in a let expression since the same effect cannot be obtained by qualifying the defining expression (due to copy compatibility). The let-kind ``-'' is a placeholder for the unkinded let form.

4.2 Dynamic Semantics

Syntax   

Stack

S ::=

∅ | S, l ↦ _v_

Heap

H ::=

∅ | H,  ↦ _v_

The system state is represented by the triple S; H; _e_ consisting of the stack, the heap, and the expression to be evaluated. Evaluation itself is a two place relationship S; H; _e_ ⇒ S; H_e_ that denotes transformation in the system state due to a single step of execution.

E-RVAL

S(l) = _v_

S; H; l ⇒ S; H; _v_

E-LEFT-TQ

S; H; _e_ : τ ⇛ S; H; _e_

E-TQ

S; H; _e_ : τ ⇒ S; H; _e_

E-LET-TQ

S; H; let _x_ : τ = _e_1 in _e_2 ⇒ S; H; let _x_ = _e_1 in _e_2

E-APP-STEP-FN

S; H; _e_1 ⇒ S; H_e_1

S; H; _e_1 _e_2 ⇒ S; H_e_1 _e_2

E-APP-STEP-ARG

S; H; _e_2 ⇒ S; H_e_2

S; H; _v_1 _e_2 ⇒ S; H_v_1 _e_2

E-APP

l ∉ dom(S)

S; H; λ_x_._e_ _v_ ⇒ S, l ↦ _v_; H; _e_[l/_x_]

E-LET-STEP

S; H; _e_1 ⇒ S; H_e_1

S; H; let _x_ = _e_1 in _e_2 ⇒ S; Hlet _x_ = _e_1 in _e_2

E-LET-M

l ∉ dom(S)

S; H; letψ _x_ = _v_1 in _e_2 ⇒ S, l ↦ _v_1; H; _e_2[l/_x_]

E-LET-P

S; H; let _x_ = _v_1 in _e_2 ⇒ S; H; _e_2[_v_1/_x_]

E-IF-STEP

S; H; _e_ ⇒ S; H_e_

S; H; if  _e_ then _e_1 else _e_2 ⇒ S; Hif  _e_1 then _e_2 else _e_3

E-IF-TRUE

S; H; if  true then _e_1 else _e_2 ⇒ S; H; _e_1

E-IF-FALSE

S; H; if  false then _e_1 else _e_2 ⇒ S; H; _e_2

E-DUP-STEP

S; H; _e_ ⇒ S; H_e_

S; H; dup(_e_) ⇒ S; Hdup(_e_)

E-DUP

 ∉ dom(H)

S; H; dup(_v_) ⇒ S; H,  ↦ _v_

E-LEFT-DEREF-STEP

S; H; _e_ ⇒ S; H_e_

S; H; _e_^ ⇛ S; H_e_^

E-DEREF-STEP

S; H; _e_ ⇒ S; H_e_

S; H; _e_^ ⇒ S; H_e_^

E-DEREF

H() = _v_

S; H; ^ ⇒ S; H; _v_

E-SET-STEP-LHS

S; H; _e_1 ⇛ S; H_e_1

S; H; _e_1 := _e_2 ⇒ S; H_e_1 := _e_2

E-SET-STEP-RHS

S; H; _e_2 ⇒ S; H_e_2

S; H; £ := _e_2 ⇒ S; H£ := _e_2

E-SET-STACK

S, l ↦ _v_1; H; l := _v_2 ⇒ S, l ↦ _v_2; H; ()

E-SET-HEAP

S; H,  ↦ _v_1^ := _v_2 ⇒ S; H,  ↦ _v_2; ()

Dynamic Semantics

Table 4.1 shows the evaluation rules for our core language. Following the theoretical development in [ 1153 ,54 ], we give separate execution semantics for left and right execution (evaluation of expressions that appear on the LHS and RHS of an assignment _e_l := _e_r) denoted by ⇛ and ⇒ respectively.

In the above operational semantics rules, a distinction is made between the stack and the heap in order to ensure that we can only capture references to heap cells (E-DUP, E-LEFT-DEREF, and E-DEREF work only on the heap). The heap locations are first class values but stack locations are not. Therefore stack locations cannot escape beyond their scope (although the bindings themselves are not removed from the stack, in the interest of of simplicity). E-RVAL represents implicit value extraction for stack locations. State updates can be performed either on the stack or on the heap (E-SET-STACK and E-SET-HEAP). We assume that the program is alpha-converted so that there are no name collisions due to inner bindings. We do not model garbage collection, and assume an infinite supply of stack and heap cells.

We cannot give one correct execution semantics for the (unkinded) expression: let _x_ = _v_ in _e_, since there is not enough syntactic support to determine whether _x_ is a (mutable) location or a polymorphic immutable term. The correct step to take is always clear from static type information. We must take the LET-M path for all mutable definitions, and the LET-P path for all polymorphic definitions. For immutable non-polymorphic definitions, either step will work, but we always choose LET-M.

Therefore, we make a distinction among two ``kinds'' of let definitions as: letψ (monomorphic, possibly mutable definition) and let (polymorphic definition). The non-polymorphic version of let is shown as letψ and not let because an immutable monomorphic/concrete definition is also defined using this construct. Now, we give separate execution semantics for each of there let forms. Since the type rules only derive a type for the correct kinds of let in each case, execution of typed expressions is always expected to take the correct path.

We use let to range over letψ and let. and will still use the unkinded term let, when either version is (equally) applicable. Note that let is different from let because two occurrences of let in the same context correspond to the same kind, whereas two occurrences of let need not match the same kind.

4.3 Static Semantics

Syntax   

Types

τ ::=

α | unit | bool | τ → τ

  ref / pointer

 

 | ⇑τ

  Mutable type

 

 | Ψτ

Type Scheme

σ ::=

τ | ∀α.σ

Binding Environment

Γ ::=

∅ | Γ, _x_ ↦ σ

Store Typing

Σ ::=

∅ | Σ,  ↦ τ | Σ, l ↦ τ

Logical Relations

Ω ::=

?true? | ?false? | Ω ∧ Ω | Ω ∨ Ω | ¬Ω | ?Predicate??(?Ω*?)?

A substitution is of Z for Y in X is written using the standard notation: X[Z/Y]. Substitutions within a type up to a ref boundary are written as: τ[Z/Y].

4.3.1 Copy Compatibility

Compatibility of types that differ in shallow mutability at copy boundaries is called copy compatibility [56], denoted by ≅, and is defined as:

τ ≅ τ
Ψτ ≅ τ

In our algebra of types, we have the equation ΨΨτ ≡ Ψτ.

We also define the operators △ and ▽ that increase or decrease the mutability of a type, defined as:

△(Ψτ) = Ψτ and △(τ) = Ψτ, where τ ≠ Ψτ
▽(Ψτ) = τ and ▽(τ) = τ, where τ ≠ Ψτ

It is obvious that ∀τ.▽(τ) ≅ τ ≅ △(τ), and ∀τ, τ.τ ≅ τ iff ▽(τ) = ▽(τ) iff △(τ) = △(τ).

4.3.2 Compatibility of Function Types

Two function types are equal regardless of the shallow mutability of the argument and return types [56]. Due to copy compatibility, two function types are equivalent in all respects regardless of the (shallow) mutability of the argument and return positions. Therefore we always write all function types in the following normalized form:2

  1. The (contravariant) argument type is written in the maximally immutable form (devoid of shallow mutability).

  2. The (covariant) return type is written in the maximally mutable form.

This ensures that the ``external'' type of a function is maximally permissive with respect to mutability. However, during type inference, if we infer the type ▽(τarg) → △(τret) as the external type of a function, this normalization can later get violated due to substitution of type-variables. Therefore, we define the ⌊τ⌋ and ⌈τ⌉ ``meta-constructors'' which (respectively) minimize and maximize the mutability of a type, but are interpreted lazily.

We also define (and implicitly use) the following equivalences in the algebra of types:

⌊τ⌋ ≡ ▽(τ) (lazily)
⌈τ⌉ ≡ △(τ) (lazily)

4.3.3 Copy Coercions

TS-MUT1

Ψτ ≼: τ

TS-MUT2

τ ≼: τ

Ψτ ≼: Ψτ

TS-REF

τ = τ

⇑τ ≼: ⇑τ

TS-FN

τ1 = τ1    τ2 = τ2

τ1 → τ2 ≼: τ1 → τ2

TS-CEIL

⌈τ⌉ ≼: τ

TS-FLOOR

τ ≼: ⌊τ⌋

TS-REFL

τ ≼: τ

TS-TRANS

τ2 ≼: τ1    τ1 ≼: τ

τ2 ≼: τ

Copy Coercion Rules

Table 4.2 shows how we can obtain copy compatibility by copy coercions (similar to subtyping). We can now define copy compatibility as:

τ1 ≅ τ2 ≡ τ1 ≼: ▽(τ2)

The TS-REF rule ensures that copy compatibility does not extend beyond a ref-boundary. Since two function types are equivalent in all respects regardless of the (shallow) mutability of the argument and return positions, we will write all function types in normalized form. The (contravariant) argument type is written in the maximally immutable form (devoid of shallow mutability), and the (covariant) return type is written in the maximally mutable form. This ensures that the ``outer'' type of a function is maximally permissive with respect to mutability. The TS-FN rule therefore is invariant in terms of its arguments and return types.

We will also write Γ; Σ ⊢ _e_ ≼: τ as a shorthand for: Γ; Σ ⊢ _e_ : τ, τ ≼: τ.

4.3.3.1 Problem with Subsumption-like Coercion Rule

In BitC, explicit qualifications are a statement of absolute typing and not type compatibility. Preserving this rule, requires that we must not have generic subsumption like rules for copy coercion. That is, if we have the following subsumption like rules:

TW-TQEXPR (WRONG)

Γ; Σ ⊢ _e_ : τ

Γ; Σ ⊢ (_e_ : τ) : τ

TW-SUB

Γ; Σ ⊢ _e_ : τ    τ ≼: τ

Γ; Σ ⊢ _e_ : τ

We can then write:

let _x_ = () in if  true then _x_ : unit else _x_ := ()

Here, in the else branch of if, we can derive the typing _x_ : Ψunit and in the then, through subsumption also obtain _x_ : unit, which violates our absolute-compatibility at qualification rule.

Therefore, we will not introduce this rule, but instead introduce copy coercion operations at all copy compatible positions in the type rules.

4.3.4 Location Semantics

The lvalue rules shown in Table 4.3 ensure that only those expressions permitted by location semantics [ 55 ,56 ] appear on the left hand side of an assignment expression.

L-ID

 ⊢lval _x_

L-HLOC

 ⊢lval 

L-SLOC

 ⊢lval l

L-DEREF

 ⊢lval _e_^

L-TQ

 ⊢lval _e_

 ⊢lval _e_ : τ

Location Semantics Rules

4.3.5 Declarative Type Rules

T-UNIT

Γ; Σ ⊢ () : unit

T-TRUE

Γ; Σ ⊢ true : bool

T-FALSE

Γ; Σ ⊢ false : bool

T-ID

Γ(_x_) = ∀α*

Γ; Σ ⊢ _x_ : τ[τ**]

T-HLOC

Σ() = τ

Γ; Σ ⊢  : ⇑τ

T-SLOC

Σ(l) = τ

Γ; Σ ⊢ l : τ

T-LAMBDA

Γ, _x_ ↦ τ1; Σ ⊢ _e_ : τ2

Γ; Σ ⊢ λ_x_._e_ : ▽(τ1) → △(τ2)

T-APP

Γ; Σ ⊢ _e_1 ≼: τ2 → τ0    Γ; Σ ⊢ _e_2 ≼: τ2    τ0 ≼: τ0

Γ; Σ ⊢ _e_1 _e_2 : τ0

T-SET

Γ; Σ ⊢ _e_1 ≼: Ψτ    Γ; Σ ⊢ _e_2 ≼: τ     ⊢lval _e_1

Γ; Σ ⊢ _e_1 := _e_2 : unit

T-IF

Γ; Σ ⊢ _e_1 ≼: bool    Γ; Σ ⊢ _e_2 ≼: τ    Γ; Σ ⊢ _e_3 ≼: τ    τ ≼: τ

Γ; Σ ⊢ if  _e_1 then _e_2 else _e_3 : τ

T-TQEXPR

Γ; Σ ⊢ _e_ : τ

Γ; Σ ⊢ (_e_ : τ) : τ

T-DUP

Γ; Σ ⊢ _e_ ≼: τ    τ ≼: τ

Γ; Σ ⊢ dup(_e_) : ⇑τ

T-DEREF

Γ; Σ ⊢ _e_ ≼: ⇑τ

Γ; Σ ⊢ _e_^ : τ

T-LET-M [TQ]

Γ; Σ ⊢ _e_1 ≼: τ1    τ ≼: τ1    Γ; Σ; _e_1 ⊢gen τ ⋖ σ     ⊢loc _x_ : σ    Γ, _x_ ↦ σ; Σ ⊢ _e_2 : τ2

Γ; Σ ⊢ (letψ _x_[:τ] = _e_1 in _e_2) : τ2

Q-LOC

σ = τ

 ⊢loc _x_ : σ

T-LET-P [TQ]

Γ; Σ ⊢ _e_1 ≼: τ1    τ ≼: τ1    Γ; Σ; _e_1 ⊢gen τ ⋖ σ     ⊢term _x_ : σ    Γ, _x_ ↦ σ; Σ ⊢ _e_2 : τ2

Γ; Σ ⊢ (let _x_[:τ] = _e_1 in _e_2) : τ2

Q-TERM

σ = ∀α*

 ⊢term _x_ : σ

Declarative Type Rules

Declarative Type rules are given in Table 4.4. The standard type judgment Γ; Σ ⊢ _e_ : τ is understood as: given a binding environment Γ and store typing Σ the expression _e_ has type τ. We write _e_ ≼: τ as a shorthand for: _e_ : τ, τ ≼: τ, for some type τ. In the type rules, we introduce copy coercions at all positions where copy compatibility is applicable. Type generalization at a let is decided by the judgment  ⊢gen .

4.3.6 Generalization

This section uses the ``full'' value restriction as proposed by Wright [3650]. Relaxations to this rule based on Garriague's scheme [52] were proposed in [56]. Those rules must be incorporated at a later stage.

G-VALUE

?Value??(?_e_?)?    ?Immut??(?τ?)?    α* = ?ftv?(τ) ∖ ?ftv?(Γ) ∖ ?ftv?(Σ)

Γ; Σ; _e_ ⊢gen τ ⋖ ∀α*

G-EXPANSIVE

Γ; Σ; _e_ ⊢gen τ ⋖ τ

Generalization Rules

The type generalization rules are shown in Table 4.5. The rule G-VALUE-PF is not actually used for typing user programs, but is only necessary to prove preservation of types.

4.3.6.1 DEFINITION: Free Type Variables

We denote the set of free type variables in a type τ as ?ftv?(τ).

?ftv?(α) = α
?ftv?(unit) = {}
?ftv?(bool) = {}
?ftv?(⇑τ) = ?ftv?(τ)
?ftv?(Ψτ) = ?ftv?(τ)
?ftv?1 → τ2) = ?ftv?1) ∪ ?ftv?2)
We also write:
?ftv?*) =  ?ftv?(τ)
?ftv?(σ) = ?ftv?*) ∪ ?ftv?(τ) where σ = ∀α*
?ftv?(Γ) =  ?ftv?(σ) forall _x_ : σ ∊ Γ
?ftv?(Σ) =  ?ftv?(τ) forall  : τ ∊ Σ or l : τ ∊ Σ
?ftv?(*C*) =  ?ftv?(τ) where *C* is a set of constraints, and τ is a type involved in any constituent constraint.
FTVS(X, Y, ... ) = FTVS(X) ∪ FTVS(Y) ∪ ..., where X, Y, ... are any of the above permissible arguments.

4.3.6.2 DEFINITION: Value Restriction

?Value?(_v_) = ?true?
?Value?(_x_) = ?true?
?Value?() = ?true?
?Value?(l) = ?true?
?Value?(_e_ : τ) = ?Value??(?_e_?)?
?Value?(dup(_e_)) = ?Value??(?_e_?)?
?Value?(_e_^) = ?Value??(?_e_?)?
?Value?(if  _e_1 then _e_2 else _e_3) = ?Value??(?_e_1?)? ∧ ?Value??(?_e_2?)? ∧ ?Value??(?_e_3?)?
?Value?(let _x_ = _e_1 in _e_2) = ?Value??(?_e_1?)? ∧ ?Value??(?_e_2?)?
?Value?(_e_) = ?false?
?Immut?(unit) = ?true?
?Immut?(bool) = ?true?
?Immut?(α) = ?true?
?Immut?1 → τ2) = ?true?
?Immut?(⇑τ) = ?Immut??(?τ?)?
?Immut?(∀α*.τ) = ?Immut??(?τ?)?
?Immut?(τ) = ?false?

4.3.7 Soundness of Declarative system

4.3.7.1 DEFINITION: Stack and Heap Typing

A heap H and a stack S are said to be well typed with respect to a binding context Γ and store typing Σ, and written Γ; Σ ⊢ H + S if

  1. dom(Σ) = dom(H) ∪ dom(S)

  2.  ∊ dom(H), Γ; Σ ⊢ H() ≼: Σ()

  3. ∀l ∊ dom(S), Γ; Σ ⊢ S(l) ≼: Σ(l)

4.3.7.2 LEMMA: Inversion of Typing Relation

  1. If Γ; Σ ⊢ () : τ then τ = unit.

  2. If Γ; Σ ⊢ true : τ then τ = bool.

  3. If Γ; Σ ⊢ false : τ then τ = bool.

  4. If Γ; Σ ⊢  : τ then τ = ⇑τ.

  5. If Γ; Σ ⊢ λ_x_._e_ : τ then τ = τ1 → τ2 such that τ1 = ▽(τ1), τ2 = △(τ2), and, Γ, _x_ ↦ τ1; Σ ⊢ _e_ : τ2.

  6. If Γ; Σ ⊢ _e_^ : τ then Γ; Σ ⊢ _e_ ≼: τ.

  7. Other cases are similar.

Proof:    Immediate from the definition of typing relation.

4.3.7.3 LEMMA: Inversion of Copy Coercion

  1. If τ ≼: bool then τ = bool or τ = Ψbool.

  2. If τ ≼: unit then τ = unit or τ = Ψbool.

  3. If τ ≼: τ1 → τ2 then τ = τ1 → τ2 or τ = Ψ(τ1 → τ2).

  4. If τ ≼: ⇑τ then τ = ⇑τ or τ = Ψ⇑τ.

  5. If τ ≼: Ψτ then τ = Ψτ such that τ ≼: τ.

  6. If τ ≼: Ψτ then τ ≼: τ.

Proof:    By induction on the copy coercion derivation.

4.3.7.4 LEMMA: Canonical Forms

  1. If _v_ is a value, and Γ; Σ ⊢ _v_ ≼: unit, then, _v_ is ().

  2. If _v_ is a value, and Γ; Σ ⊢ _v_ ≼: bool, then, _v_ is either true or false.

  3. If _v_ is a value, and Γ; Σ ⊢ _v_ ≼: ⇑τ, then, _v_ is ,  ∊ dom(Σ).

  4. If _v_ is a value, and Γ; Σ ⊢ _v_ ≼: τ1 → τ2, then, _v_ is λ_x_._e_.

Proof:    By induction on the derivation of Γ; Σ ⊢ _v_ ≼: τ.

If Γ; Σ ⊢ _v_ ≼: bool, we have Γ; Σ ⊢ _v_ : τ and τ ≼: bool by Inversion of copy coercion relation, τ = bool or τ = Ψbool. If τ = bool, it is clear that the final rule in the derivation must be T-TRUE, or T-FALSE, in which case the result is immediate. The case τ = Ψbool cannot happen because there is no rule that derives a mutable type for a value, and we assume that the induction hypothesis Γ; Σ ⊢ _v_ : τ holds.

Other cases of the lemma are similar.

4.3.7.5 LEMMA: Progress

If _e_ is a closed, well typed term, that is, ∅; Σ ⊢ _e_ : τ for some τ and Σ, given any heap H and stack S such that Γ; Σ ⊢ H + S,

  1. If  ⊢lval _e_, then _e_ is either a valid lvalue £ (that is, £ = l, l ∊ dom(S) or £ = ^,  ∊ dom(H)) or else ∃ _e_, S, H  such that:S; H; _e_ ⇛ S; H_e_.

  2. _e_ is a value _v_ or else ∃ _e_, S, H  such that S; H; _e_ ⇒ S; H_e_.

Proof:    By induction on the typing derivation.

  1. Case T-UNIT, T-TRUE, T-FALSE, T-HLOC, T-LAMBDA: (Values) Result is immediate for right execution, and cannot happen for right execution

  2. Case T-ID: cannot happen, there is no execution rule for variables.

  3. Case T-SLOC: Immediate for left execution. Right execution and can always continue with E-RVAL rule as the stack is well typed (Γ; Σ ⊢ H + S).

  4. Case T-APP: Only right execution is possible, no application is well typed as an lvalue. We have: _e_ = _e_1 _e_2, _e_1 ≼: τ1 → τ2, and _e_2 ≼: τ1. If _e_1 or _e_2 is not a value, we can take E-APP-STEP-FN or E-APP-STEP-ARG. Otherwise, when both _e_1 and _e_2 are values, by canonical forms lemma, _e_1 is of the form λ_x_._e_, and we can take the step E-APP.

  5. Case T-IF: Similar to T-APP, only right execution is permitted.

  6. Case T-SET: Only right execution is applicable. We have: _e_ = _e_1 := _e_2, _e_1 ≼: τ1 → τ2, Γ; Σ ⊢ _e_1 ≼: Ψτ, Γ; Σ ⊢ _e_2 ≼: τ, and  ⊢lval _e_1. If _e_1 not an lvalue, since we have  ⊢lval _e_1 we can take E-SET-STEP-LHS by induction hypothesis. Similarly, if _e_2 is not a value, we can take E-SET-STEP-RHS. Finally, if _e_1 = £ and _e_1 = _v_, we can take the step E-SET-STACK or E-SET-HEAP as applicable.

  7. Case T-DUP: Only right execution is permitted, and can take E-DUP-STEP or E-SUP-STEP as applicable.

  8. Case T-DEREF: We have: _e_ = _e_1^, and Γ; Σ ⊢ _e_1 ≼: ⇑τ. Execution can take E-LEFT-DEREF-STEP or E-DEREF-STEP as applicable if _e_1 is not a value. If _e_1 ≼: ⇑τ is a value, then, from the canonical forms lemma, _e_1 = ,  ∊ dom(Σ). Now, since this is an lvalue, we are done in the case of left execution. In the case of right execution, we can take step E-DEREF since the heap is well typed (Γ; Σ ⊢ H + S).

  9. Case T-LET-M: Only right execution is applicable. We have: _e_ = (letψ _x_ = _e_1 in _e_2), τ ≼: τ1, Γ; Σ; _e_1 ⊢gen τ ⋖ σ, and ⊢loc _x_ : σ, andΓ, _x_ ↦ σ; Σ ⊢ _e_2 : τ2. If _e_1 is not a value, we can take E-LET-STEP. Otherwise, we can take E-STEP-M.

  10. Case T-LET-P: Only right execution is applicable. We have: _e_ = (letψ _x_ = _e_1 in _e_2), τ ≼: τ1, Γ; Σ; _e_1 ⊢gen τ ⋖ σ, and ⊢term _x_ : σ, andΓ, _x_ ↦ σ; Σ ⊢ _e_2 : τ2. If _e_1 is not a value, we can take E-LET-STEP. Otherwise, can take E-LET-P.

  11. Case T-TQEXPR and Case T-LET-M-TQ, T-LET-P-TQ are similar.

4.3.7.6 LEMMA: Weakening

We will write Γ; Σ ⊢ _e_ : τ ⋖ σ as a shorthand for Γ; Σ ⊢ _e_ : τ, and Γ; Σ; _e_ ⊢gen τ ⋖ σ.

  1. If Γ; Σ ⊢ _e_ : τ then,

    1. If Γ ⊇ Γ then Γ; Σ ⊢ _e_ : τ.

    2. If Σ ⊇ Σ then Γ; Σ ⊢ _e_ : τ.

  2. If Γ; Σ ⊢ _e_ : τ ⋖ σ, σ = ∀α*

    1. If Γ ⊇ Γ and ?ftv?) ∩ ?ftv?*) = ∅ then Γ; Σ ⊢ _e_ : τ ⋖ σ.

    2. If Σ ⊇ Σ and ?ftv?) ∩ ?ftv?*) = ∅ then Γ; Σ ⊢ _e_ : τ ⋖ σ.

Proof:    Straightforward induction on the typing derivation.

4.3.7.7 LEMMA: Value Substitution

If Γ, _x_ : σ; Σ ⊢ _e_ : τ, ?Immut??(?σ?)? and Γ; Σ ⊢ _v_ : τv, and Γ; Σ; _e_ ⊢gen τv ⋖ σ then Γ; Σ ⊢ _e_[_v_/_x_] : τ

Proof:    By induction on the typing derivation of Γ, _x_ : σ; Σ ⊢ _e_ : τ. We proceed by case analysis on the final step of the derivation.

  1. Case T-ID: We have _e_ = _y_ where _y_ ∊ Γ, _x_, σ.

    There are two sub cases to consider. If _x_ = _y_, then, _y_[_v_/_x_] = _v_, and the result type τ is an instantiation of the type scheme σ. One of the assumptions of the lemma states that Γ; Σ ⊢ _v_ : τv ⋖ σ. That is, τ ⊑ σ, and we can infer any more-specific type (and in particular the type being instantiated at the T-ID rule) instead for this substitution of the expression _v_. Therefore, we have Γ; Σ ⊢ _e_[_v_/_x_] : τ.

    If _x_ ≠ _y_, then _y_[_v_/_x_] = _y_, and the result is immediate.

  2. Case T-LAMBDA: We have _e_ = λ_y_._e_, and τ = τ1 → τ2, and Γ, _x_ : σ, _y_ : τ1; Σ ⊢ _e_ : τ2.

    We can assume that _x_ ≠ _y_. Since it is clear that the type τ1 of _y_ can either use variables already in Γ or fresh type variables, we know that ?ftv?(Γ, _y_ : τ1) ∩ ?ftv?(σ) = ∅. Thus, by weakening lemma, we have: Γ, _y_ : τ1; Σ ⊢ _v_ : τv ⋖ σ , and, by induction hypothesis, Γ, _y_ : τ1; Σ ⊢ _e_[_v_/_x_] : τ2. Finally, by the T-LAMBDA rule, we have: Γ; Σ ⊢ λ_x_y.(_e_[_v_/_x_]) : τ1 → τ2, and thus Γ; Σ ⊢ λ_x_y._e_[_v_/_x_] : τ1 → τ2, which is the desired result.

  3. T-SET case is similar, except that the substitution cannot happen on the LHS of an assignment, since we do not perform substitution of mutable values.

  4. Other cases are similar.

4.3.7.8 LEMMA: Location Substitution

If Γ, _x_ : τ0; Σ ⊢ _e_ : τ, and for some Σ ⊇ Σ, Σ(l) = τ0 : , then Γ; Σ ⊢ _e_[l/_x_] : τ.

Proof:    By induction on the typing derivation of Γ, _x_ : τ; Σ ⊢ _e_ : τ, similar to lemma 4.3.7.7.

4.3.7.9 LEMMA: Stack and Heap Assignment

  1. If Γ; Σ ⊢ H,  ↦ _v_ + S, and Σ() ≼: τ, and Γ; Σ ⊢ _v_ : τ, then, Γ; Σ ⊢ H,  ↦ _v_ + S.

  2. Similarly, if Γ; Σ ⊢ H + S, l ↦ _v_ and Σ(l) ≼: τ, and Γ; Σ ⊢ _v_ : τ, then, Γ; Σ ⊢ H + S, l ↦ _v_.

Proof:    Immediate from the definition of stack and heap typing.

4.3.7.10 LEMMA: Preservation

If Γ; Σ ⊢ _e_ : τ and Γ; Σ ⊢ H + S then,

  1. If S; H; _e_ ⇛ S; H_e_, then, there exists a Σ ⊇ Σ such that Γ; Σ ⊢ _e_ : τ and Γ; Σ ⊢ H + S.

  2. If S; H; _e_ ⇒ S; H_e_, there exists a Σ ⊇ Σ such that Γ; Σ ⊢ _e_ ≼: τ, Γ; Σ ⊢ H + S and ▽(τ) = ▽(τ).

Proof:    By induction on the derivation of Γ; Σ ⊢ _e_ : τ. We proceed by the case analysis of the final step.

  1. Case T-ID, T-TRUE, T-FALSE, T-HLOC, T-LAMBDA cannot happen.

  2. Case T-SLOC: Only right execution is applicable. We have: _e_ = l and Σ(l) : τ. The only applicable step is E-RVAL, and we have _e_ = S(l). From the definition of stack typing, we have: S(l) ≼: Σ(l) and thus _e_ ≼: τ which implies ▽(τ) = ▽(τ).

  3. Case T-APP: _e_ = _e_1 _e_2, and Γ; Σ ⊢ _e_1 ≼: τ2 → τ0, and Γ; Σ ⊢ _e_2 ≼: τ2, and τ0 ≼: τ, and _e_ : τ where τ2 = ▽(τ20 = △(τ0) and .

    This cannot happen for left execution. For right execution, we proceed by further case analysis of the applicable execution rules for S; H; _e_ ⇒ S; H_e_.

    1. Case E-APP-STEP-FN: We have: S; H; _e_1 ⇒ S; H_e_1 and _e_ = _e_1 _e_2. By induction hypothesis, we have: Γ; Σ ⊢ _e_1 ≼: τ2 → τ0 for some Σ ⊇ Σ. One of the assumptions of the T-APP rule states that Γ; Σ ⊢ _e_2 ≼: τ2, and by weakening lemma, we have, Γ; Σ ⊢ _e_2 ≼: τ2. Finally, by the T-APP rule, we conclude that (_e_1 _e_2) : τ.

    2. Case E-APP-STEP-ARG: Similar to the previous sub-case.

    3. Case E-APP: We have: _e_1 = λ_x_._e_0 and _e_2 = _v_ and and _e_ = _e_0[l/_x_] and S, l ↦ _v_; H; _e_1 ⇒ S; H_e_1.

      By the inversion lemma for λ_x_._e_0 we have Γ, Σ ⊢ _e_0 : τ0.

      Further from location substitution lemma, we have Γ; Σ ⊢ _e_0[l/_x_] : τ0 where Σ ⊇ Σ and Σ(l) : τ2.

      Thus, we have τ0 ≼: τ0 and τ0 ≼: τ. Therefore, it is clear that ▽(τ0) = ▽(τ).

  4. Case T-SET: _e_ = _e_1 := _e_2, and Γ; Σ ⊢ _e_1 ≼: Ψτ, and Γ; Σ ⊢ _e_2 ≼: τ ⊢lval _e_1

    If the step taken is E-SET-STEP-LHS or E-SET-STEP-RHS, the result follows from the induction hypothesis and T-SET rule (as in the case of T-APP). If the step taken is E-SET-STACK or E-SET-HEAP, the result follows from the stack and heap assignment lemma.

  5. Case T-DEREF: We have: _e_ = _e_^ and Γ; Σ ⊢ _e_ ≼: ⇑τ. If the step taken is E-LEFT-DEREF-STEP or E-LEFT-DEREF, the result follows from induction hypothesis and T-DEREF rule. If the step taken is E-DEREF (right execution only) _e_ is a value, and from canonical forms lemma, we know that _e_ =  and  ∊ dom(Σ) and the result follows from the fact that Γ; Σ ⊢ H + S.

  6. Case T-LET-P: Right execution only. We have: _e_ = (let _x_ = _e_1 in _e_2) and Γ; Σ ⊢ _e_1 ≼: τ1 and τ ≼: τ1 and Γ; Σ; _e_1 ⊢gen τ ⋖ σ and Γ, _x_ ↦ σ; Σ ⊢ _e_2 : τ2. There are two sub-cases to consider:

    1. If we take step E-LET-STEP, S; H; _e_1 ⇒ S; H_e_1 and _e_ = (let _x_ = _e_1 in _e_2). If _e_ = _v_ It is clear that ?Value??(?_e_1?)? implies ?Value??(?_e_1?)?. Now, the result follows from the induction hypothesis and the E-LET-P rule.

    2. If we take the step E-LET-P, _e_ = (let _x_ = _v_ in _e_2) Since _x_ : σ has a polymorphic type, (that is, σ = ∀α*.τ) we know that ?Immut??(?τ?)?. Also, from canonical forms lemma, all values have an immutable type. Therefore, τ = τ1. Now, the result follows from value substitution lemma.

  7. Case T-LET-M: Similar to T-LET-P, except that we should always use the GEN-EXPANSIVE rule during generalization, and use the location substitution lemma instead of the value substitution lemma

  8. Cases T-IF, T-DUP, T-TQEXPR, and T-LET-M-TQ T-LET-P-TQ are similar.

4.3.7.11 DEFINITION: Stuck State

A system state S; H; _e_ is said to be stuck if _e_ ≠ _v_ and there are no S, H, and _e_ such that S; H; _e_ ⇒ S; H_e_.

4.3.7.12 THEOREM: Type Soundness

If ∅; Σ ⊢ _e_ : τ and Γ; Σ ⊢ H + S and S; H; _e_ ⇒* S; H_e_ then S; H_e_ is not stuck. That is, execution of a well typed expression cannot lead to a stuck state. Here, ⇒* represents the reflexive-transitive-closure of ⇒.

Proof:    By straightforward induction on the length of ⇒*. If _e_ = _v_, proof is immediate. Otherwise, from Lemma 4.3.7.5 (Progress), we know that we can take at least one step forward. Further, from Lemma 4.3.7.10 (Preservation), we know that a (left/right) execution of a well typed expression in with respect to a well typed stack and heap will always result in another well typed expression, stack and heap. Proof now follows from induction hypothesis.

4.3.8 Equational Inference Algorithm

Syntax   

Types

τ ::=

...

  Constrained Type

 

 | τ \ *C*

Constraint Sets

*C* ::=

∅ | {(τ = τ | τ ≅ τ | τ ≼: τ)*}

Substitutions

θ ::=

∅ | [α ↣ τ] | θ ∘ θ

We will denote applications of a list of substitutions of these substitutions as θ〈X〉, while an individual substitution is written using the standard notation X[Z/Y].

The inference judgment Γ; Σ ⊢eq _e_ : τ | *C* should be understood as: given the stack (binding) context Γ and heap (store) context Σ, we can infer the type τ for the expression _e_ under the set of constraints *C*. In the interest brevity, we will not track the freshness of type variables. We just write  ⊢new α* to mean that the sequence of type variables α* are new type variables.

TE-UNIT

Γ; Σ ⊢eq () : unit | ∅

TE-TRUE

Γ; Σ ⊢eq true : bool | ∅

TE-FALSE

Γ; Σ ⊢eq false : bool | ∅

TE-ID

Γ(_x_) = ∀α*.τ     ⊢new β*

Γ; Σ ⊢eq _x_ : τ[β**| ∅

TE-HLOC

Σ() = τ

Γ; Σ ⊢eq  : ⇑τ | ∅

TE-SLOC

Σ(l) = τ

Γ; Σ ⊢eq l : τ | ∅

TE-LAMBDA

Γ, _x_ ↦ α; Σ ⊢eq _e_ : τ | *C*

Γ; Σ ⊢eq λ_x_._e_ : ⌊α⌋ → ⌈τ⌉ | *C*

TE-APP

Γ; Σ ⊢eq _e_1 : τ1 | *C*1    Γ; Σ ⊢eq _e_2 : τ2 | *C*2     ⊢new α

Γ; Σ ⊢eq _e_1 _e_2 : α | *C*1 ∪ *C*2 ∪ {τ1 ≼: ⌊β⌋ → ⌈γ⌉,  τ2 ≼: ⌊β⌋,  ⌈γ⌉ ≼: α}

TE-IF

Γ; Σ ⊢eq _e_1 : τ1 | *C*1    Γ; Σ ⊢eq _e_2 : τ2 | *C*2    Γ; Σ ⊢eq _e_3 : τ3 | *C*3     ⊢new α

Γ; Σ ⊢eq if  _e_1 then _e_2 else _e_3 : α | *C*1 ∪ *C*2 ∪ *C*3 ∪ {τ1 ≼: bool,  τ2 ≼: β,  τ3 ≼: β,  α ≼: β}

TE-TQEXPR

Γ; Σ ⊢eq _e_ : τ | *C*

Γ; Σ ⊢eq (_e_ : τ) : τ | *C* ∪ {τ = τ}

TE-SET

Γ; Σ ⊢eq _e_1 : τ1 | *C*1    Γ; Σ ⊢eq _e_2 : τ2 | *C*2     ⊢lval _e_1     ⊢new α

Γ; Σ ⊢eq _e_1 := _e_2 : unit | *C*1 ∪ *C*2 ∪ {τ1 = Ψα,  τ1 ≼: ⌊τ2⌋}

TE-DUP

Γ; Σ ⊢eq _e_ : τ | *C*     ⊢new α

Γ; Σ ⊢eq dup(_e_) : ⇑α | *C* ∪ {α ≼: ⌊τ⌋}

TE-DEREF

Γ; Σ ⊢eq _e_ : τ | *C*     ⊢new α

Γ; Σ ⊢eq _e_^ : α | *C* ∪ {τ ≼: ⇑α}

TE-LET-M [TQ]

Γ; Σ ⊢eq _e_1 : τ1 | *C*1    *C*1 = *C*1 ∪ {τ ≼: ⌊τ1⌋}    θ ⊧unf *C*1    Γ; Σ; _e_1 ⊢gen θ〈τ〉 ⋖ σ

 ⊢loc _x_ : σ    θ〈Γ〉, _x_ ↦ σ; θ〈Σ〉 ⊢eq _e_2 : τ2 | *C*2

Γ; Σ ⊢eq letψ _x_ = _e_1 in _e_2 : τ2 | *C*2 ∪ {∀[α ↣ τ] ∊ θ,  α = τ}

TE-LET-P [TQ]

Γ; Σ ⊢eq _e_1 : τ1 | *C*1    *C*1 = *C*1 ∪ {τ ≼: ⌊τ1⌋}    θ ⊧unf *C*1    Γ; Σ; _e_1 ⊢gen θ〈τ〉 ⋖ σ

 ⊢term _x_ : σ    θ〈Γ〉, _x_ ↦ σ; θ〈Σ〉 ⊢eq _e_2 : τ2 | *C*2

Γ; Σ ⊢eq let _x_[:τ] = _e_1 in _e_2 : τ2 | *C*2 ∪ {∀[α ↣ τ] ∊ θ,  α = τ}

Equational Inference Rules

Equational Inference rules are shown in Table 4.6. We will hereinafter write LET-α to range over LET-M and LET-P. We propagate solved constraints at a let-boundary (TE-LET-α and TE-LET-α-TQ rules) as equality constraints rather then substitutions. This is done just to present the rules in a simple form. A real implementation must propagate the substitutions so that closure computation does not increase exponentially. In this equational presentation of inference, types can be fixed by equality constraints, or compatibility requirements expressed as copy coercion constraints. The coercion constraints are used as hints and we obtain the substituted type by subsumption. The solution to these constraints closely corresponds to the rule defined in BitC. In this section, we do not present the fact that mutability of local variables are determined by examining the expression in which they are used. The actual implementation in BitC uses an eager unification algorithm, and is explained in the next section.

4.3.9 Unification

Unification Rules are shown in Table 4.7. We write θ ⊧ *C* to mean that the substitution θ satisfies the set of constraints *C*. A constraint set *C* is said to be the normalized form of *C* if it is written as a set of atomic constraints by using the copy coercion rules defined in Table 4.2 (note that this conversion is total). Further this set is closed, such that all transitive relationships are explicitly added. (see Definition 4.3.9.1). The unification judgment   ⊧UNF *C* says that the constraint set *C* when written equivalent canonical form as *C* is consistent and acyclic. The statement θ ⊧unf *C* is understood as: The substitution θ obtained as a result of unification and constraint solving satisfies the set of constraints *C*.

E-UNIFY

?Close?(*C*) = *C*      ⊧consistent *C*      ⊧acyclic *C*

  ⊧UNF *C*

E-UNIFY-SOLVE

  ⊧UNF *C*    θ ⊧sol *C*

θ ⊧unf *C*

Equational Unification Rules

4.3.9.1 DEFINITION: Constraint Set Closure

  1. For each τ1 → τ2 = τ1 → τ2 ∊ *C* add τ1 = τ1 and τ2 = τ2.

  2. For each τ1 → τ2 ≼: τ1 → τ2 ∊ *C* add τ1 = τ1 and τ2 = τ2.

  3. For each ⇑τ = ⇑τ ∊ *C* add τ = τ.

  4. For each ⇑τ ≼: ⇑τ ∊ *C* add τ = τ.

  5. For each Ψτ = Ψτ ∊ *C* add τ = τ.

  6. For each Ψτ ≼: Ψτ ∊ *C* add τ ≼: τ.

  7. For each τ1 = τ2 ∊ *C* and τ2 = τ3 ∊ *C* add τ1 = τ3.

  8. For each τ1 ≼: τ2 ∊ *C* and τ2 ≼: τ3 ∊ *C* add τ1 ≼: τ3.

  9. For each τ1 = τ2 ∊ *C* and τ2 ≼: τ3 ∊ *C* add τ1 ≼: τ3.

  10. For each τ1 ≼: τ2 ∊ *C* and τ2 = τ3 ∊ *C* add τ1 ≼: τ3.

Continue till no more constraints can be added.

4.3.9.2 Solving the Constraints

SOL-EQ

α = τ ∊ *C*    θ = [α ↣ τ]    θ ⊧Sol θ〈*C* ∖ {α = τ}〉

θ ∘ θ ⊧Sol *C*

SOL-SUB

α = τ ∉ *C*    *C*a = {∀α ≼: τ1 ∊ *C*,  ∀τ2 ≼: α ∊ *C*}    τ = some type such that    θ = [α ↣ τ]    θ ⊧ *C*a

θ ⊧Sol θ〈*C* ∖ {α ≼: τ}〉

θ ∘ θ ⊧Sol *C*

Solving Equational Constraints

Judgments to solve equational constraints are shown in Table 4.8.

4.3.10 Soundness of Equational Inference

4.3.10.1 THEOREM: Soundness of Constraint Closure

If θ ⊧ *C* then θ ⊧ Close(*C*).

Proof:    Immediate from the definition of closure computation and copy coercion rules.

4.3.10.2 THEOREM: Correctness of Constraint Solver

θ ⊧Sol *C* iff θ ⊧ *C*.

Proof:    Evident from the definition of the constraint solver.

4.3.10.3 THEOREM: Correctness of Unification

Unification produces a valid substitution that satisfies all the constraints in a constraint set. That is, if θ ⊧unf *C* then θ ⊧ *C*.

Proof:    Evident the definition of unification, and Theorem 4.3.10.2.

4.3.10.4 THEOREM: Decidability of Unification

θ ⊧unf *C* is decidable.

Proof:    Follows from following facts:

  1. The constraint set *C* is finite. Closure computation follows set-union semantics and will eventually terminate after all possible constraints are added (as evident from the cope coercion relationships).

  2. Consistency check is bounded by the cardinality of the constraint set and is thus decidable.

  3. There are no forms that introduce cyclic types in this system. Even otherwise, cycle check is also bounded by the cardinality of the constraint set.

  4. The constraint solver builds a solution tree by always invoking itself on smaller sets. Therefore, there can be no infinite derivations.

4.3.10.5 LEMMA: Substitution on Declarative Derivation

If Γ; Σ ⊢ _e_ : τ then θ〈Γ〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉.

Proof:    Straightforward induction on the derivation of Γ; Σ ⊢ _e_ : τ, except for the fact that we should use appropriate α-renaming on generalized variables ∀σ ∊ Γ, so that generalized variables do not get substituted.

4.3.10.6 LEMMA: Weakening of Substitutions

  1. If θ ⊧ *C*1 ∪ *C*2 then θ ⊧ *C*1 and θ ⊧ *C*2.

  2. If θ ⊧Sol *C*1 ∪ *C*2 then θ ⊧ *C*1 and θ ⊧ *C*2.

Proof:    Part (1) is immediate. Part(2) follows from Theorem 4.3.10.2.

4.3.10.7 LEMMA: Composition of Solutions and Substitutions

If θ1 ⊧unf *C*1 and *C*2 ⊆ *C*1 then ∃ θ2  such that θ1 = θ2 ∘ θ0 and θ2 ⊧unf *C*2.

4.3.10.8 THEOREM: Soundness of Equational Inference

If Γ; Σ ⊢eq _e_ : τ | *C* and θ ⊧unf *C* then θ〈Γ〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉.

Proof:    By induction on the equational inference derivation, proceeding by case analysis on the final step of derivation (we vacuously assume α-reduction):

  1. Cases TE-UNIT, TE-TRUE, TE-FALSE, TE-ID, TE-HLOC, TE-SLOC are trivial.

  2. Case TE-LAMBDA: From induction hypothesis, we have: θ〈Γ, _x_ ↦ α〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉. This can be re-written as: θ〈Γ〉, _x_ ↦ θ〈α〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉. Now, from the T-LAMBDA rule, we can conclude that θ〈Γ〉; θ〈Σ〉 ⊢ λ_x_._e_ : θ〈α〉 → θ〈τ〉.

  3. Case TE-APP: By induction hypothesis, and Lemma 4.3.10.5 and Lemma 4.3.10.7 we have: θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 ≼: θ〈τ1〉θ〈Γ〉; θ〈Σ〉 ⊢ _e_2 ≼: θ〈τ2〉. By inspecting the constraints that get added at the T-APP step we can conclude that the result of substitution must imply θ〈τ1〉 ≼: ▽(θ〈τ2〉) → △(θ〈α〉). We also have the axiom △(θ〈α〉) ≼: θ〈α〉. Now, from T-APP rule, we can obtain θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_2 : θ〈α〉

  4. Cases TE-IF, TE-SET, TE-DUP, TE-DEREF and TE-SET are similar.

  5. Case TE-LET-α: By induction hypothesis, (similar to TE-APP case) we have: θu〈Γ〉; θu〈Σ〉 ⊢ _e_1 : θu〈τ1〉 and θu ⊧unf *C*1. Let τ be a type such that τ ≼: ⌊θ〈τ1〉⌋. This can be re-written as: θ〈τ1〉 ≼: τ and τ ≼: τ to match the requirements of T-LET-α rule. Also, since both T-LET-α and TE-LET-α use the same generalization rules, we can obtain: θu〈Γ〉; θu〈Σ〉; _e_1 ⊢gen τ ⋖ θ〈σ〉. By Lemma 4.3.10.7 θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 : θ〈τ1〉 and by suitable variable renaming we can obtainθu〈Γ〉; θu〈Σ〉; _e_1 ⊢gen τ ⋖ θ〈σ〉. By induction hypothesis, we also have: θ〈Γ〉, _x_ ↦ θ〈σ〉; θ〈Σ〉 ⊢ _e_2 : θ〈τ2〉. Therefore, from T-LET-α rule, we can obtain θ〈Γ〉; θ〈Σ〉 ⊢ let _x_ = _e_1 in _e_2 : θ〈τ2〉.

  6. Cases TE-TQEXPR and TE-LET-α-TQ are similar.

4.3.11 Inference with Eager Unification

Syntax   

Types

τ ::=

...

  Maybe Type

 

 | τ↓τ

While performing eager unification, we cannot always infer a mutable type and later apply subsumption to obtain a less mutable type in favor of polymorphism. This is because, we must differentiate between types whose mutability is fixed (equality constraint due to the TE-SET rule) or, we have inferred a mutable type that is subject to change (copy coercion constraint). In order to achieve this, we maintain compatibility constraints on types themselves. We write τ↓τ as a shorthand for the constrained type τ \ {τ ≅ τ}. We can think of the type τ↓τ as a ``maybe type'' τ which must be copy compatible but not necessarily equal to the type τ. The type τ is used as a hint to default the mutability of τ, unless it gets automatically fixed as a result of unification. In out language we only have maybe-mutability constraints, but we will still continue to use the constrained type notation τ \ *C* in some cases while expressing generic mathematical properties over constrained types.

TI-UNIT

Γ; Σ; Π ⊢i () : unit ∥ ∅; Π

TI-HLOC

Σ() = τ

Γ; Σ; Π ⊢i  : ⇑τ ∥ ∅; Π

TI-SLOC

Σ(l) = τ

Γ; Σ; Π ⊢i l : τ ∥ ∅; Π

TI-ID

Γ(_x_) = ∀α*

Γ; Σ; Π ⊢i _x_ : τ[β**] ∥ ∅; Πβ*

TI-TRUE

Γ; Σ; Π ⊢i true : bool ∥ ∅; Π

TI-FALSE

Γ; Σ; Π ⊢i false : bool ∥ ∅; Π

TI-LAMBDA

Γ, _x_ ↦ α; Σ; Πα ⊢i _e_ : τ ∥ θ; Π

Γ; Σ; Π ⊢i λ_x_._e_ : ⌊θ〈α〉⌋ → ⌈τ⌉ ∥ θ; Π

TI-TQEXPR

Γ; Σ; Π ⊢i _e_ : τ ∥ θ; Π    θ  ⊢unf τ = θ〈τ〉

Γ; Σ; Π ⊢i (_e_ : τ) : θ〈τ〉 ∥ θ ∘ θ; Π

TI-APP

Γ; Σ; Παβγδε ⊢i _e_1 : τ1 ∥ θ1; Π    θ1〈Γ〉; θ1〈Σ〉; Π ⊢i _e_2 : τ2 ∥ θ2; Π

θ1  ⊢unf θ2〈τ1〉 = β↓(⌊δ⌋ → ⌈α⌉)    θ = θ1 ∘ θ2 ∘ θ1    θ* = ?ftv?(θ〈α〉) ∖ ?ftv?(θ〈δ〉)    τ = ▽(θ〈α〉)[⌊θ⌋*/θ*]

θ2  ⊢unf τ2 = γ↓⌊θ〈δ〉⌋    τ = ε↓θ2〈τ〉

Γ; Σ; Π ⊢i _e_1 _e_2 : τ ∥ θ1 ∘ θ2 ∘ θ1 ∘ θ2; Π

TI-IF

Γ; Σ; Παβγδε ⊢i _e_1 : τ1 ∥ θ1; Π1    θ1〈Γ〉; θ1〈Σ〉; Π1 ⊢i _e_2 : τ2 ∥ θ2; Π2

θ1 ∘ θ2〈Γ〉; θ1 ∘ θ2〈Σ〉; Π2 ⊢i _e_3 : τ3 ∥ θ3; Π3    θ = θ1 ∘ θ2 ∘ θ3    θ2  ⊢unf θ〈τ2〉 = α↓β

θ3  ⊢unf θ ∘ θ2〈τ3〉 = γ↓θ2〈β〉    θ1  ⊢unf θ ∘ θ2 ∘ θ3〈τ1〉 = δ↓bool

θ = θ ∘ θ1 ∘ θ2 ∘ θ3    τ0 = ε↓join(θ〈τ2〉,  θ〈τ3〉)

Γ; Σ; Π ⊢i if  _e_1 then _e_2 else _e_3 : τ0 ∥ θ; Π3

TI-SET

Γ; Σ; Παβ ⊢i _e_1 : τ1 ∥ θ1; Π    θ1〈Γ〉; θ1〈Σ〉; Π ⊢i _e_2 : τ2 ∥ θ2; Π

θ1  ⊢unf θ2〈τ1〉 = Ψα    θ2  ⊢unf θ1〈τ2〉 = β↓θ1 ∘ θ2〈τ1

Γ; Σ; Π ⊢i _e_1 := _e_2 : unit ∥ θ1 ∘ θ2 ∘ θ1 ∘ θ2; Π

TI-DUP

Γ; Σ; Πα ⊢i _e_ : τ ∥ θ; Π    τ = α↓τ

Γ; Σ; Π ⊢i dup(_e_) : ⇑τ ∥ θ; Π

TI-DEREF

Γ; Σ; Παβ ⊢i _e_ : τ ∥ θ; Π    θ  ⊢unf τ = β↓⇑α

Γ; Σ ⊢i _e_^ : θ ∘ θ〈α〉 ∥ θ ∘ θ; Π

TI-LET-M [TQ]

Γ; Σ; Πα ⊢i _e_1 : τ1 ∥ θ1; Π1    θ  ⊢unf θ1〈τ〉 = α↓τ1    θ_x__e_2 ⊢solve θ ∘ θ1〈τ〉 :≫ τ     ⊢loc _x_ : σ

θ1 ∘ θ ∘ θ〈Γ〉; θ1 ∘ θ ∘ θ〈Σ〉; _e_1 ⊢gen τ ⋖ σ    θ1 ∘ θ ∘ θ〈Γ〉, _x_ ↦ σ; θ1 ∘ θ ∘ θ〈Σ〉; Π1 ⊢i _e_2 : τ2 ∥ θ2; Π2

Γ; Σ; Π ⊢i letψ _x_[:τ] = _e_1 in _e_2 : τ2 ∥ θ ∘ θ ∘ θ1 ∘ θ2; Π2

TI-LET-P [TQ]

Γ; Σ; Πα ⊢i _e_1 : τ1 ∥ θ1; Π1    θ  ⊢unf θ1〈τ〉 = α↓τ1    θ_x__e_2 ⊢solve θ ∘ θ1〈τ〉 :≫ τ     ⊢term _x_ : σ

θ1 ∘ θ ∘ θ〈Γ〉; θ1 ∘ θ ∘ θ〈Σ〉; _e_1 ⊢gen τ ⋖ σ    θ1 ∘ θ ∘ θ〈Γ〉, _x_ ↦ σ; θ1 ∘ θ ∘ θ〈Σ〉; Π1 ⊢i _e_2 : τ2 ∥ θ2; Π2

Γ; Σ; Π ⊢i let _x_[:τ] = _e_1 in _e_2 : τ2 ∥ θ ∘ θ ∘ θ1 ∘ θ2; Π2

Type Inference Rules

The inference judgment Γ; Σ; Π ⊢i _e_ : τ ∥ θ; Π should be understood as: given the stack (binding) context Γ and heap (store) context Σ, we can infer the type τ for the expression _e_. θ is list of substitutions obtained as a result of unifications performed in the process of inference, which must be propagate to further derivations. We thread a tape of type variables Π through the derivations (as in [57]) so that fresh type variables can be introduced reliably. Π is a tape that supplies fresh type variables. Π is the tape rolled past the current derivation. Also, we write Πα* to denote denotes the tape that is rolled past the type variables α*. Hindley Milner style inference rules are shown in Table 4.9.

The TI-LAMBDA rule uses the meta-constructors defined above to infer a normalized type for functions. The TI-APP rule infers copy compatible types by introducing maybe types at three positions — the function type itself (by unifying θ2〈τ1〉 and β↓(⌊δ⌋ → ⌈α⌉)), the argument type (by unifying τ2 with γ↓⌊θ〈δ〉⌋) and the return type (through ε↓θ2〈τ〉). Computing the type τ shows how the the hint for the result of an application is calculated. We start with an immutable version of the return type ▽(θ〈α〉). θ* is the set of type variables that appear in the return type of the function but not in its argument type. We constrain θ* to be immutable by shallow substitution with ⌊θ⌋*. This ensures that mutability not induced by the argument does not affect the return type. Later unification with τ2 ensures that the mutability induced by the actual argument is preserved in the hint. Similarly, the TI-IF rule infers copy compatible types for the two branches and the result. It calculates the most immutable type as the hint for the result type by computing the join [3051] of θ〈τ2〉 and θ〈τ3〉 (note that two copy compatible types always have a join). Other rules are similar.

4.3.12 Unification

The unification judgment θ  ⊢unf τ1 = τ2 is understood as τ1 unifies with (can be transformed to structurally equal) τ2 under the substitution θ. Eager unification rules can be found in Table 4.10. Definitions for some auxiliary functions used by the unifier are given in Table 4.11.

UNIFY

θ ⊢u ℜ(τ1) = ℜ(τ2)

θ  ⊢unf τ1 = τ2

U-REFL

∅ ⊢u τ = τ

U-COMMUT

θ ⊢u τ = τ

θ ⊢u τ = τ

U-TVAR

α ≠ τ

[α ↣ τ] ⊢u α = τ

U-REF

θ ⊢u τ1 = τ2

θ ⊢u ⇑τ1 = ⇑τ2

U-MUT

θ ⊢u τ1 = τ2

θ ⊢u Ψτ1 = Ψτ2

U-FN

θ ⊢u τ1 = τ1    θ ⊢u θ〈τ2〉 = θ〈τ2〉     ⊧acyclic θ ∘ θ

θ ∘ θ ⊢u τ1 → τ2 = τ1 → τ2

U-CT1

τ2 ≠ τ3↓τ3    τ2 ≠ α    θ ⊢u ℑ(τ1) = ▽(τ2)    θ ⊢u θ〈τ1〉 = θ〈τ2〉     ⊧acyclic θ ∘ θ

θ ∘ θ ⊢u τ1↓τ1 = τ2

U-CT2

θ ⊢u ℑ(τ1) = ℑ(τ2)    θ ⊢u θ〈τ1〉 = θ〈τ2〉     ⊧acyclic θ ∘ θ

θ ∘ θ ⊢u τ1↓τ1 = τ2↓τ2

U-CEIL

θ ⊢u τ1 = τ2

θ ⊢u ⌈τ1⌉ = ⌈τ2

U-FLOOR

θ ⊢u τ1 = τ2

θ ⊢u ⌊τ1⌋ = ⌊τ2

Unification Rules

τ ≠ τ1↓τ2

ℑ(τ) = ▽(τ)

τ = τ1↓τ2    ℑ(τ2) = τ2

ℑ(τ) = τ2

ℜ(unit) = ⌊unit

ℜ(bool) = ⌊bool

ℜ(α) = α

ℜ(τ) = τ

ℜ(Ψτ) = ⌈Ψτ

ℜ(τ) = τ

ℜ(⇑τ) = ⌊⇑τ

ℜ(τ1) = τ1    ℜ(τ2) = τ2

ℜ(τ1 → τ2) = ⌊τ1 → τ2

ℜ(τ1) = τ1    ℜ(τ2) = τ2

ℜ(τ1↓τ2) = τ1↓τ2

ℜ(▽(τ)) = τ    τ = ⌊τ

ℜ(⌊τ⌋) = τ

ℜ(▽(τ)) = τ    τ ≠ ⌊τ

ℜ(⌊τ⌋) = ⌊τ

ℜ(△(τ)) = τ

ℜ(⌈τ⌉) = τ

Auxiliary Functions for Unification

The unification judgment θ  ⊢unf τ1 = τ2 is understood as τ1 unifies with τ2 under the substitution θ. The interesting cases are U-CT1 and U-CT2. U-CT1 shown the unification of a maybe type τ1↓τ1 with an unconstrained type τ2. In this case, an immutable version of the constraint is extracted by the ℑ operator, and is unified with an immutable version of τ2. Once compatibility is established, we unify the type τ1 to equal τ2. U-CT2 shows the unification of two maybe types. After establishing compatibility, we unify the actual types τ1 and τ2 so that they ultimately resolve to the same type.

The function ℜ(τ) transforms a type τ into an equivalent canonical form where all meta constructors are made explicit at all structural levels of τ. Strictly speaking, unified types are equal only under the ℜ relationship. However, since ℜ(τ) ≡ τ, we will just say that unified types are equal.

4.3.13 Solving Copy Compatibility Constraints

The judgment θ; _x__e_ ⊢solve τ1 :≫ τ2 should be read as: the (possibly) constrained type τ1 for the identifier _x_ (possibly) used in the expression _e_ is transformed3 to the unconstrained type τ2 by solving all the copy compatibility constraints in τ1. The  ⊢solve  rule fixes the top-level mutability of an ``open'' type by examining whether _x_ is the target of an assignment in the expression _e_. The judgment  ⊢sol  will actually solve the constraints, either trivially (there are no constraints, or the constraints have already been solved by unification), or by unifying the type with the constraint. Rules for solving copy compatibility constraints can be found in Table 4.12. The relation  ⊧consistent τ1↓τ2 is defined in Section 4.3.14.5.

SOLVE-KNOWN

τ ≠ τ1↓τ2    θ ⊢sol τ :≫ τ

θ; _x__e_ ⊢solve τ :≫ τ

SOLVE-MUT

θ ⊢sol τ1↓τ2 :≫ τ    ?mutated??(?_x__e_?)?

θ; _x__e_ ⊢solve τ1↓τ2 :≫ △(τ)

SOLVE-IMMUT

θ ⊢sol τ1↓τ2 :≫ τ    ¬?mutated??(?_x__e_?)?

θ; _x__e_ ⊢solve τ1↓τ2 :≫ ▽(τ)

SOL-UNIT

∅ ⊢sol unit :≫ unit

SOL-BOOL

∅ ⊢sol bool :≫ bool

SOL-FN

θ ⊢sol τ1 :≫ τ1    θ ⊢sol θ〈τ2〉 :≫ τ2

θ ∘ θ ⊢sol τ1 → τ2 :≫ θ〈τ1〉 → τ2

SOL-MUT

θ ⊢sol τ :≫ τ

θ ⊢sol Ψτ :≫ Ψτ

SOL-REF

θ ⊢sol τ :≫ τ

θ ⊢sol ⇑τ :≫ ⇑τ

SOL-CEIL

θ ⊢sol τ :≫ τ

θ ⊢sol ⌈τ⌉ :≫ ⌈τ

SOL-FLOOR

θ ⊢sol τ :≫ τ

θ ⊢sol ⌊τ⌋ :≫ ⌊τ

SOL-CT-VAR

τ1 = α    θ ⊢sol τ2 :≫ τ2    θ = [α ↣ τ2]

θ ∘ θ ⊢sol τ1↓τ2 :≫ τ2

SOL-CT-CONST

τ1 ≠ α     ⊧consistent τ1↓τ2    θ ⊢sol τ1 :≫ τ1

θ ⊢sol τ1↓τ2 :≫ τ1

Solving copy compatibility constraints.

4.3.14 Soundness of Eager Inference

4.3.14.1 DEFINITION: Normalization of Constrained Types

The eager unification algorithm maintains constraints on types. However, these can be written in a normal form as in the case of equational inference so that all constraints only appear on the outermost type. For example:

α↓⇑β↓τ = α \ {α ≅ ⇑(β \ {β ≅ τ})}
  = α \ {α ≅ ⇑β,  β ≅ τ}
  ≡ α \ {α ≼: γ,  ⇑β ≼: γ,  β ≼: δ,  τ ≼: δ}

Therefore we define a normalization of inferred types as: Ν(τ) = τ \ *C*, wherein:

  1. τ contains no constraints within it.

  2. *C* only contains copy coercion or equality constraints.

  3. All meta-constructors in τ are fully interpreted using the equivalences defined in section 4.3.2. That is, the type τ contains no meta-constructors.

We write Ν(τ) = τ if Ν(τ) = τ \ *C*, and *C* consists only of tautological constraints.

4.3.14.2 DEFINITION: Normalization of Constraint Sets

A constraint set *C* is said to be the normalized form of *C* (that is, Ν(*C*) = *C*) if:

  • Let *C* be a set of constraints equivalent to *C*, but expressed only using subtype and equality constraints.

  • *C* = ?close??(?*C*?)?. That is, *C* is written as a set of atomic constraints by using the copy coercion rules defined in Table 4.2 (note that this conversion is total). Further, all transitive relationships are explicitly added

4.3.14.3 DEFINITION: Normalization of Contexts

The binding context Γ is said to be the normalized form of Γ (that is, Ν(Γ) = Γ) if ∀ _x_ : τ ∊ Γ , _x_ : τ ∊ Γ and Ν(τ) = τ.

Similarly, the store context Σ is said to be the normalized form of Σ (that is, Ν(Σ) = Σ) if ∀  : τ ∊ Σ , it is true that  : τ ∊ Σ and Ν(τ) = τ; and ∀ l : τ ∊ Σ , it is true that l : τ ∊ Σ, and Ν(τ) = τ.

4.3.14.4 DEFINITION: Solvable Entities

Syntax   

Solvable Entities

ω ::=

τ | Γ | Σ | ω + ω

Note that:

  1. Normalization is defined on all atomic solvable entities. We add the rule: If Ν(ω1) = ω1 \ *C*1 and Ν(ω2) = ω2 \ *C*2 then Ν(ω1 + ω2) = ω1 + ω2 \ *C*1 ∪ *C*2.

  2. As usual, we write Ν(ω) = ω if Ν(ω) = ω \ *C* where *C* consists only of tautological constraints.

  3. Substitution is defined on all atomic solvable entities. We define: θ〈ω1 + ω2〉 = θ〈ω1〉 + θ〈ω2〉.

  4. For the sake of brevity, we write ∅ + ω = ω + ∅ = ω.

  5. Definition of ⊆ over solvable entities:

    1. τ ⊆ τ if the type τ is structurally a part of the type τ.

    2. Γ ⊆ Γ if Γ contains all the mappings in Γ, and possibly more.

    3. Σ ⊆ Σ if Σ contains all the mappings in Σ, and possibly more.

    4. ω1 + ω2 ⊆ ω1 + ω2 if ω1 ⊆ ω1 and ω2 ⊆ ω2.

  6. Definition of ⊇ over solvable entities:

    1. τ ⊇ τ if the type τ is structurally a part of the type τ.

    2. Γ ⊇ Γ if Γ contains all the mappings in Γ, and possibly more.

    3. Σ ⊇ Σ if Σ contains all the mappings in Σ, and possibly more.

    4. ω1 + ω2 ⊇ ω1 + ω2 if ω1 ⊇ ω1 and ω2 ⊇ ω2.

4.3.14.5 DEFINITION: Consistency of Solvable Entities

A (possibly) constrained type or context ω is said to be consistent, that is,  ⊧consistent ω iff Ν(ω) = ω \ *C* and  ⊧consistent *C*.

4.3.14.6 DEFINITION: Reachable Store

We define |Σ|_e_1, _e_2, ... as a store such that |Σ|_e_1, _e_2, ...〈Σ〉 and |Σ|_e_1, _e_2, ... contains mappings for only those locations that are reachable from _e_1, _e_2, ... (that is, the location is syntactically a part of _e_1, _e_2, ...).

4.3.14.7 DEFINITION: MTVs

We define the function ?MTVS?(ω) = α* to be the set of all type variables within the solvable entity ω. That is, it returns the set of all type-variables α* where α occurs within a maybe type as α↓τh.

?MTVS?(α) = {}
?MTVS?(unit) = {}
?MTVS?(bool) = {}
?MTVS?(α↓τh) = α ∪ ?MTVS??(?τh?)?
?MTVS?(τ↓τh) = ?MTVS??(?τ?)? where τ ≠ α
?MTVS?(⇑τ) = ?MTVS??(?τ?)?
?MTVS?(Ψτ) = ?MTVS??(?τ?)?
?MTVS?1 → τ2) = ?MTVS??(?τ1?)? ∪ ?MTVS??(?τ2?)?
?MTVS?(∅) = {} (empty context).
?MTVS?(Γ, _x_ : τ) = ?MTVS??(?Γ?)? ∪ ?MTVS??(?τ?)?
?MTVS?(Σ,  : τ) = ?MTVS??(?Σ?)? ∪ ?MTVS??(?τ?)?
?MTVS?(Σ, l : τ) = ?MTVS??(?Σ?)? ∪ ?MTVS??(?τ?)?
?MTVS?1 + ω2) = ?MTVS??(?ω1?)? ∪ ?MTVS??(?ω2?)?

4.3.14.8 DEFINITION: FTVs (Extension)

We need to enhance the definition of FTVS as:

...
?ftv?1↓τ2) = ?ftv?1) ∪ ?ftv?(ℑ(τ2))
?ftv?1 + ω2) = ?ftv?1) ∪ ?ftv?2).

4.3.14.9 DEFINITION: NTVs

We define the set of unconstrained variables as:

?NTVS?(α) = {α}
?NTVS?(unit) = {}
?NTVS?(bool) = {}
?NTVS?(α↓τh) = ?NTVS??(?τh?)?
?NTVS?(τ↓τh) = ?NTVS??(?τ?)? where τ ≠ α
?NTVS?(⇑τ) = ?NTVS??(?τ?)?
?NTVS?(Ψτ) = ?NTVS??(?τ?)?
?NTVS?1 → τ2) = ?NTVS??(?τ1?)? ∪ ?NTVS??(?τ2?)?
?NTVS?(∅) = {} (empty context).
?NTVS?(Γ, _x_ : τ) = ?NTVS??(?Γ?)? ∪ ?NTVS??(?τ?)?
?NTVS?(Σ,  : τ) = ?NTVS??(?Σ?)? ∪ ?NTVS??(?τ?)?
?NTVS?(Σ, l : τ) = ?NTVS??(?Σ?)? ∪ ?NTVS??(?τ?)?
?NTVS?1 + ω2) = ?NTVS??(?ω1?)? ∪ ?NTVS??(?ω2?)?

4.3.14.10 DEFINITION: TVs

The set of all type variables in a solvable entity is given by TVS() function, defined as follows: Note that this function is different from FTVS(). (see Definition 4.3.14.8.

?TVS?(ω) = ?MTVS??(?ω?)? ∪ ?NTVS??(?ω?)?

4.3.14.11 DEFINITION:  ⊢sol 

We write θ ⊢sol τ to mean that the substitution is θ is obtained by solving the copy compatibility constraints in τ. We define solving all solvable entities as follows:

S-EMPTY-CTX

∅ ⊢sol ∅

S-EMPTY-TYPE

θ ⊢sol τ :≫ τ

θ ⊢sol τ

S-GAMMA

θ1 ⊢sol τ    θ2 ⊢sol θ1〈Γ〉

θ1 ∘ θ2 ⊢sol Γ, _x_ : τ

S-STORE-HL

θ1 ⊢sol τ    θ2 ⊢sol θ1〈Σ〉

θ1 ∘ θ2 ⊢sol Σ,  : τ

S-STORE-SL

θ1 ⊢sol τ    θ2 ⊢sol θ1〈Σ〉

θ1 ∘ θ2 ⊢sol Σ, l : τ

S-MULTIPLE

θ1 ⊢sol ω1    θ2 ⊢sol θ1〈ω2

θ1 ∘ θ2 ⊢sol ω1 + ω2

4.3.14.12 DEFINITION:  ⊧sol 

Ν(θ〈ω〉) = θ〈ω〉    θ = θs ∘ θo    doms) = ?MTVS??(?ω?)?    θoθ〈ω〉〉 = θ〈ω〉

θ ⊧sol ω

That is, θ solves all copy compatibility constraints in ω, but does not otherwise affect the resultant θ〈ω〉. It is, however, free to contain other substitutions that do not matter wrt θ〈ω〉. Note that  ⊧sol  is a special case of  ⊧  and  ⊧Sol .

4.3.14.13 DEFINITION: Strong Consistency

 ⊧consistent ω    ?MTVS??(?ω?)? ∩ ?NTVS??(?ω?)? = ∅

 ⊧cst ω

Since we have  ⊧consistent ω, if α↓τh1 and α↓τh2 are structurally a part of ω, we must have ℑ(α↓τh1) ≅ ℑ(α↓τh2). Further, ?MTVS??(?ω?)? ∩ ?NTVS??(?ω?)? = ∅ guarantees that constrained type variables do not appear unconstrained elsewhere in ω. We call this property strong consistency, denoted by  ⊧cst .

4.3.14.14 DEFINITION: Compatible Solutions (Substitutions)

We write θ1 ≅ θ2 if ∀ [α ↣ τ1] ∊ θ1 , it is true that [α ↣ τ2] ∊ θ2 (that is,dom1) = dom2)), and τ1 ≅ τ2.

4.3.14.15 DEFINITION: Equivalent Substitutions

We write ω ⊢eqi θ1 ≈ θ2 if θ1〈ω〉 = θ2〈ω〉

For example: α ⊢eqi [α ↣ τ] ≈ [α ↣ β] ∘ [β ↣ τ].

4.3.14.16 DEFINITION: Sub-Substitutions

  1. We write θ ⊆ θ if ∃ θ1, θ2  such that θ = θ1 ∘ θ2, and θ = θ1.

  2. We write θ ⊑ θ if ∃ θ1, θ2  such that θ = θ1 ∘ θ2, and θ ≅ θ1.

4.3.14.17 THEOREM: Correctness of Eager Unification

If Ν(τ1) = τ1 \ *C*1, and Ν(τ2) = τ2 \ *C*2, and θ  ⊢unf τ1 = τ2, then:

  1. If   ⊧UNF *C*1 and   ⊧UNF *C*2, then   ⊧UNF {τ1 = τ2} ∪ *C*1 ∪ *C*2

  2. If   ⊧consistent τ1 and   ⊧consistent τ2 then   ⊧consistent θ〈τ1〉 + θ〈τ2〉 and   ⊧acyclic θ

4.3.14.18 THEOREM: Correctness of the Constraint Solver

If θ ⊢sol ω, then θ ⊧sol ω.

We will also use the following equivalent forms (or special cases of the above statement):

  1. If θ ⊢sol ω, then  ⊧consistent θ〈ω〉.

  2. If θ ⊢sol ω, then Ν(θ〈ω〉) = θ〈ω〉.

  3. If θ ⊢sol τ :≫ τ then Ν(τ) = τ.

4.3.14.19 THEOREM: Totality of the Constraint Solver

  1. If  ⊧consistent ω then ∃ θ  such that θ ⊢sol ω.

  2. If θ ⊧sol ω then ∃ θ ⊑ θ  such that θ ⊢sol ω.

4.3.14.20 THEOREM: Decidability of Unification and Solver

θ  ⊢unf τ1 = τ2 and θ ⊢sol τ :≫ τ are decidable.

Proof:    The unifier and constraint solver builds a solution tree by always invoking itself types having smaller shapes of types (except for the U-COMMUT rule, but we can consider a canonical derivation in which no two U-COMMUT rules are used consecutively in the unification derivation). Since types are of bounded size, and there are no infinite or circular substitutions, these derivations must be bounded.

4.3.14.21 LEMMA: Properties of  ⊧sol 

  1. If θ ⊧sol ω then  ⊧consistent θ〈ω〉.

  2. If  ⊧consistent θ〈ω〉 then ∃ θ  such that θ ⊧sol ω.

  3. θ ⊧sol ω iff Ν(θ〈ω〉) = θ〈ω〉.

4.3.14.22 LEMMA: Properties of Equivalent Substitutions

If ω ⊢eqi θ1 ≈ θ2, then:

  1. θ1 ⊧sol ω implies θ2 ⊧sol ω.

  2.  ⊧consistent θ1〈ω〉 implies  ⊧consistent θ2〈ω〉.

  3.  ⊧cst θ1〈ω〉 implies  ⊧cst θ2〈ω〉.

  4. Ν(θ1〈ω〉) = θ1〈ω〉 implies Ν(θ2〈ω〉) = θ2〈ω〉.

Proof:    Evident from the definition of equivalent substitutions.

4.3.14.23 LEMMA: Strong Consistency Implies Consistency

If  ⊧cst ω, then  ⊧consistent ω.

Proof:    Evident from Definition 4.3.14.13.

4.3.14.24 LEMMA: Properties of Compatible Constrained Types

  1. If Ν(τ) = τ, and Ν(τ) = τ, and τ ≅ τ, then ⌈τ⌉ ≼: τ ≼: ⌊τ⌋

  2. If Ν(θ〈τ〉) = θ〈τ〉, then ⌈θ〈τ〉⌉ ≼: △(θ〈τ〉) ≼: θ〈τ〉 ≼: ▽(θ〈τ〉) ≼: ⌊θ〈τ〉⌋

  3. If Ν(θ〈τ〉) = θ〈τ〉 and {θ*} ⊆ ?ftv?(τ) and τ = τ[⌊θ⌋*/θ*], then ⌈θ〈τ〉⌉ ≼: θ〈τ〉 ≼: θ〈τ ≼: ▽(θ〈τ〉)

  4. If Ν(θ〈τ〉) = θ〈τ〉 and {θ*} ⊆ ?ftv?(τ) and τ = τ[⌈θ⌉*/θ*], then △(θ〈τ〉) ≼: θ〈τ ≼: θ〈τ〉 ≼: ⌊θ〈τ〉⌋.

Proof:    Evident from the definition of copy coercion relationships in Section 4.3.3.

4.3.14.25 LEMMA: Relationship of Solutions

If θ1 ⊧sol ω and θ2 ⊧sol ω then ∃ θ11, θ12, θ21, θ22  such that

  1. θ1 = θ11 ∘ θ12 and θ2 = θ21 ∘ θ22

  2. dom11) = dom21) = ?MTVS??(?ω?)?

  3. θ11 ≅ θ21

  4. θ12ω〉 = θ22ω〉 = ω

4.3.14.26 LEMMA: Uniqueness of Solutions Produced by the Solver

If θ1 ⊢sol ω and θ2 ⊢sol ω, then θ1 = θ2.

4.3.14.27 LEMMA: Relationship between  ⊢solve  and  ⊢sol 

If θ1 ⊢sol τ :≫ τ and θ2_x__e_ ⊢solve τ :≫ τ then τ ≅ τ and θ1 ≅ θ2.

4.3.14.28 LEMMA: Commutativity of  +  over Solvable Entities

  1. If θ ⊧sol ω1 + ω2 then θ ⊧sol ω2 + ω1.

  2. If θ ⊢sol ω1 + ω2 then ∃ θ ≅ θ  such that θ ⊢sol ω2 + ω1.

  3. If  ⊧consistent ω1 + ω2 then  ⊧consistent ω2 + ω1.

  4. If  ⊧cst ω1 + ω2 then  ⊧cst ω2 + ω1.

4.3.14.29 LEMMA: Weakening over Solvable Entities

  1. If  ⊧consistent ω1 + ω2 then  ⊧consistent ω1 and  ⊧consistent ω2.

  2. If  ⊧consistent ω and ω ⊆ ω then  ⊧consistent ω.

  3. If  ⊧cst ω1 + ω2 then  ⊧cst ω1 and  ⊧cst ω2.

  4. If  ⊧cst ω and ω ⊆ ω then  ⊧cst ω.

  5. If Ν(θ〈ω〉) = θ〈ω〉 and ω ⊆ ω, then Ν(θ〈ω〉) = θ〈ω.

  6. If θ ⊧sol ω1 + ω2 then ∃ θ ⊆ θ  and ∃ θ ⊆ θ  such that θ ⊧sol ω1 and θ ⊧sol ω2.

  7. If θ ⊧sol ω and ω ⊆ ω, then ∃ θ ⊆ θ  such that θ ⊧sol ω.

  8. If  ⊧cst ω1 + ω2 and θ ⊧sol ω1 + ω2 then θ ⊧sol ω1 and θ ⊧sol ω2.

  9. If  ⊧cst ω and θ ⊧sol ω and ω ⊆ ω, then θ ⊧sol ω.

4.3.14.30 LEMMA: Extension to Weakening Lemma 4.3.7.6.

If ΓΣ ⊢ _e_ : τ and Γ ⊇ Γ and Σ ⊇ Σ and Ν(Γ) = Γ and Ν(Σ) = Σ, then, ΓΣ ⊢ _e_ : τ.

4.3.14.31 LEMMA: Substitution on Strongly Compatible Entities

If

  1.  ⊧cst ωf + ω + ωs

  2.  ⊧cst θ〈ω〉 for some substitution θ.

  3. dom(θ) ∩ ?TVS??(?ωf + ω + ωs?)? ⊆ ?TVS??(?ω?)?.

Then,  ⊧cst θ〈ωf + ω + ωs〉.

Proof:    From assumption (3), we know that only substitutions in θ that can affect ωf + ω + ωs are substitutions to type variables that are present in ω. From the definition of strong solubility, we know that all constrained types are compatibly constrained throughout the solvable entity. Therefore a substitution for some type variable within ω cannot violate the strong consistency of ωf + ω + ωs.

Note that ωf or ωs need not always present since we can imagine the presence of ∅ in that position.

4.3.14.32 LEMMA: Substitution Preserves Compatibility of Solutions

If

  1. θ1 ⊧sol ω

  2. θ2 ⊧sol θ〈ω〉 for some θ

  3. dom1) ∩ dom2 ∘ θ) = ?MTVS??(?ω?)?

Then, ∃ θ1, θ0  such that

  1. θ1 ≅ θ1

  2. ω ⊢eqi θ2 ∘ θ ≈ θ1 ∘ θ0.

Proof:    By construction of θ1 and θ0.

  1. For every substitution [α ↣ τ] ∊ θ1, where α ∉ ?MTVS??(?ω?)?, add it to θ1.

  2. For every substitution [α ↣ τ] ∊ θ2 ∘ θ, where α ∉ ?MTVS??(?ω?)?, add it to θ0.

  3. ∀ α ∊ ?MTVS??(?ω?)? , ∃ [α ↣ τ] ∊ θ1  and ∃ [α ↣ τ] ∊ θ2 ∘ θ . If α appears within ω in the form α↓τh, we must have τ ≅ θ1〈ℑ(τh)〉. We must also have τ ≅ θ2 ∘ θ〈ℑ(τh)〉

    1. If τ ≅ τ, then add [α ↣ τ] to θ1.

    2. If τ ≄ τ Since we have  ⊧consistent θ2 ∘ θ〈ω〉 (from assumption (2) and Theorem 4.3.14.18) and the fact that substitution must preserve the shape of a solvable entity, τ can only differ from τ in top-level mutability, and/or by being a more specialized type (has some substitutions for type variables within τ). Therefore, ∃ θ  (which possibly uses fresh type variables) such that the substitution[α ↣ τ] in θ2 ∘ θ can be equivalently re-written as [α ↣ τ] ∘ θ where τ ≅ τ. Now add [α ↣ τ] to θ1 and θ to θ0..

4.3.14.33 LEMMA: Unification Preserves Strong Consistency

If   ⊧cst τ1 + τ2 and θ  ⊢unf τ1 = τ2 then   ⊧cst θ〈τ1〉 + θ〈τ2

Proof:    Follows from Theorem 4.3.14.17, and by observing the rules U-TVAR, U-CT1 and U-CT2.

4.3.14.34 LEMMA: Corollary to Lemma 4.3.14.33.

If  ⊧cst ω + τ + τ and θv  ⊢unf τ = τ then  ⊧cst θv〈ω〉 + θv〈τ〉 + θv〈τ

Proof:    Follows from Lemma 4.3.14.33 and Lemma 4.3.14.31.

4.3.14.35 LEMMA: Unification of Maybe Types

If θu  ⊢unf τ1 = τ0↓τ2 and θs ⊧sol θu〈τ1〉 + θu〈τ0↓τ2〉, then:

  1. θu ∘ θs〈τ1 ≼: ⌊θu ∘ θs〈τ2〉⌋ and ⌈θu ∘ θs〈τ2〉⌉ ≼: θu ∘ θs〈τ1

  2. θu ∘ θs〈τ1 ≼: ▽(θu ∘ θs〈τ2〉) and △(θu ∘ θs〈τ2〉) ≼: θu ∘ θs〈τ1

Proof:    From the definition of maybe types, we have τ0 ≅ τ2. Due to the unification θu  ⊢unf τ1 = τ0↓τ2, we have θu〈τ1〉 ≅ θu〈τ2〉. From θs ⊧sol θu〈τ1〉 + θu〈τ0↓τ2〉 and Lemma 4.3.14.21, we have:  ⊧consistent θs ∘ θu〈τ1〉 + θs ∘ θu〈τ0↓τ2〉 Therefore, we must have: θs ∘ θu〈τ1〉 ≅ θs ∘ θu〈τ2〉. Again, from Lemma 4.3.14.21, we have: Ν(θs ∘ θu〈τ1〉) = θs ∘ θu〈τ1 and Ν(θs ∘ θu〈τ0↓τ2〉) = θu〈τ0↓τ2. Since normalization does not violate any properties, we have: θs ∘ θu〈τ1 ≅ θu〈τ2. The conclusions are now evident from the definition of copy coercions in Section 4.3.3.

4.3.14.36 LEMMA: Corollary to Lemma 4.3.14.35

If θu  ⊢unf τ1 = τ0↓τ2 and θs ⊧sol θ ∘ θu〈τ1〉 + θ ∘ θu〈τ0↓τ2〉, then:

  1. θs ∘ θ ∘ θu〈τ1 ≼: ⌊θs ∘ θ ∘ θu〈τ2〉⌋ and ⌈θs ∘ θ ∘ θu〈τ2〉⌉ ≼: θs ∘ θ ∘ θu〈τ1

  2. θs ∘ θ ∘ θu〈〉 ≼: ▽(θs ∘ θ ∘ θu〈τ2〉) and △(θs ∘ θ ∘ θu〈τ2〉) ≼: θs ∘ θ ∘ θu〈τ1

Proof:    Straightforward extension to Lemma 4.3.14.35.

4.3.14.37 LEMMA: Inferred Substitutions are Consistent.

If Γ; Σ; Π ⊢i _e_ : τ ∥ θu; Π, then  ⊧cst θu〈Γ〉 + θu〈|Σ|_e_〉 + τ.

Proof:    By induction on the derivation of Γ; Σ; Π ⊢i _e_ : τ ∥ θu; Π using Lemma 4.3.14.33, and noting the fact that (1) all substitutions produced during inference are in turn produced by the unifier (or the solver) (2) all maybe types in the inference rules are introduced with fresh type variables (3) the two parts of a maybe type are never separated from each other.

Since the store Σ can contain arbitrary typing assumptions in addition to the ones required for this derivation, we define the property on a canonicalized subset of the store.

4.3.14.38 LEMMA: Inferred Substitutions are Cumulatively Consistent.

If

  1. Γ; Σ; Π ⊢i _e_1 : τ1 ∥ θu1; Π

  2. θu1〈Γ〉; θu1〈Σ〉; Π ⊢i _e_2 : τ2 ∥ θu2; Π

Then  ⊧cst θu2 ∘ θu1〈Γ〉 + θu2 ∘ θu1〈|Σ|_e_1, _e_2〉 + θu2〈τ1〉 + τ2

Proof:    Since θu2 is obtained in an environment that already has applied the substitutions in θu1, and since this derivation occurs on a different tape, θu1 and θu2 contain mutually exclusive substitutions.

The only effect θu2 can have on τ1 is by providing a substitution for some type variable α which is a non-universally quantified free type variables in θu1〈Γ〉 and is present in τ1. The result now follows from Lemma 4.3.14.37 and Lemma 4.3.14.31.

4.3.14.39 LEMMA: Substitution on Declarative Derivation [Extension to Lemma 4.3.10.5].

If

  1. ΓΣ ⊢ _e_ : τ

  2. θ is some substitution such that Ν(θ〈τ + Γ + Σ〉) = θ〈τ + Γ + Σ〉

Then, θ〈Γ〉θ〈Σ〉 ⊢ _e_ : θ〈τ〉

Proof:    Straightforward extension to Lemma 4.3.10.5.

4.3.14.40 LEMMA: Corollary to Lemma 4.3.14.39.

If

  1. ΓΣ ⊢ _e_ : τ

  2. θ is some substitution such that  ⊧consistent θ〈τ + Γ + Σ〉

Then, ∃ θe  such that: θe ∘ θ〈Γ〉θe ∘ θ〈Σ〉 ⊢ _e_ : θe ∘ θ〈τ〉

Proof:    Straightforward extension to Lemma 4.3.14.39. The extra substitution θe is necessary to solve any constraints introduced by the substitution θ.

4.3.14.41 LEMMA: Elimination of Irrelevant Substitutions.

If:

  1. θ ⊧sol τ + Γ + Σ

  2. θ〈Γ〉θ〈Σ〉 ⊢ _e_ : θ〈τ〉

Then, ∃ θ ⊆ θ  such that θ ⊧sol τ + Γ + |Σ|_e_ and θ〈Γ〉θ〈|Σ|_e_ ⊢ _e_ : θ〈τ〉

Proof:    Follows from Lemma 4.3.14.29 and by observing the declarative type rules in Table 4.4.

4.3.14.42 LEMMA: Equivalent Substitutions Preserve Declarative Derivation.

If

  1. τ + Γ + Σ ⊢eqi θ1 ≈ θ2

  2. θ1〈Γ〉θ1〈Σ〉 ⊢ _e_ : θ1〈τ〉

Then θ2〈Γ〉θ2〈Σ〉 ⊢ _e_ : θ2〈τ〉

Proof:    From Definition 4.3.14.15, we have θ1〈τ + Γ + Σ〉 = θ2〈τ + Γ + Σ〉. That is, θ1〈τ〉 = θ2〈τ〉 and θ1〈Γ〉 = θ2〈Γ〉 and θ1〈Σ〉 = θ2〈Σ〉. The result is now evident from assumption (2).

4.3.14.43 LEMMA: Compatible Substitutions Preserve Declarative Derivation.

If

  1. θs ⊧sol τ + Γ + Σ

  2. θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉

  3. θs ≅ θs

Then, θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉.

Proof:    From Definition 4.3.14.12, we know that θs = θm ∘ θo where θm contains substitutions to ?MTVS??(?τ + Γ + Σ?)? and θo is irrelevant to τ + Γ + Σ. From the definition of compatibility of substitutions, θs must have compatible substitutions to only these type variables. The required result can be obtained by straightforward induction on the derivation of θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉.

4.3.14.44 LEMMA: Corollary to Lemma 4.3.14.43.

If

  1. θs ⊧sol τ + Γ + Σ

  2. θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉

  3. θs ⊧sol τ + Γ + Σ

Then, θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉.

Proof:    Follows from Lemma 4.3.14.25 and Lemma 4.3.14.43.

4.3.14.45 LEMMA: Corollary to Lemma 4.3.14.44.

If

  1. θe ⊧sol τ + Γ + |Σ|_e_

  2. θe〈Γ〉θe〈|Σ|_e_ ⊢ _e_ : θe〈τ〉

  3. θs ⊧sol τ + Γ + Σ

Then, θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉.

Proof:   

  1. From assumption (3), the fact that |Σ|_e_ ⊆ Σ, and Lemma 4.3.14.29, we conclude that ∃ θs ⊆ θs  such that θs ⊧sol τ + Γ + |Σ|_e_.

  2. From assumption (2) and (3) and case (1) above, and Lemma 4.3.14.44, we have θs〈Γ〉θs〈|Σ|_e_ ⊢ _e_ : θs〈τ〉.

  3. From case (1), we can write: θs = θo ∘ θs.

  4. From assumption (3) and Lemma 4.3.14.21, we have Ν(θs〈Γ + Σ + τ〉) = θs〈Γ + Σ + τ〉.

  5. Since we have: |Σ|_e_ ⊆ Σ, (and thus θs〈|Σ|_e_〉 ⊆ θs〈Σ〉), from case (4) and Lemma 4.3.14.29, we obtain Ν(θs〈Γ + |Σ|_e_ + τ〉) = θs〈Γ + |Σ|_e_ + τ〉.

  6. Case (5) can be re-written as: Ν(θo〈θs〈Γ + |Σ|_e_ + τ〉〉) = θo〈θs〈Γ + |Σ|_e_ + τ〉〉.

  7. From cases (2), (6) and Lemma 4.3.14.39, we have: θo〈θs〈Γ〉〉θo〈θs〈|Σ|_e_〉〉 ⊢ _e_ : θo〈θs〈τ〉〉. That is, θs〈Γ〉θs〈|Σ|_e_ ⊢ _e_ : θs〈τ〉.

  8. Since we have: Σ ⊇ |Σ|_e_, (and thus θs〈Σ〉 ⊇ θs〈|Σ|_e_〉), from cases (4), (7), and Lemma 4.3.14.30, we finally obtain: θs〈Γ〉θs〈Σ〉 ⊢ _e_ : θs〈τ〉.

4.3.14.46 LEMMA: Altering Shallow Mutability.

If

*} ⊆ ?ftv?(τ) and θ ⊧sol τ, then θ ⊧sol τ[⌊θ⌋*/θ*] and θ ⊧sol τ[⌈θ⌉*/θ*].

Proof:    Since a substitution is performed shallowly (up to the reference boundary), it cannot violate copy compatibility (copy coercion) constraints. Since all type variables are still as general as before, and no new variables are used, θ is still a solution for all constraints in the modified type. Note that we can end up with types of the form ⌊⌈τ⌉⌋ (or ⌈⌊τ⌋⌉), which is equivalent to ⌊τ⌋ (or ⌈τ⌉).

4.3.14.47 LEMMA: Corollary to Lemma 4.3.14.46.

If

*} ⊆ ?ftv?(τ) and θ ⊧sol ωf + τ + ωs, then θ ⊧sol ωf + τ[⌊θ⌋*/θ*] + ωs and θ ⊧sol ωf + τ[⌈θ⌉*/θ*] + ωs.

Proof:    Straightforward extension to Lemma 4.3.14.47

4.3.14.48 LEMMA: Corollary to Lemma 4.3.14.47.

If

*} ⊆ ?ftv?(τ) and θs ⊧sol θ〈ωf + τ + ωs〉, then θs ⊧sol θ〈ωf + τ[⌊θ⌋*/θ*] + ωs〉 and θs ⊧sol θ〈ωf + τ[⌈θ⌉*/θ*] + ωs〉.

Proof:    Straightforward extension to Lemma 4.3.14.47

4.3.14.49 LEMMA: Valid Substitutions Preserve Declarative Derivation.

If

  1. θ1 ⊧sol τ + Γ + Σ

  2. θ1〈Γ〉θ1〈Σ〉 ⊢ _e_ : θ1〈τ〉

  3. θ2 ⊧sol θ〈τ + Γ + Σ〉 for some substitution θ

  4. dom1) ∩ dom2 ∘ θ) = ?MTVS??(?τ + Γ + Σ?)?

Then, θ2 ∘ θ〈Γ〉θ2 ∘ θ〈Σ〉 ⊢ _e_ : θ2 ∘ θ〈τ〉

Proof:   

  1. From Lemma 4.3.14.32, assumption (1), (3), and (4), we conclude that ∃ θ1, θ0  such that

    1. θ1 ≅ θ1

    2. τ + Γ + Σ ⊢eqi θ2 ∘ θ ≈ θ1 ∘ θ0

  2. From assumption (1), (2), case (1.a) above, and Lemma 4.3.14.43, we have: θ1〈Γ〉θ1〈Σ〉 ⊢ _e_ : θ1〈τ〉

  3. From assumption (3) and Lemma 4.3.14.21, we have Ν(θ2 ∘ θ〈τ + Γ + Σ〉) = θ2 ∘ θ〈τ + Γ + Σ〉

  4. From cases (3), (1.b), and Lemma 4.3.14.22, we have Ν(θ1 ∘ θ0〈τ + Γ + Σ〉) = θ1 ∘ θ0〈τ + Γ + Σ〉 That is, Ν(θ0〈θ1〈τ + Γ + Σ〉〉) = θ0〈θ1〈τ + Γ + Σ〉〉.

  5. From cases (2), (4), and Lemma 4.3.14.39, we have: θ0〈θ1〈Γ〉〉θ0〈θ1〈Σ〉〉 ⊢ _e_ : θ0〈θ1〈τ〉〉. That is, θ1 ∘ θ0〈Γ〉θ1 ∘ θ0〈Σ〉 ⊢ _e_ : θ1 ∘ θ0〈τ〉.

  6. Finally, from cases (1.b), (4), and Lemma 4.3.14.42, we have: θ2 ∘ θ〈Γ〉θ2 ∘ θ〈Σ〉 ⊢ _e_ : θ2 ∘ θ〈τ〉.

4.3.14.50 THEOREM: Soundness of Eager Inference

If:

  1. Γ; Σ; Π ⊢i _e_ : τ ∥ θu; Π

  2. θs ⊢sol τ + θu〈Γ〉 + θu〈Σ〉.

  3. θsu = θs ∘ θu

Then, θsu〈Γ〉θsu〈Σ〉 ⊢ _e_ : θs〈τ〉.

Proof:    By induction on the derivation of Γ; Σ; Π ⊢i _e_ : τ ∥ θ1; Π. We proceed by case analysis on the last step (again assuming α-reduction vacuously):

  1. Cases TI-UNIT, TI-TRUE. TI-FALSE, TI-ID, TI-HLOC, TI-SLOC are trivial.

  2. Case TI-LAMBDA:

    1. We know that:

      1. Γ, _x_ ↦ α; Σ; Πα ⊢i _e_i : τr ∥ θu; Π

        Γ; Σ; Π ⊢i λ_x_._e_i : ⌊θu〈α〉⌋ → ⌈τr⌉ ∥ θu; Π

      2. θs ⊢sol ⌊θu〈α〉⌋ → ⌈τr⌉ + θu〈Γ〉 + θu〈Σ〉

    2. From induction hypothesis, we have:

      1. If θsi ⊢sol τr + θu〈Γ, _x_ : α〉 + θu〈Σ〉

      2. Then, θsi ∘ θu〈Γ, _x_ : α〉θsi ∘ θu〈Σ〉 ⊢ _e_i : θsi〈τr

      3. That is, θsi〈θu〈Γ, _x_ : α〉〉θsi〈θu〈Σ〉〉 ⊢ _e_i : θsi〈τr

    3. From case (2.b.i) above, and Definition 4.3.14.11 rule S-GAMMA, we have: θsi ⊢sol τr + θu〈Γ〉 + θu〈α〉 + θu〈Σ〉

    4. From case (2.c) above and Lemma 4.3.14.28 (commutativity), we conclude that ∃ θsi ≅ θsi  such that θsi ⊢sol τr + θu〈α〉 + θu〈Γ〉 + θu〈Σ〉.

    5. From Definition 4.3.14.11 rule S-MULTIPLE, we can write: θsi = θf ∘ θo where θf ⊢sol τr + θu〈α〉 and θo ⊢sol θf〈θu〈Γ〉 + θu〈Σ〉〉.

    6. Given θf ⊢sol τr + θu〈α〉, using the SOL-FN rule, we can conclude that θf ⊢sol ⌊θu〈α〉⌋ → ⌈τr⌉.

    7. Therefore, θsi ⊢sol ⌊θu〈α〉⌋ → ⌈τr⌉ + θu〈Γ〉 + θu〈Σ〉. That is, θsi = θs

    8. From cases (d) and (g), we conclude that θsi ≅ θs.

    9. From case (2.b.i) and Theorem 4.3.14.18, we have: θsi ⊧sol τr + θu〈Γ, _x_ : α〉 + θu〈Σ〉.

    10. From cases (2.i), (2.b.iii), (2.h), and Lemma 4.3.14.43, we have: θs〈θu〈Γ, _x_ : α〉〉θs〈θu〈Σ〉〉 ⊢ _e_i : θs〈τr

    11. Since θsu = θs ∘ θu, we can write θsu〈Γ, _x_ : α〉θsu〈Σ〉 ⊢ _e_i : θs〈τr. That is, θsu〈Γ〉, _x_ : θsu〈α〉θsu〈Σ〉 ⊢ _e_i : θs〈τr. Or, θsu〈Γ〉_x_ : θsu〈α〉θsu〈Σ〉 ⊢ _e_i : θs〈τr.

    12. From the T-LAMBDA rule, we have: θsu〈Γ〉θsu〈Σ〉 ⊢ λ_x_._e_i : ▽(θsu〈α〉) → △(θs〈τr)

    13. By definition, ▽(θsu〈α〉) → △(θsu〈τr) = ⌊θsu〈α〉⌋ → ⌈θs〈τr〉⌉ = ⌊θsu〈α〉⌋ → ⌈θs〈τr〉⌉ = θs〈⌊θu〈α〉⌋ → ⌈τr⌉〉

    14. From cases (2.j) and (2.k) , we finally have: θsu〈Γ〉θsu〈Σ〉 ⊢ λ_x_._e_i : θs〈⌊θu〈α〉⌋ → ⌈τr⌉〉

  3. Case TI-APP:

    1. We know that:

      1. [I]    Γ; Σ; Παβγδε ⊢i _e_f : τf ∥ θuf; Π

        [II]    θuf〈Γ〉; θuf〈Σ〉; Π ⊢i _e_a : τa ∥ θua; Π

        [III]    θvf  ⊢unf θua〈τf〉 = β↓(⌊δ⌋ → ⌈α⌉)

        [IV]    θ* = ?ftv?vf〈α〉) ∖ ?ftv?vf〈δ〉)

        [V]    τrh = ▽(θvf〈α〉)[⌊θ⌋*/θ*]

        [VI]    θva  ⊢unf τa = γ↓⌊θvf〈δ〉⌋

        [VII]    τres = ε↓θva〈τrh

        Γ; Σ; Π ⊢i _e_f _e_a : τres ∥ θva ∘ θvf ∘ θua ∘ θuf; Π

        Note that this inference rule is the same as the one in Table 4.9, in which all substitutions are written in every step (without abbreviation), and some redundant substitutions removed.

      2. θu = θva ∘ θvf ∘ θua ∘ θuf

      3. θs ⊢sol τres + θu〈Γ〉 + θu〈Σ〉

      4. θsu = θs ∘ θu

      5. _e_ = _e_f _e_a

    2. From induction hypothesis (wrt [I]), we have:

      1. If θsf ⊢sol τf + θuf〈Γ〉 + θuf〈Σ〉

      2. Then, θsf ∘ θuf〈Γ〉θsf ∘ θuf〈Σ〉 ⊢ _e_f : θsf〈τf

    3. Similarly, from induction hypothesis (wrt [II]), we have:

      1. If θsa ⊢sol τa + θua ∘ θuf〈Γ〉 + θua ∘ θuf〈Σ〉

      2. Then, θsa ∘ θua ∘ θuf〈Γ〉θsa ∘ θua ∘ θuf〈Σ〉 ⊢ _e_a : θsa〈τa

    4. From cases (3.b.i), (3.c.i) and Theorem 4.3.14.18, we have:

      1. θsf ⊧sol τf + θuf〈Γ〉 + θuf〈Σ〉

      2. θsa ⊧sol τa + θua ∘ θuf〈Γ〉 + θua ∘ θuf〈Σ〉

      1. From [I], [II], and Lemma 4.3.14.38, we have:  ⊧cst θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_f, _e_a〉 + θua〈τf〉 + τa.

      2. We know that |Σ|_e_f _e_a = |Σ|_e_f, _e_a, and thus from case (3.a.v), |Σ|_e_ = |Σ|_e_f, _e_a.

      3. From cases (3.e.i) and (3.e.ii), we obtain:  ⊧cst θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_〉 + θua〈τf〉 + τa.

      4. From case (3.e.iii) and Lemma 4.3.14.23, we obtain:
         ⊧consistent θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_〉 + θua〈τf〉 + τa.

    5. From case (3.e.iv) and Lemma 4.3.14.21, we conclude that ∃ θe  such that θe ⊧sol θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_〉 + θua〈τf〉 + τa.

      1. We know that |Σ|_e_f ⊆ |Σ|_e_. Therefore, from cases (3.e.iii), (3.f), and Lemma 4.3.14.29 (weakening), we have: θe ⊧sol θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_f〉 + θua〈τf〉.

        That is, θe ⊧sol θua〈θuf〈Γ〉〉 + θua〈θuf〈|Σ|_e_f〉〉 + θua〈θuf〈τf〉〉. Note that θuf〈τf〉 = τf

      2. From (3.b.i), (3.d.i), and Lemma 4.3.14.41, we can conclude that ∃ θsf  such that:

        1. θsf ⊧sol θuf〈Γ〉 + θuf〈|Σ|_e_f〉 + θuf〈τf

        2. θsf〈θuf〈Γ〉〉θsf〈θuf〈|Σ|_e_f〉〉 ⊢ _e_f : θsf〈θuf〈τf〉〉.

      3. It is clear that domsf) ∩ dome ∘ θua) = ?MTVS??(?θuf〈Γ〉 + θuf〈|Σ|_e_f〉 + θuf〈τf?)?, because the derivation [II] occurs in an environment that already contains the substitutions θsf (and thus θsf), and it works on a different tape Π, and thus uses fresh type variables when new type variables are introduced.

    6. From cases (3.g.ii.A), (3.g.ii.B), (3.g.i), (3.g.iii), and Lemma 4.3.14.49, we have: θe ∘ θua〈θuf〈Γ〉〉θe ∘ θua〈θuf〈|Σ|_e_f〉〉 ⊢ _e_f : θe ∘ θua〈θuf〈τf〉〉.

      That is, θe ∘ θua ∘ θuf〈Γ〉θe ∘ θua ∘ θuf〈|Σ|_e_f ⊢ _e_f : θe ∘ θua ∘ θuf〈τf.

    7. Since |Σ|_e_ ⊇ |Σ|_e_f, we have θe ∘ θua ∘ θuf〈|Σ|_e_〉 ⊇ θe ∘ θua ∘ θuf〈|Σ|_e_f〉. From case (f) and Lemma 4.3.14.21 and Lemma 4.3.14.29, we can obtain Ν(θe ∘ θua ∘ θuf〈Γ〉) = θe ∘ θua ∘ θuf〈Γ〉 and Ν(θe ∘ θua ∘ θuf〈|Σ|_e_〉) = θe ∘ θua ∘ θuf〈|Σ|_e_ Now, from case (h) and Lemma 4.3.14.30, we have θe ∘ θua ∘ θuf〈Γ〉θe ∘ θua ∘ θuf〈|Σ|_e_ ⊢ _e_f : θe ∘ θua ∘ θuf〈τf.

    8. Similarly, we have θe ∘ θua ∘ θuf〈Γ〉θe ∘ θua ∘ θuf〈|Σ|_e_ ⊢ _e_a : θe ∘ θua ∘ θuf〈τa.

      1. It is evident that  ⊧cst β↓(⌊δ⌋ → ⌈α⌉)

      2. Since α, β, and δ are fresh type variables, we can write (using case (3.e.iii)):  ⊧cst θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_〉 + θua〈τf〉 + τa + β↓(⌊δ⌋ → ⌈α⌉).

      3. Due to Lemma 4.3.14.28 (commutativity) and case (3.k.ii), we can write:
         ⊧cst θua ∘ θuf〈Γ〉 + θua ∘ θuf〈|Σ|_e_〉 + τa + θua〈τf〉 + β↓(⌊δ⌋ → ⌈α⌉).

      4. From case (3.k.iii), [III], and Lemma 4.3.14.34, we have:
         ⊧cst θvf ∘ θua ∘ θuf〈Γ〉 + θvf ∘ θua ∘ θuf〈|Σ|_e_〉 + τa + θvf〈θua〉 + θvf〈β↓(⌊δ⌋ → ⌈α⌉)〉.

    9. Again, due to case (3.k.iv) and Lemma 4.3.14.23, and Lemma 4.3.14.21, we conclude that ∃ θe  such that
      θe ⊧sol θvf ∘ θua ∘ θuf〈Γ〉 + θvf ∘ θua ∘ θuf〈|Σ|_e_〉 + τa + θvf ∘ θua〈τf〉 + θvf〈β↓(⌊δ⌋ → ⌈α⌉)〉.

      Similar to the argument in cases (g) through (j), we obtain:

      1. θe ∘ θvf ∘ θua ∘ θuf〈Γ〉θe ∘ θvf ∘ θua ∘ θuf〈|Σ|_e_ ⊢ _e_f : θe ∘ θvf ∘ θua ∘ θuf〈τf.

      2. θe ∘ θvf ∘ θua ∘ θuf〈Γ〉θe ∘ θvf ∘ θua ∘ θuf〈|Σ|_e_ ⊢ _e_a : θe ∘ θvf ∘ θua ∘ θuf〈τa.

    10. Since θvf ∘ θua ∘ θuf〈τa〉 = τa and θu = θva ∘ θvf ∘ θua ∘ θuf, similar to cases (k) and (j) above, for the unification performed in step [VI], we conclude that ∃ θe  such that:

      1. Let

        1. ω1 = θva ∘ θvf ∘ θua ∘ θuf〈Γ〉

        2. ω2 = θva ∘ θvf ∘ θua ∘ θuf〈|Σ|_e_

        3. ω3 = θva ∘ θvf ∘ θua〈τf

        4. ω4 = θva〈τa

        5. ω5 = θva ∘ θvf〈β↓(⌊δ⌋ → ⌈α⌉)〉

        6. ω6 = θva〈γ↓⌊θvf〈δ〉⌋〉

      2.  ⊧cst ω1 + ω2 + ω3 + ω4 + ω5 + ω6.

      3. θe ⊧sol ω1 + ω2 + ω3 + ω4 + ω5 + ω6.

      4. θe ∘ θu〈Γ〉θe ∘ θu〈|Σ|_e_ ⊢ _e_f : θe ∘ θu〈τf.

      5. θe ∘ θu〈Γ〉θe ∘ θu〈|Σ|_e_ ⊢ _e_a : θe ∘ θu〈τa.

      1. From [III], we have: θvf  ⊢unf θua〈τf〉 = β↓(⌊δ⌋ → ⌈α⌉)

      2. From cases (3.m.ii), (3.m.iii) and Lemma 4.3.14.29 (weakening), we have: θe ⊧sol ω3 + ω5〈〉. That is, θe ⊧sol θva ∘ θvf ∘ θua〈τf〉 + θva ∘ θvf〈β↓(⌊δ⌋ → ⌈α⌉)〉〈〉.

      3. The case (3.n.ii) can be re-written as: θe ⊧sol θva ∘ θvf〈θua〈τf〉〉 + θva ∘ θvf〈β↓(⌊δ⌋ → ⌈α⌉)〉.

      4. From cases (3.n.i), (3.n.iii) and Lemma 4.3.14.36, we have:
        θe ∘ θva ∘ θvf〈θua〈τf〉〉 ≼: ▽(θe ∘ θva ∘ θvf〈⌊δ⌋ → ⌈α⌉〉).

        That is, θe ∘ θva ∘ θvf ∘ θua〈τf ≼: θe ∘ θva ∘ θvf〈⌊δ⌋ → ⌈α⌉〉.

      5. The case (3.n.iv) can be re-written as:
        θe ∘ θva ∘ θvf ∘ θua ∘ θuf〈τf ≼: θe ∘ θva ∘ θvf ∘ θua ∘ θuf〈⌊δ⌋ → ⌈α⌉〉, since these substitutions have no effect.

      6. Due to case (3.a.ii), the above case (3.n.v) is equivalent to θe ∘ θu〈τf ≼: θe ∘ θu〈⌊δ⌋ → ⌈α⌉〉

      7. The case (3.n.vi) is further equivalent to θe ∘ θu〈τf ≼: ⌊θe ∘ θu〈δ〉⌋ → ⌈θe ∘ θu〈α〉⌉.

      8. Similarly, from the unification performed in step [VI], we have: θe ∘ θu〈τa ≼: ⌊θe ∘ θu〈δ〉⌋.

      1. From cases (3.m.iv) and (3.n.vii) we obtain:
        θe ∘ θu〈Γ〉θe ∘ θu〈|Σ|_e_ ⊢ _e_f ≼: ⌊θe ∘ θu〈δ〉⌋ → ⌈θe ∘ θu〈α〉⌉.

      2. From cases (3.m.v) and (3.n.viii) we obtain:
        θe ∘ θu〈Γ〉θe ∘ θu〈|Σ|_e_ ⊢ _e_a ≼: ⌊θe ∘ θu〈δ〉⌋.

      1. From (3.m.ii) and (3.m.iii) and Lemma 4.3.14.29, we have θe ⊧sol ω1 + ω2 + θva ∘ θvf〈α〉.

      2. The case (3.p.i) can be re-written as: θe ⊧sol θva〈ω1 + ω2 + θvf〈α〉〉. (Note: ω1 and ω2 already contain substitutions from θva).

      3. From [IV], we have {θ*} ⊆ ?ftv?vf〈α〉)

      4. From cases (3.p.ii), (3.p.iii) and Lemma 4.3.14.48, we have: θe ⊧sol θva〈ω1 + ω2 + θvf〈α〉[⌊θ⌋*/θ*]〉. (Note: Here, we instantiated ωs of Lemma 4.3.14.48 to ∅).

      5. From (3.m.iv), it is evident that: θe ⊧sol ω1 + ω2 + θva〈▽(θvf〈α〉)[⌊θ⌋*/θ*]〉. That is, θe ⊧sol ω1 + ω2 + θva〈τrh〉.

      1. Let θE = θe ∘ [ε ↣ θva〈τrh〉]. A substitution for ε with any type τ ≅ θva〈τrh〉 will actually work in this case.

      2. From cases (3.p.v), (3.q.1) Lemma 4.3.14.21, and Definition 4.3.14.12, we can easily obtain: θE ⊧sol ω1 + ω2 + γ↓⌊θvf〈δ〉⌋. That is, θE ⊧sol ω1 + ω2 + τret.

      1. Similar to case (3.p.i), we obtain θe ⊧sol θva〈θvf〈α〉〉

      2. From case (3.r.i) and Lemma 4.3.14.21, we obtain: Ν(θe〈θva〈θvf〈α〉〉〉) = θe〈θva〈θvf〈α〉〉〉. That is, Ν(θe ∘ θva〈θvf〈α〉〉) = θe ∘ θva〈θvf〈α〉〉.

      3. From cases (3.r.ii), (3.p.iii) and Lemma 4.3.14.24, we obtain: θe ∘ θva〈⌈θvf〈α〉⌉〉 ≼: θe ∘ θva〈▽(θvf〈α〉)[⌊θ⌋*/θ*].

      4. It is evident that θva ∘ θvf〈α〉 ⊧eqi θe ≈ θE. Therefore, we can also say θva〈⌈θvf〈α〉⌉〉 ⊧eqi θe ≈ θE and θva〈▽(θvf〈α〉)[⌊θ⌋*/θ*]〉 ⊧eqi θe ≈ θE.

      5. From cases (3.r.iii) and (3.r.iv), we have: θE ∘ θva〈⌈θvf〈α〉⌉〉 ≼: θE ∘ θva〈▽(θvf〈α〉)[⌊θ⌋*/θ*]. That is, ⌈θE ∘ θva ∘ θvf〈α〉⌉ ≼: θE ∘ θva〈τrh.

      6. From case (3.q.i), we know that θE〈τres = θE ∘ θva〈τrh. (We actually only need a compatibility result here, in case we chose any other compatible type in case (3.q.i)).

      7. From cases (3.r.v) and (3.v.vi), we obtain ⌈θE ∘ θva ∘ θvf〈α〉⌉ ≼: θE〈τres.

      8. The case (3.r.vii) can be written as ⌈θE ∘ θu〈α〉⌉ ≼: θE〈τres, since the extra substitutions have no effect.

      1. From case (3.q.i), it is evident that ω1 + ω2 + ⌊θe ∘ θu〈δ〉⌋ → ⌈θe ∘ θu〈α〉⌉ ⊧eqi θE ≈ θe. Therefore, from case (3.o.i) and Lemma 4.3.14.42, we have: θE ∘ θu〈Γ〉θE ∘ θu〈|Σ|_e_ ⊢ _e_f ≼: ⌊θE ∘ θu〈δ〉⌋ → ⌈θE ∘ θu〈α〉⌉.

      2. Similarly, we have: θE ∘ θu〈Γ〉θE ∘ θu〈|Σ|_e_ ⊢ _e_a ≼: ⌊θE ∘ θu〈δ〉⌋.

    11. Now, from cases (3.s.i), (3.s.ii), (3.r.viii) and T-APP rule, we obtain: θE ∘ θu〈Γ〉θE ∘ θu〈|Σ|_e_ ⊢ _e_f _e_a : θE〈τres.

      1. From case (3.q.ii) and Lemma 4.3.14.28 (commutativity), we obtain: θE ⊧sol τret + ω1 + ω2.

      2. From case (3.a.iii) and Theorem 4.3.14.18, we have: θs ⊧sol τres + θu〈Γ〉 + θu〈Σ〉

      3. From cases (3.u.i), (t), (3.u.ii), and Lemma 4.3.14.45, we obtain: θs ∘ θu〈Γ〉θs ∘ θu〈Σ〉 ⊢ _e_f _e_a : θs〈τres.

      4. Finally, from cases (3.a.iv) and (3.a.v), we obtain the desired result: θsu〈Γ〉θsu〈Σ〉 ⊢ _e_ : θs〈τres.

  4. Cases TI-LET-α: Similar to case (4) (TI-APP), except for the use of Lemma 4.3.14.27.

  5. Cases TI-IF, TI-DUP, TI-DEREF, TI-SET, TI-TQEXPR, TI-LET-α-TQ are similar.

4.3.14.51 LEMMA: Corollary–1 to Soundness of Eager Inference

If:

  1. Γ; Σ; Π ⊢i _e_ : τ ∥ θu; Π

  2. θs ⊢sol τ :≫ τ

  3. θe ⊢sol θs ∘ θu〈Γ + |Σ|_e_

  4. θ = θe ∘ θs ∘ θu

Then θ〈Γ〉θ〈Σ〉 ⊢ _e_ : τ.

Proof:    Follows from Theorem 4.3.14.50 Definition 4.3.14.4, and Lemma 4.3.14.37.

4.3.14.52 THEOREM: Corollary–2 to Soundness of Eager Inference

If:

  1. Γ; Σ; Π ⊢i _e_ : τ ∥ θu; Π

  2. θs ⊢sol τ :≫ τ

Then ∃ θe  such that

  1. θ = θe ∘ θs ∘ θu

  2. θ〈Γ〉θ〈Σ〉 ⊢ _e_ : τ.

Proof:    Follows from Lemma 4.3.14.51.

Chapter 5: Implementation

The bootstrap compiler for BitC has been implemented in C++. Currently, the backend emits portable C code. The core of the compiler involves 28,686 lines of C++ code, of which implementation of type system accounts for about 6,894 lines and the implementation of polymorphism accounts for 992 lines.

The bootstrap compiler for BitC implements polymorphism by brute-force polyinstantiation. This simplifies the implementation of overloading (type-classes) at the cost of requiring whole-program compilation. The algorithm is incremental, supporting use in an interactive environment [37]. More sophisticated techniques for implementing polymorphism over unboxed types are explored in the literature [ 24 ,13 ,32 ]. We view the current implementation as experimental, though it does have the practical advantage (important to us) that emitted types and code are directly inter-callable with C.

There are several proposals for implementing polymorphism over unboxed types. For example, by using coercions [24] into a boxed representation when used in a polymorphic context, using dictionaries [13] that is, passing extra type-parameters to functions, hybrid variations of the above [32] or full polyinstantiation (C++ templates). Depending on the option we pick, there are different trade-offs with respect to the amount of RTTI support needed, separate compilation, efficiency, code size, etc.

Chapter 6: Related Work

Cyclone [17] supports first class polymorphism and polymorphic recursion for functions definitions. This approach is feasible in Cyclone, where there is a distinction between functions (code) and function pointers (data). In an expression language with inner functions, it is more intuitive to treat all first class values alike. Cyclone supports partial type reconstruction so that so that most types and instantiations of polymorphic types can be automatically inferred. Grossman provides a detailed account of using quantified types with imperative C style mutation and & operator in Cyclone [1153]. His formalization develops a general theory wherein any expression can be polymorphic, but requires explicit annotation for all polymorphic definitions and instantiations. Since C (and Cyclone) have no notion of immutability, both languages require explicit annotation of polymorphism. In contrast, we believe that the best way to integrate polymorphism into the systems programming paradigm is by automatic — albeit incomplete — inference. One contribution of our work (in comparison to [1153]) is that we give a formal specification and proof of correctness of the inference algorithm, not just the type system.

Smith and Volpano have proposed an ML-style polymorphic type system for a dialect of C [33]. Their system uses different binding constructs for polymorphic and mutable bindings — let, letvar, letarr. They impose the ML-like restriction that all first class references, vars and arrays must be mutable, and function arguments and let-bound identifiers be immutable, because of which they do not have to deal with copy compatibility issues. Their language does not have structures and union types, and thus does not address complications due to parametrized types, and unboxed composite types with mixed unboxed mutable and immutable types. Our language, while being strictly more expressive, also provides a more natural expression of programs.

A monadic model [20] of mutable state is used in pure functional languages like Haskell [19]. The main advantage of this approach is that the type system provides guarantees not only about the immutability of locations but also distinguishes side effecting computations from others. Therefore, this model is well suited for integration with theorem provers and deduction systems. However, this model is also considerably different from the normal programming idioms used by systems programmers. Hallgren et al. have recently proposed a monadic interface to low level hardware and formally specified certain behavioral properties about it [12]. They have also implemented a prototype operating systems in Haskell based on this system, and it would be interesting to see if this approach scales to a full implementation that provides real time guarantees. In BitC, we have considered providing syntactic constructs that guarantee side-effect free computation. For example we could have a defining form defpure that is similar to define, but allows purely applicative definitions only. This has the advantage of providing a separation of stateful computation from pure ones, as well as the simplicity of staying within the Hindley-Milner type system.

Diatchki et al have proposed support for bit-level word types in Haskell [8]. Their solution could be extended to the full defrepr mechanism of BitC. Communication with the authors has revealed that there is a proposal for extending their prototype interpreter into a full implementation in the GHC compiler [10]. The VFiasco project aims at formalizing the semantics of C++ and using it directly for verification of the Fiasco microkernel written in C++. They present formal semantics for some datatypes of C++ in [14], but do not model C++ pointers, unions or mutability.

Cqual [9] provides a general framework for inference and use of type qualifiers. One of the applications presented in [9] is inference of (maximal) const qualifications for types in C programs, similar to mutability inference in BitC. However, their system does not deal with polymorphism or parametrized composite datatypes. SysObjC [3] extends the C programming language with object-like value types, but does address type safety in the face of polymorphism.

C# is a safe, high-level language supports mutability and low-level representation, but does not support type inference. Spec# [4] is an extension of C# that also provides an integrated verification framework. Their framework is complementary to our system, and can benefit from BitC's mutability model [34].

Chapter 7: Conclusion

In this paper, we have proposed a well-founded first-class mutability model. It is well founded in the sense that types are definitive about the mutability of all locations, and every location has one and only one type across all aliases. The model is first class in the sense that it supports unboxed objects and mutability of stack variables. This makes a language with this type system suitable for systems programming as well as for integration with a verification framework.

There is a fundamental conflict of goals between the ability to infer principal types and to allow freedom of mutability-compatibility at copy boundaries. We have identified various trade-offs and some design choices in this regard, along with their pros and cons. We have proposed a solution to this problem that uses certain hinting mechanisms to infer types based on the ``natural'' flow of type information in an expression. We have also provided a formal framework and for out type system and proved it sound. The type system is implemented as part of the BitC language compiler. Source code for the BitC compiler can be obtained from http://coyotos.org.

Bibliography

  1. Thomas Ball, Todd Millstein, Sriram K. Rajamani ``Polymorphic predicate abstraction.'' Microsoft Research Technical Report MSR_2001_10 June 2002.
  2. Thomas Ball and Sriram K. Rajamani. ``The SLAM Project: Debugging System Software via Static Analysis.'' Proc. 2002 ACM SIGPLAN-SIGACT Conference on Principles of Programming Languages, 2002.
  3. Ádám Balogh and Zoltán Csörnyei. ``SysObjC: C Extension for Development of Object-Oriented Operating Systems.'' Proc. Third ECOOP Workshop on Programming Languages and Operating Systems. San Jose, CA. October 2006.
  4. Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte. ``The Spec# programming system: An overview.'' CASSIS 2004, LNCS vol. 3362 2004.
  5. E. Brewer, J. Condit, B. McCloskey, and F. Zhou. ``Thirty Years is Long Enough: Getting Beyond C.'' Proc. Tenth Workshop on Hot Topics in Operating System (HotOS X), USENIX, 2005.
  6. The Coq Development Team ``The Coq Proof Assistant Reference Manual'' http://coq.inria.fr/doc/main.html
  7. R. Deline and M. Fahndrich ``Enforcing high-level protocols in low-level software.'' Proc. of the ACM Conference on Programming Language Design and Implementation pp 59­69. 2001.
  8. Iavor S. Diatchki, Mark P. Jones, and Rebekah Leslie. ``High- level Views on Low-level Representations.'' Proc. 10th ACM Conference on Functional Programming pp. 168–179. September 2005.
  9. Jeffrey S. Foster, Manuel Fähndrich, and Alexander Aiken. ``A Theory of Type Qualifiers.'' Proc. SIGPLAN Conference on Programming Language Design and Implementation (PLDI'99). pp. 192–203. 1999.
  10. The GHC Team ``The Glorious Glasgow Haskell Compilation System User's Guide, Version 6.6'' http://www.haskell.org/ghc/docs/ latest/html/users_guide/index.html
  11. D. Grossman, ``Quantified Types in an Imperative Language'' ACM Transactions on Programming Languages and Systems 2006.
  12. T. Hallgren, M. P. Jones, R. Leslie, and A. Tolmach. ``A Principled Approach to Operating System Construction in Haskell.'' Proc. International Conference on Functional Programming (ICFP'05), Sep. 2005. Tallinn, Estonia. pp. 116–128.
  13. R. Harper and G. Morrisett. ``Compiling polymorphism using intentional type analysis.'' ACM Symp. on Principles of Programming Languages pp 130-141, January 1995
  14. Michael Hohmuth and Hendrik Tews ``The VFiasco approach for a verified operating system.'' ECOOP Workshop on Programming Languages and Operating Systems 2005.
  15. ISO, International Standard ISO/IEC 8652:1995 (Information Technology — Programming Languages — Ada) International Standards Organization (ISO). 1995.
  16. ISO, International Standard ISO/IEC 9899:1999 (Programming Languages - C) International Standards Organization (ISO). 1999.
  17. T. Jim, G. Morrisett, D. Grossman, M. Hicks, J. Cheney, and Y. Wang ``Cyclone: A safe dialect of C.'' Proc. of USENIX Annual Technical Conference pp 275­288, 2002.
  18. Mark P. Jones ``Qualified types: theory and practice.'' Cambridge Distinguished Dissertations In Computer Science ISBN:0-521-47253-9, 1995
  19. Simon Peyton Jones (ed.). Haskell 98 Language and Libraries: The Revised report. Cambridge University Press. 2003.
  20. Launchbury, J. and Peyton Jones, S. L. ``State in Haskell.'' LISP and Symbolic Computation 8, 4 (Dec.), pp 293-341, 1995.
  21. M. Kaufmann, J. S. Moore. ``Computer Aided Reasoning: An Approach'' Kluwer Academic Publishers, 2000.
  22. Brian W. Kernighan and Dennis M. Ritchie. The C Programming Language. Prentice Hall, 1988
  23. Xavier Leroy, ``The Objective Caml System Release 3.09, Documentation and User's Manual.'' http://caml.inria.fr/pub/docs/ manual-ocaml/index.html
  24. X. Leroy, ``Unboxed objects and polymorphic typing.'' ACM SIGPLAN Symposium on Principles of Programming Languages pages 177--188, January 1992. 8(4):343--355, 1995.
  25. Robin Milner ``A theory of type polymorphism in programming.'' Journal of Computer and System Sciences pp 348-375, 1978.
  26. Robin Milner, Mads Tofte, Robert Harper, and David MacQueen. The Definition of Standard ML - Revised The MIT Press, May 1997.
  27. G. Necula, S. Mcpeak, and W. Weimer ``CCured: Type-safe retrofitting of legacy code.'' Proc. of Symposium on Principles of Programming Languages pp 128­139, 2002.
  28. T. Nipkow, L. C. Paulson and M. Wenzel ``Isabelle/HOL — A Proof Assistant for Higher-Order Logic.'' Springer LNCS series volume 2283 2002.
  29. Frank Pfenning and Carsten Schürmann. ``System description: Twelf — a meta-logical framework for deductive systems.'' Proc. of International Conference on Automated Deduction (CADE-16) pp 202-206, Springer-Verlag LNAI 1632, 1999.
  30. Benjamin C. Pierce ``Types and Programming Languages'' The MIT Press, Massachusetts Institute of Technology ISBN 0-262-16209-1, 2002.
  31. Benjamin C. Pierce and David N. Turner. ``Local Type Inference.'' In Proc. of Symposium on Principles of Programming Languages pp 252-265, 1998.
  32. Zhong Shao. ``Flexible representation analysis.'' Proc. ACM SIGPLAN International conference on Functional programming pp 85 - 98, 1997.
  33. G. Smith and D. Volpano. ``A sound polymorphic type system for a dialect of C.'' Science of Computer Programming 32(2--3):49--72, 1998.
  34. Spec# team ``Spec# 1.0.6404 for Microsoft Visual Studio 2005 Release Notes'' http://research.microsoft.com/specsharp /1.0.6404/relnotes.htm
  35. Matthew S. Tschantz and Michael D. Ernst, ``Javari: Adding reference immutability to Java'' Object-Oriented Programming Systems, Languages, and Applications pp 211-230, October 2005.
  36. A. K. Wright, ``Simple Imperative Polymorphism'' Lisp and Symbolic Computation 8(4):343--355, 1995.
  37. Swaroop Sridhar ``Implementation of Polymorphism in BitC'' http://www.coyotos.org/ docs/bitc/polyinst.html
  38. S. Sridhar and J. Shapiro. ``Type Inference for Unboxed Types and First Class Mutability'' Proc. 3rd ECOOP Workshop on Programming Languages and Operating Systems (PLOS 2006) San Jose, CA. 2006.
  39. J. S. Shapiro, Eric Northup, M. Scott Doerrie, and Swaroop Sridhar. Coyotos Microkernel Specification, 2006, available online at www.coyotos.org.
  40. Tim Harris, Simon Marlow and Simon Peyton Jones ``Haskell on a shared-memory multiprocessor'' Proc. of the 2005 ACM SIGPLAN workshop on Haskell. pp 49-61, 2005.
  41. H. XI ``Imperative programming with dependent types.'' Proc. of IEEE Symposium on Logic in Computer Science pp 375­387, 2000.
  42. J. S. Shapiro, J. M. Smith, and D. J. Farber. ``EROS, A Fast Capability System'' Proc. 17th ACM Symposium on Operating Systems Principles. Dec 1999. pp. 170–185. Kiawah Island Resort, SC, USA.
  43. M. Fähndrich, M. Aiken, C. Hawblitzel, O. Hodson, G. Hunt, J. R. Lauris, and S. Levi. ``Language Support for Fast and Reliable Message-based Communication in Singularity OS.'' Proc. EUROSYS 2006, Leuven Belgium. 2006
  44. M. Aiken, M. Fähndrich, C. Hawblitzel, G. Hunt, and J. R. Lauris. ``Deconstructing Process Isolation.'' Microsoft Technical Report MSR-TR-2006-43. Microsoft, Inc. 2006
  45. M. Fähndrich and R. DeLine "Adoption and Focus: Practical Linear Types for Imperative Programming." Proc. of the ACM Conference on Programming Language Design and Implementation June 2002.
  46. Bernd Schoeller. ``Strengthening Eiffel contracts using models.'' Hung Dang Van and Zhiming Liu, editors, Proceeding of the Workshop on Formal Aspects of Component Software FACS'03, September 2003.
  47. Bart Jacobs, Jan Smans, Frank Piessens, Wolfram Schulte. ``A Simple Sequential Reasoning Approach for Sound Modular Verification of Mainstream Multithreaded Programs'' Proc. of Multithreading in Hardware and Software: Formal Approaches to Design and Verification (TV 2006), Seattle, 2006.
  48. Geoffrey Smith and Dennis Volpano. ``Polymorphic typing of variables and references'' ACM Transactions on Programming Languages and Systems Pages: 254 - 267, May 1996
  49. Simon Peyton Jones and Philip Wadler ``Imperative functional programming.'' Proc. ACM SIGPLAN Principles of Programming Languages. 1993
  50. A. K. Wright, ``Simple Imperative Polymorphism'' Lisp and Symbolic Computation 8(4):343--355, 1995.
  51. Benjamin C. Pierce ``Types and Programming Languages'' The MIT Press, Massachusetts Institute of Technology ISBN 0-262-16209-1, 2002.
  52. J. Garrigue, ``Relaxing the Value Restriction'' International Symposium on Functional and Logic Programming 2004.
  53. D. Grossman, ``Quantified Types in an Imperative Language'' ACM Transactions on Programming Languages and Systems 2006.
  54. D. Grossman ``Safe programming at the C level of abstraction.'' Ph.D. dissertation. Cornell University 2003.
  55. J. S. Shapiro, S. Sridhar, M. S. Doerrie, ``BitC Language Specification'' http://coyotos.org/docs/bitc/spec.html
  56. S. Sridhar, J. S. Shapiro ``Design of Type and Mutability Inference in BitC'' Systems Research Laboratory Technical Report SRL2006-01 Johns Hopkins University, 2006. http://www.coyotos.org/docs/bitc/mutinfer.html
  57. Jeff Vaughan ``A proof of correctness for the Hindley-Milner type inference algorithm'' http://www.seas.upenn.edu/~vaughan2/docs/hmproof.pdf

  1. We use texttt font to show program fragments and an emphasized texttt font to show the inferred types.

  2. Of course, types are not displayed in this form to the user. Function types are always printed in ``interface form,'' which does not expose the ``internal'' mutability of argument or return types [56].

  3. This transformation can be thought of as a (safe) coercion. Hence the use of the operator .