220 likes | 358 Views
Relatively Complete Verification of Higher-Order Programs (via Automated Refinement Type Inference). Tachio Terauchi Nagoya University. TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: A A A A A A A A A. Verifying higher-order functional programs.
E N D
Relatively Complete Verification of Higher-Order Programs (via Automated Refinement Type Inference) Tachio Terauchi Nagoya University TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: AAAAAAAAA
Verifying higher-order functional programs let rec F x c = c x in and G x y = assert x+1 ¸ y; F (y+1) (G y) in and Main w = F (w+1) (G w) Show : 8w:int. Main w is assertion safe • Refinement types approach: • F : x:int -> c:({u|u¸ x} -> *) -> * • G : x:int -> y:{u|x+1 ¸ y} -> * • Main: w:int -> *
Much recent work on automated inference • Liquid types [Rondon, Kawaguchi, Jhala PLDI’08] • Dependent type inference with interpolants [Unno, Kobayashi PPDP’09] • Dependent types from counterexamples [Terauchi POPL’10] • Predicate abstraction and CEGAR for higher-order model checking [Kobayashi, Sato, Unno PLDI’11] • HMC: Verifying functional programs using abstract interpreters [Jhala, Majumdar, Rybalchenko CAV’11] • “?” [Jagannathan 2011] • Leverages advances in first-order program verification: • predicate abstraction, interpolation, CEGAR, SMT solvers, Test generation, etc. • “Model Checkers” for higher-order programs Incomplete
Completeness and Incompleteness • Def: Refinement type system is soundiff • If a program is typable then it is safe. • Def: Refinement type system is completeiff • A program is typableiff it is safe. • Existing refinement type systems are sound, but incomplete • Different from incompleteness of inference algorithms
Relative Completeness • Higher-order program verification is undecidable • Because it contains 1st-order program verification • Complete verification for 1st-ord programs • E.g., Hoare logic • Relative to expressive 1st-ord theory like PA • Allow arbitrary PA formulas as refinement predicates? i.e., { u | µ } µ2 PA • Sufficient for ord-1 programs • Not for general higher-order programs
Refinement types incompleteness in detail • Problem:Higher-order functions • A typical relative completeness proof of Hoare logic • wpre(x = e, µ) = µ[x/e] • wpre(s1;s2, µ) = wpre(s1,wpre(s2, µ)) , … • Show the logic (PA) can express wpre(…) • Then, s is safe iffwpre(s,true) = true. • What’s wpre(f x, µ) where f : int -> * and x : int? • Or wpre(g f, µ) with g : (int -> *) -> * and f : (int -> *)?
Weakest pre.of higher-order terms • Ideally: • wpre(f x, true) = “f x runs safe” • Ex. F f x = f x • F : f:(int->*)->x:{ u : int | “f u runs safe” }->* • But, ref. pred. inference becomes higher-order program verification! • To prevent circularity, we only allow 1st-ord formulas • I don’t have relative completeness for all higer-order programs yet • I will show: How to get relative completeness for a class that haven’t been covered previously
Closure Boudedness • Def: Size of a closure is the number of base-type values captured in the closure • Def: A program is closure-boundedif its evaluation only generates closures of bounded size • Def: Closure pattern is a closure with base-type values abstracted • E.g., F (G ®) ® represent infinite number of closures F (G 0) 0, F (G 0) 1, F (G 1) -1, … • In lambda, ¸x.(y (¸z.z®) ®), etc. • Lemma: Closure-bounded iff finite # of patterns
Contribution • Relative completeness for closure-bounded programs • High-level Idea : Environment Passing Parametrize type of F with G ®, H ® • F c f = c f • G x k = k x • H x y = assert x · y; F (G y+1) (H y) • Main w = F (G w+1) (H w) Show: 8w:int. Main w as safe F : 8a1.8a2.c:(({u|µ1}->*)->*)->f:({u|µ2} ->*) ->* Intersection types to handle different contexts (this example needs none)
Details • Naïve Approach : • Symbolically evaluate the program for n steps and recordclosure patterns seen • Build type shapes from the patterns • Check if typable restricted to the type shapes • If not typable, increase n and repeat • (In parallel) check for unsafety
Symbolic Eval => Closure Patterns • Patterns A := F | ® | F A • Ex. • Evaluate from M with w : int • Patterns: F (G ®) ®, … • F c x = c x • G x y = assert x+1 · y ; F (G y) (y+1) • M w = F (G w) (w+1)
Patterns => Type shapes • Types t := * | x:t -> t | {u|µ} | 8x.t | [A].t Æ [A].t • Type shapes = N £ types with µ erased • E.g., (3, f:{u|_}->*) • ord(A) = order of simple type of A • A => (v,t’) inductively on the order of A • Ex. A = F : • Seen patterns: F (G ®) ®, F (H ®®) ®, … • ty(F) = (0, [(G ®), ®].8a1.t1->®.s->* Æ[(H ®®), ®].8a18a2.t2->®.s->*) • ty(®) = (0,s), ty(G ®) = (1,t1), ty(H ®®) = (2, t2), …
Checking typability • Shapes & patterns => derivation structure • For each F, have patterns A1, …, An for its args • Make type derivation per F, Ai • Ex. Patterns : F (G ®) (K ®) ® for F c f x = c f x • Track concrete patterns: c: G a1, f: K a2, x: x, …, up to base-type parameter variables • At function applications • Look up ty(...) for matching abstract patterns • Instantiate with captured base-type variables • Use “top” type to handle unmatched shapes • Infer satisfying assignment for µ’s • If none found, fail
Summary of naïve approach • Symbolically evaluate the program for n steps and recordclosure patterns seen • Build type shapes from the patterns • Check if typable restricted to the type shapes • If not typable, increase n and repeat • (In parallel) check for unsafety • Thm: This is rel. comp. for closure-bounded programs • Pf. Like that of Hoare logic. “Thread” weakest precondition through the type derivation. • Cor: 1st-order program verification can be “lifted” to closure-bounded higher-order program verification
Naïve approach • Relatively complete but not very clever • Patterns sufficient but not always necessary • Fails for non-closure-bounded programs • Better approach: • Try type inference w/o patterns • If type inference fails, then infer patterns • Repeat with the added patterns • Also, just add candidate 8x.t, e[e’] and have type inf. alg. figure out the rest (i.e., rid patterns from type inference)
Check typability w/o patterns • Leverage existing algorithms • Liquid types [Rondon, Kawaguchi, Jhala PLDI’08] • Dependent type inference with interpolants [Unno, Kobayashi PPDP’09] • Dependent types from counterexamples [Terauchi POPL’10] • Predicate abstraction and CEGAR for higher-order model checking [Kobayashi, Sato, Unno PLDI’11] • HMC: Verifying functional programs using abstract interpreters [Jhala, Majumdar, Rybalchenko CAV’11]
If inference fails, infer patterns • Use the symbolic evaluation as in naïve • Or use counterexample from type inference [Terauchi POPL’10][Kobayashi, Sato, Unno PLDI’11] • Unwound program slice without recursion • Infer patterns that occur in slice via flow analysis
Check typability with the added patterns • Like the naïve approach, but • Instead of using the patterns • i.e., c : G w, …, and [(G ®), ®].8x.t1->®.s->*, …, etc. • Use the built type shapes minus the patterns • c : 8x.{u | _} -> *, … • And how to instantiate them • Let backend type inference alg. resolve type matching (as well as µ inference) • Implement as program translation
Program Translation • Universal types 8x.t modeled by x:int -> t • Instantiationse[e’]modeled by e e’ • F c f = c f • G x k = k x • H x y = assert x · y • main w = F (G w) (H w) • F a1a2c f = c f • G x k = k x • H x y = assert x · y • main w = F w (G w) w (H w) ty(F) = (nil,8a1.8a2.c:(({u|_}->*)->*)->f:({u|_} ->*) ->*) ty(G ®) = ([w],…) ty(H ®) = ([w],…) Multiple trans. when ty(…) has intersection types (none needed for this example)
Summary of translation approach • Try type inference w/o patterns • If type inference fails, then infer patterns • Repeat with the added patterns • Uses off-the-shelf refinement type inference algorithms • Complete relative to • Underlying refinement type inference • And patterngeneration • Incomplete in practice (obviously)
Preliminary Experimental Results • Depcegar [Terauchi POPL’10]
Conclusions • Relatively complete verification framework for higher-order programs • Based on refinement types • Good for “model checking” like automation • Frees the backend theorem prover/decision prover from directly reasoning about higher-order functions • High-level : Environment passing • Theory : Rel. comp. for closure-bounded programs • Practice: Iterative translation