1 / 28

Extended Static Checking for Haskell (ESC/Haskell)

Extended Static Checking for Haskell (ESC/Haskell). Dana N. Xu University of Cambridge advised by Simon Peyton Jones Microsoft Research, Cambridge. Program Errors Give Headache!. Module UserPgm where f :: [Int]->Int f xs = head xs `max` 0 : … f [] …. Module Prelude where

laksha
Download Presentation

Extended Static Checking for Haskell (ESC/Haskell)

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Extended Static Checking for Haskell(ESC/Haskell) Dana N. Xu University of Cambridge advised bySimon Peyton Jones Microsoft Research, Cambridge

  2. Program Errors Give Headache! Module UserPgm where f :: [Int]->Int f xs = head xs `max` 0 : … f [] … Module Prelude where head :: [a] -> a head (x:xs) = x head [] = error “empty list” Glasgow Haskell Compiler (GHC) gives at run-time Exception: Prelude.head: empty list

  3. Preconditions head xs @ requires { not (null xs) } head (x:xs’) = x f xs = head xs `max` 0 Warning: f [] calls head which may fail head’s precondition! f_ok xs = if null xs then 0 else head xs `max` 0 A precondition (ordinary Haskell) null :: [a] -> Bool null [] = True null (x:xs) = False not :: Bool -> Bool not True = False not False = True

  4. Postconditions rev xs @ ensures { null $res ==> null xs } rev [] = [] rev (x:xs’) = rev xs’ ++ [x] … case (rev xs) of [] -> head xs (x:xs’) -> … A postcondition (ordinary Haskell) Crash! (==>) :: Bool -> Bool -> Bool (==>) True x = x (==>) False x = True

  5. Expressiveness of the Specification Language data T = T1 Bool | T2 Int | T3 T T sumT :: T -> Int sumT x @ requires { noT1 x } sumT (T2 a) = a sumT (T3 t1 t2) = sumT t1 + sumT t2 noT1 :: T -> Bool noT1 (T1 _) = False noT1 (T2 _) = True noT1 (T3 t1 t2) = noT1 t1 && noT1 t2

  6. Expressiveness of the Specification Language sumT :: T -> Int sumT x @ requires { noT1 x } sumT (T2 a) = a sumT (T3 t1 t2) = sumT t1 + sumT t2 rmT1 :: T -> T rmT1 x @ ensures { noT1 $res } rmT1 (T1 a) = if a then T2 1 else T2 0 rmT1 (T2 a) = T2 a rmT1 (T3 t1 t2) = T3 (rmT1 t1) (rmT1 t2) For all crash-free t::T, sumT (rmT1 t) will not crash.

  7. Functions without Annotations data T = T1 Bool | T2 Int | T3 T T noT1 :: T -> Bool noT1 (T1 _) = False noT1 (T2 _) = True noT1 (T3 t1 t2) = noT1 t1 && noT1 t2 (&&) True x = x (&&) False x = False No abstraction is more compact than the function definition itself!

  8. Higher Order Functions all :: (a -> Bool) -> [a] -> Bool all f [] = True all f (x:xs) = f x && all f xs filter p xs @ ensures { all p $res } filter p [] = [] filter p (x:xs’) = case (p x) of True -> x : filter p xs’ False -> filter p xs’

  9. Various Examples zip xs ys @ requires { sameLen xs ys} zip xs ys @ ensures { sameLen $res xs } sameLen [] [] = True sameLen (x:xs) (y:ys) = sameLen xs ys sameLen _ _ = False f91 n @ requires { n <= 101 } f91 n @ ensures { $res == 91 } f91 n = case (n <= 100) of True -> f91 (f91 (n + 11)) False -> n – 10

  10. sorted [] = True sorted (x:[]) = True sorted (x:y:xs) = x <= y && sorted (y : xs) insert i xs @ ensures { sorted xs ==> sorted $res } insertsort xs @ ensures { sorted $res } merge xs ys @ ensures { sorted xs & sorted ys ==> sorted $res } mergesort xs @ ensures { sorted $res } bubbleHelper :: [Int] -> ([Int], Bool) bubbleHelper xs @ ensures { not (snd $res) ==> sorted (fst $res) } bubblesort xs @ ensures { sorted $res } Sorting

  11. What we can’t do g1 x @ requires {True} g1 x = case (prime x > square x) of True -> x False -> error “urk” g2 xs ys = case (rev (xs ++ ys) == rev ys ++ rev xs) of True -> xs False -> error “urk” Crash! Crash! • Hence, three possible outcomes: • (1) Definitely Safe (no crash, but may loop) • (2) Definite Bug (definitely crashes) • (3) Possible Bug

  12. LanguageSyntax following Haskell’s lazy semantics

  13. head (x:xs) = x head [] = BAD “head” head (x:xs) = x Preprocessing 1. Filling in missing pattern matchings. 2. Type checking the pre/postconditions. head xs @ requires { xs /= [] } head :: [a] -> a head (x:xs) = x head :: Eq a => [a] -> a

  14. Symbolic Pre/Post Checking At the definition of each function f, assuming the given precondition holds, we check • No pattern matching failure • Precondition of all calls in the body of f holds • Postcondition holds for f itself.

  15. Given f x = e, f.pre and f.post Goal: show fchk is crash-free! Theorem: if so, then given precondition of f holds: 1. No pattern matching failure 2. Precondition of all calls in the body of f holds 3. Postcondition holds for f itself

  16. The Representative Function No need to look inside OK calls All crashes in f are exposed in f#

  17. Simplifier

  18. Expressive specification does not increase the complication of checking filter f xs @ ensures { all f $res } filterchk f xs = case xs of [] -> True (x:xs’) -> case (all f (filter f xs’)) of True -> … all f (filter f xs’) …

  19. Arithmetic via External Theorem Prover foo :: Int -> Int -> Int foo i j @ requires { i > j } foo# i j = case i > j of False -> BAD “foo” True -> … goo i = foo (i+8) i goochk i = case(i+8 > i)of False -> BAD “foo” True -> … >>ThmProver i+8>i >>Valid! >>ThmProver push(i>j) push(not (j<0)) (i>0) >>Valid! case i > j of True -> case j < 0 of False -> case i > 0 of False -> BAD “f”

  20. Counter-Example Guided Unrolling sumT :: T -> Int sumT x @ requires { noT1 x } sumT (T2 a) = a sumT (T3 t1 t2) = sumT t1 + sumT t2 After simplifying sumTchk, we may have: case ((OK noT1) x) of True -> case x of T1 a -> BAD “sumT” T2 a -> a T3 t1 t2 -> case ((OK noT1) t1) of False -> BAD “sumT” True -> case ((OK noT1) t2) of False -> BAD “sumT” True -> (OK sumT) t1 + (OK sumT) t2

  21. Step 1:Program Slicing – Focus on the BAD Paths case ((OK noT1) x) of True -> case x of T1 a -> BAD “sumT” T3 t1 t2 -> case ((OK noT1) t1) of False -> BAD “sumT” True -> case ((OK noT1) t2) of False -> BAD “sumT”

  22. Step 2: Unrolling case (case x of T1 a -> False T2 a -> True T3 t1 t2 -> (OK noT1) t1 && (OK noT1) t2)) of True -> case x of T1 a -> BAD “sumT” T3 t1 t2 -> case((OK noT1) t1)of False -> BAD “sumT” True -> case((OK noT1) t2)of False -> BAD “sumT” (OK noT1) t1 (OK noT1) t2 ((OK noT1) t1) ((OK noT1) t2)

  23. Keeping Known Information case (case(NoInline ((OK noT1) x))of True -> case x of T1 a’ -> False T2 a’ -> True T3 t1’ t2’ -> (OK noT1) t1’ && (OK noT1) t2’)) of True -> case x of T1 a -> BAD “sumT” T3 t1 t2 -> case ((OK noT1) t1) of False -> BAD “sumT” True -> case ((OK noT1) t2) of False -> BAD “sumT” (OK noT1) t1 (OK noT1) t2 ((OK noT1) t1) ((OK noT1) t2) case(NoInline ((OK noT1) t1))of case(NoInline ((OK noT1) t2))of

  24. Counter-Example Guided Unrolling – The Algorithm

  25. Tracing

  26. Counter-Example Generation f3chk xs z = case xs of [] -> 0 (x:y) -> case x > z of True -> Inside “f2” <l2> (Inside “f1” <l1> (BAD “f1”)) False -> … Warning <l3>: f3 (x:y) z where x>z calls f2 which calls f1 which may fail f1’s precondition! f3 [] z = 0 f3 (x:xs) z = case x > z of True -> f2 x z False -> ... f1 x z @ requires { x < z } f2 x z = 1 + f1 x z

  27. Contributions • Checks each program in a modular fashion on a per function basis. The checking is sound. • Pre/postcondition annotations are in Haskell. • Allow recursive function and higher-order function • Unlike VC generation, we treat pre/postcondition as boolean-valued functions and use symbolic simplification. • Handle user-defined data types • Better control of the verification process • First time that Counter-Example Guided approach is applied to unrolling. • Produce a trace of calls that may lead to crash at compile-time. • Our prototype works on small but significant examples • Sorting: insertion-sort, merge-sort, bubble-sort • Nested recursion

  28. Future Work • Allowing pre/post declaration for data types. data A where A1 :: Int -> A A2 :: [Int] -> [Bool] -> A A2 x y @ requires { length x == length y} • Allowing pre/post declaration for parameters in higher-order function. map f xs @ requires {all f.pre xs} map f [] = [] map f (x:xs’) = f x : map f xs’ • Allowing polymorphism and support full Haskell.

More Related