static contract checking for haskell
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 PresentationTRANSCRIPT
![Page 1: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/1.jpg)
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
![Page 2: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/2.jpg)
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”
![Page 3: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/3.jpg)
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
![Page 4: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/4.jpg)
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!
![Page 5: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/5.jpg)
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!
![Page 6: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/6.jpg)
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
![Page 7: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/7.jpg)
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.
![Page 8: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/8.jpg)
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’
![Page 9: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/9.jpg)
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]
![Page 10: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/10.jpg)
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!
![Page 11: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/11.jpg)
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
![Page 12: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/12.jpg)
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 []}
![Page 13: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/13.jpg)
13
LanguageSyntax
following Haskell’slazy semantics
![Page 14: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/14.jpg)
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)
![Page 15: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/15.jpg)
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
![Page 16: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/16.jpg)
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:
![Page 17: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/17.jpg)
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
![Page 18: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/18.jpg)
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}
![Page 19: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/19.jpg)
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
![Page 20: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/20.jpg)
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}
![Page 21: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/21.jpg)
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)
![Page 22: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/22.jpg)
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!
![Page 23: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/23.jpg)
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
![Page 24: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/24.jpg)
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]
![Page 25: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/25.jpg)
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.)
![Page 26: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/26.jpg)
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)
![Page 27: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/27.jpg)
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
![Page 28: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/28.jpg)
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
![Page 29: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/29.jpg)
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
![Page 30: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/30.jpg)
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
![Page 31: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/31.jpg)
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
![Page 32: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/32.jpg)
32
Lattice for Expressions (¹ )
UNR
BAD
(3,BAD)
5
x . x(3, 4)
x. 6
![Page 33: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/33.jpg)
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}
![Page 34: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/34.jpg)
34
Conclusion
Contract
Haskell Program
Glasgow Haskell Compiler (GHC)
Where the bug is
Why it is a bug
![Page 35: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/35.jpg)
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.
![Page 36: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/36.jpg)
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
![Page 37: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/37.jpg)
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
![Page 38: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/38.jpg)
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)
![Page 39: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/39.jpg)
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])
![Page 40: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/40.jpg)
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)
![Page 41: Static Contract Checking for Haskell](https://reader035.vdocument.in/reader035/viewer/2022062423/56814960550346895db6b479/html5/thumbnails/41.jpg)
41