static contract checking for haskell

Post on 15-Jan-2016

45 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

Static Contract Checking for Haskell. Dana N. Xu University of Cambridge Joint work with. Simon Peyton Jones Microsoft Research Cambridge. Koen Claessen Chalmers University of Technology. Module UserPgm where f :: [Int]->Int f xs = head xs `max` 0 : … f [] …. - PowerPoint PPT Presentation

TRANSCRIPT

1

Static Contract Checking for Haskell

Dana N. Xu

University of Cambridge

Joint work with Simon Peyton Jones

Microsoft Research CambridgeKoen Claessen

Chalmers University of Technology

2

Module UserPgm where

f :: [Int]->Intf xs = head xs `max` 0

: … f [] …

Program Errors Give Headache!

Glasgow Haskell Compiler (GHC) gives at run-time

Exception: Prelude.head: empty list

Module Prelude where

head :: [a] -> ahead (x:xs) = xhead [] = error “empty list”

3

Types>> > > > > Contracts >> > > > >

head (x:xs) = x

head :: [Int] -> Int

…(head 1)…

head 2 {xs | not (null xs)} -> {r | True}

…(head [])…

Bug!

Bug!Contract

(original Haskell boolean expression)

Type not :: Bool -> Boolnot True = Falsenot False = True

null :: [a] -> Boolnull [] = Truenull (x:xs) = False

4

Contract Checkinghead 2 {xs | not (null xs)} -> {r | True}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

No more warnings from compiler!

5

Satisfying a predicate contract

e 2 {x | p} if (1) p[e/x] gives True and

(2) e is crash-free.

Arbitrary boolean-valued Haskell expression

Recursive function, higher-order function,

partial functioncan be called!

6

Expressiveness of the Specification Language

data T = T1 Bool | T2 Int | T3 T T

sumT :: T -> Int sumT 2 {x | noT1 x} -> {r | True}sumT (T2 a) = asumT (T3 t1 t2) = sumT t1 + sumT t2

noT1 :: T -> BoolnoT1 (T1 _) = FalsenoT1 (T2 _) = TruenoT1 (T3 t1 t2) = noT1 t1 && noT1 t2

7

Expressiveness of the Specification LanguagesumT :: T -> IntsumT 2 {x | noT1 x} -> {r | True}sumT (T2 a) = asumT (T3 t1 t2) = sumT t1 + sumT t2

rmT1 :: T -> TrmT1 2 {x | True} -> {r | noT1 r}rmT1 (T1 a) = if a then T2 1 else T2 0rmT1 (T2 a) = T2 armT1 (T3 t1 t2) = T3 (rmT1 t1) (rmT1 t2)

For all crash-free t::T, sumT (rmT1 t) will not crash.

8

Higher Order Functions

all :: (a -> Bool) -> [a] -> Boolall f [] = Trueall f (x:xs) = f x && all f xs

filter :: (a -> Bool) -> [a] -> [a]filter 2 {f | True} -> {xs | True} -> {r | all f r}filter f [] = []filter f (x:xs’) = case (f x) of True -> x : filter f xs’ False -> filter f xs’

9

Contracts for higher-order function’s parameterf1 :: (Int -> Int) -> Int

f1 2 ({x | True} -> {y | y >= 0}) -> {r | r >= 0}f1 g = (g 1) - 1

f2:: {r | True}

f2 = f1 (\x -> x – 1)

Error: f1’s postcondition fails because (g 1) >= 0 does not imply (g 1) – 1 >= 0Error: f2 calls f1 which fails f1’s precondition

[Findler&Felleisen:ICFP’02, Blume&McAllester:ICFP’04]

10

Functions without Contractsdata T = T1 Bool | T2 Int | T3 T T

noT1 :: T -> BoolnoT1 (T1 _) = FalsenoT1 (T2 _) = TruenoT1 (T3 t1 t2) = noT1 t1 && noT1 t2

(&&) True x = x(&&) False x = False

No abstraction is more compact than the function definition itself!

11

Contract Synonymcontract Ok = {x | True}contract NonNull = {x | not (null x)}

head :: [Int] -> Inthead 2 NonNull -> Okhead (x:xs) = x

