1
HASKELL TO LOGIC THROUGH DENOTATIONAL SEMANTICS
Dimitrios Vytiniotis, Koen Claessen,Simon Peyton Jones, Dan Rosén
POPL 2013, January 2013
2
Real programs contain assertionsdimitris@artemis:~/GHC/ghc-head/ghc/compiler/typecheck$ grep -i ASSERT ./*hs ./FamInst.lhs: = ASSERT( isAlgTyCon tycon )./Inst.lhs: ; wrap <- ASSERT( null rest && isSingleton theta )./TcCanonical.lhs: = ASSERT( tyConArity tc <= length tys ) -- Type functions are saturated./TcCanonical.lhs: = ASSERT( not (isKind t1) && not (isKind t2) )./TcClassDcl.lhs: = ASSERT( ok_first_pred ) local_meth_ty./TcClassDcl.lhs: rho_ty = ASSERT( length sel_tyvars == length inst_tys )./TcDeriv.lhs: ASSERT( null sigs )./TcDeriv.lhs: = ASSERT2( equalLength rep_tc_tvs all_rep_tc_args, ppr cls <+> ppr rep_tc )./TcDeriv.lhs: arg_ty <- ASSERT( isVanillaDataCon data_con )./TcEnv.lhs: -> ASSERT( lvl == lvl1 ) id./TcEnv.lhs: TopLevel -> ASSERT2( isEmptyVarSet id_tvs, ppr id $$ ppr (idType id) ) ./TcErrors.lhs: = ASSERT( isEmptyBag insols )./TcErrors.lhs: = ASSERT( not (null matches) )./TcErrors.lhs: = ASSERT( length matches > 1 )./TcEvidence.lhs: | otherwise = ASSERT( arity < n_tys )./TcEvidence.lhs:mkTcForAllCo tv (TcRefl ty) = ASSERT( isTyVar tv ) TcRefl (mkForAllTy tv ty)./TcEvidence.lhs:mkTcForAllCo tv co = ASSERT( isTyVar tv ) TcForAllCo tv co./TcEvidence.lhs:mkTcForAllCos tvs (TcRefl ty) = ASSERT( all isTyVar tvs ) TcRefl (mkForAllTys tvs ty)./TcEvidence.lhs:mkTcForAllCos tvs co = ASSERT( all isTyVar tvs ) foldr TcForAllCo co tvs./TcEvidence.lhs: = ASSERT (tc `hasKey` eqTyConKey)./TcEvidence.lhs: = ASSERT( equalLength tvs cos )./TcExpr.lhs: = ASSERT( not (isSigmaTy res_ty) )./TcExpr.lhs: = ASSERT( notNull upd_fld_names )
(from the GHC type checker)
3
This work
Automated static verification of higher-order functional programs
www.github.com/danr/contracts
Tool works on subset of Haskell,uses GHC as frontend
4
Our settingVerify Haskell code: higher-order, lazy but pure
Don’t aim for high expressiveness, go for simple, easy-to-prove (e.g. structural) properties
• Automatically discharge all tedious but simple goals that a programmer has to manually and repeatedly check
Re-use existing technology:• Automated theorem provers (e.g. SMT solvers), model finders• ACL2? Boogie?• Prolog? Property-directed reachability? [Bjorner et al]
No “best” solution yet.Our choice for this work
5
Programs and propertiesrisers [] = []risers [x] = [[x]]risers (x:y:ys) = case risers (y:ys) of
[] -> error “urk” (s:ss) -> if x <= y then (x:s):ss else [x]:s:ss
1. can risers crash?2. non-empty input ⟶ non-empty
result?
risers CF && {xs | not (null xs)} -> CF && {ys | not (null ys)}
Syntax of “contracts” (“refinements” more appropriate):C ::= {x | p} | (x:C1)->C2 | C1 && C2 | CF
Just an ordinary Haskell expression of type Bool “crash-free”
6
Design
module Foof x y = …g x = …
g C-- Preludedata [a] = [] | a : asdata Bool = True | False… Functions over these…
𝑇
𝜑𝑛𝑒𝑔
𝑇 𝑑𝑒𝑓
HaskellSource
First Order Logic
Formulae
Unsatisfiable Contract holds!
HALOtranslation to First Order
Logic
Z3/Equinox/E/Vampire/Paradox
Theorem Prover
Satisfiable Probably contract doesn’t hold but who knows
<loop>Can’t tell anything
7
Key idea: let denotational semantics guide us
A λ/case-lifted language
𝐷∞≈ (Π𝑛1𝐷∞+…+Π𝑛𝑘
𝐷∞+ (𝐷∞⇒𝐷∞ )+1𝑏𝑎𝑑)⊥Standard
construction
One product of cpos for each constructor of arity
Continuous function space Distinguished
one-element cpo
Lifting
8
… and use itself as FOL structure
Logical language:
A translation of expressions to logical terms:𝓔𝓔𝓔
Interpreted as the ‘apply’ combinator in
apply (,_) = apply (,_) = apply(fun(d),d’) = d(d’) apply(_,_) =
Interpreted as injection into the appropriate product
Interpreted as
9
Function definitions become FOL axioms
Theory
Theorem:
head (Cons x xs) = xhead _ = error
NB: A Good Thing!
10
Axiomatize (some) true facts about
Theorem:
Theory
data List a = Cons a (List a)| Nil
11
Higher-order functionshead (Cons x xs) = xhead _ = error
∀ 𝑥 𝑥𝑠 .𝑎𝑝𝑝(h𝑒𝑎𝑑𝑝𝑡𝑟 , 𝑥)=h𝑒𝑎𝑑(𝑥 )
double f x = f (f x)
∀ 𝑥 𝑦 .𝑑𝑜𝑢𝑏𝑙𝑒 (𝑥 , 𝑦 )=𝑎𝑝𝑝 (𝑥 ,𝑎𝑝𝑝 (𝑥 , 𝑦 ))
∀ 𝑥 𝑦 .𝑎𝑝𝑝 (𝑎𝑝𝑝 (𝑑𝑜𝑢𝑏𝑙 𝑒𝑝𝑡𝑟 ,𝑥 ) , 𝑦 )=𝑑𝑜𝑢𝑏𝑙𝑒 (𝑥 , 𝑦 )
Interpreted as the apply(.,.) combinator in
12
Refinements denotationally and logically
≙ ≙ Logically
Denotationally
13
Soundness via denotational semantics
• Assume that:
• Then:
• By previous theorems:
• … hence:
• … which is equivalent to:
14
Automating inductionCurrently support fixpoint induction
add Z y = y add (S x) y = S (add x y)
add CF -> CF -> CF
Assume contract holds for uninterpreted function add_rec add Z y = y add (S x) y = S (add_rec x y)add_rec CF -> CF -> CF---------------------------add CF -> CF -> CF
NB: A sound thing to do by admissibility of contracts
15
Admissibility and why it mattersIn Haskell, data types are not inductive. Hence your familiar induction principle is simply unsound!
ones = 1 : ones
f Z = []f (S x) = 1 : f xLemma: forall x. f x ≠ onesProof:• Holds for UNR• Holds for Z• Assume holds for x; then holds for (S x)Right?
WRONG!Let: u = S uThen: f u = ones
Logical inequality, not admissible!
16
Admissibility and inductionAdmissibility
= If P is true for all elements of a chain, then true for the limit.
Not all predicates are admissible
𝑎𝑑𝑚𝑖𝑠𝑠𝑖𝑏𝑙𝑒 (𝑃 ) 𝑃 (UNR)𝑃 (h )⟹𝑃 (𝐹 h)𝑃 ( 𝑓 𝑖𝑥 𝐹 )
[FixInd ]
Theorem: All predicates are admissible. Comes for-free!
Base contracts are Haskell functions, and those are
continuous!
17
Happily implemented on top of GHC API
Z3 rocks for provable properties!
Disclaimer:• 40-80 FOL axioms/problem• Use of fixpoint induction
18
More features (and non-features)
More features:• Incremental verification
• Prove spec for “g”, use either the spec or definition of “g”, or both to prove other specifications …
• Some support for lemmas• Mutual (automatic) fixpoint induction• Primitive arithmetic constraints via SMT2 (in Z3)• Experimental features: logical equality, finite unfoldings
Not there:• Pre/post inference, strengthening of IH• Support for counterexamples (see next slide)
19
What’s next: counterexamplesUnprovable contracts (because they’re false or we’re incomplete) Paradox Equinox Z3 Vampire E-proverAnyMorphism.big_sat_app_any_morphism_fail_step P:---- X:---- Z:---- V:---- E:----Loop.sat_id_loop_pred P:0.00 X:0.01 Z:0.01 V:---- E:0.01Loop.sat_id_recursive_true P:---- X:---- Z:---- V:---- E:0.01PredLog.sat_concatMap_cf_missing_step P:---- X:---- Z:---- V:---- E:----PredLog.sat_concatMap_retains_missing_step P:---- X:---- Z:---- V:---- E:----PredLog.sat_flattenAnd_cf_missing_step P:---- X:---- Z:---- V:---- E:----PredLog.sat_flattenAnd_retains_missing_step P:---- X:---- Z:---- V:---- E:----... Recursion.sat_qfac_cf_broken_step P:---- X:---- Z:---- V:---- E:----Recursion.sat_rev_cf_broken_step P:---- X:---- Z:---- V:---- E:----Risers.big_sat_risersBy_nonEmpty_broken2_step P:---- X:---- Z:---- V:---- E:----Risers.big_sat_risersBy_nonEmpty_broken_step P:---- X:---- Z:---- V:---- E:----Risers.sat_risers_broken2_step P:---- X:---- Z:---- V:---- E:----Risers.sat_risers_broken3_step P:---- X:---- Z:---- V:---- E:----Risers.sat_risers_broken_step P:---- X:---- Z:---- V:---- E:----Risers.sat_risers_missing_le_step P:---- X:---- Z:---- V:---- E:----Shrink.big_sat_shrink_lazy_step P:---- X:---- Z:---- V:---- E:----
Timeouts …
We now know why, and how to address this:
stay tuned
20
What’s next: usability
Proving is reasonably fast, now explore:• Automatic strengthening of induction hypotheses• Pretty printing models as counterexamples• More induction principles• Testing in larger scale• Interfacing with theorem provers for manual proofs?
Lots of man-hours needed, come help please!
Related work Liquid Types [Jhala et al]• Predicate abstraction• Inference• Quantifiers driven by type system
ESC/Haskell [Xu et al]• Contracts are programs• Symbolic
execution/inliningZeno [Sonnex et al]• Automated equality proofs• Clever heuristics• Strict semantics
Catch [Mitchell]• Pattern match errors• Via dataflow analysis
Dafny & Boogie [Leino et al], ACL2
Leon [Suter et al]• Specialized decision
procedure for FP• Good for first-order
F7/F* [Swamy et al]Hoare logic for FP [Regis-Gianas & Pottier]• HO logics• CBV *really* helps
HO model checking, MoChi [Kobayashi et al]• Specialized decision procedures• Lots of techniques stacked• Good for inference, good for
counterexamples
Symbolic execution-based [Tobin-Hochstadt and Van Horn][Xu]• Abstraction, lots of “smaller” queries to
theorem prover
HOLCF-based verification [Huffman]• Reasoning in a very rich logic that contains
formalization of a domain theory• More sophisticated axiomatization, ability to
reason about parametricity and monad laws
22
What we did and what I learnt • We’ve given a semantic basis for the verification of
Haskell programs• We demonstrated that it is implementable
We can verify FP in a simple and robust way:• For this particular case a simple solution seems to do the job.• It appears affordable to use a very precise abstraction of your
program and trust your 2013 theorem proving technology
Thank you for your attention!
Top Related