420 likes | 434 Views
Imperative Programming with Dependent Types. Hongwei Xi University of Cincinnati. Talk Overview. Motivation Program error detection Compilation certification Proof-carrying code Programming language Xanadu Design decisions Dependent type system Programming examples Conclusion.
E N D
Imperative Programming with Dependent Types Hongwei Xi University of Cincinnati
Talk Overview • Motivation • Program error detection • Compilation certification • Proof-carrying code • Programming language Xanadu • Design decisions • Dependent type system • Programming examples • Conclusion
Some Advantages of Types • Detecting program errors at compile-time • Enabling compiler optimizations • Facilitating 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 trusted
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 handle safe array access
Program Extraction Proof synthesis Dependent ML Narrowing the Gap NuPrl Coq ML
compilation |.| A Challenging Problem • How can we prove the correctness of a (realistic) compiler? • Verifying that the semantics of P is the same as the semantics of |P| for every program P • But this simply seems too challenging (and is unlikely to be feasible) Source program P Target code |P|
compilation |.| Compilation Certification • Assume that P(P)holds, i.e., P has the property P • Then there is likely some property |P| related to P such that |P|(|P|) holds • A compiler can be designed to produce a certificate that asserts |P| does have the property |P| Source program P: P(P) holds Target code |P|: |P|(|P|) holds
Poof-Carrying Code Verifying Executing Proof-Carrying Code Code Unpacking Proof Memory Safety Termination
proof translation Proof of P(P) Proof of |P|(|P|) compilation |.| Proof Construction • Building type derivations at source level with a practical type inference algorithm • Translating such type derivations into proofs at target level Source program P Target code |P|
Talk Overview • Motivation • Program error detection • Compilation certification • Proof-carrying code • Programming language Xanadu • Design decisions • Dependent type system • Programming examples • Conclusion
Xanadu: an Exotic Place In Xanadu did Kubla Khan A stately pleasure-dome decree … … -- Samuel Taylor Coleridge
Xanadu: an “Exotic” Language • Xanadu is an imperative programming language with C/Java-like syntax that supports a restricted form of dependent types • The type of a variable in Xanadu may change during execution • The programmer may need to provide dependent type annotations for type-checking purpose
A Type for Arrays • A polymorphic type for arraysrecord<‘a> array { size: int; data[]: ‘a } • But this does not enforce that the integer stored insizeis the size of the array to which data points size data
A Dependent Type for Arrays • A polymorphic type for arrays{n:nat}record <‘a> array(n) { size: int(n); data[n]: ‘a}
Informal Comments /* the function should not be applied to a negative integer */ int factorial (x: int) { /* defensive programming */ if (x < 0) exit(1); if (x == 0) return 1;else return (x * factorial (x-1)); } Note: factorial(-1) is well-typed!
Formalizing Comments {n:nat} int factorial (x: int(n)) { if (x == 0) return 1;else return (x * factorial (x-1)); } Note:factorial (-1) is now ill-typed and rejected!
Informal Comments /* arrays a and b are of equal size */ float dotprod (float a[], float b[]) { int i; float sum = 0.0; if (a.size != b.size) exit(1); for (i = 0; i < a.size; i = i + 1) { sum = sum + a[i] * b[i]; } return sum; }
Formalizing Comments {n:nat} float dotprod (a: <float> array(n),b: <float> array(n)){ /* dotprod is assigned the following type: {n:nat}. (<float> array(n),<float> array(n)) -> float */ /* function body */ … … … }
Some Design Decisions • Practical type-checking • Realistic programming features • Conservative extension • Pay-only-if-you-use policy
Examples of Dependent Types • int(i): singleton type containing the only integer equal to i, where a ranges over all integers • <‘a> array(n): type for arrays of size n in which all elements are of type ‘a, where n ranges over all natural numbers
Examples of Dependent Types • int(i,j) is defined as[a:int | i < a < j]int(a),that is, the sum of all types int(a) for i < a < j • int[i,j), int(i,j], int[i,j] are defined similarly • nat is defined as[a:int | a >=0]int(a)
Array Initialization in Xanadu {n:nat} unit init (vec: <int> array(n)) { var: int ind;; invariant: [i:int | i>=0] (ind: int(i)) for (ind=0; ind<vec.size; ind=ind+1){ vec.data[ind] = 0; } }
A Slight Variation {n:nat} unit init (vec: <int> array(n)) { var: int ind;; invariant: [i:int | i<=n] (ind: int(i)) for (ind=vec.size; ind>=0; ind=ind-1){ vec.data[ind] = 0; } }
Another Slight Variation {n:nat} unit init (vec: <int> array(n)) { var: nat ind;; for (ind=0; ind<vec.size; ind=ind+1){ vec.data[ind] = 0; } }
Binary Search in Xanadu {n:nat} int bsearch (key: int, vec: <int> array(n)) { var: int l, h, m, x;; l = 0; h = vec.size - 1; invariant: [i:int,j:int | 0<=i<=j+1<=n] (l:int(i),h:int(j)) while (l <= h) { m = (l + h) / 2; x = vec.data[m]; if (x < key) { l = m - 1; } else if (x > key) { h = m + 1; } else { return m; } } return –1; }
A Slight Variation {n:nat} int bsearch (key: int, vec: <int> array(n)) { var: l: int[0, n], h: int[-1, n); int m, x;; l = 0; h = vec.size - 1; while (l <= h) { m = (l + h) / 2; x = vec.data[m]; if (x < key) { l = m - 1; } else if (x > key) { h = m + 1; } else { return m; } } return –1; }
2-dimensional Arrays • A polymorphic type for 2-dimensional arrays:{m:nat,n:nat}record<‘a>array2(m,n) { row: int(m);col: int(n);data[m][n]:‘a}
Sparse Arrays • A polymorphic type for sparse arrays: {m:nat,n:nat}record<‘a> sparseArray(m,n) { row: int(m); col: int(n); data[m]: <int[0,n) * ‘a> list}
Dependent Union Types • A polymorphic type for lists: union <‘a> list with nat = { Nil(0);{n:nat} Cons(n+1) of ‘a * <‘a> list(n)} • Nil: <‘a> list(0) • Cons:{n:nat}.‘a * <‘a> list(n) -> <‘a> list(n+1)
Dependent Union Types • A polymorphic type for binary trees:union <‘a> tree with (nat,nat) = { E(0,0);{sl:nat,sr:nat,hl:nat,hr:nat} B(sl+sr+1,1+max(hl,hr)) of <‘a> tree(sl,hl) * ‘a * <‘a> tree(sr,hr) } • The two indices represent size and height, respectively
Type Judgment in Xanadu • f1;D1;G |- e: (f2;D2;t) • f: Context for index variables • D: Context for variables with mutable types • G: Context for variables with fixed types
Typing an Assignment f1;D1;G |- e: (f2;D2;t) --------------------------- f1;D1;G |- x = e: (f2;D2[x->t];unit)
Typing a Loop • f1;D1 |- $f.Df1,f;D;G|- e1:(f2;D2;bool(i))f2,i=1;D2;G |- e2:(f3;D3;unit)f3;D3 |- $f.D----------------------------f1;D1;G |-while(e1,e2):(f2,i=0;D2;unit) • Note:$f.D is a loop invariant
Reverse Append in Xanadu (‘a) {m: nat, n: nat} <‘a> list(m+n)revApp (xs: <‘a> list(m), ys: <‘a> list(n)) { var: ‘a x;;invariant: [m1: nat, n1: nat | m1+n1=m+n] (xs: <‘a> list(m1), ys: <‘a> list(n1))while (true) { switch (xs) { case Nil: return ys; case Cons(x, xs): ys = Cons(x, ys); } } exit; /* can never be reached */ }
A Generated Constraint The following integer constraint is generated when the revApp example is type-checked: m:nat,n:nat,m1:nat,n1:nat,m1+n1=m+n,a:nat,m1=a+1 |= a+(n1+1)=m+n
Talk Overview • Motivation • Program error detection • Compilation certification • Proof-carrying code • Programming language Xanadu • Design decisions • Dependent type system • Programming examples • Conclusion
Current Status of Xanadu • A prototype implementation of Xanadu in Objective Caml that • performs two-phase type-checking, and • generates assembly level code • An interpreter for interpreting assembly level code • A variety of examples (FFT, Heapsort, Quicksort, Gaussian elimination, etc.) athttp://www.ececs.uc.edu/~hwxi/Xanadu/Xanadu.html
Conclusion • It is still largely an elusive goal in practice to verify the correctness of a program • It is therefore important to identify those program properties that can be effectively verified for realistic programs
Conclusion • We have designed a type-theoretic approach to capturing some simple arithmetic reasoning in programming • The preliminary studies indicate that this approach allows the programmer to capture many more properties in realistic programs while retaining practical type-checking
Future Work • Adding more programming features into Xanadu • in particular, OO features • Type-preserving compilation: constructing a compiler for Xanadu that can translate dependent types from source level into bytecode level • Incorporating dependent types into (a subset of) Java and …
Related Work • Here is some related work • Dependent types in practical programming (Xi & 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)
End of the Talk • Thank You! Questions?