{-# contract Ok = {x | True} -#}{-# contract NonNull = {x | not (null x)} #-}{-# contract head :: NonNull -> Ok #-}

Actual Syntax

12

Questions on e 2 tbot = bot

x. x 2 {x | bot} ! {r | True} ?x. error “f” 2 {x | bot} ! {r | True} ?x. x 2 {x | True} ! {r | bot} ?x. head [] 2 {x | True} ! {r | bot} ?(True, 2) 2 {x | (snd x) > 0} ?(head [], 3) 2 {x | (snd x) > 0} ?error “f” 2 ? ? 2 {x | False} ? 2 {x | head []}

13

LanguageSyntax

following Haskell’slazy semantics

14

Two special constructors BAD is an expression that crashes.error :: String -> aerror s = BAD

head (x:xs) = xhead [] = BAD

UNR (short for “unreachable”) is an expression that gets stuck. This is not a crash, although execution comes to a halt without delivering a result. (identifiable infinite loop)

15

Syntax of Contracts(related to [Findler:ICFP02,Blume:ICFP04,Hinze:FLOPS06,Flanagan:POPL06])

Full version: x’:{x | x >0} -> {r | r > x’}Short hand: {x | x > 0} -> {r | r > x}

k:({x | x > 0} -> {y | y > 0}) -> {r | r > k 5}

t 2 Contractt ::= {x | p} Predicate Contract

| x:t1 ! t2 Dependent Function Contract

| (t1, t2) Tuple Contract

| Any Polymorphic Any Contract

16

Contract Satisfaction(related to [Findler:ICFP02,Blume:ICFP04,Hinze:FLOPS06])

e" means e diverges or e !* UNR

e 2 {x | p} , e" or (e is crash-free and p[e/x]!*{BAD, False} [A1]

e2 x:t1! t2 , e" or 8 e1 2 t1. (e e1) 2 t2[e1/x] [A2]

e 2 (t1, t2) , e" or (e1 2 t1 and e2 2 t2) [A3]

e 2 Any , True [A4]

Given ` e :: and `c t :: , we define e 2 t as follows:

17

Answers on e 2 tx. x 2 {x | bot} ! {r | True}x. BAD 2 {x | bot} ! {r | True} x. x 2 {x | True} ! {r | bot}x. BAD 2 {x | True} ! {r | bot} x. BAD 2 {x | True} ! Any(True, 2) 2 {x | (snd x) > 0} (BAD, 3) 2 {x | (snd x) > 0} BAD 2 AnyUNR, bot 2 {x | False}UNR, bot 2 {x | BAD} BAD 2 (Any, Any) BAD 2 Any ! Any

18

Lattice for Contracts

{x | True}

Any

{x | False}

({x | x>0}, Any)

({x | x>0}, {r | r>0})

{x | x>0}->{r | r>x}

Any -> {r | r>0}

19

Laziness

fst 2 ({x | True}, Any) -> {r | True}fst (a,b) = a …fst (5, error “f”)…

fstN :: (Int, Int) -> IntfstN 2 ({x | True}, Any) -> {r | True}fstN (a, b) n = if n > 0 then fstN (a+1, b) (n-1)

else a

g2 = fstN (5, error “fstN”) 100

Every expression satisfies Any

20

What to Check?Does function f satisfies its contract t (written f2 t)?

At the definition of each function f,

Check f 2 t assuming all functions called in f satisfy their contracts.

Goal: main 2 {x | True}

21

How to Check?Define e 2 t

Construct

e B t

(e “ensures” t)

Grand Theorem e 2 t , e B t is crash-free

(related to Blume&McAllester:ICFP’04)

Normal form e’

Simplify (e B t)

If e’ is syntactically safe,

then Done!

This Talk

ESC/Haskell (Xu:HW’06)

22

What we can’t do?g1, g2 2 Ok -> Okg1 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”

Hence, three possible outcomes: (1) Definitely Safe (no crash, but may loop)(2) Definite Bug (definitely crashes)(3) Possible Bug

Crash!

Crash!

23

Crashing

Definition (Crash).

A closed term e crashes iff e !* BAD

Definition (Crash-free Expression)

An expression e is crash-free iff

8 C. BAD 2 C, ` C[[e]] :: (), C[[e]] !* BAD

24

Definition of B and C (B pronounced ensures C pronounced requires)

e B {x | p} = case p[e/x] of

True -> eFalse -> BAD

e C {x | p} = case p[e/x] of

True -> e

False -> UNR

Example:5 2 {x | x > 0} 5 B {x | x > 0}= case (5 > 0) of True -> 5 False -> BAD

related to [Findler:ICFP02,Blume:ICFP04,Hinze:FLOPS06]

25

e B x:t1 ! t2 = v. (e (vC t1)) B t2[vCt1/x]

e C x:t1 ! t2 = v. (e (vB t1)) C t2[vBt1/x]

e B (t1, t2) = case e of (e1, e2) -> (e1 B t1, e2 B t2)

e C (t1, t2) = case e of (e1, e2) -> (e1 C t1, e2 C t2)

Definition of B and C (cont.)

26

Higher-Order Function

f1 B ({x | True} -> {y | y >= 0}) -> {r | r >= 0}= … B C B= v1. case (v1 1) >= 0 of True -> case (v1 1) - 1 >= 0 of True -> (v1 1) -1 False -> BAD False -> UNR

f1 :: (Int -> Int) -> Intf1 2 ({x | True} -> {y | y >= 0}) -> {r | r >= 0}f1 g = (g 1) - 1

f2:: {r | True}f2 = f1 (\x -> x – 1)

27

Modified Definition of B and C

e B {x | p} = case p[e/x] of

True -> e

False -> BAD

e B {x | p} = e `seq` case fin (p[e/x]) of

True -> e

False -> BAD

old

new

e_1 `seq` e_2 = case e_1 of {DEFAULT -> e_2}

if e !* BAD,

then (fin e) ! False

else (fin e) ! e

28

Why need seq and fin?

(UNR B {x | False}) !* BAD

But UNR 2 {x | False}

x. x 2 {x | BAD} ! {x | True}

But x. x B {x | BAD} ! {x | True}

!* v. (v `seq` BAD) which is NOT crash-free.

e B {x | p} = e `seq` case fin (p[e/x]) of

True -> e

False -> BAD Without seq

Without fin

29

e B Any = UNRe C Any = BAD

f5 2 Any -> {r | True}f5 x = 5

error :: String -> a -- HM Typeerror 2 {x | True} -> Any -- Contract

Definition of B and C for Any

30

Properties of B and CKey Lemma:For all closed, crash-free e, and closed t,(e C t) 2 t

Projections: (related to Findler&Blume:FLOPS’06)For all e and t, if e 2 t, then (a) e ¹ e B t(b) e C t ¹ e

Definition (Crashes-More-Often):e1 ¹ e2 iff for all C, ` C[[ei]] :: () for i=1,2 and C[[e2]] !* BAD ) C[[e1]] !* BAD

