Extended Static Checking for Haskell (ESC/Haskell)

Slides:



Advertisements
Similar presentations
Automated Theorem Proving Lecture 1. Program verification is undecidable! Given program P and specification S, does P satisfy S?
Advertisements

Static Contract Checking for Haskell Dana N. Xu University of Cambridge Ph.D. Supervisor: Simon Peyton Jones Microsoft Research Cambridge.
Semantics Static semantics Dynamic semantics attribute grammars
CSE341: Programming Languages Lecture 2 Functions, Pairs, Lists Dan Grossman Winter 2013.
F28PL1 Programming Languages Lecture 14: Standard ML 4.
Extended Static Checking for Haskell (ESC/Haskell) Dana N. Xu University of Cambridge advised by Simon Peyton Jones Microsoft Research, Cambridge.
0 PROGRAMMING IN HASKELL Chapter 4 - Defining Functions.
Introduction to ML Last time: Basics: integers, Booleans, tuples,... simple functions introduction to data types This time, we continue writing an evaluator.
0 PROGRAMMING IN HASKELL Chapter 6 - Recursive Functions Most of this should be review for you.
0 PROGRAMMING IN HASKELL Chapter 6 - Recursive Functions.
Simon Peyton Jones Microsoft Research Joint work with Dana Xu, University of Cambridge Dec 2007.
Software Engineering Prof. Dr. Bertrand Meyer March 2007 – June 2007 Chair of Software Engineering Static program checking and verification Slides: Based.
PrasadCS7761 Haskell Data Types/ADT/Modules Type/Class Hierarchy Lazy Functional Language.
1 Static Contract Checking for Haskell Dana N. Xu INRIA France Work done at University of Cambridge Joint work with Simon Peyton Jones Microsoft Research.
0 PROGRAMMING IN HASKELL Chapter 7 - Defining Functions, List Comprehensions.
Sound Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen Chalmers University of Technology.
Functional Programming With examples in F#. Pure Functional Programming Functional programming involves evaluating expressions rather than executing commands.
1-Nov-15 Haskell II Functions and patterns. Data Types Int + - * / ^ even odd Float + - * / ^ sin cos pi truncate Char ord chr isSpace isUpper … Bool.
11/1/20151 GC16/3011 Functional Programming Lecture 5 Miranda patterns, functions, recursion and lists.
1 Functional Programming Lecture 6 - Algebraic Data Types.
Reasoning about programs March CSE 403, Winter 2011, Brun.
1 Static Contract Checking for Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen.
Recursion on Lists Lecture 5, Programmeringsteknik del A.
0 PROGRAMMING IN HASKELL Chapter 4 - Defining Functions.
Lee CSCE 314 TAMU 1 CSCE 314 Programming Languages Haskell: More on Functions and List Comprehensions Dr. Hyunyoung Lee.
1 Static Contract Checking for Haskell Dana N. Xu University of Cambridge Joint work with Simon Peyton Jones Microsoft Research Cambridge Koen Claessen.
0 PROGRAMMING IN HASKELL Based on lecture notes by Graham Hutton The book “Learn You a Haskell for Great Good” (and a few other sources) Odds and Ends,
Haskell Chapter 5, Part II. Topics  Review/More Higher Order Functions  Lambda functions  Folds.
An introduction to functional programming using Haskell CENG242 –Recitation 1.
6-Jul-16 Haskell II Functions and patterns. Data Types Int + - * / ^ even odd Float + - * / ^ sin cos pi truncate Char ord chr isSpace isUpper … Bool.
© M. Winter COSC 4P41 – Functional Programming Some functions id :: a -> a id x = x const :: a -> b -> a const k _ = k ($) :: (a -> b) -> a -> b.
GADTs meet their match George Karachalias(Ghent University, Belgium) Tom Schrijvers (KU Leuven, Belgium) Dimitrios Vytiniotis(Microsoft Research Cambridge,
Set Comprehensions In mathematics, the comprehension notation can be used to construct new sets from old sets. {x2 | x  {1...5}} The set {1,4,9,16,25}
Set Comprehensions In mathematics, the comprehension notation can be used to construct new sets from old sets. {x2 | x  {1...5}} The set {1,4,9,16,25}
Functional Programming
Conditional Expressions
Recursion.
Programming Languages
Types CSCE 314 Spring 2016.
Functional Programming Lecture 12 - more higher order functions
Functions and patterns
A lightening tour in 45 minutes
Haskell Chapter 4.
PROGRAMMING IN HASKELL
Reasoning About Code.
Reasoning about code CSE 331 University of Washington.
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
CSE 3302 Programming Languages
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
Computer Science 312 Haskell Lists 1.
PROGRAMMING IN HASKELL
Programming Languages
Lecture 15 (Notes by P. N. Hilfinger and R. Bodik)
6 Chapter Functions.
PROGRAMMING IN HASKELL
Hoare-style program verification
PROGRAMMING IN HASKELL
CSCE 314: Programming Languages Dr. Dylan Shell
Higher Order Functions
PROGRAMMING IN HASKELL
PROGRAMMING IN HASKELL
CSE 3302 Programming Languages
Functions and patterns
CSCE 314: Programming Languages Dr. Dylan Shell
PROGRAMMING IN HASKELL
Functions and patterns
PROGRAMMING IN HASKELL
Static Contract Checking for Haskell
Presentation transcript:

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 head :: [a] -> a head (x:xs) = x head [] = error “empty list” Glasgow Haskell Compiler (GHC) gives Exception: Prelude.head: empty list

A precondition (ordinary Haskell) 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

A postcondition (ordinary Haskell) Postconditions rev xs @ ensures { null $res ==> null xs } rev [] = [] rev (x:xs’) = rev xs’ ++ [x] f xs = case (rev xs) of [] -> head xs (x:xs’) -> … Warning: f [] calls head which may fail head’s precondition! A postcondition (ordinary Haskell) (==>) :: Bool -> Bool -> Bool (==>) True x = x (==>) False x = True

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!

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.

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

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

Sorting 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 }

What we can’t do Hence, three possible outcomes: 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 Crash! Crash! Hence, three possible outcomes: (1) Definitely Safe (no crash, but may loop) (2) Definite Bug (definitely crashes) (3) Possible Bug

Language Syntax Following Haskell’s Lazy semantics

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

Symbolic Pre/Post Checking A 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.

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

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

Simplifier

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’) …

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 >>ThmProver i+8>I >>Valid! >>ThmProver push(i> j) push(j<0) (i>0) >>Valid! case i > j of True -> case j < 0 of False -> case i > 0 of False -> BAD “f”

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 True -> (OK sumT) t1 + (OK sumT) t2

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

Step 2: Unrolling case ((\x -> case x of T1 a’ -> False T2 a’ -> True T3 t1’ t2’ -> (OK noT1) t1’ && (OK noT1) t2’) 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 21

Step 2: Unrolling case ((\x -> case x of T1 a’ -> False T2 a’ -> True T3 t1’ t2’ -> (OK noT1) t1’ && (OK noT1) t2’) 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 (OK noT1) t1’ (OK noT1) t2’ ((OK noT1) t1) ((OK noT1) t1) 22

Keeping Known Information case (case (NoInline ((OK noT1) x)) of True -> (\x -> case x of T1 a’ -> False T2 a’ -> True T3 t1’ t2’ -> (OK noT1) t1’ && (OK noT1) t2’) 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 (OK noT1) t1’ (OK noT1) t2’ ((OK noT1) t1) ((OK noT1) t1) 23

Counter-Example Guided Unrolling – The Algorithm

Tracing

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!

Contributions Checks each program in a modular fashion on a per function basis. 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

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.