# Static Contract Checking for Haskell Dana N. Xu University of Cambridge Ph.D. Supervisor: Simon Peyton Jones Microsoft Research Cambridge.

## Presentation on theme: "Static Contract Checking for Haskell Dana N. Xu University of Cambridge Ph.D. Supervisor: Simon Peyton Jones Microsoft Research Cambridge."— Presentation transcript:

Static Contract Checking for Haskell Dana N. Xu University of Cambridge Ph.D. Supervisor: Simon Peyton Jones Microsoft Research Cambridge

From Types to Contracts head [] = BAD head (x:xs) = x head :: [a] -> a …(head 1)… head {xs | not (null xs)} -> {r | True} …(head [])… Bug! Contract (arbitrary Haskell boolean expression) Type null :: [a] -> Bool null [] = True null (x:xs) = False BAD means should not happen: crash

What we want Adapt Findler-Felleisens ideas for dynamic (high-order) contract checking. Do static contract checking for a lazy language. Contract Haskell function Glasgow Haskell Compiler (GHC) Where the bug is Why it is a bug

Three Outcomes (1) Definitely Safe (no crash, but may loop) (2) Definite Bug (definitely crashes) (3) Possible Bug

5 Sorting sorted [] = True sorted (x:[]) = True sorted (x:y:xs) = x <= y && sorted (y : xs) insert :: Int -> [Int] -> [Int] insert {i | True} -> {xs | sorted xs} -> {r | sorted r} merge :: [Int] -> [Int] -> [Int] merge {xs | sorted xs}->{ys | sorted ys}->{r | sorted r} bubbleHelper :: [Int] -> ([Int], Bool) bubbleHelper {xs | True} -> {r | not (snd r) ==> sorted (fst r)} insertsort, mergesort, bubblesort {xs | True} -> {r | sorted r} (==>) True x = x (==>) False x = True

AVL Tree balanced :: AVL -> Bool balanced L = True balanced (N t u) = balanced t && balanced u && abs (depth t - depth u) <= 1 data AVL = L | N Int AVL AVL insert, delete :: AVL -> Int -> AVL insert {x | balanced x} -> {y | True} -> {r | notLeaf r && balanced r && 0 <= depth r - depth x && depth r - depth x <= 1 } delete {x | balanced x} -> {y | True} -> {r | balanced r && 0 <= depth x - depth r && depth x - depth r <= 1} (&&) True x = x (&&) False x = False

The Contract Idea for Higher-Order Function [Findler/Felleisen] f1 :: (Int -> Int) -> Int f1 ({x | x > 0} -> {y | y >= 0}) -> {r | r >= 0} f1 g = (g 0) - 1 f2 :: {r | True} f2 = f1 (\x -> x – 5) Blame f1 : f1 calls g with wrong argument f1 does not satisfy its post-condition Blame f2 : f2 calls f1 with wrong argument f3 :: {r | True} f3 = f1 (\x -> x – 1) f3 is Ok. Cant tell at run-time

What is a Contract? Contract t ::= {x | p} Predicate Contract | x:t 1 -> t 2 Dependent Function Contract | (t 1, t 2 ) Tuple Contract | Any Polymorphic Any Contract 3 { x | x > 0 } (3, []) Any 3 { x | True } (3, []) (Ok, {ys | null ys}) Ok = {x | True} arbitrary Haskell boolean expression inc x:{x | x>0} -> {y | y == x + 1} Precondition Postcondition Postcondition can mention argument arbitrary constructor

What we want? Check f If main Ok, then the whole program cannot crash. If not, show which function to blame and why. Beauty of Contract Checking

Define e t Construct e t (e ensures t) Main Theorem e t iff e t is crash-free (related to Blume&McAllester:JFP06) some e Symbolically simplify (e t) See if BAD is syntactically in e. If yes, DONE ; else give BLAME [POPL10] ESC/Haskell [Haskell06]