31

About 30 Lemmas Lemma [Monotonicity of Satisfaction ]:If e1 2 t and e1 ¹ e2, then e22 tLemma [Congruence of ¹]:e1 ¹ e2 ) 8 C. C[[e1]] ¹ C[[e2]]Lemma [Idempotence of Projection]:8 e, t. e B t B t ≡ e B t 8 e, t. e C t C t ≡ eC t Lemma [A Projection Pair]:8 e, t. e B t C t ¹ eLemma [A Closure Pair]:8 e, t. e ¹ eC t B t

32

Lattice for Expressions (¹ )

UNR

BAD

(3,BAD)

5

x . x(3, 4)

x. 6

33

Lattice for Contracts

{x | True}

Any

{x | False}

({x | x>0}, Any)

({x | x>0}, {r | r>0})

{x | x>0}->{r | r>x}

Any -> {r | r>0}

34

Conclusion

Contract

Haskell Program

Glasgow Haskell Compiler (GHC)

Where the bug is

Why it is a bug

35

Contributions Automatic static contract checking instead of dynamic contract checking. Compared with ESC/Haskell

Allow pre/post specification for higher-order function’s parameter through contracts.

Reduce more false alarms caused by Laziness and in an efficient way. Allow user-defined data constructors to be used to define contracts Specifications are more type-like, so we can define contract synonym.

We develop a concise notation (B and C) for contract checking, which enjoys many properties. We give a new and relatively simpler proof of the soundness and completeness of dynamic contract checking, this proof is much trickier than it looks.

We implement the idea in GHC Accept full Haskell Support separate compilation as the verification is modular. Can check functions without contract through CEG unrolling.

36

Sortingsorted [] = Truesorted (x:[]) = Truesorted (x:y:xs) = x <= y && sorted (y : xs)

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

37

Various Exampleszip :: [a] -> [b] -> [(a,b)]zip :: {xs | True} -> {ys | sameLen xs ys} -> {rs | sameLen rs xs }

sameLen [] [] = TruesameLen (x:xs) (y:ys) = sameLen xs yssameLen _ _ = False

f91 :: Int -> Intf91 :: { n <= 101 } -> {r | r == 91 }f91 n = case (n <= 100) of

True -> f91 (f91 (n + 11)) False -> n – 10

38

f :: {x | x > 0} -> {r | True}f x = x

f B {x | x > 0} -> {r | True}=(x.x) B {x | x > 0} -> {r | True}= v. (x.x (v C {x | x > 0})) B {r | True}= v. (case v > 0 of True -> v False -> UNR)

g = … f…

f C {x | x > 0} -> {r | True}= v. (case v > 0 of True -> v False -> BAD)

39

Constructor Contracts{x | x > 0} : {xs | all (< 0) xs}

{x | x > 0} : Any

Any : Any

Support any user-defined data constructors!

(related to [Hinze&Jeuring&Löh:FLOPS’06])

40

How to Check e 2 t?Given e and t We construct a term (eBt) (pronounced e “ensures” t)

related to Findler&Felleisen:ICFP’02 (dynamic contract checking algorithm)

Prove (eBt) is crash-free. simplify the term (eBt) to e’ and e’ is syntactically safe,

then we are done.

Theorem 1 (related to Blume&McAllester:ICFP’04)

eBt is crash-free , e 2 t

Theorem 2simpl (eBt) is syntactically safe ) eBt is crash-free

This Talk

ESC/Haskell (HW’06)

41

top related