haskell to logic through denotational semantics

22
HASKELL TO LOGIC THROUGH DENOTATIONAL SEMANTICS Dimitrios Vytiniotis, Koen Claessen, Simon Peyton Jones, Dan Rosén POPL 2013, January 2013 1

Upload: osborn

Post on 24-Feb-2016

37 views

Category:

Documents


0 download

DESCRIPTION

Haskell to logic through denotational semantics . Dimitrios Vytiniotis, Koen Claessen, Simon Peyton Jones, Dan Rosén POPL 2013, January 2013. Real programs contain assertions. dimitris@artemis :~/GHC/ ghc -head/ ghc /compiler/ typecheck $ grep - i ASSERT ./* hs - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Haskell to logic  through denotational semantics

1

HASKELL TO LOGIC THROUGH DENOTATIONAL SEMANTICS

Dimitrios Vytiniotis, Koen Claessen,Simon Peyton Jones, Dan Rosén

POPL 2013, January 2013

Page 2: Haskell to logic  through denotational semantics

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)

Page 3: Haskell to logic  through denotational semantics

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

Page 4: Haskell to logic  through denotational semantics

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

Page 5: Haskell to logic  through denotational semantics

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”

Page 6: Haskell to logic  through denotational semantics

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

Page 7: Haskell to logic  through denotational semantics

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

Page 8: Haskell to logic  through denotational semantics

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

Page 9: Haskell to logic  through denotational semantics

9

Function definitions become FOL axioms

Theory

Theorem:

head (Cons x xs) = xhead _ = error

NB: A Good Thing!

Page 10: Haskell to logic  through denotational semantics

10

Axiomatize (some) true facts about

Theorem:

Theory

data List a = Cons a (List a)| Nil

Page 11: Haskell to logic  through denotational semantics

11

Higher-order functionshead (Cons x xs) = xhead _ = error

∀ 𝑥 𝑥𝑠 .𝑎𝑝𝑝(h𝑒𝑎𝑑𝑝𝑡𝑟 , 𝑥)=h𝑒𝑎𝑑(𝑥 )

double f x = f (f x)

∀ 𝑥 𝑦 .𝑑𝑜𝑢𝑏𝑙𝑒 (𝑥 , 𝑦 )=𝑎𝑝𝑝 (𝑥 ,𝑎𝑝𝑝 (𝑥 , 𝑦 ))

∀ 𝑥 𝑦 .𝑎𝑝𝑝 (𝑎𝑝𝑝 (𝑑𝑜𝑢𝑏𝑙 𝑒𝑝𝑡𝑟 ,𝑥 ) , 𝑦 )=𝑑𝑜𝑢𝑏𝑙𝑒 (𝑥 , 𝑦 )

Interpreted as the apply(.,.) combinator in

Page 12: Haskell to logic  through denotational semantics

12

Refinements denotationally and logically

≙ ≙ Logically

Denotationally

Page 13: Haskell to logic  through denotational semantics

13

Soundness via denotational semantics

• Assume that:

• Then:

• By previous theorems:

• … hence:

• … which is equivalent to:

Page 14: Haskell to logic  through denotational semantics

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

Page 15: Haskell to logic  through denotational semantics

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!

Page 16: Haskell to logic  through denotational semantics

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!

Page 17: Haskell to logic  through denotational semantics

17

Happily implemented on top of GHC API

Z3 rocks for provable properties!

Disclaimer:• 40-80 FOL axioms/problem• Use of fixpoint induction

Page 18: Haskell to logic  through denotational semantics

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)

Page 19: Haskell to logic  through denotational semantics

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

Page 20: Haskell to logic  through denotational semantics

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!

Page 21: Haskell to logic  through denotational semantics

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

Page 22: Haskell to logic  through denotational semantics

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!