Post on 21-Dec-2015
transcript
2
Overview
Motivation Program error detection at compile-time Compilation certification Proof carrying code (PCC)
Dependently typed programming languages Design decisions Dependent ML (functional) and Xanadu (imperative) Theoretical development Practical applications
ConclusionDemo
3
Program Error Detection
Unfortunately one often pays a price for [languages which impose no disciplines of types] in the time taken to find rather inscrutable bugs — anyone who mistakenly applies CDR to an atom in LISP and finds himself absurdly adding a property list to an integer, will know the symptoms. -- Robin Milner A Theory of Type Polymorphism in Programming
Our work in this direction is inspired by and closely related to the work on refinement types (Davies, Freeman and Pfenning)
4
Some Advantages of Types
Detecting program errors at compile-timeEnabling compiler optimizationsFacilitating program verification Using types to encode program properties Verifying the encoded properties through
type-checking
Serving as program documentation Unlike informal comments, types are formally
verified and can thus be fully trusted
5
Limitations of (Simple) Types
Not general enough Many correct programs cannot be typed For instance, downcasts are widely
used in Java
Not specific enough Many interesting program properties
cannot be captured For instance, types in Java cannot
guarantee safe array access
7
Safe Array Subscripting
int(n): the singleton type for expressions of value n, where n ranges over integers‘a array(n): the type for arrays of size n, where n ranges over natural numberslength: {n:nat} ‘a array(n) -> int(n)
sub: {i:nat,n:nat | i < n} ‘a array(n) * int(i) -> ‘a
update: {i:nat,n:nat | i < n} ‘a array(n) * int(i) * ‘a -> unit
8
Dot Product in DML
fun dotprod (u, v) =let fun loop (i, len, sum) = if i = len then sum else loop (i+1, len, sum + sub(u,i) * sub(v,i))
in loop (0, length (u), 0)end
withtype {i:nat | i <= n} int (i) * int(n) * int -> int
withtype {n:nat} int array(n) * int array(n) -> int
9
A polymorphic type for arrays record <‘a> array { size: int; data[]: ‘a }But this does not enforce that the integer stored in size is the size of the array to which data points
size data
A Type for Arrays
10
A Dependent Type for Arrays
A polymorphic type for arrays
{n:nat}record <‘a> array(n) { size: int(n); data[n]: ‘a}
11
Dot Product in Xanadu
int dp (u: <int>array , v: <int>array ) { var: int i, sum;; sum = 0;
for (i=0; i < u.size; i = i+1) { sum = sum + u.data[i] * v.data[i]; } return sum;
}
{n:nat}
invariant: [a:int | a >= 0] (i: int(a))
(n) (n)
12
Some Design Decisions
Practical type-checkingRealistic programming featuresConservative extensionPay-only-if-you-use policy
13
ML0: start point
base types ::= int | bool | (user defined datatypes)
types ::= | | patterns p ::= x | c(p) | <> | <p1, p2>match clauses ms ::= (p e) | (p e | ms)expressions
e ::= x | f | c | if (e, e1, e2) | <> | <e1, e2> | lam x:. e | fix f:. e | e1(e2) | let x=e1 in e2 end | case e of ms
values v ::= x | c | <v1, v2> | lam x:. econtext ::= . | , x:
14
Integer Constraint Domain
We use a for index variablesindex expressions
i, j ::= a | c | i + j | i – j | i * j | i / j | …index propositions
P, Q ::= i < j | i <= j | i > j | i >= j | i = j | i <> j | P Q | P Q
index sorts ::= int | {a : | P }index variable contexts ::= . | , a: | , Pindex constraints ::= P | P | a:
15
Dependent Types
dependent types ::= ... | (i) | a: . | a: .
For instance,int(0), bool array(16);
nat = [a:int | a >= 0] int(a); {a:int | a >= 0} int list(a) -> int
list(a)
16
DML0ML0 + dependent types
expressionse ::= ... | a: .v | e[i] | <i e> | open e1 as <a x> in e2 end
values v ::= ... | a: .v | <i v>typing judgment e
20
Erasure: from DML0 to ML0
The erasure function erases all syntax related to type index
| bool(1) | = |bool(0)| = bool | [a:int | a >= 0] int(a) | = int | {n:nat} ‘a list(n) -> ‘a list(n) | =
‘a list -> ‘a list | open e1 as <a x> in e2 end | =
let x = |e1| in |e2| end
21
Relating DML0 to ML0
answer:type in DML0
program:type in DML0
|program|:|type| in ML0 |answer|:|type| in ML0
evaluation
evaluation
erasure erasure
Type preservation holds in DML0
A program is already typable in ML0 if it is typable in DML0
22
Elaboration for DML0
Elaboration is a mapping that maps an external language into an internal language We have constructed an algorithm
doing elaboration for DML0
We have proven the correctness of the algorithm
23
An Example of Elaboration
fun zip ([], []) = []| zip (x :: xs, y :: ys) = (x, y) :: zip (xs, ys)
withtype {n:nat} ‘a list(n) * ‘b list(n) -> (‘a * ‘b) list(n)
fun zip[0] ([], []) = [] | zip[a+1] (cons[a] (x, xs), cons[a] (y, ys)) = cons[a] ((x, y), zip[a] (xs, ys))withtype {n:nat} ‘a list(n) * ‘b list(n) -> (‘a * ‘b) list(n)
24
A Sample Constraint
The following constraint is generated during type-checking the zip function:
p:nat. q:nat. p + 1 = n q + 1 = n p = q
25
A Use of Existential Types
fun filter p [] = [] | filter p (x :: xs) = if p(x) then x :: (filter p xs) else filter p xs
withtype(‘a -> bool) ->{n:nat} ‘a list(n) ->[m:nat | m <= n] ‘a
list(m)‘a list (* [m:nat] ‘a list(m) *)
26
Polymorphism
Polymorphism is largely orthogonal to dependent typesWe have adopted a two phase type-checking algorithm
27
References and Exceptions
A straightforward combination of effects with dependent types leads to unsoundnessWe have adopted a form of value restriction to restore the soundness
28
Quicksort in DML
fun qs [] = []| qs (x :: xs) = par (x, xs, [], [])
and par (x, [], l, g) = qs (l) @ (x :: qs (g))| par (x, y :: ys, l, g) = if y <= x then par (x, ys, y :: l, g) else par (x, ys, l, y :: g)
withtype {n:nat} int list(n) -> int list(n)
withtype {p:nat,q:nat,r:nat} int * int list(p) * int list(q) * int list(r) -> int list(p+q+r+1)
29
Quicksort in DML (cont’d)
Note that qs(xs) is a permutation of xsdatatype intlist with (nat, nat) =
Nil(0,0)| {i:int,s:int,l:nat} Cons(s+i,l+1) of int(i) * intlist(s,l)
Nil: intlist(0,0)Cons: {i:int,s:int,l:nat}
int(i) * intlist(s,l) -> intlist(s+i,l+1)qs: {s:int,l:nat} intlist(s,l) -> intlist(s,l)
30
Binary Search in Xanadu
int bs(key: int, vec: <int> array ) { var: int l, m, u, x;; l = 0; u = vec.size - 1;
while (l <= u) { m = l + (u-l) / 2; x = vec.data[m]; if (x < key) { l = m + 1; } else if (x > key) { u = m - 1; } else { return m; } } return –1;}
invariant: [i:int,j:int | 0<=i<=j+1<=n] (l:int(i), u:int(j))
{n:nat}(n)
31
Termination Verification
Termination is a liveness property can not be verified at run-time is often proven with a well-founded
metric that decreases whenever a recursive function call is made
32
Ackermann Function in DML
fun ack (m, n) =if m = 0 then n+1else if n = 0 then ack (m-1, 1) else ack (m-1, ack (m, n-1))
withtype{m:nat,n:nat} int(m) * int(n) -> int
<m,n> =>
33
Metric Typing Judgments
Definition (Metric) Let = <i1,...,in> be a tuple of index expressions. We write : metric if we have ij:nat for 1 j n.
We use a for a decorated type
We use the judgemntefto mean that for each occurrence of f[i] in e, [a->i] holds, where f is declared in to have type a
36
Reducibility
Definition Suppose that e is a closed expression of type and e * v holds for some value v. is a base type. Then e is reducible Then e is reducible if e(v1) is
reducible for every reducible value v1 of type .
Then e is reducible if v=<v1, v2> and v1, v2 are reducible.
a: . Then e is reducible if e[i] is reducible for every i:.
a: . Then e is reducible if v=<i | v1> and v1 is reducible.
37
-reducibility
Definition Let e be a well-typed closed function fun f[a:]: is v and be a closed metric. e is-reducible if e[i] is reducible for each i satisfying [a->i] .
Theorem Every closed expression e is reducible if it is well-typed in DML0,
38
Quicksort in DML
fun qs [] = []| qs (x :: xs) = par (x, xs, [], [])
withtype {n:nat} int list(n) -> int list(n)and par (x, [], l, g) = qs (l) @ (x :: qs (g))
| par (x, y :: ys, l, g) = if y <= x then par (x, ys, y :: l, g) else par (x, ys, l, y :: g)
withtype{p:nat,q:nat,r:nat}int * int list(p) * int list(q) * int list(r) ->int list(p+q+r+1)
<n,0> =>
<p+q+r,p+1> =>
39
Ongoing Research
Compilation certificationDependently typed assembly language (with Robert Harper)Proof construction for proof-carrying code
40
Compiler Correctness
How can we prove the correctness of a (realistic) compiler?
Verifying that the semantics of e is the same as the semantics of |e| for every program e
But this simply seems too challenging (and is unlikely to be feasible)
Source program
e
Target code
|e|
compilation
| . |
41
Semantics-preserving Compilation
e -------------> |e|
D of ev ----> |D| of |e||v|
This seems unlikely to be feasible in practice
42
Compilation Certification
Assume that eholds, i.e., e has the property Then eshould hold, tooA compiler can be designed to produce a certificate to assert that edoes have the property
Target code
e: e holds
Source program
e: e holds
compilation
| . |
43
Type-preserving Compilation
e --------------> |e| e: -----------> |e|:||
D of e: ----> |D| of |e|:|| D and |D| are both represented in LF The LF type-checker does all type-checking!
44
Proof-Carrying Code
Proof
Poof-CarryingCode
Unpacking
Verifying
Executing
Memory SafetyTermination
Code
45
Proof Construction
Building type derivations at source level with a practical type inference algorithmTranslating such type derivations into proofs at target level
Target code
eSource program
e
compilation
| . |
proof
translationProof of e Proof of e
46
Contributions
Novel language designReduction of type-checking to constraint satisfactionUnobtrusive programming via elaborationSolid theoretical foundationPrototype implementation and evaluation
47
Related Work
Refinement types (Freeman, Davis & Pfenning)Cayenne (Augustsson)TALC Compiler (Morrisett et al at Cornell)Safe C compiler: Touchstone (Necula & Lee)TIL compiler (the Fox project at CMU)FLINT compiler (Shao et al at Yale)Secure Internet Programming (Appel, Felten et al at Princeton)