e {x | p} = case p[e/x] of True -> e False -> BAD e x:t 1 -> t 2 = v. (e (v t 1 )) t 2 [(v t 1 )/x] e (t 1, t 2 ) = case e of (e 1, e 2 ) -> (e 1 t 1, e 2 t 2 ) e Any = UNR Wrappers and ( pronounced ensures pronounced requires) related to [Findler-Felleisen:ICFP02]

Wrappers and ( pronounced ensures pronounced requires) related to [Findler-Felleisen:ICFP02] e {x | p} = case p[e/x] of True -> e False -> UNR e x:t 1 -> t 2 = v. (e (v t 1 )) t 2 [v t 1 /x] e (t 1, t 2 ) = case e of (e 1, e 2 ) -> (e 1 t 1, e 2 t 2 ) e Any = BAD

Some Interesting Details Practice Theory Adding tags, e.g. BAD f Achieves precise blaming More tags to trace functions to blame Achieves the same goal of [Meunier:POPL06] Using a theorem prover Counter-example guided unrolling Contracts that loop Contracts that crash Lovely Lemmas

Summary Static contract checking is a fertile and under-researched area Distinctive features of our approach – Full Haskell in contracts; absolutely crucial – Declarative specification of satisfies – Nice theory (with some very tricky corners) – Static proofs – Modular Checking – Compiler as theorem prover

After Ph.D. Postdoc project in 2009: probabilistic contract for component base design [ATVA2010] Current project at Xavier Leroys team (INRIA) - a verifying compiler: 1. Apply the idea to OCaml compiler by allowing both static and dynamic contract checking 2. Connect with Coq to verify more programs statically. 3. Use Coq to prove the correctness of the framework. 4. Apply new ideas back to Haskell (e.g. GHC).

Static and Dynamic Program with Specifications Run time error attributes blame to the right place Compile time error attributes blame to the right place Dynamic checking Static checking No blaming means Program cannot crashs Or, more plausibly: If you guarantee that f t, then the program cannot crash

What exactly does it mean to say that et e satisfies contract t? e t

When does e satisfy a contract? Brief, declarative… inc x:{x | x > 0} -> {y | y == x + 1} Precondition Postcondition Postcondition can mention argument

When does e satisfy a contract? The delicate one is the predicate contract. Our decision: e {x | p} iff e is crash-free crash-free Question: What expression is crash-free ? crash-free e is crash-free iff no blameless context can make e crash e is crash-free iff C. BAD s C. C[e] * BAD

Crash-free Examples Lemma: e is syntactically safe => e is crash-free. Crash-free? \x -> x YES (1, True)YES (1, BAD) NO \x -> if x > 0 then x else (BAD, x) NO \x -> if x*x >= 0 then x + 1 else BAD Hmm.. YES

When does e satisfy a contract? See the paper for … Why e must be crash-free to satisfy predicate contract? Why divergent expression satisfies all contract? What if contract diverges (i.e. p diverges)? What if contract crashes (i.e. p crashes)?

How can we mechanically check that et e satisfies contract t? e t ???

Example head { xs | not (null xs) } -> Ok = \v. head (v { xs | not (null xs)}) Ok e Ok = e = \v. head (v { xs | not (null xs)}) = \v. head (case not (null v) of True -> v False -> UNR) head:: [a] -> a head [] = BAD head (x:xs) = x

\v. head (case not (null v) of True -> v False -> UNR) null :: [a] -> Bool null [] = True null (x:xs) = False not :: Bool -> Bool not True = False not False = True = \v. head (case v of [] -> UNR (p:ps) -> p) Now inline not and null Now inline head = \v. case v of [] -> UNR (p:ps) -> p head:: [a] -> a head [] = BAD head (x:xs) = x So head [] fails with UNR, not BAD, blaming the caller

Static and Dynamic Program with Specifications Run time error attributes blame to the right place Compile time error attributes blame to the right place Dynamic checking Static checking [Findler, Felleisen, Blume, Hinze, Loh, Runciman, Chitil] [Flanaghan, Mitchell, Pottier]

Download ppt "Static Contract Checking for Haskell Dana N. Xu University of Cambridge Ph.D. Supervisor: Simon Peyton Jones Microsoft Research Cambridge."

Similar presentations