420 likes | 435 Views
Explore the significance of dependent types in programming, emphasizing detection of errors, compiler optimization, and program verification. Discover how types act as reliable documentation for programs, unlike informal comments. Delve into the limitations of simple types and solutions offered through proof-carrying code and certification. Learn about the integration of dependent types in programming languages through Xanadu and practical examples of their application.
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?