1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
|
module ContractProver
( addPreConditions, verifyPostConditions, verifyPreConditions
) where
import Control.Monad ( unless )
import Contract.Names
import Control.Monad.IO.Class ( liftIO )
import Control.Monad.Trans.Class ( lift )
import Control.Monad.Trans.State ( evalStateT, gets, put )
import Data.List ( elemIndex, find, init, intercalate
, maximum, minimum )
import Data.Maybe ( isJust )
import FlatCurry.Annotated.Goodies
import FlatCurry.Annotated.Types
import FlatCurry.Show ( showCurryType )
import FlatCurry.Types ( showQName )
import Language.SMTLIB.Goodies
import qualified Language.SMTLIB.Types as SMT
import CheckSMT
import Common
import Curry2SMT
import FlatCurry.Typed.Build
import FlatCurry.Typed.Read
import FlatCurry.Typed.Simplify
import FlatCurry.Typed.Types
import ToolOptions
import TransState
import VerifierState
verifyPostConditions :: TAProg -> VStateM TAProg
verifyPostConditions prog = do
postconds <- gets $ postConds . trInfo
let fdecls = progFuncs prog
newfuns <- provePostConds postconds fdecls
return $ updProgFuncs (const newfuns) prog
where
provePostConds [] fdecls = return fdecls
provePostConds (pof : pofs) fdecls =
provePostCondition pof fdecls >>= provePostConds pofs
provePostCondition :: TAFuncDecl -> [TAFuncDecl] -> VStateM [TAFuncDecl]
provePostCondition postfun allfuns = do
maybe (do liftIO $ putStrLn $ "Postcondition: " ++ pcname ++ "\n" ++
"Operation of this postcondition not found!"
return allfuns)
(\checkfun -> evalStateT (provePC (simpFuncDecl checkfun))
emptyTransState)
(find (\fd -> toPostCondName (snd (funcName fd)) ==
decodeContractName pcname)
allfuns)
where
pcname = snd (funcName postfun)
provePC :: TAFuncDecl -> TransStateM [TAFuncDecl]
provePC checkfun = do
let (postmn,postfn) = funcName postfun
mainfunc = snd (funcName checkfun)
orgqn = (postmn, reverse (drop 5 (reverse postfn)))
farity = funcArity checkfun
ftype = funcType checkfun
targsr = zip [1..] (argTypes ftype ++ [resultType ftype])
bodyformula <- extractPostConditionProofObligation [1 .. farity]
(farity+1) (funcName checkfun) (funcRule checkfun)
precondformula <- preCondExpOf orgqn (init targsr)
postcondformula <- applyFunc postfun targsr >>= pred2SMT
let title = "verify postcondition of '" ++ mainfunc ++ "'..."
lift $ printWhenIntermediate $ "Trying to " ++ title
mbsmt <- checkPostCon ("SMT script to " ++ title)
(tand [precondformula, bodyformula])
true postcondformula
maybe (lift $ do
printWhenStatus $ mainfunc ++ ": POSTCOND CHECK ADDED"
addPostCondToStats mainfunc False
return $ map (addPostConditionTo (funcName postfun)) allfuns)
(\proof -> lift $ do
printWhenStatus $ mainfunc ++ ": POSTCONDITION VERIFIED"
whenOption optStoreProof $ liftIO $
writeFile ("PROOF_" ++ showQNameNoDots orgqn ++ "_" ++
"SatisfiesPostCondition.smt") proof
addPostCondToStats mainfunc True
return allfuns)
mbsmt
addPostConditionTo :: QName -> TAFuncDecl -> TAFuncDecl
addPostConditionTo pfname fdecl = let fn = funcName fdecl in
if toPostCondQName fn == pfname
then updFuncBody (const (addPostConditionCheck fn (funcRule fdecl))) fdecl
else fdecl
addPostConditionCheck :: QName -> TARule -> TAExpr
addPostConditionCheck _ (AExternal _ _) =
error $ "Trying to add postcondition to external function!"
addPostConditionCheck qf@(mn,fn) (ARule ty lhs rhs) =
AComb ty FuncCall
((mn, "checkPostCond_" ++ type2ID tt ++ "_" ++ type2ID (annExpr rhs)),
FuncType ty (FuncType (FuncType ty boolType)
(FuncType stringType (FuncType tt ty))))
[ rhs
, AComb boolType (FuncPartCall 1) (toPostCondQName qf, ty) args
, string2TAFCY fn
, tupleExpr args
]
where
args = map (\ (i,t) -> AVar t i) lhs
tt = tupleType (map annExpr args)
extractPostConditionProofObligation :: [Int] -> Int -> QName -> TARule
-> TransStateM SMT.Term
extractPostConditionProofObligation _ _ _ (AExternal _ s) =
return $ tcomb ("External: " ++ s) []
extractPostConditionProofObligation args resvar fname
(ARule ty orgargs orgexp) = do
let exp = rnmAllVars renameRuleVar orgexp
rtype = resType (length orgargs) (stripForall ty)
put $ makeTransState
(maximum (resvar : allVars exp) + 1)
((resvar, rtype, Just (fname, 0, 1))
: map (\(i,x,y) -> (x, y, Just (fname, i, 1)))
(zip3 [1..] args $ map snd orgargs))
binding2SMT True (resvar,exp)
where
maxArgResult = maximum (resvar : args)
renameRuleVar r = maybe (r + maxArgResult + 1)
(args!!)
(elemIndex r (map fst orgargs))
resType n te =
if n==0
then te
else case te of
FuncType _ rt -> resType (n-1) rt
_ -> error $ "Internal errror: resType: " ++ show te
verifyPreConditions :: TAProg -> VStateM TAProg
verifyPreConditions prog = do
newfuns <- mapM provePreCondition $ progFuncs prog
return (updProgFuncs (const newfuns) prog)
provePreCondition :: TAFuncDecl -> VStateM TAFuncDecl
provePreCondition fdecl = do
printWhenIntermediate $
"Operation to be checked: " ++ snd (funcName fdecl)
newrule <- optPreConditionInRule (funcName fdecl) (funcRule fdecl)
return (updFuncRule (const newrule) fdecl)
optPreConditionInRule :: QName -> TARule -> VStateM TARule
optPreConditionInRule _ rl@(AExternal _ _) = return rl
optPreConditionInRule qn@(_,fn) (ARule rty rargs rhs) = do
let targs = zip [1..] (map snd rargs)
st = makeTransState
(maximum (0 : map fst rargs ++ allVars rhs) + 1)
(map (\(i,(x,y)) -> (x, y, Just (qn, i, 0))) (zip [1..] rargs))
(flip evalStateT) st $ do
precondformula <- preCondExpOf qn targs
setAssertion precondformula
newrhs <- optPreCondInExp rhs
return $ ARule rty rargs newrhs
where
optPreCondInExp exp = case exp of
AComb ty ct (qf,tys) args -> do
precond <- getAssertion
nargs <- mapM optPreCondInExp args
preconds <- lift $ gets $ preConds . trInfo
if toPreCondQName qf `elem` map funcName preconds
then do
lift $ printWhenIntermediate $ "Checking call to " ++ snd qf
(bs,_) <- normalizeArgs nargs
setNameOfVars qf $ map fst bs
bindexps <- mapM (binding2SMT True) bs
precondcall <- preCondExpOf qf
(zip (map fst bs) (map annExpr args))
let title = "SMT script to verify precondition of '" ++ snd qf ++
"' in function '" ++ fn ++ "'"
valid <- checkPreCon title precond (tand bindexps) precondcall
fn (map fst rargs)
if valid == Just True
then lift $ do
printWhenStatus $
fn ++ ": PRECONDITION OF '" ++ snd qf ++ "': VERIFIED"
addPreCondToStats (snd qf ++ "(" ++ fn ++ ")") True
return $ AComb ty ct (toNoCheckQName qf, tys) nargs
else lift $ do
printWhenStatus $
fn ++ ": PRECOND CHECK ADDED TO '" ++ snd qf ++ "'"
addPreCondToStats (snd qf ++ "("++ fn ++ ")") False
return $ AComb ty ct (qf,tys) nargs
else return $ AComb ty ct (qf,tys) nargs
ACase ty ct e brs -> do
ne <- optPreCondInExp e
freshvar <- getFreshVar
be <- binding2SMT True (freshvar,ne)
addToAssertion be
addVarTypes [ (freshvar, annExpr ne) ]
nbrs <- mapM (optPreCondInBranch freshvar) brs
return $ ACase ty ct ne nbrs
AOr ty e1 e2 -> do
ne1 <- optPreCondInExp e1
ne2 <- optPreCondInExp e2
return $ AOr ty ne1 ne2
ALet ty bs e -> do
nes <- mapM optPreCondInExp (map snd bs)
ne <- optPreCondInExp e
return $ ALet ty (zip (map fst bs) nes) ne
AFree ty fvs e -> do
ne <- optPreCondInExp e
return $ AFree ty fvs ne
ATyped ty e et -> do
ne <- optPreCondInExp e
return $ ATyped ty ne et
_ -> return exp
optPreCondInBranch dvar branch = do
ABranch p e <- renamePatternVars branch
addToAssertion (tvar dvar =% pat2SMT p)
ne <- optPreCondInExp e
return (ABranch p ne)
renamePatternVars :: TABranchExpr -> TransStateM TABranchExpr
renamePatternVars (ABranch p e) =
if isConsPattern p
then do
fv <- getFreshVarIndex
let args = map fst (patArgs p)
minarg = minimum (0 : args)
maxarg = maximum (0 : args)
rnm i = if i `elem` args then i - minarg + fv else i
nargs = map (\ (v,t) -> (rnm v,t)) (patArgs p)
setFreshVarIndex (fv + maxarg - minarg + 1)
addVarTypes nargs
return $ ABranch (updPatArgs (map (\ (v,t) -> (rnm v,t))) p)
(rnmAllVars rnm e)
else return $ ABranch p e
addPreConditions :: TAProg -> VStateM TAProg
addPreConditions prog = do
newfuns <- mapM addPreCondition (progFuncs prog)
return $ updProgFuncs (const (concat newfuns)) prog
where
addPreCondition fdecl@(AFunc qf ar vis fty rule) = do
preconds <- gets $ preConds . trInfo
return $
if toPreCondQName qf `elem` map funcName preconds
then let newrule = checkPreCondRule qf rule
in [updFuncRule (const newrule) fdecl,
AFunc (toNoCheckQName qf) ar vis fty rule]
else [fdecl]
checkPreCondRule :: QName -> TARule -> TARule
checkPreCondRule qn (ARule rty rargs _) =
ARule rty rargs (addPreConditionCheck rty FuncCall qn rty
(map (\ (v,t) -> AVar t v) rargs))
checkPreCondRule qn (AExternal _ _) = error $
"addPreConditions: cannot add precondition to external operation '" ++
snd qn ++ "'!"
addPreConditionCheck :: TypeExpr -> CombType -> QName -> TypeExpr
-> [TAExpr] -> TAExpr
addPreConditionCheck ty ct qf@(mn,fn) tys args =
AComb ty FuncCall
((mn, "checkPreCond_" ++ type2ID tt),
FuncType ty (FuncType boolType (FuncType stringType (FuncType tt ty))))
[ AComb ty ct (toNoCheckQName qf,tys) args
, AComb boolType ct (toPreCondQName qf, pctype) args
, string2TAFCY fn
, tupleExpr args
]
where
argtypes = map annExpr args
tt = tupleType argtypes
pctype = foldr FuncType boolType argtypes
type2ID :: TypeExpr -> String
type2ID te = case te of
TCons (mn,tc) tes | mn == "Prelude" && null tes
-> intercalate "_" (tc2ID tc : map type2ID tes)
_ -> "Any"
where
tc2ID tc | tc == "[]" = "List"
| tc == "()" = "Unit"
| otherwise = tc
toNoCheckQName :: (String,String) -> (String,String)
toNoCheckQName (mn,fn) = (mn, fn ++ "'NOCHECK")
showQNameNoDots :: QName -> String
showQNameNoDots = map (\c -> if c=='.' then '_' else c) . showQName
|