Title: Static Contract Checking for Haskell
1Static 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
2Program Errors Give Headache!
- Module UserPgm where
- f Int-gtInt
- f xs head xs max 0
-
- f
Module Prelude where head a -gt a head
(xxs) x head error empty list
Glasgow Haskell Compiler (GHC) gives at
run-time Exception Prelude.head empty list
3Typesgtgt gt gt gt gt Contracts gtgt gt gt gt gt
- head (xxs) x
- head Int -gt Int
- (head 1)
- head 2 xs not (null xs) -gt r True
- (head )
Type
not Bool -gt Bool not True False not False
True null a -gt Bool null True null
(xxs) False
Bug!
Contract (original Haskell boolean expression)
Bug!
4Contract Checking
- head 2 xs not (null xs) -gt r True
- head (xxs) x
- f xs head xs max 0
- Warning f calls head
- which may fail heads precondition!
- f_ok xs if null xs then 0
- else head xs max 0
No more warnings from compiler!
5Satisfying a predicate contract
Arbitrary boolean-valued Haskell expression
- e 2 x p if (1) pe/x gives True and
- (2) e is crash-free.
Recursive function, higher-order function,
partial function can be called!
6Expressiveness of the Specification Language
data T T1 Bool T2 Int T3 T T sumT T -gt
Int sumT 2 x noT1 x -gt r True sumT (T2
a) a sumT (T3 t1 t2) sumT t1 sumT t2 noT1
T -gt Bool noT1 (T1 _) False noT1 (T2 _)
True noT1 (T3 t1 t2) noT1 t1 noT1 t2
7Expressiveness of the Specification Language
- sumT T -gt Int
- sumT 2 x noT1 x -gt r True
- sumT (T2 a) a
- sumT (T3 t1 t2) sumT t1 sumT t2
- rmT1 T -gt T
- rmT1 2 x True -gt r noT1 r
- 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 tT, sumT (rmT1 t) will not
crash.
8Higher Order Functions
- all (a -gt Bool) -gt a -gt Bool
- all f True
- all f (xxs) f x all f xs
- filter (a -gt Bool) -gt a -gt a
- filter 2 f True -gt xs True -gt r all f
r - filter f
- filter f (xxs) case (f x) of
- True -gt x filter f xs
- False -gt filter f xs
9Contracts for higher-order functions parameter
f1 (Int -gt Int) -gt Int f1 2 (x True -gt y
y gt 0) -gt r r gt 0 f1 g (g 1) - 1 f2
r True f2 f1 (\x -gt x 1)
Error f1s postcondition fails because (g
1) gt 0 does not imply (g 1) 1
gt 0 Error f2 calls f1 which fails f1s
precondition
FindlerFelleisenICFP02, BlumeMcAllesterICFP
04
10Functions without Contracts
- data T T1 Bool T2 Int T3 T T
- noT1 T -gt 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!
11Contract Synonym
- contract Ok x True
- contract NonNull x not (null x)
- head Int -gt Int
- head 2 NonNull -gt Ok
- head (xxs) x
- - contract Ok x True -
- - contract NonNull x not (null x) -
- - contract head NonNull -gt Ok -
Actual Syntax
12Questions on e 2 t
- bot 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) gt 0 ?
- (head , 3) 2 x (snd x) gt 0 ?
- error f 2 ?
- ? 2 x False
- ? 2 x head
13LanguageSyntax
following Haskells lazy semantics
14Two special constructors
- BAD is an expression that crashes.
- error String -gt a
- error s BAD
- head (xxs) x
- head 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)
15Syntax of Contracts(related to
FindlerICFP02,BlumeICFP04,HinzeFLOPS06,Flanaga
nPOPL06)
t 2 Contract t x p Predicate Contract
xt1 ! t2 Dependent Function Contract
(t1, t2) Tuple Contract Any
Polymorphic Any Contract
Full version xx x gt0 -gt r r gt
x Short hand x x gt 0 -gt r r gt
x k(x x gt 0 -gt y y gt 0) -gt r r gt k
5
16Contract Satisfaction(related to
FindlerICFP02,BlumeICFP04,HinzeFLOPS06)
Given e ? and c t ?, we define e 2 t as
follows
e 2 x p , e" or (e is crash-free and
pe/x!BAD,
False A1 e2 xt1! t2 , e" or
8 e1 2 t1. (e e1) 2 t2e1/x A2 e 2
(t1, t2) , e" or (e1 2 t1 and e2 2 t2)
A3 e 2 Any , True
A4
e" means e diverges or e ! UNR
17Answers on e 2 t
- ? x. 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) gt 0
- (BAD, 3) 2 x (snd x) gt 0
- BAD 2 Any
- UNR, bot 2 x False
- UNR, bot 2 x BAD
- BAD 2 (Any, Any) BAD 2 Any ! Any
18Lattice for Contracts
Any
(x xgt0, Any)
Any -gt r rgt0
x True
(x xgt0, r rgt0)
x xgt0-gtr rgtx
x False
19Laziness
- fst 2 (x True, Any) -gt r True
- fst (a,b) a
-
- fst (5, error f)
- fstN (Int, Int) -gt Int
- fstN 2 (x True, Any) -gt r True
- fstN (a, b) n if n gt 0
- then fstN (a1, b) (n-1)
- else a
- g2 fstN (5, error fstN) 100
Every expression satisfies Any
20What 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
21How to Check?
This Talk
Define e 2 t
Grand Theorem e 2 t , e B t is crash-free
(related to BlumeMcAllesterICFP04)
Construct e B t (e ensures t)
ESC/Haskell (XuHW06)
Simplify (e B t)
Normal form e
If e is syntactically safe, then Done!
22What we cant do?
- g1, g2 2 Ok -gt Ok
- g1 x case (prime x gt square x) of
- True -gt x
- False -gt error urk
- g2 xs ys
- case (rev (xs ys) rev ys rev xs) of
- True -gt xs
- False -gt error urk
Crash!
Crash!
Hence, three possible outcomes (1) Definitely
Safe (no crash, but may loop) (2) Definite Bug
(definitely crashes) (3) Possible Bug
23Crashing
- 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, Ce (), Ce ! BAD
24Definition of B and C (B pronounced ensures C
pronounced requires)
- e B x p
- case pe/x of
- True -gt e
- False -gt BAD
- e C x p
- case pe/x of True -gt e
- False -gt UNR
- Example
- 5 2 x x gt 0
-
- 5 B x x gt 0
- case (5 gt 0) of
- True -gt 5
- False -gt BAD
related to FindlerICFP02,BlumeICFP04,HinzeFLOP
S06
25Definition of B and C (cont.)
- e B xt1 ! t2
- ? v. (e (vC t1)) B t2vCt1/x
- e C xt1 ! t2
- ? v. (e (vB t1)) C t2vBt1/x
- e B (t1, t2)
- case e of
- (e1, e2) -gt (e1 B t1, e2 B t2)
- e C (t1, t2)
- case e of
- (e1, e2) -gt (e1 C t1, e2 C t2)
26Higher-Order Function
f1 (Int -gt Int) -gt Int f1 2 (x True -gt y
y gt 0) -gt r r gt 0 f1 g (g 1) - 1 f2
r True f2 f1 (\x -gt x 1)
- f1 B (x True -gt y y gt 0) -gt r r gt
0 - B C
B - ? v1. case (v1 1) gt 0 of
- True -gt case (v1 1) - 1 gt 0 of
- True -gt (v1 1) -1
- False -gt BAD
- False -gt UNR
27Modified Definition of B and C
- e B x p
- case pe/x of
- True -gt e
- False -gt BAD
old
if e ! BAD, then (fin e) ! False else (fin e) !
e
- e B x p
- e seq case fin (pe/x) of
- True -gt e
- False -gt BAD
new
e_1 seq e_2 case e_1 of DEFAULT -gt e_2
28Why need seq and fin?
e B x p e seq case fin (pe/x) of
True -gt e False -gt BAD
Without seq
(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.
Without fin
29Definition of B and C for Any
e B Any UNR e C Any BAD
f5 2 Any -gt r True f5 x 5 error String
-gt a -- HM Type error 2 x True -gt Any
-- Contract
30Properties of B and C
- Key Lemma
- For all closed, crash-free e, and closed t,
- (e C t) 2 t
- Projections (related to FindlerBlumeFLOPS06)
- For all e and t, if e 2 t, then
- e ¹ e B t
- e C t ¹ e
- Definition (Crashes-More-Often)
- e1 ¹ e2 iff for all C, Cei () for
i1,2 and - Ce2 ! BAD ) Ce1 !
BAD
31About 30 Lemmas ?
- Lemma Monotonicity of Satisfaction
- If e1 2 t and e1 ¹ e2, then e22 t
- Lemma Congruence of ¹
- e1 ¹ e2 ) 8 C. Ce1 ¹ Ce2
- 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 ¹ e
- Lemma A Closure Pair
- 8 e, t. e ¹ eC t B t
32Lattice for Expressions (¹ )
BAD
(3,BAD)
? x. 6
5
(3, 4)
? x . x
UNR
33Lattice for Contracts
Any
(x xgt0, Any)
Any -gt r rgt0
x True
(x xgt0, r rgt0)
x xgt0-gtr rgtx
x False
34Conclusion
Where the bug is
Contract
Glasgow Haskell Compiler (GHC)
Why it is a bug
Haskell Program
35Contributions
- Automatic static contract checking instead of
dynamic contract checking. - Compared with ESC/Haskell
- Allow pre/post specification for higher-order
functions 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.
36Sorting
(gt) True x x (gt) False x True
- sorted True
- sorted (x) True
- sorted (xyxs) x lt y sorted (y xs)
- insert i True -gt xs sorted xs
- -gt r sorted r
- merge Int -gt Int -gt Int
- merge xs sorted xs -gt ys sorted ys
- -gt r sorted r
- bubbleHelper Int -gt (Int, Bool)
- bubbleHelper xs True
- -gt r not (snd r) gt sorted (fst
r) - Insertsort, mergesort, bubblesort xs True
- -gt r sorted
r
37Various Examples
- zip a -gt b -gt (a,b)
- zip xs True -gt ys sameLen xs ys
- -gt rs sameLen rs xs
- sameLen True
- sameLen (xxs) (yys) sameLen xs ys
- sameLen _ _ False
- f91 Int -gt Int
- f91 n lt 101 -gt r r 91
- f91 n case (n lt 100) of
- True -gt f91 (f91 (n 11))
- False -gt n 10
38- f x x gt 0 -gt r True
- f x x
- f B x x gt 0 -gt r True
- (?x.x) B x x gt 0 -gt r True
- ?v. (?x.x (v C x x gt 0)) B r True
- ?v. (case v gt 0 of
- True -gt v
- False -gt UNR)
- g f
f C x x gt 0 -gt r True ?v. (case v gt 0
of True -gt v False -gt BAD)
39Constructor Contracts
- x x gt 0 xs all (lt 0) xs
- x x gt 0 Any
- Any Any
- Support any user-defined data constructors!
- (related to HinzeJeuringLöhFLOPS06)
40How to Check e 2 t?
This Talk
- Given e and t
- We construct a term (eBt) (pronounced e ensures
t) - related to FindlerFelleisenICFP02 (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 BlumeMcAllesterICFP
04) - eBt is crash-free , e 2 t
- Theorem 2
- simpl (eBt) is syntactically safe ) eBt is
crash-free
ESC/Haskell (HW06)
41(No Transcript)