Sound and Complete Type Inference in BitC
SRL Technical Report SRL2008-02
Abstract
This paper introduces a new type system designed for safe systems programming. The type system features a new mutability model that combines unboxed types with a consistent typing of mutability. The type system is provably sound, supports polymorphism, and eliminates the need for alias analysis to determine the immutability of a location. A sound and complete type inference algorithm for this system is presented.
1 Introduction
Recent advances in the theory and practice of programming languages have resulted in modern languages and tools that provide certain correctness guarantees regarding the execution of programs. However, these advances have not been effectively applied to the construction of systems programs, the core components of a computer system. One of the primary causes of this problem is the fact that existing languages do not simultaneously support modern language features — such as static type safety, type inference, higher order functions and polymorphism — as well as features that are critical to the correctness and performance of systems programs such as prescriptive data structure representation and mutability. In this paper, we endeavor to bridge this gap between modern language design and systems programming. We first discuss the support for these features in existing languages, identify the challenges in combining these feature sets and then describe our approach toward solving this problem.
Representation Control A systems programming language must be expressive enough to specify details of representation including boxed/unboxed data-structure layout and stack/heap allocation. For systems programs, this is both a correctness as well as a performance requirement. Systems programs interact with the hardware through data structures such as page tables whose representation is dictated by the hardware. Conformance to these representation specifications is necessary for correctness. Languages like ML [16] intentionally omit details of representation from the language definition, since this greatly simplifies the mathematical description of the language. Compilers like TIL [23] implement unboxed representation as a discretionary optimization. However, in systems programs, statements about representation are prescriptive, not descriptive. Formal treatment of representation is required in systems programming languages.
Systems programs also rely on representation control for performance since it affects cache locality and paging behavior. This expressiveness is also crucial for interfacing with external C [9] or assembly code and data. For example, a careful implementation of the TCP/IP protocol stack in Standard ML incurred a substantial overhead of up to 10x increase in system load and a 40x slowdown in accessing external memory relative to the equivalent C implementation [ 1 ,3 ]. This shows that representation control is as important as, or even more important than, high level algorithms for the performance of systems tasks.
Complete Mutability One of the key features essential for systems programming is support for mutability. The support for mutability must be `complete' in the sense that any location — whether on the stack, heap, or within other unboxed structures — can be mutated. Allocation of mutable cells on the stack boosts performance because (1) the top of the stack is typically accessible from the data cache (2) stack locations are directly addressable and therefore do not require the extra dereferencing involved in the case of heap locations (3) stack allocation does not involve garbage collection overhead. This is particularly important for high confidence and/or embedded kernels as they cannot tolerate unpredictable variance in overhead caused by heap allocation and collection. ML-like languages require all mutable (ref) cells to reside on the heap. In pure languages like Haskell [14], the support for mutability is even more restrictive than ML. These restricted models of mutability are insufficiently expressive from a systems programming perspective.
Consistent Mutability
The mutability support in a language is said to be
`consistent' if the (im)mutability
of every location is invariant across all aliases over program
execution. In this model, there is a sound notion of
immutability of locations. This benefits tools that perform
static analysis or model checking because conclusions drawn
about the immutability of a location need never be
conservative. It also increases the amount of optimization that
a compiler can safely perform without complex alias
analysis. Polymorphic type inference systems such as
Hindley-Milner algorithm [15] also
rely on a sound notion of immutability. ML supports consistent
mutability since types are definitive about the (im)mutability
of every location. In contrast, C does not support this
feature. For example, in C it is legal to write:
const bool *cp = ...;
bool *p = cp;
*p = false; // OK!
The alleged ``constness'' of the location pointed to by
cp is a local property (only) with
respect to the alias cp and not a
statement of true immutability of the target location.
The analysis and optimization of critical
systems programs can be improved by using a language
with a consistent mutability model.
Type Inference and Polymorphism Type inference achieves the advantages of static typing with a lower burden on the programmer, facilitating rapid prototyping and development. Polymorphic type inference (c.f. ML or Haskell) combines the advantages of static type safety with much of the convenience provided by dynamically typed languages like Python [18]. Automatic inference of polymorphism simplifies generic programming, and therefore increases the reuse and reliability of code. Safe languages like Java [17], C# [4], or Vault [2] do not support type inference. Cyclone [10] features partial type inference and supports polymorphism only for functions with explicit type annotations.
The following table summarizes the support available in existing languages for the above features and static type safety:
| C/Asm | Safe-C | CCured | Cyclone | Vault | Java | ML | Haskell | |
| Representation | ?Y? | ?Y? | ?Y? | |||||
| Complete Mutability | ?Y? | ?Y? | ?Y? | ?Y? | ?Y? | ?Y? | ||
| Consistent Mutability | ?Y? | ?Y? | ||||||
| Static Type Safety | ?Y? | ?Y? | ?Y? | ?Y? | ?Y? | |||
| Poly. Type Inference | ?Y? | ?Y? |
In this paper, we present a new type system and formal foundations for a safe systems programming language that supports all of the above features.
The combination of mutability and unboxed representation presents several challenges for type inference. Mutability is an attribute of the location storing a value and not the value itself. Therefore, two expressions across a copy boundary (ex: arguments copied at a function call) can differ in their mutability. We refer to this notion of mutability compatibility of types as copy compatibility. Copy compatibility creates ramifications for syntax-directed type and mutability inference. Type inference is further complicated due to well known problems with the interaction of mutability and polymorphism [25]. This has forced a second-class treatment of mutability in ML-like languages and a lack of inferred polymorphism in others.
We present a sound and complete polymorphic type inference algorithm for a language that supports consistent and complete mutability. In order to overcome the challenges posed by copy compatibility, the underlying type system uses a system of constrained types that range over mutability and polymorphism. Safety of the type system as well as the soundness and completeness of the type inference algorithm have been proved.
2 Informal Overview
..-2exIn this section, we give an informal description of our type system and inference algorithm. For purposes of presentation in this paper, we define *B*, a core systems programming language calculus. *B* is a direct expression of lambda calculus with side effects, extended to be able to reflect the semantics of explicit representation.
|
|
The type ⇑τ represents a reference (pointer) type and Ψρ represents a mutable type. The expression dup(_e_), where _e_ has type τ, returns a reference of type ⇑τ to a heap-allocated copy of the value of _e_. The ^ operator is used to dereference heap cells. Pairs (,) are unboxed structures whose constituent elements are contiguously allocated on the stack, or in their containing data-structure. _e_.1 and _e_.2 perform selection from pairs. We define !1 = 2 and !2 = 1. The let construct can be used for allocating (possibly mutable) stack variables and to create let-polymorphic bindings. let _x_[:τ] = _e_ represents optional type qualification of let-bound variables.
The Mutability Model
*B* supports consistent, complete mutability.
The mutability support is complete since the
:= operator mutates both stack locations
(let-bound locals, function parameters) and heap locations
(dup-ed values). It can also perform
in-place updates to individual fields of unboxed pairs.
The mutability support is consistent since we impose the
``one location, one type'' rule. For example, in the following
expression,
let _cp_ : ⇑bool = dup(true) in let _p_ : ⇑Ψbool = _cp_
(* Error *)
_cp_ has the type reference to bool
(⇑bool), which is incompatible with that of
_p_, reference to mutable-bool
(⇑Ψbool).
Unlike ML, := does not dereference its target.
The expressions that can appear on the left of an assignment
:= are restricted to
left expressions (defined by the above grammar).
This not only preserves the programmer's mental model of the
relationship between locations storage, but also ensures that
compiler transformations are semantics preserving.
Copy Compatibility
*B* is a
call-by-value language, and supports
copy compatibility, which permits locations across a
copy boundary to differ in their mutability.
For example, in the following expression:
let _fnxn_ = λ_x_.(_x_ := false) in let _y_ : bool = true in _fnxn_ _y_
the type of
_fnxn_ is
(Ψbool) → unit, whereas that of the actual argument
_y_ is
bool.
Since
_x_ is a copy of
_y_ and occupies a different location,
this expression is type safe.
Thus, we write
bool ≅ Ψbool, where ≅ indicates copy compatibility.
Copy compatibility must not extend past a reference boundary in order to ensure that every location has a unique type. We define copy compatibility for *B* as:
|
|
|
|
|
Copy compatibility is allowed at all positions where a copy is performed: at argument passing, new variable binding, assignment, and basically in all expressions where a left-expression is not expected or returned. For example, the expression (_x_ : τ) : Ψτ is ill typed, but the branches of a conditional can have different but copy compatible types as in if true then _a_ : τ else _b_ : Ψτ .
..-4pt2.1 Type Inference
..-3ptWe now consider the problem of designing a type inference algorithm for *B*. Due to copy compatibility, it is no longer possible to infer a unique (simple) type for all expressions. For example, in the expression let _p_ = true, we know that the type of the literal true is bool, but the type of _p_ could either be bool or Ψbool. Therefore, unlike ML, we cannot use a straightforward syntax-directed type inference algorithm in *B*.
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? Unfortunately,
in a language with copy compatibility, this 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 function that returns the first element of a
pair, and
_m_
is a variable of type
Ψbool, we will have to write:
let _xyz_ = dup(_fst_ (_m_, false) : Ψbool ✗ bool) : ⇑Ψbool in ...
Therefore, if mutability is not inferred, it results in a
substantial increase in the number of programmer annotations,
and type inference becomes ineffective.
It is desirable that the inference algorithm must
automatically infer polymorphism (without any programmer
annotations) as well, since this leads to better software
engineering by maximizing code reuse.
Therefore, the desirable characteristics of a type inference
algorithm for
*B* are:
(1) It must be sound, complete, and decidable without programmer
annotations.
(2) It must automatically infer both polymorphism and
mutability.
(3) It must infer types that are intelligible to the programmer.
That is, it must
avoid the main drawback of many inference
systems with subtyping, where
the inferred principal type is presented as a set of
equations and inequations.
In order to address the above requirements, we propose a variant of the Hindley-Milner algorithm [15]. This algorithm uses constrained types that range over mutability and polymorphism in order to infer principal types for *B* programs.
Polymorphism Over Mutability
In order to infer principal types in a language with copy
compatibility, we define the following constrained types that
allow us to infer types with variable mutability.
Let
≃ be a equivalence relation on types such that
ρ ≃ Ψρ.
Let
τ \ η denote a constrained type where
τ is constrained by the set of (in)equations
η. We write :
α⇃ρ ≡ α \ {α ≃ ρ}:
any type equal to base type
ρ except for top level mutability.
ς↓ρ ≡ ς \ {ς ≅ ρ}:
any type copy compatible with
ρ, where
ς = α or Ψα.
Now, in the expression let _p_ = true, we can give _p_ the type α↓bool. During inference, the type can later get resolved to either bool or Ψbool. The forms α⇃ρ and ς↓ρ respectively provide fine grained and coarse grained control over expressing types with variable mutability. For example:
Type |
Instances |
Non-Instances |
|
α⇃(bool ✗ unit) |
bool ✗ unit, Ψ(bool ✗ unit) |
Ψbool ✗ unit |
|
α↓(bool ✗ unit) |
bool ✗ unit, Ψ(Ψbool ✗ Ψunit) |
unit ✗ bool |
|
Ψbool ✗ unit, β⇃(bool ✗ unit) |
||
|
Ψα↓(bool ✗ unit) |
Ψ(bool ✗ unit), Ψ(bool ✗ Ψunit) |
bool ✗ unit, β⇃(bool ✗ unit) |
|
α↓⇑bool |
⇑bool, Ψ⇑bool |
⇑Ψbool |
By embedding constraints within types, we obtain an elegant representation of constrained types that are self contained. The programmer is just presented a type, rather than a type associated with a set of unsolved inequations. Every type of the form ς↓ρ can be realized through a canonical representation using α⇃ρ types. However, types of the form ς↓ρ are critical for type inference. For example, the type α↓β represents a type that is compatible with β, even if β later resolves to a more concrete (ex: pair) type.
Since we allow copy compatibility at function argument and return positions, two function types are equal regardless of the shallow mutability of the argument and return types. Therefore, we follow a convention of writing all function types with immutable types at copy compatible positions. The intuition here is that the type of a function must be described in the interface form, and must hide the ``internal'' mutability information. For example, the function λ_x_.(_x_ := true), has external type bool → unit even though the internal type is Ψbool → unit.
*B* is a let-polymorphic language. At a let boundary, we would like to quantify over variables that range over mutability, in order to achieve mutability polymorphism. The next sections discuss certain complications that arise during the inference of such types, present our solution to the problem.
Soundness implications Like ML, *B* enforces the value restriction [25] to preserve soundness of polymorphic typing. This means that the type of _x_ in let _x_ = _e_1 in _e_2 can only be generalized if _e_1 is an immutable syntactic value. For example, in the expression let _id_ = λ_x_._x_, the type of _id_ before generalization is β↓(α → α). However, giving _id_ the generalized type ∀αβ.β↓(α → α) is unsound, since it permits expressions such as let _id_ = λ_x_._x_ in (_id_ := λ_x_.true, _id_ ()) to type check. We can give _id_ either the polymorphic type ∀α.α → α, or the monomorphic type β↓(α → α). However, neither is a principal type for _id_.
Overloading Polymorphism Due to the above interaction of polymorphism and unboxed mutability, a traditional HM-style inference algorithm cannot defer decisions about the mutability of types past their generalization. Therefore, current algorithms fix the mutability of types before generalization based on certain heuristics — thus sacrificing completeness [22]. In order to alleviate this problem, we use a new form of constrained types that range over both mutability and polymorphism.
We introduce constraints ★ϰ_x_(τ) to enforce consistency restrictions on instantiations of generalized types. The constraint ★ϰ_x_(τ) requires that the identifier _x_ only be instantiated according to the kind ϰ, where ϰ = ψ or ∀. If ϰ = ψ, the instantiation of _x_ must be monomorphic. That is, all uses of _x_ must instantiate τ to the same type τ′. Here, τ′ is permitted to be a mutable type. If ϰ = ∀, different uses of _x_ can instantiate τ differently, but all such instantiations must be immutable. At the point of definition (let), if the exact instantiation kind of a variable is unknown, we add the constraint ★κ_x_(τ), where κ ranges over ψ and ∀. The correct instantiation kind is determined later based on the uses of _x_, and consistency semantics are enforced accordingly. The variable _x_ in ★κ_x_(τ) represents the program point (let) at which this constraint is generated. We assume that there are no name collisions so that every such _x_ names a unique program point.
In this approach, the definition of
_id_ will be given the principal constrained type:
let _id_ = λ_x_._x_ in _e_ _id_ : ∀αβ.β↓(α → α) \ {★κ_id_(β↓(α → α))}
Every time
_id_ is instantiated to type
τ′ in
_e_, the constraints
★κ_id_(τ′) are collected.
_e_ is declared type correct only if the set of all
instantiated constraints are consistent for some
κ. Note that we do not quantify over
κ.
|
Example of _e_ |
Constraint set |
Kind assignment |
|
(_id_ true, _id_ ()) |
{★κ_id_(bool → bool), ★κ_id_(unit → unit)} |
κ ↦ ∀ |
|
_id_ := λ_x_._x_ |
{★κ_id_(Ψ(γ → γ))} |
κ ↦ ψ |
|
(_id_ true, _id_ := λ_x_.()) |
{★κ_id_(bool → bool), ★κ_id_(Ψ(unit → unit))} |
Type Error |
|
(_id_, _id_) |
{★κ_id_(β1↓(α1 → α1)), ★κ_id_(β2↓(α2 → α2))} |
κ ↦ ψ or ∀ |
The final case type checks with either kind, under the type assignments (α1 = α2, β1 = β2) if κ ↦ ψ and (β1 = α1 → α1, β2 = α2 → α2) if κ ↦ ∀. The intuition behind ★κ_x_(τ) constraints is to achieve a form of overloading over polymorphism and mutability. We can think of ★κ_x_(τ) as a type class [11] constraint that has exactly one possibly mutable instance ★ψ_x_(τm), and an infinite number of ★∀_x_(τp) instances where all types τp* are immutable.
In practice, once the correct kind of instantiation is inferred, the type scheme can be presented in a simplified form to the programmer. For example, consider the expression let _f_ = λ_x_.if _x_^ then () else () in (_f_ _m_, _f_ _n_), where _m_ : ⇑Ψbool and _n_ : ⇑bool. Here, _f_ : ∀αβ.β↓(⇑α↓bool → unit)\{★κ_f_(β↓(⇑α↓bool → unit))}. However, based on the polymorphic usage, we conclude that κ ↦ ∀. We can now simplify the type scheme of _f_ to obtain _f_ : ∀α.⇑α↓bool → unit. Since all function types are immutable, the mutability of the argument type need not be fixed, thus preserving mutability polymorphism. In order to ensure that type inference is modular, the ★κ_x_(τ) constraints must not be exposed across a module boundary. For every top-level definition in a module, an arbitrary choice of κ = ψ or κ = ∀ must be made for every surviving ★κ_x_(τ) constraint.
In summary, we have used a system of constrained types to design a polymorphic type inference system that meets all of the design goals set at the beginning of this section. In the next section, we present a formal description of our type system and inference algorithm.
3 Formal Description
|
|
Figure 1.
Extended *B* grammar
In order to formalize the semantics of *B*, we extend the calculus with stack and heap locations (Fig. 1). Heap locations are first class values, but stack locations are not. Further, we annotate all let expressions with a kind — letψ: monomorphic, possibly mutable definition, and let∀: polymorphic definitions. The two kinds of let expressions have different execution semantics. We write letκ to range over the two kinds of let expressions. This distinction is similar to Smith and Volpano's Polymorphic-C [21]. However, unlike Polymorphic-C, let-kind is meta syntax, and is not a part of the input program. The correct kind of let is inferred from the static type information. We do not show the semantics for type-qualified expressions as they are trivial.
Rule |
Pre-conditions |
Evaluation Step |
E-Rval |
S(l) = _v_ |
S; H; l ⇒ S; H; _v_ |
E-# |
S; H; _e_ ⇒ S′; H′; _e_′ |
S; H; *R*[_e_] ⇒ S′; H′; *R*[_e_′] |
E-App |
l ∉ dom(S) |
S; H; λ_x_._e_ _v_ ⇒ S, l ↦ _v_; H; _e_[l/_x_] |
E-If |
?b?1 = true ?b?2 = false |
S; H; if ?b?i then _e_1 else _e_2 ⇒ S; H; _e_i |
E-.i |
S; H; (_v_1, _v_2).i ⇒ S; H; _v_i |
|
E-Dup |
ℓ ∉ dom(H) |
S; H; dup(_v_) ⇒ S; H, ℓ ↦ _v_; ℓ |
EL-^# |
S; H; _e_ ⇒ S′; H′; _e_′ |
S; H; _e_^ ⇛ S′; H′; _e_′^ |
E-^ |
H(ℓ) = _v_ |
S; H; ℓ^ ⇒ S; H; _v_ |
E-:=# |
S; H; _l_ ⇛ S′; H′; _l_′ |
S; H; _l_ := _e_ ⇒ S′; H′; _l_′ := _e_ |
E-:=Stack |
S, l ↦ _v_; H; l := _v_′ ⇒ S, l ↦ _v_′; H; () |
|
E-:=Heap |
S; H, ℓ ↦ _v_; ℓ^ := _v_′ ⇒ S; H, ℓ ↦ _v_′; () |
|
E-:=S.p |
_v_′!i = _v_!i S, l ↦ _v_i; H; l.?p? := _v_′i |
S, l ↦ (_v_1, _v_2); H; l.i.?p? := _v_′i |
⇒ S, l ↦ _v_′i; H; () |
⇒ S, l ↦ (_v_′1, _v_′2); H; () |
|
E-:=H.p |
_v_′!i = _v_!i S; H, ℓ ↦ _v_i; ℓ^.?p? := _v_′i |
S; H, ℓ ↦ (_v_1, _v_2); ℓ^.i.?p? := _v_′i |
⇒ S; H, ℓ ↦ _v_′i; () |
⇒S; H, ℓ ↦ (_v_′1, _v_′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_] |
Figure 2.
Small Step Operational Semantics
Dynamic Semantics The system state is represented by the triple S; H; _e_ consisting of the stack S, the heap H, and the expression _e_ to be evaluated. Evaluation itself is a two place relation S; H; _e_ ⇒ S′; H′; _e_′ that denotes a single step of execution. Fig. 2 shows the evaluation rules for our core language. We assume that the program is alpha-converted so that there are no name collisions due to inner bindings. Following the theoretical development of [6], we give separate execution semantics for left evaluation (execution of left expressions _l_ on the LHS of an assignment, denoted by ⇛) and right evaluation (⇒) respectively.
Since the E-Dup and E-^ rules work only on the heap, we can only capture references to heap cells. Stack locations cannot escape beyond their scope since E-Rval rule performs implicit value extraction from stack locations in rvalue contexts. State updates can be performed either on the stack or on the heap (E-:=* rules). The stack is modeled as a pseudo-heap. This enables us to abstract away details such as closure-construction and garbage collection while illustrating the core semantics, as they can later be reified independently.
The execution semantics do not perform a copy operation in all cases where copy compatibility is permitted. For example, the E-If rule does not introduce a copy step in the branching expression. Since if-expressions are not lvalues, they cannot be the target of an assignment. Therefore, the value that either branch evaluates to, can itself be used in all cases where a copy of that value can be.
|
τ |
△(τ) |
▽(τ) |
▴(τ) |
▾(τ) |
*I*(τ) |
{|τ|} |
|
α |
Ψα |
α |
Ψα |
α |
α |
{α} |
|
unit |
Ψunit |
unit |
Ψunit |
unit |
unit |
∅ |
|
bool |
Ψbool |
bool |
Ψbool |
bool |
bool |
∅ |
|
τ1 → τ2 |
Ψ(τ1 → τ2) |
τ1 → τ2 |
Ψ(τ1 → τ2) |
τ1 → τ2 |
τ1 → τ2 |
{|τ1|} ∪ {|τ2|} |
|
⇑τ |
Ψ⇑τ |
⇑τ |
Ψ⇑τ |
⇑τ |
⇑*I*(τ) |
{|τ|} |
|
Ψρ |
△(ρ) |
▽(ρ) |
▴(ρ) |
▾(ρ) |
*I*(ρ) |
{|ρ|} |
|
τ1 ✗ τ2 |
Ψ(△(τ1) ✗ △(τ2)) |
▽(τ1) ✗ ▽(τ2) |
Ψ(τ1 ✗ τ2) |
τ1 ✗ τ2 |
*I*(τ1) ✗ *I*(τ2) |
{|τ1|} ∪ {|τ2|} |
|
α⇃ρ |
△(ρ) |
▽(ρ) |
▴(ρ) |
▾(ρ) |
*I*(ρ) |
{α⇃ρ} ∪ {|ρ|} |
|
ς↓ρ |
△(ρ) |
▽(ρ) |
▴(ς)↓ρ |
▾(ς)↓ρ |
*I*(ρ) |
{ς↓ρ} ∪ {|ρ|} |
|
τ |
?Immut??(?τ?)? |
?Mut??(?τ?)? |
□(τ) |
θ〈τ〉 |
|
α |
?false? |
?false? |
?false? |
τ if [α ↣ τ] ∊ θ, else α. |
|
unit |
?true? |
?false? |
?true? |
unit |
|
bool |
?true? |
?false? |
?true? |
bool |
|
τ1 → τ2 |
?true? |
?false? |
?true? |
θ〈τ1〉 → θ〈τ2〉 |
|
⇑τ |
?Immut??(?τ?)? |
?Mut??(?τ?)? |
□(τ) |
⇑θ〈τ〉 |
|
Ψρ |
?false? |
?true? |
□(ρ) |
Ψθ〈ρ〉 |
|
τ1 ✗ τ2 |
?Immut??(?τ1?)? ∧ ?Immut??(?τ2?)? |
?Mut??(?τ1?)? ∨ ?Mut??(?τ2?)? |
□(τ1) ∧ □(τ2) |
θ〈τ1〉 ✗ θ〈τ2〉 |
|
α⇃ρ |
?false? |
?Mut??(?▾(ρ)?)? |
□(ρ) |
α′⇃θ〈ρ〉 if θ〈α〉 = α′ |
|
ρ′ if θ〈α〉 = ρ′ ≠ α′ |
||||
|
ς↓ρ |
?false? |
?Mut??(?ς?)? ∨ ?Mut??(?▽(ρ)?)? |
□(ρ) |
ς′↓θ〈ρ〉 if θ〈ς〉 = ς′ |
|
ϱ if θ〈ς〉 = ϱ ≠ ς′ |
Figure 3.
Operations and Predicates on Types
Static Semantics Fig. 3 defines several operators and predicates on types that we use in this section. The operators ▴ and ▾ respectively increase and decrease the shallow top-level mutability of a type. △ and ▽ maximize / minimize the mutability of a type up to a reference or function boundary. *I* removes all mutability in a type up to a function boundary. We write τ1 =▾ τ2 as shorthand for ▾(τ1) = ▾(τ2) and τ1 =▽ τ2 for ▽(τ1) = ▽(τ2). In our algebra of types, the mutable type constructor is idempotent (ΨΨτ ≡ Ψτ). We also define the equivalences: α⇃ρ ≡ α⇃ρ′, where ρ =▾ ρ′ and ς⇃ρ ≡ ς⇃ρ′, where ρ =▽ ρ′. The predicates ?Immut? and ?Mut? identify types that are observably immutable and mutable respectively. The □(τ) predicate tests if the type τ is concretizable by fixing variables that range over mutability.
θ〈τ〉 denotes the application of a substitution θ on τ as defined in Fig. 3. θ〈_e_〉 performs substitutions for κ annotations in _e_. {|τ|} denotes the set of all constrained types and unconstrained type variables structurally present in τ. θ〈 〉 and {| |} are extended to σ, Γ, Σ, and {τ*} in the natural, capture-avoiding manner.
Definition I (Canonical Expressions). An expression _e_ is said to be canonical if all let expressions in _e_ are annotated with one of the kinds ψ or ∀.
Definition II (Consistency of Constrained types).
Let
?mtv??(?τ*?)?,
?Mtv??(?τ*?)?, and
?ntv??(?τ*?)? be the set of all type variables appearing in
{τ*} constrained by
α⇃ρ, by
ς↓ρ and unconstrained
respectively.
We say that the set of types
{τ*} is consistent,
written
⊪ {τ*}, if:
(1) For all
{α⇃ρ, α⇃ρ′} ⊆ {|τ*|}, we have
ρ =▾ ρ′.
(2) For all
{ς↓ρ, ς′↓ρ′} ⊆ {|τ*|} such that
ς =▾ ς′, we have
ρ =▽ ρ′.
(3)
?mtv??(?τ*?)?,
?Mtv??(?τ*?)?, and
?ntv??(?τ*?)?
are mutually exclusive.
Definition III (Consistency of substitutions).
A substitution
θ
is said to be consistent over a set of types
{τ*}, written
θ ⊪ {τ*} if:
(1)
⊪ θ{|τ*|}.
(2) For all
α⇃ρ ∊ {|τ*|},
we have
θ〈α〉 = β,
or
θ〈α〉 = ρ′
such that
ρ′ =▾ θ〈ρ〉.
(3) For all
ς↓ρ ∊ {|τ*|},
we have
θ〈ς〉 = ς′,
or
θ〈ς〉 = ϱ
such that
ϱ =▽ θ〈ρ〉.
Definition IV (Consistency of ★ constraints).
A set of ★ constraints
*D* is said to be consistent, written
⊧ *D* if:
(1) For all
★∀_x_(τ) ∊ *D*, we have
?Immut??(?τ?)?.
(2) For all
★ψ_x_(τ1) ... ★ψ_x_(τn) ∊ *D*,
we have
τ1 = ... = τn.
(3) For all
★ϰ1_x_(τ1) ∊ *D*
and
★ϰ2_y_(τ2) ∊ *D*,
ϰ1 ≠ ϰ2
implies
_x_ ≠ _y_.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Figure 4.
Declarative Type Rules
Declarative Type Rules Fig. 4 presents a declarative definition of the type system of *B*. In this type system, copy compatibility is realized through copy coercion (⊴:) rules that are similar to subtyping rules (S-* rules in Fig. 4). Since reference types ⇑τ are handled only by S-Refl, types cannot coerced beyond a reference boundary. Also, two function types are coercible only if they are structurally identical. Here, the contravariance/covariance of argument/return types is unnecessary as we can follow a standard convention with respect to the mutability of argument/return types at copy positions. The rules for typing expressions (T-* rules) introduce these coercions at all copy-compatible positions.
The type judgment *D*; Γ; Σ ⊢ _e_ : τ is understood as: given a binding environment Γ and store typing Σ, the expression _e_ has type τ subject to the set of ★ constraints *D*. We write _e_ ⊴: τ as a shorthand for _e_ : τ′ and τ′ ⊴: τ, for some type τ′. The rule T-Lambda permits the interface type of a function be different from its internal type, as explained in Sec. 2.1. The rule T-App introduces copy-coercions at argument and return positions of an application. T-Let-M rule types let expressions monomorphically, and thus requires a letψ annotation. In this case, the expression _e_1 is permitted to be expansive (i.e. need not be a syntactic value υ). The T-Let-MP rule types let expressions where the expression being bound is a syntactic value. It assigns _x_ a constrained type scheme along with the constraint ★ϰ_x_(τ). The T-Id rule instantiates types and constraints. The instantiated constraints are collected over the entire derivation, so that we can enforce instantiation consistency. ⊧new α* identifies fresh type variables.
We prove the soundness of our type system by demonstrating subject reduction. Here, we prove that the type of an expression is preserved exactly by left-execution, which ensures that the type of a location does not change during the execution of a program. We also show that right execution preserves types except for shallow mutability. The result of a right execution can only be used in copy compatible positions, or as the target of a dereference. In the former case, preservation of shallow mutability is unnecessary, and in the later, the type within the reference is preserved exactly.
The interesting case is the safety of polymorphic let expressions. The T-Let-MP rule does not require that the type τ being quantified over be immutable, but adds the ★ϰ_x_(τ) constraint. Now, if we have a derivation *D*; Γ; Σ ⊢ _e_ : τ such that ⊧ *D*, then one of the two cases must follow. (1) If any instantiation of τ is mutable, then ϰ = ψ. In this case, execution proceeds through the E-Let-M rule, which create a stack location for _x_. Therefore, _x_ is permitted to be the target of an assignment. ⊧ *D* guarantees that all instantiations of τ are identical, which ensures that the type of a location cannot change. (2) If τ is instantiated polymorphically, then ϰ = ∀. Execution proceeds through the E-Let-P rule, which performs a value substitution. Here, ⊧ *D* guarantees that all instantiations are deeply immutable. Therefore, _x_ cannot be directly used (in the forms _x_ or _x_.?p?) as the target of an assignment, which ensures that the value substitution cannot lead to a stuck state.
Definition V (Consistent Type Derivation). Let {|*D*; Γ; Σ ⊢ _e_ : τ|} denote the extension {| |} function to the set of all types used in the derivation of *D*; Γ; Σ ⊢ _e_ : τ. We say that *D*; Γ; Σ ⊢* _e_ : τ is a consistent derivation if *D*′; Γ; Σ ⊢ _e_ : τ for some *D*′ ⊆ *D*, and ⊪ {|*D*|} ∪ {|*D*′; Γ; Σ ⊢ _e_ : τ|}.
Definition VI (Stack and Heap Typing)
A heap H and a stack
S are said to be well
typed with respect to
Γ,
Σ and
*D*, written
*D*; Γ; Σ ⊢* H + S, if:
(1)
dom(Σ) = dom(H) ∪ dom(S)
(2)
∀ℓ ∊ dom(H), *D*; Γ; Σ ⊢* H(ℓ) : τ such that Σ(ℓ) =▽ τ
(3)
∀l ∊ dom(S), *D*; Γ; Σ ⊢* S(l) : τ such that Σ(l) =▽ τ
Definition VII (Valid Lvalues). We say that an lvalue £ is valid with respect to a stack S and heap H, written H + S ⊢v £ if for some ?p?, either (1) £ = l or £ = l.?p? where l ∊ dom(S); or (2) £ = ℓ^ or £ = ℓ^.?p? where ℓ ∊ dom(H).
Lemma I (Progress).
If
_e_
is a closed canonical well typed expression,
that is,
*D*; ∅; Σ ⊢* _e_ : τ for some
τ and
Σ,
given any heap and stack such that
*D*; ∅; Σ ⊢* H + S,
(1) If
_e_ is a left expression
(_e_ = _l_), then
_e_
is either a valid lvalue (that is,
_e_ = £ and
H + S ⊢v £) 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_′.
Lemma II (Preservation).
For any canonical expression
_e_,
if
*D*; Γ; Σ ⊢* _e_ : τ,
*D*; Γ; Σ ⊢* H + S and
⊧ *D* then,
(1) If
S; H; _e_ ⇛ S′; H′; _e_′, then,
∃ Σ′ ⊇ Σ
such that
*D*; Γ; Σ′ ⊢* _e_′ : τ
and
*D*; Γ; Σ′ ⊢* H′ + S′.
(2) If
S; H; _e_ ⇒ S′; H′; _e_′, then,
∃ Σ′ ⊇ Σ
such that
*D*; Γ; Σ′ ⊢* _e_′ : τ′,
*D*; Γ; Σ′ ⊢* H′ + S′ and
τ =▽ τ′.
Definition VIII (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_′.
Theorem I (Type Soundness). Let ⇒* denote the reflexive-transitive-closure of ⇒. For any canonical expression _e_, if *D*; ∅; Σ ⊢* _e_ : τ, *D*; ∅; Σ ⊢* H + S, ⊧ *D*, and S; H; _e_ ⇒* S′; H′; _e_′, then S′; H′; _e_′ is not stuck. That is, execution of a closed, canonical, well typed expression cannot lead to a stuck state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Figure 5.
Type Inference Algorithm
| U-Empty | *U*(∅) | = | (∅, 〈〉) |
| U-Refl | *U*({τ = τ} ∪ *C*) | = | *U*(*C*) |
| U-Sym | *U*({τ1 = τ2} ∪ *C*) | = | *U*({τ2 = τ1} ∪ *C*) |
| U-Var | *U*({α = τ} ∪ *C*) | α ∉ τ | = | (*D*, θa ∘ θu) where θa = [α ↣ τ] and |
| *U*(θa〈*C*〉) = (*D*, θu) | |||
| U-Fn | *U*({τa → τr = τ′a → τ′r} ∪ *C*) | = | *U*(*C* ∪ {τa = τ′a, τr = τ′r}) |
| U-Ref | *U*({⇑τ1 = ⇑τ2} ∪ *C*) | = | *U*(*C* ∪ {τ1 = τ2}) |
| U-Mut | *U*({Ψρ1 = Ψρ2} ∪ *C*) | = | *U*(*C* ∪ {ρ1 = ρ2}) |
| U-Pair | *U*({τ1 ✗ τ2 = τ′1 ✗ τ′2} ∪ *C*) | = | *U*(*C* ∪ {τ1 = τ′1, τ2 = τ′2}) |
| U-Ct1 | *U*({α⇃ρ1 = β⇃ρ2} ∪ *C*) | = | *U*(*C* ∪ {ρ1 =▾ ρ2, α = β}) |
| U-Ct2 | *U*({α⇃ρ = ρ′} ∪ *C*) | = | *U*(*C* ∪ {ρ =▾ ρ′, α = ρ′}) |
| U-Ct3 | *U*({ς1↓ρ1 = ς2↓ρ2} ∪ *C*) | = | *U*(*C* ∪ {ρ1 =▽ ρ2, ς1 = ς2}) |
| U-Ct4 | *U*({ς↓ρ = ϱ} ∪ *C*) | = | *U*(*C* ∪ {ρ =▽ ϱ, ς = ϱ}) |
| U-K | *U*({κ = ϰ} ∪ *C*) | = | (*D*, θk ∘ θu) where θk = [κ ↣ ϰ] and |
| *U*(θk〈*C*〉) = (*D*, θu) | |||
| U-Om1 | *U*({★ψ_x_(τ1), ★ψ_x_(τ2)} ∪ *C*) | = | (*D* ∪ θ{★ψ_x_(τ1), ★ψ_x_(τ2)}, θ) where |
| *U*(*C* ∪ {τ1 = τ2}) = (*D*, θ) | |||
| U-Op1 | *U*({★∀_x_(τ)} ∪ *C*) | □(τ) | = | (*D* ∪ θ{★∀_x_(τ)}, θ) where |
| *U*(*C* ∪ {τ = *I*(τ)}) = (*D*, θ) | |||
| U-Om2 | *U*({★κ_x_(τ)} ∪ *C*) | ?Mut??(?τ?)? | = | (*D*, θk ∘ θu) where θk = [κ ↣ ψ] and |
| *U*(θk〈{★κ_x_(τ)} ∪ *C*〉) = (*D*, θu) | |||
| U-Op2 | *U*({★κ_x_(τ1), ★κ_x_(τ2)} ∪ *C*) | = | (*D*, θk ∘ θu) where θk = [κ ↣ ∀] and |
| where *U*({τ1 = τ2} ∪ *C*) = ⊥ | *U*(θk〈{★κ_x_(τ1), ★κ_x_(τ2)} ∪ *C*〉) = (*D*, θu) | ||
| U-Error | *U*(?c? ∪ *C*) | ?c? ∉ *C*v ∪ *C*s ∪ *C*p | = | ⊥ |
| *C*v = ∀ α, ς, ρ, τ, τ′ | α ∉ τ′ . {τ = τ, α = τ′, τ′ = α, α⇃ρ = τ, τ = α⇃ρ, ς↓ρ = τ, τ = ς↓ρ} |
| *C*s = ∀ ρ, ρ′, τ, τ′, τ1, τ′1 . {τ → τ1 = τ′ → τ′1, ⇑τ = ⇑τ′, Ψρ = Ψρ′} |
| *C*p = ∀ _x_, κ, ϰ, τ, τ′ | ¬?Mut??(?τ′?)? . {κ = ϰ, ★ψ_x_(τ), ★∀_x_(τ′), ★κ_x_(τ)} |
Figure 6.
Unification Algorithm
Type Inference Algorithm Type inference is a program transformation that accepts a program in which let expressions are not annotated with their kinds, and returns the same program with let expressions annotated with their kinds and all expressions annotated with their types. The type inference algorithm is shown in Fig. 5. The inference judgment Γ; Σ ⊢i _e_ : τ | *C* is understood as: given a binding environment Γ and store typing Σ, the expression _e_ has type τ subject to the constraints *C*.
The inference algorithm introduces constrained types of the form ς↓ρ at all copy compatible positions. For example, the I-App rule introduces copy compatibility for the function type itself, the argument and the return types. The I-Sel rule represents the pair type as ε⇃(α↓β ✗ γ↓δ), which (1) permits top-level mutability of the pair type to be either mutable or immutable (2) ensures that the type of the selection is exactly same as the type of the field being selected (3) propagates full copy compatibility ``one level down.''
The unification algorithm is shown in Fig. 6. The unification of a constraint set *C* either fails with an error ⊥, or produces the pair (*D*, θ). θ is a solution for all equality constraints and some of the ★ constraints in *C*. *D* is the set of ★ constraints in *C* on which θ has been applied. ∪ represents disjoint union of sets.
The U-Ct* rules perform unification of constrained types with other constrained or unconstrained types. First, immutable versions of the two types are unified to establish compatibility (through constraints involving =▾ and =▽). Then, the constrained type is made to exactly equal the other type by unifying its variable part with the other type. The key observation here is that the copy compatibility is a special restricted form of subtyping. Since the type of the copy can be anywhere in the lattice of copy compatible types, subtyping requirements are always with respect a local maxima (the most immutable compatible type). We exploit this behavior to design a simple unification algorithm that only uses equality constraints over constrained types.
The U-Om1 ensures that all instantiations of monomorphic kind are the same. U-Op1 rule forces any concretizable instantiation of polymorphic kind to be immutable. The U-Om2 rule infers monomorphic kind based on the mutability of the instantiated type, and U-Op2 infers polymorphic kind if a variable _x_ is instantiated polymorphically to two types that do not inter-unify.
Definition IX (Constraint Satisfaction). The satisfaction of a constraint set *C* by a substitution θ is defined as follows.
|
|
Definition X (Notational Derivations).
We write:
(1)
θ; Γ; Σ ⊢i _e_ : τ | *D* if
Γ; Σ ⊢i _e_ : τ | *C*,
θ ⊢sol *C* ↝ *D*, and
θ ⊪ {Γ, Σ, τ, *C*}
(2)
θ; *D*; Γ; Σ ⊢* _e_ : τ if
θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢* θ〈_e_〉 : θ〈τ〉
Lemma III (Correctness of Unification). If *U*(*C*) = (*D*, θ), then θ ⊢sol *C* ↝ *D*
Lemma IV (Satisfiability of Unified Constraints). If *U*(*C*) = (*D*, θu), then there exists a substitution θs such that θu ∘ θs ⊢sat *C* ↝ *D*.
Lemma V (Principality of Unification). If *U*(*C*) = (*D*, θu), where *C* is a set of constraints obtained from the type inference algorithm, then, for all θs such that θs ⊢sol *C* ↝ *D*′, we have θs ⊇ θu.
Lemma VI (Decidability of Unification). The problem of computing a canonical derivation of *U*(*C*) for an arbitrary *C*, where no two applications of U-Sym rule happen consecutively is decidable.
Theorem II (Soundness of Type Inference).
If
θ; Γ; Σ ⊢i _e_ : τ | *D*, then
θ; *D*; Γ; Σ ⊢* _e_ : τ.
Lemma VII (Type Checkability). If Γ; Σ ⊢i _e_ : τ | *C* and *U*(*C*) = (*D*, θ), then ∃ θ′ such that ⊧ θ′〈*D*〉 and θ ∘ θ′〈_e_〉 is canonical, and θ ∘ θ′; *D*; Γ; Σ ⊢* _e_ : τ.
Theorem III (Completeness of Type Inference). If θ; *D*; Γ; Σ ⊢* _e_ : τ, then there exists a θ′ ⊇ θ such that θ′; Γ; Σ ⊢i _e_ : τ | *D*.
4 Related Work
Grossman [6] provides a theory of using quantified types with imperative C style mutation for Cyclone. However, his formalization requires explicit annotation for all polymorphic definitions and instantiations. In contrast, we believe that the best way to integrate polymorphism into the systems programming paradigm is by automatic inference. A further contribution of our work (in comparison to [6]) is that we give a formal specification and proof of correctness of the inference algorithm, not just the type system. Cyclone [10] uses region analysis to provide safe support for the address & operator. This technique is complementary to our work, and can be used to incorporate & operator in *B*.
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 [2], referential immutability [ 19 ,24 ] (transitive immutability-by-reference), etc. These techniques are orthogonal and complementary to the immutability-by-location property in *B*. For example, we could have types like (const Ψτ) that can express both global and local usage properties of a location.
A monadic model [13] of mutability is used in pure functional languages like Haskell [14]. In this model, the type system distinguishes side-effecting computations from pure ones (and not just mutable locations from immutable ones). Even though this model is beneficial for integration with verification systems, it is considerably removed from the idioms needed by systems programmers. For example, Hughes argues that there is no satisfactory way of creating and using global mutable variables using monads [7]. There have been proposals for adding unboxed representation control to Haskell [ 12 ,8 ]. However, these systems are pure and therefore and do not consider the effects of mutability.
Cqual [5] provides a framework of type qualifiers, which can be used to infer maximal const qualifications for C programs. However, CQual does not deal with polymorphism of types. In a monomorphic language, we can infer types and qualifiers independently. Adding polymorphism to CQual would introduce substantial challenges, particularly if polymorphism should be automatically inferred. The inference of types and qualifiers (mutability) becomes co-dependent: we need base types to infer qualifiers; but, we also need the qualifiers to infer base types due to the value restriction. *B* supports a polymorphic language and performs simultaneous inference of base types and mutability.
5 Conclusions
In this paper, we have defined a language and type system for systems programming which integrates all of unboxed representation, consistent complete mutability support, and polymorphism. The mutability model is expressive enough to permit mutation of unboxed/stack locations, and at the same time guarantees that types are definitive about the mutability of every location across all aliases.
Complete support for mutability introduces challenges for type inference at copy boundaries. We have developed a novel algorithm that infers principal types using a system of constrained types. To our knowledge, this is the first sound and complete algorithm that infers both mutability and polymorphism in a systems programming language with copy compatibility.
The type inference algorithm is implemented as part of the BitC [20] language compiler. The core of the compiler involves 22,433 lines of C++ code, of which implementation of the type system accounts for about 7,816 lines. The source code can be obtained from http://bitc-lang.org.
Bibliography
- E. Biagioni, R. Harper, and P. Lee ``A network protocol stack in Standard ML'' Higher Order and Symbolic Computation, Vol.14, No.4 , 2001.
- R. Deline and M. Fähndrich, ``VAULT: a programming language for reliable systems'' http://research.microsoft.com/vault , 2001
- H. Derby, ``The performance of FoxNet 2.0'' Technical Report CMU-CS-99-137 School of Computer Science, Carnegie Mellon University, June 1999.
- ECMA International ``Standard ECMA-334 C# Language Specification'' http://www.ecma-international.org/publications/standards/Ecma-334.htm
- J. S. Foster, R. Johnson, J. Kodumal, and A. Aiken ``Flow-Insensitive Type Qualifiers'' Trans. on Programming Languages and Systems. 28(6):1035-1087, Nov. 2006.
- D. Grossman, ``Quantified Types in an Imperative Language'' ACM Transactions on Programming Languages and Systems , 2006.
- J. Hughes ``Global variables in Haskell'' Journal of Functional Programming archive Volume 14, Issue 5, Sept. 2004.
- I. S. Diatchki, M. P. Jones, and R. Leslie. ``High- level Views on Low-level Representations.'' Proc. ACM Int. Conference on Functional Programming pp. 168–179, 2005.
- International Std. Organization ISO/IEC 9899:1999 (Prog. Languages - C), 1999.
- 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 275288, 2002.
- M. P. Jones ``Qualified types: theory and practice.'' Cambridge Distinguished Dissertations In Computer Science ISBN:0-521-47253-9, 1995
- S. L. Peyton Jones and J. Launchbury ``Unboxed values as first class citizens in a non-strict functional language.'' Functional Programming Languages and Computer Architecture , 1991
- S. L. Peyton Jones and P. Wadler ``Imperative functional programming.'' Proc. ACM SIGPLAN Principles of Programming Languages. , 1993
- S. L. Peyton Jones (ed.). Haskell 98 Language and Libraries: The Revised report. Cambridge University Press, 2003.
- R. Milner ``A theory of type polymorphism in programming.'' Journal of Computer and System Sciences pp 348-375, 1978.
- R. Milner, M. Tofte, R. Harper, and D.MacQueen. The Definition of Standard ML - Revised The MIT Press, May 1997.
- J. Gosling, B. Joy, G. Steele, and G. Bracha ``The Java Language Specification,'' Third Edition http://java.sun.com/docs/books/jls
- G. van Rossum, ``Python Reference Manual'' F. L. Drake, Jr. (ed.) http://docs.python.org/ref/ref.html , 2006.
- J. S. Shapiro, J. M. Smith, and D. J. Farber. ``EROS: a fast capability system'' ACM Symposium on Operating Systems Principles , Dec. 1999.
- J. S. Shapiro, S. Sridhar, M. S. Doerrie, ``BitC Language Specification'' http://www.bitc-lang.org/docs/bitc/spec.html
- 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.
- S. Sridhar and J. S. Shapiro. ``Type Inference for Unboxed Types and First Class Mutability'' Proc. 3rd Workshop on Prog. Languages and Operating Systems, 2006.
- D. Tarditi, G. Morrisett, P. Cheng, C. Stone, R. Harper, and P. Lee. ``TIL: A type-directed optimizing compiler for ML'' Proc. ACM SIGPLAN PLDI , 1996.
- M. S. Tschantz and M. D. Ernst, ``Javari: Adding reference immutability to Java'' Object-Oriented Programming Systems, Languages, and Applications , Oct 2005.
- A. Wright, ``Simple Imperative Polymorphism'' Lisp and Symbolic Comp. 8(4):343-355, 1995.
|
|
|
|
|
|
|
|
|
|
|
|
|
Figure 7.
Substitution Rules
Appendix A: Properties of *B* Type Algebra
Syntax
Cmp. Constraints |
?c? ::= |
α. | α ≃ ρ | α ≅ ρ |
Cmp. Constraint Sets |
?C? ::= |
∅ | {?c?*} | ?C? ∪ ?C? |
Poly. Constraints |
?d? ::= |
★ϰ_x_(τ) |
Poly. Constraint Sets |
*D* ::= |
∅ | {?d?*} | *D* ∪ *D* |
Unf. Constraints |
?u? ::= |
τ = τ | κ = ϰ | ?d? |
Unf. Constraint Sets |
*C* ::= |
∅ | {?u?*} | *C* ∪ *C* | *C* ∪ *C* |
Solvable Entities |
ω ::= |
τ | *C* | σ | Γ | Σ |
We represent mathematical properties as: assumption ⊧property subject. As a matter of notational convenience in the case of substitutions, we write: θa.b to mean θa ∘ θb. We also abbreviate substitution over sets θ〈{...}〉 as θ{...}. The operator ∪ represents the disjoint union of sets. The ⊘ operator denotes mutual exclusion of sets. That is, χ1 ⊘ χ2 iff χ1 ∩ χ2 = ∅. As is customary, we write χ1 ⊘ χ2 ⊘ χ3 iff χ1 ⊘ χ2 and χ2 ⊘ χ3.
Definition 1: (Algebraic equivalences)
In our algebra of types, we define the following equivalence: ΨΨρ = Ψρ. That is, the mutable type constructor is idempotent.
Definition 2: (Structural Containment)
We define a structural containment relation τ ∊ ω as follows:
-
τ ∊ τ′ if τ is structurally present as a part of τ′.
-
τ ∊ ∀α*.τ′ if τ ∊ {α*} or τ ∊ τ′.
-
τ ∊ Γ if ∃ _x_ ↦ σ ∊ Γ such that τ ∊ σ.
-
τ ∊ Σ if ∃ ?L? ↦ τ′ ∊ Σ such that τ ∊ τ′.
-
τ ∊ ω* if τ ∊ ω, for any ω ∊ {ω*}.
Definition 3: (Well-formed Substitutions)
A substitution θ is said to be well-formed with respect to a sentence ?X? in the above grammar if:
-
θ is idempotent.
-
θ〈?X?〉 is still a valid sentence in the same language.
The first condition requires that for any ?X?, θ〈?X?〉 = θ〈θ〈?X?〉〉. The actual condition we require here is that the substitution θ satisfies the occurs check. Since any substitution that satisfies the occurs check can be written equivalently as an idempotent substitution, we require this stronger property without loss of generality. This means that substitutions such as [α ↣ β] ∘ [β ↣ unit] are not well formed, and must instead be written (equivalently) as: [α ↣ unit] ∘ [β ↣ unit]. An implication of idempotence is that dom(θ) ⊘ range(θ).
The second condidtion requires that the substitution θ does not violate the syntax of the language. For example, the substitution [α ↣ β⇃τ] is not a well formed substitution on Ψα. In the rest of the document, we say substitutions to mean well-formed substitutions unless otherwise specified.
Definition 4: (Specialization)
We write τ1 ⊑ τ2, that is, τ1 is a specialization of (or less general than) τ2 iff ∃ θ such that τ1 = θ〈τ2〉. We write τ1 ⊒ τ2, that is, τ1 is more general than τ2 iff τ2 ⊑ τ1.
We write θ1 ⊑ θ2 iff ∃ θ such that θ1 = θ ∘ θ2. We write θ1 ⊒ θ2 iff θ2 ⊑ θ1.
Definition 5: (Constraint Satisfaction)
We write θ ⊧sol *C* to denote the fact that the substitution θ satisfies the set of constraints *C* as defined below:
|
|
|
Definition 6: (Application to Solvable Entities)
If ?F? is a function from τ to {τ}, we extend the definition of the function to all solvable entities (that is, ω to {τ}) as follows:
| ?F??(?∀α*.τ \ *D*?)? | = | ?F??(?τ?)? ∪ ?F??(?*D*?)? ∖ {α*} |
| ?F??(?Γ?)? | = | ∪ ?F??(?σj?)?, ∀ _x_j ↦ σj ∊ Γ |
| ?F??(?Σ?)? | = | ∪ ?F??(?τj?)?, ∀ ?L?j ↦ τj ∊ Σ |
We also write
| ?F??(?ω*?)? | = | ∪ ?F??(?ω?)? |
| ?F??(?{ω*}?)? | = | ∪ ?F??(?ω?)? |
| ?F??(?θ?)? | = | ?F??(?dom(θ)?)? ∪ ?F??(?range(θ)?)? |
| ?F??(?α.?)? | = | ?F??(?α?)? |
| ?F??(?α ≃ τ?)? | = | ?F??(?α?)? ∪ ?F??(?τ?)? |
| ?F??(?α ≅ τ?)? | = | ?F??(?α?)? ∪ ?F??(?τ?)? |
| ?F??(?τ1 = τ2?)? | = | ?F??(?τ1?)? ∪ ?F??(?τ2?)? |
| ?F??(?★ϰ_x_(τ)?)? | = | ?F??(?τ?)? |
| ?F??(?*C*?)? | = | ∪ ?F??(??u?j?)?, ∀ ?u?j ∊ *C* |
| ?F??(??C??)? | = | ∪ ?F??(??c?j?)?, ∀ ?c?j ∊ ?C? |
Definition 7: (Constraint Collection)
{|τ|} denotes the set of all constrained types and unconcstrained type variables structurally present in τ, as defined in Fig. 3.
The {|τ|} relation, is extended to {|ω|} through the ?F? operator (Definition 6), except in the case of type schemes (σ). Here, we take a monomorphic view of constraints embedded in type schemes and define {|σ|} as:
| {|∀α*.τ\*D*|} | = | {|τ|} ∪ {|*D*|} |
The collected constraints can be understood to be working in a flat overlay type system, which operates above the tree-structured underlying type system that respects variable quantification.
Definition 8: (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) |
| ?ftv?(τ1 ✗ τ2) | = | ?ftv?(τ1) ∪ ?ftv?(τ2) |
| ?ftv?(α⇃ρ) | = | ?ftv?(α) ∪ ?ftv?(ρ) |
| ?ftv?(ς↓ρ) | = | ?ftv?(ς) ∪ ?ftv?(ρ) |
Definition 9: (mtvs, Mtvs, Ntvs)
?mtv??(?ω?)?, ?Mtv??(?ω?)?, and ?ntv??(?ω?)? denote the set of all type variables appearing in ω constrained by α⇃ρ, by ς↓ρ and unconstrained respectively.
-
?mtv??(?τ*?)? = {α | α⇃ρ ∊ {|τ*|}}
-
?Mtv??(?τ*?)? = {α | α↓ρ ∊ {|τ*|} or Ψα↓ρ ∊ {|τ*|}}
-
?ntv??(?τ*?)? = {α | α ∊ {|τ*|}}
Definition 10: (Compatibility Constraint Set)
We write ℜ(?C?) if the constraint set ?C?
-
A type variable α appears at most once in ?C? in only one of the positions: α., α ≃ ρ, or α ≅ ρ.
-
∀ α ≃ ρ ∊ ?C?, ρ = ▾(ρ).
-
∀ α ≅ ρ ∊ ?C?, ρ = ▽(ρ).
We write dom(?C?) = {α* | α. ∊ ?C?, α ≃ ρ ∊ ?C?, or α ≅ ρ ∊ ?C?}. We write range(?C?) = {ρ* | α ≃ ρ ∊ ?C?, or α ≅ ρ ∊ ?C?}.
Definition 11: (Consistency of Maybe types)
For any constraint set ?C? such that ℜ(?C?), we write ?C? ⊩ τ to denote that fact that the constraints embedded in τ are consistent with ?C?. Similarly for ?C? ⊩ {ω*}.
|
|
|
|
|
|
Definition 12: (Consistency of substitutions)
A consistent substitution is a well formed substitution that does not violate the constraints embedded as maybe types.
|
|
Lemma 1: (Properties of consistent types)
-
⊪ {ω*} iff
-
∀ {α⇃ρ, α⇃ρ′} ⊆ {|ω*|}, we have ρ =▾ ρ′.
-
∀ {ς↓ρ, ς′↓ρ′} ⊆ {|ω*|} such that ς =▾ ς′, we have ρ =▽ ρ′.
-
?mtv??(?ω*?)? ⊘ ?Mtv??(?ω*?)? ⊘ ?ntv??(?ω*?)?
-
-
If ?C? ⊩ {ω*}, then ⊪ {ω*}.
-
If ⊪ {ω*}, then ∃ ?C? such that ?C? ⊩ {ω*}.
-
If ⊪ {ωb*}, ?C? ⊩ {ωs*} and {ωs*} ⊆ {ωb*}, then ∃ ?C?′ ⊇ ?C? such that ?C? ⊩ {ωb*}.
-
⊪ {ω*} iff ⊪ {|ω*|}
-
?C? ⊩ {ω*} iff ?C? ⊩ {|ω*|}
-
If ?C? ⊩ {ω*}, then ?ftv?({ω*}) ⊆ dom(?C?).
-
If ?C? ⊩ {ω*} and dom(?C?) = ?ftv?({ω*}), then ?ftv?(range(?C?)) ⊆ ?ftv?({ω*}).
Proof 1
Evident from Definition 11.
Lemma 2: (Weakening of Consistency)
If {ω2*} ⊆ {ω1*}, then,
-
?C? ⊩ {ω1*} implies ?C? ⊩ {ω2*}
-
⊪ {ω1*} implies ⊪ {ω2*}
-
θ ⊧cst {ω1*} implies θ ⊧cst {ω2*}
-
θ ⊪ {ω1*} implies θ ⊪ {ω2*}
-
If ?C? ⊩ {ω*}, then ∀ ?C?′ ⊇ ?C? such that ℜ(?C?′), ?C?′ ⊩ {ω*}.
-
If θ ⊪ {ω*}, then θ ⊧cst {ω*}
-
If θ ⊪ {ω*}, then ⊪ θ{|ω*|}.
Lemma 3: (Substitution Canonicalization)
For any type τ and substitution θ, such that θ ⊧cst {τ}, we have:
-
▾(θ〈▾(τ)〉) = ▾(θ〈▴(τ)〉) = ▾(θ〈τ〉)
-
▴(θ〈▴(τ)〉) = ▴(θ〈▾(τ)〉) = ▴(θ〈τ〉)
-
▽(θ〈▽(τ)〉) = ▽(θ〈△(τ)〉) = ▽(θ〈τ〉)
-
△(θ〈△(τ)〉) = △(θ〈▽(τ)〉) = △(θ〈τ〉)
Proof 1
By straightforward induction on the structure of τ.
Lemma 4: (Substitution over mutability Minimization)
If θ ⊧cst {τ1, τ2}, then
-
τ1 =▾ τ2 implies θ〈τ1〉 =▾ θ〈τ2〉
-
τ1 =▽ τ2 implies θ〈τ1〉 =▽ θ〈τ2〉
Proof 1
-
Property 1: We have τ1 =▾ τ2. That is, ▾(τ1) = ▾(τ2). Therefore, we must have θ〈▾(τ1)〉 = θ〈▾(τ2)〉, and further, ▾(θ〈▾(τ1)〉) = ▾(θ〈▾(τ2)〉).
-
From θ ⊧cst {τ1, τ2} and Lemma 2 (weakening), we have θ ⊧cst τ1.
-
Now using case (2) and Lemma 3, we obtain ▾(θ〈▾(τ1)〉) = ▾(θ〈τ1〉). Similarly, we obtain ▾(θ〈▾(τ2)〉) = ▾(θ〈τ2〉).
-
Substituting the results of case (3) in case (1), we obtain ▾(θ〈τ1〉) = ▾(θ〈τ2〉). That is, θ〈τ1〉 =▾ θ〈τ2〉.
-
Property 2: Similar to Property 1.
Lemma 5: (Aggregation of Consistency)
-
If θ ⊧cst {ω1*} and θ ⊧cst {ω2*}, then θ ⊧cst {ω1*, ω2*}.
-
If ?C? ⊩ {ω1*} and ?C? ⊩ {ω2*}, then ?C? ⊩ {ω1*, ω2*}.
Lemma 6: (Strengthening of Consistency)
If
-
?C? ⊩ {τ1, τ2}
-
θ ⊧cst {τ1, τ2}
-
θ〈?C?〉 ⊩ θ{|τ1|}
-
dom(θ) ∩ ?ftv?(τ2) ⊆ ?ftv?(τ1)
Then, θ〈?C?〉 ⊩ θ{|τ2|}
Proof 1
Let ?C?s = θ〈?C?〉. The proof is by induction on the structure of τ2. We proceed by case analysis on the final step.
-
From premise (1) and Lemma 1 (property-3), we obtain ?C? ⊩ {|τ1, τ2|}. Using Lemma 2 (weakening), we obtain ?C? ⊩ {τ1}, ?C? ⊩ {|τ1|}, ?C? ⊩ {τ2}, and ?C? ⊩ {|τ2|}.
-
Case: τ2 = α. Due to ?C? ⊩ {|τ2|}, we have α. ∊ ?C?. We proceed by further case analysis.
-
Case [α ↣ τ] ∉ θ: Here, θ〈τ2〉 = τ2 = α. Since θ〈α.〉 = α., α. ∊ ?C?s. Further, θ{|τ2|} = θ{|α|} = θ{α} = {α}. Now, it is evident that ?C?s ⊩ θ{|τ2|}.
-
Case [α ↣ τ] ∉ θ: Here, θ〈τ2〉 = τ.
-
Since α ∊ dom(θ), due to premise (3), we have α ∊ ?ftv?(τ1), and due to premise (1) {α} ⊆ {|τ1|}.
-
Therefore, θ{α} ⊆ θ{|τ1|}. That is, {τ} ⊆ θ{|τ1|}.
-
From case (3.b.ii), premise (3), and Lemma 2 (weakening), we obtain ?C?s ⊩ {τ}.
-
We know that {|τ2|} = {|α|} = {α}. Therefore, θ{|τ2|} = θ{α} = {τ}.
-
From cases (3.c and 3.d), we conclude that ?C?s ⊩ θ{|τ2|}.
-
-
-
Case τ2 = α⇃ρ:
-
Due to premise (1), we have α ≃ ρc ∊ ?C?, for some ρc =▾ ρ.
-
By induction hypothesis, we have ?C?s ⊩ θ{|ρ|}. Note that we do not apply induction hypothesis on α since it is not an independent first-class component of the type α⇃ρ.
-
Since {|τ2|} = {|ρ|} ∪ {α⇃ρ}, θ{|τ2|} = θ{|ρ|} ∪ θ{α⇃ρ}. Given the hypothesis in case (3.b), if we have ?C?s ⊩ θ{α⇃ρ}, we can prove ?C?s ⊩ θ{α⇃ρ} using Lemma 5.
Now, we proceed by further case analysis to prove ?C?s ⊩ θ{α⇃ρ}.
-
Case θ〈α〉 = α: That is, α ∉ dom(θ). Here, θ〈α⇃ρ〉 = α⇃θ〈ρ〉.
-
Case θ〈α〉 = β: Here, θ〈α⇃ρ〉 = β⇃θ〈ρ〉.
-
Since α ∊ dom(θ), due to premise (3), we have α ∊ ?ftv?(τ1). Due to premise (1), Definition 11, and Lemma 1 (property-1), it must be true that {α⇃ρ1} ⊆ {|τ1|}, for some ρ1 =▾ ρ.
-
Therefore, {β⇃θ〈ρ1〉} ⊆ θ{|τ1|}
-
From premise (3), case (3.e.ii) and Lemma 2 (weakening), we obtain ?C?s ⊩ β⇃θ〈ρ1〉.
-
From case (3.e.iii) and Definition 11, we conclude that β ≃ ρc ∊ ?C?s such that ρc =▾ θ〈ρ1〉.
-
From case (3.e.i), premise (2), and Lemma 4, we obtain θ〈ρ1〉 =▾ θ〈ρ〉. Using this with case (3.e.iv), we conclude that ρc =▾ θ〈ρ〉.
-
From cases (3.e.iv and 3.e.v), and Definition 11, we obtain ?C?s ⊩ β⇃θ〈ρ〉. That is, ?C?s ⊩ θ〈α⇃ρ〉.
-
-
Case θ〈α〉 = ρ′: Here, θ〈α⇃ρ〉 = ρ′.
-
Similar to case (3.d.i), we have {α⇃ρ1} ⊆ {|τ1|}, for some ρ1 =▾ ρ. Therefore, {ρ′} ⊆ θ{|τ1|}.
-
From premise (3), case (3.f.i) and Lemma 2 (weakening), we obtain ?C?s ⊩ {ρ′}. That is, ?C?s ⊩ θ〈α⇃ρ〉.
-
-
-
Case τ2 = α↓ρ: Similar to case (2).
-
τ2 = α↓ρ and τ2 = α↓ρ are trivial.
-
τ2 = Ψρ and τ2 = ⇑τ′2 follow from induction hypothesis.
-
τ2 = τ′2 → τ″2 and τ2 = τ′2 ✗ τ″2 follow from induction hypothesis and Lemma 5.
Lemma 7: (Corollary to Strengthening of Consistency)
If ?C? ⊩ {ωb*}, θ ⊧cst {ωb*}, {ωs*} ⊆ {ωb*}, θ〈?C?〉 ⊩ θ{|ωs*|}, and dom(θ) ∩ ?ftv?(ωb*) ⊆ ?ftv?(ωs*) then, θ〈?C?〉 ⊩ θ{|ωb*|}.
Appendix B: Declarative Type System
Definition 1: (Canonical Expressions)
We say that an expression _e_ is canonical if all let expressions in _e_ are annotated with one of the kinds ψ or ∀.
Definition 2: (Weakened Type Derivation)
We write *D*; Γ; Σ ⊢s _e_ : τ if *D*′; Γ; Σ ⊢ _e_ : τ for some *D*′ ⊆ *D*.
Definition 3: (Constraint Collection over Type Derivation)
We write {|*D*; Γ; Σ ⊢ _e_ : τ|} to denote the set of all constrained types and unconstrained type variables used in the derivation of *D*; Γ; Σ ⊢ _e_ : τ.
| {|*D*; Γ; Σ ⊢ _x_ : τ|} | = | {|Γ, Σ, τ, *D*|} |
| {|*D*; Γ; Σ ⊢ () : unit|} | = | {|Γ, Σ, *D*|} |
| {|*D*; Γ; Σ ⊢ true : bool|} | = | {|Γ, Σ, *D*|} |
| {|*D*; Γ; Σ ⊢ false : bool|} | = | {|Γ, Σ, *D*|} |
| {|*D*; Γ; Σ ⊢ ℓ : ⇑τ|} | = | {|Γ, Σ, τ, *D*|} |
| {|*D*; Γ; Σ ⊢ l : τ|} | = | {|Γ, Σ, τ, *D*|} |
| {|*D*; Γ; Σ ⊢ λ_x_._e_ : τ′1 → τ′2|} | = | {|*D*; Γ, _x_ ↦ τ1; Σ ⊢ _e_ : τ2|} |
| ∪ {|τ′1, τ′2|} | ||
| {|*D*; Γ; Σ ⊢ _e_1 _e_2 : τ|} | = | {|*D*; Γ; Σ ⊢ _e_1 : τ1|} |
| ∪ {|*D*; Γ; Σ ⊢ _e_2 : τ2|} | ||
| ∪ {|τa, τr, τ|} |
Other cases are similar. We write:
{|*D*; Γ; Σ ⊢s _e_ : τ|} = {|*D*′; Γ; Σ ⊢s _e_ : τ, *D*|}.
Definition 4: (Consistent Type Derivation)
We say that ?C?; *D*; Γ; Σ ⊢ _e_ : τ, is a consistent type derivation under the constraint set ?C? if *D*; Γ; Σ ⊢ _e_ : τ and ?C? ⊩ {|*D*; Γ; Σ ⊢ _e_ : τ|}.
Similarly, we write ?C?; *D*; Γ; Σ ⊢s _e_ : τ if *D*; Γ; Σ ⊢s _e_ : τ and ?C? ⊩ {|*D*; Γ; Σ ⊢s _e_ : τ|}.
We say that *D*; Γ; Σ ⊢* _e_ : τ, is a consistent type derivation if *D*; Γ; Σ ⊢s _e_ : τ and ⊪ {|*D*; Γ; Σ ⊢s _e_ : τ|}.
Definition 5: (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 *D*; Γ; Σ ⊢s H + S if
-
dom(Σ) = dom(H) ∪ dom(S)
-
∀ℓ ∊ dom(H), *D*; Γ; Σ ⊢s H(ℓ) : τ such that Σ(ℓ) =▽ τ
-
∀l ∊ dom(S), *D*; Γ; Σ ⊢s S(l) : τ such that Σ(l) =▽ τ
Similarly, we define ?C?; *D*; Γ; Σ ⊢s H + S and *D*; Γ; Σ ⊢* H + S
Definition 6: (Valid Lvalues)
We say that an lvalue £ is valid with respect to a stack S and heap H, written H + S ⊢v £ if one of the following conditions hold for some ?p?:
-
£ = l or £ = l.?p?, where l ∊ dom(S).
-
£ = ℓ^ or £ = ℓ^.?p?, where ℓ ∊ dom(H).
Lemma 1: (Inversion of Typing Relation)
-
If *D*; Γ; Σ ⊢s () : τ then τ = unit.
-
If *D*; Γ; Σ ⊢s true : τ then τ = bool.
-
If *D*; Γ; Σ ⊢s false : τ then τ = bool.
-
If *D*; Γ; Σ ⊢s ℓ : τ then τ = ⇑τ′.
-
If *D*; Γ; Σ ⊢s λ_x_._e_ : τ then τ = τ′1 → τ′2, such that *D*; Γ, _x_ ↦ τ1; Σ ⊢ _e_ : τ2 and τ1 =▽ τ′1 and τ2 =▽ τ′2.
-
If *D*; Γ; Σ ⊢s _e_^ : τ then *D*; Γ; Σ ⊢s _e_ ⊴: ⇑τ.
-
Other cases are similar.
Proof 1
Immediate from the definition of typing relation.
Lemma 2: (Inversion of Copy Coercion)
For any type ρ and α, let ?Q??(?ρ?)? = {ρ, Ψρ, α⇃ρ, α⇃Ψρ, α↓ρ, α↓Ψρ, Ψα↓ρ, Ψα↓Ψρ}. Then,
-
If τ ⊴: bool then τ ∊ ?Q??(?bool?)?
-
If τ ⊴: unit then τ ∊ ?Q??(?unit?)?.
-
If τ ⊴: ⇑τ′ then τ ∊ ?Q??(?⇑τ′?)?.
-
If τ ⊴: τ1 → τ2 then τ ∊ ?Q??(?τ1 → τ2?)?.
-
If τ ⊴: τ1 ✗ τ2 then τ ∊ ?Q??(?τ′1 ✗ τ′2?)?, such that τ1 ⊴: τ′1 and τ2 ⊴: τ′2.
-
If τ ⊴: Ψρ then τ = Ψρ′, such that ρ′ ⊴: ρ.
Proof 1
By induction on the copy coercion derivation.
Lemma 3: (Canonical Forms)
-
If *D*; Γ; Σ ⊢s _v_ ⊴: unit, then, _v_ is ().
-
If *D*; Γ; Σ ⊢s _v_ ⊴: bool, then, _v_ is either true or false.
-
If *D*; Γ; Σ ⊢s _v_ ⊴: ⇑τ, then, _v_ is ℓ, ℓ ∊ dom(Σ).
-
If *D*; Γ; Σ ⊢s _v_ ⊴: τ1 → τ2, then, _v_ is λ_x_._e_.
-
If *D*; Γ; Σ ⊢s _v_ ⊴: τ1 ✗ τ2, then, _v_ is (_v_1, _v_2).
Proof 1
By inspecting the possibilities for the derivation of *D*; Γ; Σ ⊢s _v_ ⊴: τ.
According to the grammar of the language *B*, values can be of one of the following forms: (), true, false, ℓ, λ_x_._e_, (_v_, _v_)
Consider the case (2), where *D*; Γ; Σ ⊢s _v_ ⊴: bool. That is, *D*; Γ; Σ ⊢s _v_ : τ and τ ⊴: bool. From Lemma 2 (inversion of copy-coercion), we know that τ ∊ ?Q??(?bool?)?. That is, for some α, the type τ equals one of: bool, Ψbool, α⇃bool, α⇃Ψbool, α↓bool, α↓Ψbool, Ψα↓bool, or Ψα↓Ψbool. If τ = bool, it is clear that the final rule in the derivation must be T-Bool. A derivation with these rules is only possible if _v_ = ?b?. That is, _v_ = true or _v_ = false. Further, the cases like τ = Ψbool cannot happen because there is no rule that derives a mutable/maybe type for a value.
Other cases of the lemma are similar.
Theorem 1: (Progress)
If _e_ is a closed well typed canonical expresssion, that is, *D*; ∅; Σ ⊢s _e_ : τ for some τ and Σ, given any heap H and stack S such that *D*; ∅; Σ ⊢s H + S,
-
If _e_ is a left expression (_e_ = _l_), then _e_ is either a valid lvalue (that is, _e_ = £ and H + S ⊢v £) or else ∃ _e_′, S′, H′ such that S; H; _e_ ⇛ S′; H′; _e_′.
-
_e_ is a value _v_ or else ∃ _e_′, S′, H′ such that S; H; _e_ ⇒ S′; H′; _e_′.
Proof 1
By induction on the type derivation. *D*; ∅; Σ ⊢s _e_ : τ
-
Case T-Unit, T-Bool, T-Hloc, T-Lambda: (Values) Result is immediate for right execution, and cannot happen for left execution.
-
Case T-Id: cannot happen. _e_ = _x_ is not a closed term.
-
Case T-Sloc: Immediate for left execution. Right execution and can always continue with E-Rval rule as the stack is well typed (*D*; ∅; Σ ⊢s H + S).
-
Case T-App: Only right execution is possible. We have: _e_ = _e_1 _e_2, *D*; ∅; Σ ⊢s _e_1 ⊴: τ1 → τ2, and *D*; ∅; Σ ⊢s _e_2 ⊴: τ1. If _e_1 is not a value, execution can continue via the E-# (Right context redex) due to induction hypothesis. Similarly, if _e_2 is not a value, we can again take E-#. If both _e_1 and _e_2 are values, since we know that *D*1; ∅; Σ ⊢s _e_1 ⊴: τ1 → τ2, from Lemma 3 (canonical forms) we conclude that _e_1 is of the form λ_x_._e_′. Now, we can take the step E-App.
-
Case T-If: Similar to T-App, only right execution is permitted.
-
Case T-Set: Only right execution is applicable. We have: _e_ = _l_ := _e_, *D*; ∅; Σ ⊢s _l_ ⊴: Ψρ, and *D*; ∅; Σ ⊢s _e_ ⊴: ρ. If _l_ not an lvalue (that is, _l_ ≠ £), we can take E-L# for any _l_ by induction hypothesis. If _e_ is not a value, we can take E-#.
Finally, we consider the case where _l_ = £ and _e_ = _v_. £ should be one of l, l.?p?, ℓ^, or ℓ^.?p?. Now, we proceed by induction on the length of £.
-
Suppose _l_ = £ = l. We know from induction hypothesis that that H; S ⊢v l and from Definition 6, we conclude that l ∊ dom(S). Now, execution can continue with E-:=Stack.
-
Similarly, if _l_ = £ = ℓ^, the execution can continue with step E-:=Heap.
-
Assume (by hypothesis) that the execution can continue for some _e_′1 = l.?p?.
-
Now, let _e_1 = l.1.?p?.
-
We know that *D*; ∅; Σ ⊢s _e_1 ⊴: Ψρ, which is equivalent to *D*; ∅; Σ ⊢s _e_1 : τ1 and τ1 ⊴: Ψρ. That is, *D*; ∅; Σ ⊢s l.1.?p? : τ1. The first two steps of this derivation must be T-Sloc and T-Sel. From the assumption of T-Sel rule, we must have *D*; ∅; Σ ⊢s l ⊴: τ1 ✗ τ2 for some type τ2. Now, from the assumption of T-Sloc rule, we must have Σ(l) ⊴: τ1 ✗ τ2.
-
Now, if S(l) = _v_, since we have *D*; ∅; Σ ⊢s S : H, we conclude that *D*; ∅; Σ ⊢s _v_ ⊴: τ1 ✗ τ2.
-
From case (f) and Lemma 2 (inversion of copy-coercion), we conclude that _v_ = (_v_1, _v_2).
-
From cases (c and g), we conclude that the execution can continue using the E-:=S.p rule.
-
Similarly, if _e_1 = l.2.?p?, the execution can again continue using E-:=S.p rule.
-
Similar to the above induction, we can show that if _e_1 = ℓ^.i.?p?, execution can continue using the E-:=H.p rule.
-
-
Case T-Dup: Only right execution is permitted, and can take E-# or E-Dup as applicable.
-
Case T-Deref: We have: _e_ = _e_1^, and *D*; ∅; Σ ⊢s _e_1 ⊴: ⇑τ. Execution can take EL-^# or E-# as applicable if _e_1 is not a value. If _e_1 is a value, then, from Lemma 3 (canonical forms), we conclude that _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-^.
-
Case T-Pair: Similar to case T-Dup
-
Case T-Sel: Similar to case T-Deref.
-
Case T-Let-M: Only right execution is applicable. We have: _e_ = letψ _x_ = _e_1 in _e_2, *D*; ∅; Σ ⊢s _e_ ⊴: τ. If _e_1 is not a value, we can take E-#. Otherwise, we can take E-Let-M.
-
Case T-Let-MP: Only right execution is applicable. We have: _e_ = (letϰ _x_ = _e_1 in _e_2), ∅; Σ ⊢s _e_ ⊴: τ. If _e_1 is not a value, we can take E-#. Otherwise, the execution can take E-Let-P if ϰ = ∀ or E-Let-M if ϰ = ψ.
Lemma 4: (Weakening)
If *D*; Γ; Σ ⊢ _e_ : τ, then ∀ Γ′ ⊇ Γ and Σ′ ⊇ Σ, *D*; Γ′; Σ′ ⊢ _e_ : τ.
If *D*; Γ; Σ ⊢s _e_ : τ, then ∀ Γ′ ⊇ Γ, Σ′ ⊇ Σ and *D*′ ⊇ *D*, *D*′; Γ′; Σ′ ⊢ _e_ : τ.
Proof 1
By straightforward induction on the derivation of *D*; Γ; Σ ⊢ _e_ : τ.
Lemma 5: (Properties of Copy Coercion)
-
If τ ⊴: τ′, then,
-
τ ⊴: ▽(τ′) and △(τ) ⊴: τ′
-
△(τ) = △(τ′) and ▽(τ) = ▽(τ′).
-
-
If △(τ) = △(τ′) or equivalently ▽(τ) = ▽(τ′), then τ ⊴: ▽(τ′) and △(τ) ⊴: τ′.
-
If τ ⊴: τ1, and τ ⊴: τ2, then, ∃ τ3 such that τ1 ⊴: τ3, and τ2 ⊴: τ3.
-
If τ1 ⊴: τ, and τ2 ⊴: τ, then, ∃ τ3 such that τ3 ⊴: τ1, and τ3 ⊴: τ2.
Proof 1
Property 1 and 2: By straightforward induction on the derivation of τ1 ⊴: τ2.
Property 3: By construction of τ3.
-
From property 1.b above, we have τ =▽ τ1. Similarly, we have τ =▽ τ2. Therefore, τ =▽ τ1 =▽ τ2.
-
Let us pick τ3 such that τ3 = τ =▽ τ1 =▽ τ2.
-
From S-Refl rule in figure 4, we know that τ1 ⊴: τ1. From this using property 1.a, we obtain τ1 ⊴: ▽(τ1). From case (2), we can write this as τ1 ⊴: τ3. Similarly, we obtain τ2 ⊴: τ3.
Property 4: Similar to Property 3.
Lemma 6: (Subtype Substitution)
-
If τ1 ⊴: τ2 and θ ⊧cst {τ1, τ2}, then, θ〈τ1〉 ⊴: ▽(θ〈τ2〉) and △(θ〈τ1〉) ⊴: θ〈τ2〉
-
If θ ⊧cst {α⇃ρ1, β⇃ρ2}, then θ〈α⇃ρ1〉 =▾ θ〈β⇃ρ2〉
-
If θ ⊧cst {ς1↓ρ1, ς2↓ρ2}, then θ〈ς1↓ρ1〉 =▽ θ〈ς2↓ρ2〉
-
If θ ⊧cst {ς1↓ρ1, ς2↓ρ2}, then △(θ〈ς1↓ρ1〉) ⊴: θ〈ς2↓ρ2〉 and θ〈ς1↓ρ1〉 ⊴: ▽(θ〈ς2↓ρ2〉).
Lemma 7: (Type Renaming)
For any substitution θ = [α ↣ β]* where {β*} ⊘ ?ftv?(Γ, Σ, τ),
-
If *D*; Γ; Σ ⊢ _e_ : τ then θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉.
-
If ?C?; *D*; Γ; Σ ⊢ _e_ : τ then θ〈?C?〉; θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉.
Proof 1
By straightforward induction on the derivation of *D*; Γ; Σ ⊢ _e_ : τ and ?C?; *D*; Γ; Σ ⊢ _e_ : τ.
Lemma 8: (Consistency of Substitution over Type Derivation)
If ⊪ {|*D*; Γ; Σ ⊢ _e_ : τ|}, and θ ⊧cst {Γ, Σ, τ, *D*}, then, θ ⊧cst {|*D*; Γ; Σ ⊢ _e_ : τ|}.
Proof 1
By induction on the derivation of *D*; Γ; Σ ⊢ _e_ : τ.
-
At any step of the derivation, let τs be a type used in the derivation such that τs ∊ Γ, τs ∊ Σ, and τs ∊ τ do not (necessarily) hold. For example, τ1 and τ2 in T-Lambda, τa → τr in T-App, or the type obtained by any sub-derivation (for some sub-expression).
-
Through a suitable renaming of variables, and using Lemma 7, we can ensure dom(θ) ∩ ?ftv?(τs) ⊆ ?ftv?(Γ, Σ, τ, ).
-
We want to show θ ⊧cst τs. Therefore, we proceed by induction on the structure of τs. The only interesting cases are τs = α⇃ρ and τs = ς↓ρ.
-
Case τs = α⇃ρ:
-
By induction hypothesis, we have θ ⊧cst ρ.
-
If α ∊ ?ftv?(Γ, Σ, τ):
-
Due to the premise ⊪ {|*D*; Γ; Σ ⊢ _e_ : τ|}, and Definition 11, ∃ α⇃ρ′ ∊ Γ, Σ, or τ such that ρ =▾ ρ′.
-
From the premise θ ⊧cst {Γ, Σ, τ, *D*}, and Lemma 2 (weakening), we obtain θ ⊧cst ρ′. From this, case (4.a) and Lemma 5 (aggregation), we obtain θ ⊧cst {ρ, ρ′}.
-
From cases (4.b.i and 4.b.ii), and Lemma 4, we obtain θ〈ρ〉 =▾ θ〈ρ′〉.
-
From the premise θ ⊧cst {Γ, Σ, τ}, case (4.b.i), and Definition 12, we know that θ〈α〉 = β, for some β, or θ〈α〉 = ρ″, such that ρ″ =▾ θ〈ρ′〉. Using case (4.b.iii), we can write this as θ〈ρ′〉 =▾ θ〈ρ〉 =▾ ρ″.
-
-
If α ∉ ?ftv?(Γ, Σ, τ), then, we know that α ∉ dom(θ), and therefore, θ〈α〉 = α.
Therefore, in all cases, for some β and ρ″, we have θ〈α〉 = β, or θ〈α〉 = ρ″ such that ρ″ =▾ θ〈ρ〉.
-
-
Case τs = ς↓ρ: Similar to the previous case, we conclude that for some ς′ and ρ″, we have θ〈ς〉 = ς′, or θ〈ς〉 = ρ″ such that ρ″ =▽ θ〈ρ〉.
-
From cases (4 and 5), and Definition 12, we conclude that θ ⊧cst τs.
-
By repeating the same argument over all types τs used in the derivation of *D*; Γ; Σ ⊢ _e_ : τ, we finally obtain θ ⊧cst {|*D*; Γ; Σ ⊢ _e_ : τ|}.
Lemma 9: (Composition Consistency)
If:
θ1 ⊪ {ω*}, θ2 ⊪ {ω*}, θ1 ⊪ {θ2〈ω*〉}, and dom(θ2) ⊘ ?ftv?(θ1),
Then, ∃ θ′2 such that:
dom(θ2) = dom(θ′2), θ′2〈θ1〈ω*〉〉 = θ1〈θ2〈ω*〉〉, and θ′2 ⊪ {θ1〈ω*〉}.
Proof 1
By construction of θ′2. Let θ be an idempotent substitution equivalent to θ1 ∘ θ2. θ′2 can be choosen as a part of θ that contains substitutions only for dom(θ2).
Lemma 10: (Type Substitution)
If
-
?C?; *D*; Γ; Σ ⊢ _e_ : τ
-
θ ⊧cst {|*D*; Γ; Σ ⊢ _e_ : τ|}
-
θ〈?C?〉 ⊩ θ{|*D*; Γ; Σ ⊢ _e_ : τ|}
Then, θ〈?C?〉; θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_ : θ〈τ〉.
Proof 1
By induction on the derivation of *D*; Γ; Σ ⊢ _e_ : τ. We proceed by case analysis on the last step of the derivation. Let ?C?s = θ〈?C?〉.
-
Cases T-Unit, T-Bool, T-Hloc and T-Sloc are trivial.
-
Case T-Id:
-
We have
-
Γ(_x_) = ∀α*.τx\*D*x θx ⊪ {τx, *D*x} dom(θx) = {α*}
θx〈*D*x〉; Γ; Σ ⊢ _x_ : θ〈τ〉
-
_e_ = _x_, τ = θx〈τx〉 and *D* = θx〈*D*x〉
-
θ ⊧cst {|*D*; Γ; Σ ⊢ _x_ : θx〈τx〉|}.
-
?C?s ⊩ θ{|*D*; Γ; Σ ⊢ _x_ : θx〈τx〉|}
-
-
From premise (4), we have ?ftv?(θ) ∩ {α*} = ∅. Since dom(θx) = {α*}, we have dom(θx) ∩ ?ftv?(θ) = ∅.
-
From cases (2.a.i and 2.b), we have θ〈Γ〉(_x_) = ∀α*.θ〈τx〉.
-
-
From Definition 3, we have {|*D*; Γ; Σ ⊢ _x_ : τ|} = {|Γ, Σ, τ, *D*|}
-
From case (2.a.iii and 2.d.i) and Lemma 2 (weakening), we have θ ⊧cst {τx, *D*x}. (Note: {|τx|} ⊆ {|Γ|}).
Again using case (2.a.iv), we obtain ?C?s ⊩ θ{|τx, *D*x|}, which implies ⊪ θ{|τx, *D*x|}.
Now, using Definition 12, we obtain θ ⊪ {τx, *D*x}.
-
From case (2.a.i), we have θx ⊪ {τx, *D*x}.
-
Similar to case (2.d.ii), we obtain θ ⊪ {θx〈τx〉, θx〈*D*x〉}.
-
-
From cases (2.d.ii, 2.d.iii, 2.d.iv, and 2.b), and Lemma 9, we conclude that ∃ θm such that:
-
dom(θm) = dom(θx) = {α*}.
-
θm〈θ〈τx〉〉 = θ〈θx〈τx〉〉. That is, θm〈θ〈τx〉〉 = θ〈τ〉.
-
θm〈θ〈*D*x〉〉 = θ〈θx〈*D*x〉〉. That is, θm〈θ〈*D*x〉〉 = θ〈*D*〉.
-
θm ⊪ {θ〈τx〉, θ〈*D*x〉}.
-
-
From cases (2.c, 2.e.i and 2.e,iv), and the T-Id rule we obtain θm〈θ〈*D*x〉〉; θ〈Γ〉; θ〈Σ〉 ⊢ _x_ : θm〈θ〈τx〉〉.
-
From cases (2.f, 2.e.ii and 2.e.iii), we obtain θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _x_ : θ〈τ〉.
-
From Definition 3, we have: {|θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _x_ : θ〈τ〉|} = {|θ〈Γ〉, θ〈Σ〉, θ〈τ〉, θ〈*D*〉|}. Clearly, {|θ〈Γ〉, θ〈Σ〉, θ〈τ〉, θ〈*D*〉|} ⊆ {|θ{|Γ, Σ, τ, *D*|}|}.
-
From cases (2.a.iv and 2.d.i), we have ?C?s ⊩ θ{|Γ, Σ, τ, *D*|}. Now, from Lemma 1 (property-3), we obtain ?C?s ⊩ {|θ{|Γ, Σ, τ, *D*|}|}.
-
Now, from cases (3.i and 3.h) and Lemma 2 (weakening), we obtain ?C?s ⊩ {|θ〈Γ〉, θ〈Σ〉, θ〈τ〉, θ〈*D*〉|} and therefore ?C?s ⊩ {|θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _x_ : θ〈τ〉|}
-
Finally, from cases (2.g and 2.j) and Definition 4, we conclude that θ〈?C?〉; θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _x_ : θ〈τ〉.
-
-
Case T-Lambda: Follows from induction hypothesis, using Lemma 4 and the T-Lambda rule.
-
Case T-App:
-
In this case, we have:
-
*D*1; Γ; Σ ⊢ _e_1 : τ1 τ1 ⊴: τa → τr
*D*2; Γ; Σ ⊢ _e_2 ⊴: τ2 τ2 ⊴: ▽(τa) △(τr) ⊴: τ
Γ; Σ ⊢ _e_1 _e_2 : τ
-
*D* = *D*1 ∪ *D*2
-
θ ⊧cst {|*D*; Γ; Σ ⊢ _e_1 _e_2 : τ|}
-
θ〈?C?〉 ⊩ θ{|*D*; Γ; Σ ⊢ _e_1 _e_2 : τ|}
-
-
By induction hypothesis (by using case (4.a.i), weakening on cases (4.a.iii and 4.a.iv), and premise (4)), we have: ?C?s; θ〈*D*1〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 : θ〈τ1〉 and ?C?s; θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_2 : θ〈τ2〉. That is,
-
θ〈*D*1〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 : θ〈τ1〉
-
?C?s ⊩ {|θ〈*D*1〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 : θ〈τ1〉|}
-
θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_2 : θ〈τ2〉
-
?C?s ⊩ {|θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_2 : θ〈τ2〉|}
-
-
From case (4.a.i), we have τ1 ⊴: τa → τr. Using weakening on case (4.a.iii) and Lemma 6, we obtain θ〈τ1〉 ⊴: ▽(θ〈τa → τr〉), which is equivalent to θ〈τ1〉 ⊴: θ〈τa〉 → θ〈τr〉.
-
Similarly, we obtain θ〈τ2〉 ⊴: ▽(θ〈▽(τa)〉). Using weakening on case (4.a.iii) and Lemma 3, we have ▽(θ〈▽(τa)〉) = ▽(θ〈τa〉). Therefore, θ〈τ2〉 ⊴: ▽(θ〈τa〉).
-
Similarly, we obtain △(θ〈τr〉) ⊴: θ〈τ〉.
-
From cases (4.b.i, 4.b.iii, 4.c, 4.d and 4.e) and using T-App rule, we conclude that θ〈*D*1〉 ∪ θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_1 : θ〈τ〉 That is, θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_1 : θ〈τ〉.
-
Using weakening on case (4.a.iv), we obtain ?C?s ⊩ {τa → τr, τ}. Using this and cases (4.b.ii and 4.b.iv), and Lemma 5 (aggregation), along with Definition 3 (constraint collection), we conclude that ?C?s ⊩ {|θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_2 : θ〈τ〉|}
-
From cases (4.f and 4.g), we obtain θ〈?C?〉; θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_2 : θ〈τ〉
-
-
Case T-Let-MP: In this case, we have
*D*1; Γ; Σ ⊢ υ ⊴: τ1 τx ⊴: τ1 *D*x = *D*1 ∪ {★ϰ_x_(τx)}
{α*} = ?ftv?(τx, *D*x) ∖ ?ftv?(Γ, Σ) ⊧new β*
*D*2; Γ, _x_ ↦ ∀α*.τx\*D*x; Σ ⊢ _e_ : τ2
*D*[β*/α*] ∪ *D*2; Γ; Σ ⊢ (letϰ _x_ = υ in _e_) : τ2
Proof is similar to T-App case. Without loss of generality, we can assume that ?ftv?(θ) ⊘ {α*} since type schemes are equal under α-renaming of generalized variables.
-
Other cases are similar.
Lemma 11: (Value Substitution)
If
-
?C?; *D*; Γ, _x_ : ∀α*.τv \ *D*v; Σ ⊢ _e_ : τ
-
?C?; *D*v; Γ; Σ ⊢ _v_ : τv
-
?Immut??(?τv?)?
Then, ∃ ?C?′ ⊇ ?C? such that ?C?′; *D*; Γ; Σ ⊢ _e_[_v_/_x_] : τ
Proof 1
By induction on the type derivation of ?C?; *D*; Γ, _x_ : ∀α*.τv; Σ ⊢ _e_ : τ We proceed by case analysis on the final step of the derivation.
-
Case T-Id: We have _e_ = _y_, where _y_ ∊ Γ, _x_, σ.
-
If _e_ = _y_ ≠ _x_, then, _y_[_v_/_x_] = _y_. and the desired result ?C?; *D*; Γ; Σ ⊢ _y_ : τ is immediate from the assumtion ?C?; *D*; Γ, _x_ : ∀α*.τv; Σ ⊢ _e_ : τ.
-
If _e_ = _x_ = _y_, we have _y_[_v_/_x_] = _v_, *D* = θ〈*D*v〉 and τ = θ〈τv〉. Let Γ′ = Γ, _y_ : ∀α*.τv. In this case, we have
-
Γ′(_y_) = ∀α*.τv\*D*v θ ⊪ {τv, *D*v} dom(θ) = {α*}
θ〈*D*v〉; Γ′; Σ ⊢ _y_ : θ〈τv〉
-
?C? ⊩ {|θ〈*D*v〉; Γ′; Σ ⊢ _y_ : θ〈τv〉|}
-
?C?; *D*v; Γ; Σ ⊢ _v_ : τv. That is,
-
*D*v; Γ; Σ ⊢ _v_ : τv
-
?C? ⊩ {|*D*v; Γ; Σ ⊢ _v_ : τv|}
-
We need to show that ∃ ?C?′ ⊇ ?C? such that ?C?′; θ〈*D*v〉; Γ; Σ ⊢ _v_ : θ〈τv〉.
-
-
We know that
-
{α*} ⊘ ?ftv?(Γ). That is, θ〈Γ〉 = Γ.
-
{α*} ⊘ ?ftv?(Σ). That is, θ〈Σ〉 = Σ.
-
Without loss of generality, we can assume that ?ftv?(range(θ)) consists of variables in ?ftv?(Γ, Σ, τv, *D*v), or fresh type variables. This property can always be made to hold by suitable renaming of variables and using Lemma 7.
-
Without loss of generality, we can assume that {α*} ⊆ ?ftv?(τv, *D*v), since any {β*} ⊆ {α*} such that β ∉ ?ftv?(τv, *D*v) can be ignored.
-
-
From case (1.b.i and 1.b.ii) we can say that ∃ ?C?big ⊇ ?C? such that ?C?big ⊩ θ{|τv, *D*v|}. This is because:
-
From case (1.b.i) we have θ ⊪ {τv, *D*v}. From this, using Lemma 2 (weakening), we obtain ⊪ θ{|τv, *D*v|}.
-
From case (1.b.ii) and Lemma 2 (weakening), we obtain ?C? ⊩ {θ〈τv〉, θ〈*D*v〉}.
-
Since {θ〈τv〉, θ〈*D*v〉} ⊆ θ{|τv, *D*v|}, using case (1.d.i) and Lemma 1 (property-4), we conclude that ∃ ?C?big ⊇ ?C? such that ?C?big ⊩ θ{|τv, *D*v|}.
-
-
Forall β ≃ ρ or β ≅ ρ ∊ ?C?, where β ∉ {α*}, we must have ?ftv?(ρ) ⊘ {α*}. Otherwise, due to case (1.d.iii), there exists β ≃ θ〈ρ〉 or β ≅ θ〈ρ〉 ∊ ?C?big such that θ〈ρ〉 ≠ ρ, which violates the restriction ℜ(?C?big).
-
We can write ?C? = ?C?e ∪ ?C?t ∪ ?C?a ∪ ?C?r where:
-
dom(?C?e) = ?ftv?(Γ, Σ)
-
dom(?C?t) = ?ftv?(τv, *D*v) ∖ ?ftv?(Γ, Σ) ∖ {α*}
-
dom(?C?a) = {α*}
-
dom(?C?e) ⊘ dom(?C?t) ⊘ dom(?C?a) ⊘ dom(?C?r)
-
-
-
Let ?C?et = ?C?e ∪ ?C?t and ?C?ar = ?C?a ∪ ?C?r
-
From case(1.f.i), weakening on (1.b.ii), and Lemma 1 (property-8), we have ?ftv?(range(?C?e)) ⊆ ?ftv?(Γ, Σ).
-
Similarly, using case (1.e), we conclude that ?ftv?(range(?C?t)) ⊆ ?ftv?(Γ, Σ, τv, *D*v) ∖ {α*}.
-
Therefore, we have ?ftv?(?C?et) ⊘ dom(?C?at).
-
-
Let
-
{β*} = dom(?C?r).
-
θa = [α ↣ γ]*, where γ* are new type variables.
-
θb = [β ↣ δ]*, where δ* are new type variables.
-
θab = θa ∘ θb
-
?C?ab = ?C?[γ*/α*][δ*/β*].
Due to case (1.g.iv), ?C?ab = ?C?et ∪ ?C?′ar, where ?C?′ar = ?C?ar[γ*/α*][δ*/β*].
-
τva = θa〈τv〉, and *D*va = θa〈*D*v〉
-
θ′ = [γ ↣ τs]*, where θ = [α ↣ τs]*
Note that forall τs above, θab〈τs〉 = τs due to case (1.c.iii). Similarly, due to cases (3.c.i, 3.c.ii and 3.h.vii) we have θ′〈Γ〉 = Γ, θ′〈Σ〉 = Σ, θ′〈τva〉 = θ〈τv〉 = τ, and θ′〈*D*va〉 = θ〈*D*v〉 = *D*.
-
-
-
Let ?C?′ = ?C? ∪ ?C?ab
-
This can be written as:
?C?′ = ?C?et ∪ ?C?ar ∪ ?C?et ∪ ?C?′ar = ?C? ∪ ?C?′ar. -
Since dom(?C?′ar) consists of new type variables, we have ℜ(?C?′).
-
Further, since θ′〈?C?〉 = ?C?, we have θ′〈?C?′〉 = ?C? ∪ θ′〈?C?′ar〉.
-
-
From cases (1.b.iii and 1.h) using Lemma 7 (renaming), we obtain ?C?ab; θab〈*D*v〉; θab〈Γ〉; θab〈Σ〉 ⊢ _v_ : θab〈τv〉.
From cases (1.f and 1.h), we can write this as ?C?ab; *D*va; Γ; Σ ⊢ _v_ : τva.
-
From cases (1.j, 1.i.i and 1.i.iii) and Lemma 2 (weakening) (property-5), we obtain ?C?′; *D*va; Γ; Σ ⊢ _v_ : τva.
-
Similarly, by applying variable renaming on case (1.b.i and 1.b.ii), we obtain
-
Γ′(_y_) = ∀γ*.τva\*D*va θ ⊪ {τva, *D*va}
dom(θ′) = {γ*}
θ′〈*D*va〉; Γ′; Σ ⊢ _y_ : θ′〈τva〉
-
?C?′ ⊩ {|θ′〈*D*va〉; Γ′; Σ ⊢ _y_ : θ′〈τva〉|}
-
-
From case (1.l.i), we have θ′ ⊪ {τva, *D*va}, which implies by weakening, θ′ ⊧cst {τva, *D*va}, and further due to case (1.h.vii), θ′ ⊧cst {Γ, Σ, τva, *D*va}. Case (1.h) implies ⊪ {|*D*va; Γ; Σ ⊢ _v_ : τva|}. Using these facts with Lemma 8, we conclude that θ′ ⊧cst {|*D*va; Γ; Σ ⊢ _v_ : τva|}.
-
-
From case (1.l.i), similar to case (1.d), we conclude that ∃ ?C?″ ⊇ ?C?′ | ?C?″ ⊩ θ′{|τva, *D*va|}. Due to idempotence of the substitution θ′, we can write this as θ′〈?C?″〉 ⊩ θ′{|τva, *D*va|}.
-
By weakening on case (1.k), we obtain ?C?″; *D*va; Γ; Σ ⊢ _v_ : τva. This implies ?C?″ ⊩ {|*D*va; Γ; Σ ⊢ _v_ : τva|}
-
Evidently, {τva, *D*va} ⊆ {|*D*va; Γ; Σ ⊢ _v_ : τva|}
-
From cases (1.c.iv and 1.h.ii) we conclude that dom(θ′) ⊆ ?ftv?(τva, *D*va). This implies dom(θ′) ∩ ?ftv?({|*D*va; Γ; Σ ⊢ _v_ : τva|}) ⊆ ?ftv?({τva, *D*va})
-
From cases (1.n.ii, 1.m, 1.n.iii, 1.n.i and 1.n.iv) and Lemma 7 (strengthening), we conclude that θ′〈?C?″〉 ⊩ θ′{|*D*va; Γ; Σ ⊢ _v_ : τva|}.
-
-
From cases (1.n.ii, 1.m and 1.n.v) and Lemma 10 (type substitution), we conclude that θ′〈?C?″〉; θ′〈*D*va〉; θ′〈Γ〉; θ′〈Σ〉 ⊢ _v_ : θ′〈τva〉. From case (1.h.vii), this can be written as θ′〈?C?″〉; *D*; Γ; Σ ⊢ _v_ : τ.
-
Now, ?C?″ = ?C? ∪ ?C?rest. From case (1.i.iv), we have θ′〈?C?″〉 = ?C? ∪ θ′〈?C?rest〉. Therefore, θ′〈?C?″〉 ⊇ ?C?.
-
-
Case T-Lambda: We have
*D*; Γ, _x_ ↦ ∀α*.τv, _y_ ↦ τ1; Σ ⊢ _e_ : τ2
τ1 =▽ τ′1 τ2 =▽ τ′2
*D*; Γ, _x_ ↦ ∀α*.τv; Σ ⊢ λ_x_._e_ : τ′1 → τ′2
We can assume that _x_ ≠ _y_. Now, the result follows from the induction hypothesis, and the T-Lambda rule.
-
Case T-App:
-
In this case, writing Γ′ = Γ, _x_ ↦ ∀α*.τv, we have:
-
*D*1; Γ′; Σ ⊢ _e_1 : τ1 τ1 ⊴: τa → τr
*D*2; Γ′; Σ ⊢ _e_2 : τ2 τ2 ⊴: ▽(τa) △(τr) ⊴: τ
*D*; Γ′; Σ ⊢ _e_1 _e_2 : τ
-
?C? ⊩ {|*D*; Γ′; Σ ⊢ _e_1 _e_2 : τ|}
-
*D* = *D*1 ∪ *D*2
-
-
From case (3.i) and weakening on case (3.ii), we have ?C?; *D*1; Γ′; Σ ⊢ _e_1 : τ1. Now, by induction hypothesis, we know that ∃ ?C?′ ⊇ ?C? | ?C?′; *D*1; Γ; Σ ⊢ _e_1[_v_/_x_] : τ1.
-
Similarly, we have ?C?; *D*2; Γ′; Σ ⊢ _e_2 : τ2. By Lemma 2 (weakening), we obtain ?C?′; *D*2; Γ′; Σ ⊢ _e_2 : τ2. Again, by induction hypothesis, we know that ∃ ?C?″ ⊇ ?C?′ | ?C?″; *D*2; Γ; Σ ⊢ _e_2[_v_/_x_] : τ2.
-
From case (3.b) and Lemma 2 (weakening), we obtain ?C?″; *D*1; Γ; Σ ⊢ _e_1[_v_/_x_] : τ1.
-
Again by weakening wrt case (3.a.ii), we have ?C?″ ⊩ {τa, τr, τ}.
-
From cases (3.c and 3.d) and copy-coersion relations in case (3.a.i), using the T-App rule we obtain *D*; Γ; Σ ⊢ _e_1[_v_/_x_] _e_2[_v_/_x_] : τ. Using cases (3.c, 3.d and 3.e) and Definition 3, we conclude that ?C?″; *D*; Γ; Σ ⊢ _e_1[_v_/_x_] _e_2[_v_/_x_] : τ.
-
-
T-Set case is to T-App. Premise (3) guarantees that the substitution cannot happen on the LHS of an assignment except within a dereferenced expression.
-
Other cases are similar.
Lemma 12: (Corollary to Value Substitution)
If
-
?C?; *D*; Γ, _x_ : ∀α*.τv \ *D*v ∪ ★∀_x_(τv); Σ ⊢ _e_ : τ
-
?C?; *D*v; Γ; Σ ⊢ _v_ : τv
-
⊧ *D*
Then, ∃ ?C?′ ⊇ ?C? such that ?C?′; *D*; Γ; Σ ⊢ _e_[_v_/_x_] : τ
Proof 1
Similar to Lemma 11. The premise ⊧ *D* guarantees that ∀ ★ϰ_x_(τ′v) ∊ *D*, ?Immut??(?τ′v?)?, which ensures that all instantiations of _x_ are immutable.
Lemma 13: (Location Substitution)
If ?C?; *D*; Γ, _x_ : τx; Σ ⊢ _e_ : τ, then ?C?; *D*; Γ; Σ, l ↦ τx ⊢ _e_[l/_x_] : τ.
Proof 1
By induction on the type derivation of ⊢ Γ, _x_ : τ; Σ, similar to Lemma 11.
Lemma 14: (Corollary to Location Substitution)
If ?C?; *D*; Γ, _x_ : ∀α*.τv \ *D*v ∪ ★ψ_x_(τv); Σ ⊢ _e_ : τ and ⊧ *D*, then ?C?; *D*; Γ; Σ, l ↦ τx ⊢ _e_[l/_x_] : τ.
Proof 1
Similar to Lemma 13.
Lemma 15: (Stack and Heap Assignment Safety)
-
If ?C?; *D*; Γ; Σ ⊢s H, ℓ ↦ _v_ + S, and ?C?; *D*; Γ; Σ ⊢s _v_′ : τ, and Σ(ℓ) =▽ τ, then ?C?; *D*; Γ; Σ ⊢s H, ℓ ↦ _v_′ + S.
-
If ?C?; *D*; Γ; Σ ⊢s H + S, l ↦ _v_, and ?C?; *D*; Γ; Σ ⊢s _v_′ : τ, and Σ(l) =▽ τ, then ?C?; *D*; Γ; Σ ⊢s H + S, l ↦ _v_′.
Proof 1
Immediate from the definition of stack and heap typing.
Theorem 2: (Subject Reduction)
For any canonical expression _e_, if ?C?; *D*; Γ; Σ ⊢s _e_ : τ, ?C?; *D*; Γ; Σ ⊢s H + S and ⊧ *D*, then,
-
If S; H; _e_ ⇛ S′; H′; _e_′, then, ∃ Σ′ ⊇ Σ and ?C?′ ⊇ ?C? such that ?C?′; *D*; Γ; Σ′ ⊢s _e_′ : τ and ?C?′; *D*; Γ; Σ′ ⊢s H′ + S′.
-
If S; H; _e_ ⇒ S′; H′; _e_′, then, ∃ Σ′ ⊇ Σ and ?C?′ ⊇ ?C? such that ?C?′; *D*; Γ; Σ′ ⊢s _e_′ : τ′ and ?C?′; *D*; Γ; Σ′ ⊢s H′ + S′ and τ =▽ τ′.
Proof 1
By induction on the derivation of ?C?; *D*; Γ; Σ ⊢ _e_ : τ. We proceed by the case analysis of the final step.
-
Case T-Id, T-Bool, T-Hloc, T-Lambda cannot happen.
-
Case T-Sloc: _e_ = l. We have, for some *D*′ ⊆ *D*,
Σ(l) = τ
*D*′; Γ; Σ ⊢ l : τ
-
⇒ E-Rval:
S(l) = _v_
S; H; l ⇒ S; H; _v_
-
_e_′ = S(l) = _v_, H′ = H, and S′ = S
-
From ?C?; *D*; Γ; Σ ⊢s H + S and Definition 5 (Stack and Heap Typing), we have ?C?; *D*; Γ; Σ ⊢s S(l) : τ′ | S(l) =▽ τ′. That is, ?C?; *D*; Γ; Σ ⊢s _v_ : τ′, where τ =▽ τ′.
-
-
-
Case T-App: _e_ = _e_1 _e_2. We have for some *D*1 ⊆ *D* and *D*2 ⊆ *D*,
*D*1; Γ; Σ ⊢ _e_1 : τ1 τ1 ⊴: τa → τr
*D*2; Γ; Σ ⊢ _e_2 : τ2 τ2 ⊴: ▽(τa) △(τr) ⊴: τ
*D*1 ∪ *D*2; Γ; Σ ⊢ _e_1 _e_2 : τ
-
⇒ E-# (1):
S; H; _e_1 ⇒ S′; H′; _e_′1
S; H; _e_1 _e_2 ⇒ S′; H′; _e_′1 _e_2
-
By using Lemma 2 (weakening) on the assumptions of T-App rule, we obtain ?C?; *D*; Γ; Σ ⊢s _e_1 : τ1. By induction hypothesis, we conclude that ∃ Σ′ ⊇ Σ and ?C?′ ⊇ ?C? such that ?C?′; *D*; Γ; Σ′ ⊢s _e_1 : τ′1 and ?C?′; *D*; Γ; Σ′ ⊢s H′ + S′ and τ1 =▽ τ′1.
-
From case (3), we have τ1 ⊴: τa → τr. From case (3.a.i) we have τ1 =▽ τ′1. Since τa → τr = ▽(τa → τr), using Lemma 5, we conclude that τ′1 ⊴: τa → τr.
-
Using weakening lemmas Lemma 4 and Lemma 2, we obtain ?C?′; *D*; Γ; Σ′ ⊢s _e_2 : τ2.
-
From case (3), we already have τ2 ⊴: ▽(τa) and △(τr) ⊴: τ. Clearly, these types are consistent with respect to the weakened constraints set ?C?′.
-
Now, using the T-App rule and Definition 3, we obtain ?C?′; *D*; Γ; Σ′ ⊢s _e_′1 _e_2 : τ.
-
-
⇒ E-# (2):
S; H; _e_2 ⇒ S′; H′; _e_′2
S; H; _v_1 _e_2 ⇒ S′; H′; _v_1 _e_′2
Similar to E-# (1).
-
⇒ E-App:
l ∉ dom(S)
S; H; λ_x_._e_x _v_ ⇒ S, l ↦ _v_; H; _e_x[l/_x_]
-
Re-writing case (3) for _e_1 = λ_x_._e_x and _e_2 = _v_ and using Lemma 1 (inversion of typing), we have, for some *D*1 ⊆ *D* and *D*2 ⊆ *D*,
*D*2; Γ; Σ ⊢ _v_ : τ2*D*1, Γ, _x_ ↦ τ′a; Σ ⊢ _e_x : τ′r
τa =▽ τ′a τr =▽ τ′r
*D*1; Γ; Σ ⊢ λ_x_._e_x : τa → τr
τ1 ⊴: τa → τr τ2 ⊴: ▽(τa) △(τr) ⊴: τ
*D*1 ∪ *D*2; Γ; Σ ⊢ λ_x_._e_x _v_ : τ
We also have ?C? ⊩ {|*D*1 ∪ *D*2; Γ; Σ ⊢ λ_x_._e_x _v_ : τ|}.
-
From case (3.c.i), using weakening, we obtain ?C?; *D*1; Γ, _x_ ↦ τ′a; Σ ⊢ _e_x : τ′r
-
Using case (3.c.ii) and Lemma 13 (location substitution), we obtain ?C?; *D*1; Γ; Σ, l ↦ τ′a ⊢ _e_x[l/_x_] : τ′r and therefore, ?C?; *D*; Γ; Σ, l ↦ τ′a ⊢s _e_x[l/_x_] : τ′r.
-
From △(τr) ⊴: τ, using Lemma 5, we conclude that τr =▽ τ. We already have τr =▽ τ′r. Therefore, we conclude that τ′r =▽ τ.
-
We have ?C?; *D*; Γ; Σ ⊢s _v_ : τ2. From τ2 ⊴: ▽(τa), we obtain τa =▽ τ2. From case (3.c.i), we have τa =▽ τ′a. Therefore, we obtain τ′a =▽ τ2. Using these facts along with the premise ?C?; *D*; Γ; Σ ⊢s H + S and Definition 5, we conclude that ?C?; *D*; Γ; Σ, l ↦ τ′a ⊢s H + S, l ↦ _v_.
-
-
-
Case T-Set: _e_ = _e_1 := _e_2
*D*1; Γ; Σ ⊢ _l_ : τ1 τ1 ⊴: Ψρ
*D*2; Γ; Σ ⊢ _e_ : τ2 τ2 ⊴: ρ
*D*1 ∪ *D*2; Γ; Σ ⊢ _l_ := _e_ : unit
Cases E-L# and E-# follow from induction hypothesis, similar to the T-App case. Cases E-:=Stack, E-:=Heap, E=:=S.p and E-:=H.p follow from Lemma 15 (stack and heap assignment safety).
-
Case T-Let-M: _e_ = letψ _x_ = _e_1 in _e_2
*D*1; Γ; Σ ⊢ _e_1 : τ1 τ1 ⊴: τ′ τ ⊴: τ′
*D*2, Γ, _x_ ↦ τ; Σ ⊢ _e_2 : τ2
*D*1 ∪ *D*2; Γ; Σ ⊢ (letψ _x_ = _e_1 in _e_2) : τ2
The case E-# follows from induction hypothesis using the T-Let-M rule. The case E-Let-M follows from Lemma 13 (location substitution) similar to T-App.
-
Case T-Let-MP: _e_ = let∀ _x_ = υ in _e_
*D*1; Γ; Σ ⊢ υ : τv τv ⊴: τ1 τ ⊴: τ1
*D*′ = *D*1 ∪ {★ϰ_x_(τ)} {α*} = ?ftv?(τ, *D*′) ∖ ?ftv?(Γ, Σ)
*D*2, Γ, _x_ ↦ ∀α*.τ \ *D*′; Σ ⊢ _e_ : τ′
*D*[β*/α*] ∪ *D*2; Γ; Σ ⊢ (letϰ _x_ = υ in _e_) : τ2
The case E-# follows from induction hypothesis using the T-Let-P rule. This is because υ can only take E-# steps that only have leaf derivations of E-Rval step E-Rval steps, which does not change the constraint set *D*1 in the type derivation. If ϰ = ∀, the case E-Let-P follows from Lemma 12 (value substitution). Otherwise, if ϰ = ψ, the case E-Let-M follows from Lemma 14 (location substitution). The case ϰ = κ cannot happen since _e_ is a canonical expression.
Theorem 3: (Preservation)
For any canonical expression _e_, if *D*; Γ; Σ ⊢* _e_ : τ, *D*; Γ; Σ ⊢* H + S and ⊧ *D* then,
-
If S; H; _e_ ⇛ S′; H′; _e_′, then, ∃ Σ′ ⊇ Σ such that *D*; Γ; Σ′ ⊢* _e_′ : τ and *D*; Γ; Σ′ ⊢* H′ + S′.
-
If S; H; _e_ ⇒ S′; H′; _e_′, then, ∃ Σ′ ⊇ Σ such that *D*; Γ; Σ′ ⊢* _e_′ : τ′ and *D*; Γ; Σ′ ⊢* H′ + S′ and τ =▽ τ′.
Proof 1
From *D*; Γ; Σ ⊢* _e_ : τ and *D*; Γ; Σ ⊢* H + S, using Definition 4 and Definition 11, we conclude that ∃ ?C?d and ?C?sh such that ?C?d; *D*; Γ; Σ ⊢ _e_ : τ and ?C?sh; *D*; Γ; Σ ⊢ H + S. Since the only type variables that need to be used in common in both derivations are those that are present in Γ and Σ, we can construct a ?C? such that ?C? ⊇ ?C?d, ?C? ⊇ ?C?sh, and ℜ(?C?). Now, by weakening, we have ?C?; *D*; Γ; Σ ⊢s _e_ : τ and ?C?; *D*; Γ; Σ ⊢s H + S. The result now follows from Theorem 2 (subject reduction).
Definition 7: (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_′.
Theorem 4: (Type Soundness)
Let ⇒* denote the reflexive-transitive-closure of ⇒. For any canonical expression _e_, if *D*; ∅; Σ ⊢* _e_ : τ, *D*; ∅; Σ ⊢* H + S, ⊧ *D*, and S; H; _e_ ⇒* S′; H′; _e_′ then S′; H′; _e_′ is not stuck. That is, execution of a closed, canonical, well typed expression cannot lead to a stuck state.
Proof 1
By straightforward induction on the length of ⇒*. If _e_ = _v_, proof is immediate. Otherwise, from Lemma 1 (Progress), we know that we can take at least one step forward. Further, from Lemma 3 (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.
Appendix C: Type Inference
Definition 1: (Type Inference)
Type inference is a program transformation that accepts a program in which let expressions are not annotated with their kinds, and returns the same programs in which let expressions are annotated with their kinds and all expressions are annotated with their types.
Definition 2: (Constraint Collection over Inference Derivation)
Similar to Definition 3, we write {|Γ; Σ ⊢i _e_ : τ | *C*|} to denote the set of all constrained types and unconstrained type variables used in the derivation of Γ; Σ ⊢i _e_ : τ | *C*.
| {|Γ; Σ ⊢i () : unit | ∅|} | = | {|Γ, Σ|} |
| {|Γ; Σ ⊢i _x_ : τ | *C*|} | = | {|Γ, Σ, τ, *C*|} |
Other cases are similar.
Definition 3: (Notational Derivations)
We define the following derivations for notational convenience:
|
|
|
|
|
Theorem 1: (Correctness of Unification)
If *U*(*C*) = (*D*, θ), then θ ⊢sol *C* ↝ *D*
Proof 1
By straightforward induction on the derivation of *U*(*C*).
Lemma 1: (Consistency of Unification)
If ⊪ {*C*} and *U*(*C*) = (*D*, θ), then ⊪ θ{|*C*|}
Proof 1
By straightforward induction on the derivation of *U*(*C*).
Theorem 2: (Satisfiability of Unified Constraints)
If *U*(*C*) = (*D*, θu), then ∃ θs such that θu.s ⊢sat *C* ↝ *D*.
Proof 1
By induction on the derivation of *U*(*C*).
Theorem 3: (Principality of Unified Types)
If *U*(*C*) = (*D*, θu), where *C* is a set of constraints obtained from the type inference algorithm, then ∀ θs such that θs ⊢sol *C* ↝ *D*′, we have θu ⊑ θs.
Proof 1
By induction on the derivation of *U*(*C*). The interesting case is *U*(ς↓ρ = ϱ), in particular *U*(ς↓ρ = α⇃ρ′). This case is handled by noting that the inference algorithm only produces α⇃ρ′ types in the pair-selection rule, where ρ′ is of the form α1↓ρ1 ✗ α2↓ρ2.
Theorem 4: (Decidability of Unification)
A canonical derivation of *U*(*C*), where no two applications of the refelexive rule happen consequetively, halts forall *C*. That is, ∀ *C*, *U*(*C*) decidably either succeeds with (*D*, θ) or fails with ⊥.
Proof 1
Let the degree of unification be defined as the tuple (number of α or κ variables, number of MPC constaints in *C*, the size of types within the equality constraints in *C*). Now, the proof is by induction on the derivation of *U*(*C*), where we show that the degree of unification reduces in every recursive call.
Lemma 2: (Consistency of Inferred Types)
If Γ; Σ ⊢i _e_ : τ | *C* and ⊪ {Γ, Σ}, then ⊪ {|Γ; Σ ⊢i _e_ : τ | *C*|}.
Proof 1
By straightforward induction on the derivation of Γ; Σ ⊢i _e_ : τ | *C*, using Lemma 1.
Lemma 3: (Substitution Consistency over Inference Derivation)
If Γ; Σ ⊢i _e_ : τ | *C* and θ ⊪ {Γ, Σ, τ, *C*}, then θ ⊪ {|Γ; Σ ⊢i _e_ : τ | *C*|}.
Proof 1
By straightforward induction on the derivation of Γ; Σ ⊢i _e_ : τ | *C*, using Lemma 1, and using the fact that we can assume ?ftv?(θ) ∩ ?ftv?({|Γ; Σ ⊢i _e_ : τ | *C*|}) = ?ftv?(Γ, Σ, τ, *C*).
Theorem 5: (Soundness of Type Inference)
If ?C?; θ; Γ; Σ ⊢i _e_ : τ | *D* then ?C?; θ; *D*; Γ; Σ ⊢ _e_ : τ.
Proof 1
By induction on the derivation of Γ; Σ ⊢i _e_ : τ | *C*.
-
From the premise ?C?; θ; Γ; Σ ⊢i _e_ : τ | *C* of the theorem, using Definition 3 and Definition 12, we obtain:
-
Γ; Σ ⊢i _e_ : τ | *C*
-
θ ⊢sol *C* ↝ *D*
-
θ ⊪ {Γ, Σ, τ, *C*}. Using Lemma 3, this can be written as θ ⊪ {|Γ; Σ ⊢i _e_ : τ | *C*|}
-
θ ⊧cst {|Γ; Σ ⊢i _e_ : τ | *C*|}
-
?C? ⊩ {|θ〈Γ; Σ ⊢i _e_ : τ | *C*〉|}
Now, we proceed by case analysis on the last step of the derivation.
-
-
Cases I-unit, I-Bool, I-Hloc and I-Sloc are trivial
-
Case I-Id: In this case, we have
Γ(_x_) = ∀α*.τx\*D*x θx = [α ↣ β]* ⊧new β*
Γ; Σ ⊢i _x_ : θx〈τx〉 | θx〈*D*x〉
Proof folows from cases (1.b and 1.c) using T-ID rule and the fact that variable replacement is always consistent.
-
Case I-Lambda:
-
In this case, we have
Γ, _x_ ↦ β↓α; Σ ⊢i _e_r : τr | *C* ⊧new αββ′γγ′δ
Γ; Σ ⊢i λ_x_._e_r : β′↓α → γ′↓δ | *C* ∪ {τr = γ↓δ}
-
From cases (1 and 4.a), by weakening, we have ?C?; θ; Γ, _x_ ↦ β↓α; Σ ⊢i _e_r : τr | *C*. Now, from induction hypothesis, we have ?C?; θ; *D*; Γ, _x_ ↦ β↓α; Σ ⊢ _e_r : τr
-
From case (1.d), using weakening, we obtain θ ⊧cst {β↓α, β′↓α}. Now, from Lemma 6 (property 3), we conclude that θ〈β↓α〉 =▽ θ〈β′↓α〉. Similarly, we conclude that θ〈γ↓δ〉 =▽ θ〈γ′↓δ〉.
-
From case (1.b) and the constraint τr = γ↓δ, we obtain θ〈τr〉 = θ〈γ↓δ〉. Now, using case (4.c), we obtain θ〈τr〉 =▽ θ〈γ′↓δ〉.
-
From cases (4.b, 4.c and 4.d) and the T-Lambda rule, we obtain θ; *D*; Γ; Σ ⊢ λ_x_._e_r : β′↓α → γ′↓δ. Finally, using this with case (1), we conclude that ?C?; θ; *D*; Γ; Σ ⊢ λ_x_._e_r : β′↓α → γ′↓δ.
-
-
Case I-App:
-
In this case, we have
Γ; Σ ⊢i _e_1 : τ1 | *C*1 Γ; Σ ⊢i _e_2 : τ2 | *C*2
⊧new αββ′γγ′δε
Γ; Σ ⊢i _e_1 _e_2 : ε↓γ | *C*
where,
*C* = *C*1 ∪ *C*2 ∪ {τ1 = α↓(β′↓β → γ′↓γ), τ2 = δ↓β}. -
Using weakening on case (1), we obtain
-
θ ⊢sol *C*1 ↝ *D*1 and θ ⊢sol *C*2 ↝ *D*2
-
θ ⊪ {|Γ; Σ ⊢i _e_1 : τ1 | *C*1|} and
θ ⊪ {|Γ; Σ ⊢i _e_2 : τ2 | *C*2|} -
?C? ⊩ {|θ〈Γ; Σ ⊢i _e_1 : τ1 | *C*1〉|} and
?C? ⊩ {|θ〈Γ; Σ ⊢i _e_2 : τ2 | *C*2〉|}.
-
-
From cases (5.a and 5.b), we obtain ?C?; θ; Γ; Σ ⊢i _e_1 : τ1 | *C*1 and ?C?; θ; Γ; Σ ⊢i _e_2 : τ2 | *C*2.
-
From case (5.c) and induction hypothesis, we obtain ?C?; θ; *D*1; Γ; Σ ⊢ _e_1 : τ1 and ?C?; θ; *D*1; Γ; Σ ⊢ _e_1 : τ1.
-
Note that since θ ⊢sol *C* ↝ *D* and *C* = *C*1 ∪ *C*2 ∪ {τ1 = α↓(β′↓β → γ′↓γ), τ2 = δ↓β}, we have *D* = *D*1 ∪ *D*2.
-
From case (1.b) and the constraint τ1 = α↓(β′↓β → γ′↓γ), we have θ〈τ1〉 = θ〈α↓(β′↓β → γ′↓γ)〉.
-
Since θ〈τ1〉 ⊴: ▽(θ〈τ1〉), we have θ〈τ1〉 ⊴: ▽(θ〈α↓(β′↓β → γ′↓γ)〉). From case (1.d) and Lemma 3, we have ▽(θ〈α↓(β′↓β → γ′↓γ)〉) = ▽(θ〈▽(α↓(β′↓β → γ′↓γ))〉). Further, we have ▽(θ〈▽(β′↓β → γ′↓γ)〉) = ▽(θ〈β′↓β → γ′↓γ〉) = θ〈β′↓β〉 → θ〈γ′↓γ〉.
-
From cases (5.f and 5.g), we have θ〈τ1〉 ⊴: θ〈β′↓β〉 → θ〈γ′↓γ〉.
-
Similarly, using the constraint τ2 = δ↓β, we obtain θ〈τ2〉 ⊴: ▽(θ〈δ↓β〉).
-
Similarly, using the constraint τ2 = δ↓β, we obtain θ〈τ2〉 ⊴: ▽(θ〈δ↓β〉).
-
From case (1.d), using weakening, we obtain θ ⊧cst {γ′↓γ, ε↓γ}. Now, from Lemma 6 (property 4), we conclude that △(θ〈γ′↓γ〉) ⊴: θ〈ε↓γ〉.
-
From cases (5.d, 5.h, 5.i and 5.k) and the T-App rule, we conclude that θ; *D*; Γ; Σ ⊢ _e_1 _e_2 : ε↓γ. Finally, using this with case (1), we conclude that *C*; θ; *D*; Γ; Σ ⊢ _e_1 _e_2 : ε↓γ.
-
-
Case I-Let-Exp: In this case, we have:
Γ; Σ ⊢i _e_1 : τ1 | *C*1 _e_1 ≠ υ
Γ, _x_ ↦ α↓β; Σ ⊢i _e_2 : τ2 | *C*2 ⊧new αβγκ
Γ; Σ ⊢i letκ _x_ = _e_1 in _e_2 : τ2 | *C*
where *C* = *C*1 ∪ {τ1 = γ↓β, κ = ψ} ∪ *C*2.
Proof is similar to the I-App case, proof follows from induction hypothesis using the T-Let-M rule, case (1) and Lemma 6 (property 4).
-
Case I-Let-Val:
-
In this case, we have:
Γ; Σ ⊢i υ : τ1 | *C*1 *C*′1 = *C*1 ∪ {τ1 = γ↓β}
*U*(*C*′1) = (*D*u, θu) *D*s = *D*u ∪ {★κ_x_(τs)}
τs = θu〈δ↓β〉 {α*} = ?ftv?(τs, *D*s) ∖ ?ftv?(θu〈Γ〉, θu〈Σ〉)
Γ, _x_ ↦ ∀α*.τs\*D*s; Σ ⊢i _e_ : τ2 | *C*2 ⊧new βγδε*κ
Γ; Σ ⊢i letκ _x_ = _e_1 in _e_2 : τ2 | *C*′1[ε*/α*] ∪ *C*2
-
From case (7.a) and Theorem 1, we have θu ⊢sol *C*′ ↝ *D*u
-
By weakening on case (1.b), we have θ ⊢sol *C*′1[ε*/α*] ↝ *D*1 and θ ⊢sol *C*2 ↝ *D*2 for some *D*1 and *D*2. Further, *D* = *D*1 ∪ *D*2
-
From cases (7.c and 7.d), Theorem 3 and the fact that the variables α* are purely local to the derivation Γ; Σ ⊢i υ : τ1 | *C*1, we conclude that ∃ θ′ such that
-
θu ⊑ θ′
-
θ′ ⊢sol *C*′ ↝ θ′〈*D*u〉
-
?C?; θ′; Γ; Σ ⊢i υ : τ1 | *C*1
-
θ′〈Γ〉 = θ〈Γ〉, θ′〈Σ〉 = θ〈Σ〉, θ′〈τ1〉 = θ〈τ1〉, θ′〈τs〉 = θ〈τs〉, and θ′〈*D*s〉 = θ〈*D*s〉.
-
{α*} = ?ftv?(θ〈τs〉, θ〈*D*s〉) ∖ ?ftv?(θ′〈Γ〉, θ′〈Σ〉).
-
-
From case (7.d.iii) and induction hypothesis, we obtain ?C?; θ′; *D*u; Γ; Σ ⊢ υ : τ1. Due to case (7.d.iv), this can be written as ?C?; θ; *D*u; Γ; Σ ⊢ υ : τ1.
-
Let τ′1 = ▽(θ〈τ1〉). Evidently, θ〈τ1〉 ⊴: τ′1. using this in case (7.e), we obtain ?C?; θ; *D*u; Γ; Σ ⊢ υ ⊴: τ′1.
-
Similar to the I-App case, using Lemma 6 (property 4), we obtain θ〈τs〉 ⊴: τ′1.
-
Evidently, θ〈*D*〉 = θ〈*D*u〉 ∪ {★κ_x_(θ〈τ〉)}.
-
From cases (7.d.v and 7.d.iv), we have {α*} = ?ftv?(θ〈τs〉, θ〈*D*s〉) ∖ ?ftv?(θ〈Γ〉, θ〈Σ〉).
-
From weakening on case (1) and induction hypothesis, we have ?C?; θ; *D*2; Γ, _x_ ↦ ∀α*.τs\*D*s; Σ ⊢ _e_ : τ2.
-
Finally, from cases (7.f, 7.g, 7.h, 7.i, 7.j and 1), using the T-Let-MP rule, we conclude that ?C?; θ; *D*; Γ; Σ ⊢ let _x_ = υ in _e_ : τ2.
-
Theorem 6: (Completeness of Type Inference)
If ?C?; θ; *D*; Γ; Σ ⊢ _e_ : τ, then ∃ θ′ ⊇ θ and ?C?′ ⊇ ?C? such that ?C?′; θ′; Γ; Σ ⊢i _e_ : τ | *D* and dom(θ′) ∖ dom(θ) = dom(*C*′) ∖ dom(*C*) = {α* | α is a fresh variable used in Γ; Σ ⊢i _e_ : τ | *C*}.
Proof 1
By induction on the derivation of ?C?; θ; *D*; Γ; Σ ⊢ _e_ : τ.
-
From the premise ?C?; θ; *D*; Γ; Σ ⊢ _e_ : τ of the theorem, using using Definition 3 and Definition 4, we obtain:
-
θ; *D*; Γ; Σ ⊢ _e_ : τ
-
θ ⊪ {Γ, Σ, τ, *D*}
-
?C? ⊩ {|θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ θ〈_e_〉 : θ〈τ〉|}
-
Γ; Σ ⊢i _e_ : τ | *C*
-
θ′ ⊢sol *C* ↝ *D*
-
θ′ ⊪ {Γ, Σ, τ, *C*}
-
?C?′ ⊩ {|θ′〈Γ; Σ ⊢i _e_ : τ | *C*〉|}
Now, we proceed by case analysis on the last step of the derivation.
-
-
Cases T-Unit, T-Bool, T-Hloc, and T-Sloc are trivial.
-
Case T-ID: In this case, we have:
θ〈Γ〉(_x_) = ∀α*.θ〈τ〉\θ〈*D*〉 θx ⊪ {θ〈τ〉, θ〈*D*〉}
dom(θx) = {α*}
θ ∘ θx〈*D*〉; Γ; Σ ⊢ _x_ : θ ∘ θx〈τ〉
Without change in meaning, we can write Γ(_x_) = ∀γ*.τ\*D*. Now, we can write:
Γ(_x_) = ∀γ*.τ\*D* θ = [γ ↣ α]* ⊧new α*
Γ; Σ ⊢i _x_ : θy〈τ〉 | θy〈*D*〉
Let θ′ = θ ∘ θx. The result now follows from I-Id rule and case (1.c).
-
Case T-Lambda:
-
In this case, we have
θ〈*D*〉; θ〈Γ〉, _x_ ↦ θ〈τ1〉; Σ ⊢ _e_ : θ〈τ2〉
θ〈τ1〉 =▽ θ〈τ′1〉 θ〈τ2〉 =▽ θ〈τ′2〉
θ〈*D*〉; θ〈Γ〉; θ〈Σ〉 ⊢ λ_x_._e_ : θ〈τ′1 → τ′2〉
-
By induction hypothesis, we have, for some θ″ and ?C?″ such that ?C? ⊆ ?C?″ and θ ⊒ θ″, we have
-
?C?′; θ′; Γ, _x_ ↦ τ1; Σ ⊢i _e_ : τ2 | *C*
-
θ ⊢sat *C* ↝ *D*.
-
-
We pick θ′ such that θ′ = θ″ ∘ θnew, where
-
dom(θnew) = {α, β, β′, γ, γ′, δ}
-
θ′〈β↓α〉 = θ〈τ1〉
-
θ′〈γ↓α〉 = θ〈τ2〉
-
θ′〈β′↓α〉 = θ〈τ′1〉, possible due to θ〈τ1〉 =▽ θ〈τ′1〉, which continues to hold under the consistent substitution θ′, due to Lemma 6 (property 2).
-
θ′〈γ↓α〉 = θ〈τ2〉, possible due to θ〈τ2〉 =▽ θ〈τ′2〉, which continues to hold under θ′, due to Lemma 6 (property 2).
-
-
Evidently, θ′ ⊢sat *C* ∪ {τ = γ↓δ} ↝ *D*.
-
We also extend ?C?″ to ?C?′ with entries for new type variables.
-
Now, using I-Lambda rule and case (1.c), we obtain ?C?′; θ′; Γ; Σ ⊢i λ_x_._e_ : β′↓α → γ′↓δ | *D*
-
-
Case T-App:
-
In this case, we have:
θ〈*D*1〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 : θ〈τ1〉
θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_2 : θ〈τ2〉
θ〈τ1〉 ⊴: τa → τr θ〈τ2〉 ⊴: ▽(τa) △(τr) ⊴: θ〈τ〉
θ〈*D*1〉 ∪ θ〈*D*2〉; θ〈Γ〉; θ〈Σ〉 ⊢ _e_1 _e_2 : τ
-
From induction hypothesis with respect to _e_1, we have, for some *C*1, θ1 ⊒ θ, and ?C?1 ⊇ ?C?
-
Γ; Σ ⊢i _e_1 : τ1 | *C*1
-
θ1 ⊢sol *C*1 ↝ *D*1
-
θ1 ⊪ {Γ, Σ, τ1, *C*1}
-
?C?1 ⊩ {|θ1〈Γ; Σ ⊢i _e_1 : τ1 | *C*1〉|}
-
-
From induction hypothesis with respect to _e_2, we have, for some *C*2, θ2 ⊒ θ, and ?C?2 ⊇ ?C?
-
Γ; Σ ⊢i _e_2 : τ2 | *C*2
-
θ2 ⊢sol *C*2 ↝ *D*2
-
θ2 ⊪ {Γ, Σ, τ2, *C*2}
-
?C?2 ⊩ {|θ2〈Γ; Σ ⊢i _e_2 : τ2 | *C*2〉|}
-
-
Since the new type variables used in the two derivations do not collide, and since θ1 / ?C?1 and θ2 / ?C?2 extend θ / ?C?2 with entries for new type variables only, we can write:
-
Let θ12 = θ1 ∘ θ2 and ?C?12 = ?C?1 ∪ ?C?2.
-
θ12 ⊢sol *C*1 ∪ *C*2 ↝ *D*1 ∪ *D*2
-
θ12 ⊪ {Γ, Σ, τ1, τ2, *C*1, *C*2}
-
?C?12 ⊩ {|θ12〈Γ; Σ ⊢i _e_1 : τ1 | *C*1〉, θ12〈Γ; Σ ⊢i _e_2 : τ2 | *C*2〉|}
-
-
We pick θ′ such that θ′ = θ12 ∘ θnew, where
-
dom(θnew) = {α, β, β′, γ, γ′, δ}
-
θ′〈β′↓β → γ′↓γ〉 = θ12〈τa → τr〉.
-
θ′〈α↓(β′↓β → γ′↓γ)〉 = θ12〈τ1〉, possible due to θ〈τ1〉 ⊴: τa → τr, which continues to hold under the consistent substitution θ′ due to Lemma 6 (property 1).
-
θ′〈δ↓β〉 = θ12〈τ2〉, possible due to θ〈τ2〉 ⊴: ▽(τa), which continues to hold under θ′.
-
θ′〈ε↓γ〉 = θ12〈τ〉, possible due to △(τr) ⊴: θ〈τ〉, which continues to hold under θ′.
-
-
From cases (5.b.i and 5.b.ii) and the I-App rule, we obtain Γ; Σ ⊢i _e_1 _e_2 : ε↓γ | *C*, where *C* = *C*1 ∪ *C*2 ∪ {τ1 = α↓(β′↓β → γ′↓γ), τ2 = δ↓β}.
-
From cases (5.d.ii, 5.e.ii, 5.e.iii, 5.e.iv, 5.e.v and 5.f), we conclude that θ′ ⊢sol *C* ↝ *D*1 ∪ *D*2
-
From cases (5.d.iii and 5.e and 5.f), we obtain θ′ ⊪ {Γ, Σ, τ, *C*}
-
By extending ?C?12 to ?C?′ with appropriate entries for α, β, β′, γ, γ′, and δ, and using case (5.d.iv), we obtain ?C?′ ⊩ {|θ′〈Γ; Σ ⊢i _e_1 _e_2 : τ | *C*〉|}.
-
Finally, from cases (5.f, 5.g, 5.h and 5.i), we conclude that ?C?′; θ′; Γ; Σ ⊢i _e_1 _e_2 : τ | *D*1 ∪ *D*2.
-
-
Other cases are similar.
Theorem 7: (Soundness of Type Inference)
If θ; Γ; Σ ⊢i _e_ : τ | *D* then θ; *D*; Γ; Σ ⊢* _e_ : τ.
Theorem 8: (Type Chekability)
If Γ; Σ ⊢i _e_ : τ | *C* and *U*(*C*) = (*D*u, θu), then, ∃ θs such that θs ⊢sat *D*u ↝ *D*, θu.s〈_e_〉 is canonical, and θu.s; *D*; Γ; Σ ⊢* _e_ : τ.
Theorem 9: (Completeness of Type Inference)
If θ; *D*; Γ; Σ ⊢* _e_ : τ, then ∃ θ′ ⊇ θ such that θ′; Γ; Σ ⊢i _e_ : τ | *D*.