{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Data.SBV.Tools.WeakestPreconditions (
Program(..), Stmt(..), assert, stable
, Invariant, Measure, Stable
, VC(..)
, ProofResult(..)
, WPConfig(..), defaultWPCfg
, wpProve, wpProveWith
, traceExecution, Status(..)
) where
import Data.List (intercalate)
import Data.Maybe (fromJust, isJust, isNothing)
import Control.Monad (when)
import Data.SBV
import Data.SBV.Control
data Program st = Program { forall st. Program st -> Symbolic ()
setup :: Symbolic ()
, forall st. Program st -> st -> SBool
precondition :: st -> SBool
, forall st. Program st -> Stmt st
program :: Stmt st
, forall st. Program st -> st -> SBool
postcondition :: st -> SBool
, forall st. Program st -> Stable st
stability :: Stable st
}
type Stable st = [st -> st -> (String, SBool)]
type Invariant st = st -> SBool
type Measure st = st -> [SInteger]
data Stmt st = Skip
| Abort String
| Assign (st -> st)
| If (st -> SBool) (Stmt st) (Stmt st)
| While String (Invariant st) (Maybe (Measure st)) (st -> SBool) (Stmt st)
| Seq [Stmt st]
assert :: String -> (st -> SBool) -> Stmt st
assert :: forall st. String -> (st -> SBool) -> Stmt st
assert String
nm st -> SBool
cond = forall st. (st -> SBool) -> Stmt st -> Stmt st -> Stmt st
If st -> SBool
cond forall st. Stmt st
Skip (forall st. String -> Stmt st
Abort String
nm)
stable :: EqSymbolic a => String -> (st -> a) -> st -> st -> (String, SBool)
stable :: forall a st.
EqSymbolic a =>
String -> (st -> a) -> st -> st -> (String, SBool)
stable String
nm st -> a
f st
before st
after = (String
nm, st -> a
f st
before forall a. EqSymbolic a => a -> a -> SBool
.=== st -> a
f st
after)
isTotal :: Stmt st -> Bool
isTotal :: forall st. Stmt st -> Bool
isTotal Stmt st
Skip = Bool
True
isTotal (Abort String
_) = Bool
True
isTotal (Assign st -> st
_) = Bool
True
isTotal (If st -> SBool
_ Stmt st
tb Stmt st
fb) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all forall st. Stmt st -> Bool
isTotal [Stmt st
tb, Stmt st
fb]
isTotal (While String
_ st -> SBool
_ Maybe (Measure st)
msr st -> SBool
_ Stmt st
s) = forall a. Maybe a -> Bool
isJust Maybe (Measure st)
msr Bool -> Bool -> Bool
&& forall st. Stmt st -> Bool
isTotal Stmt st
s
isTotal (Seq [Stmt st]
ss) = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all forall st. Stmt st -> Bool
isTotal [Stmt st]
ss
data VC st m = BadPrecondition st
| BadPostcondition st st
| Unstable String st st
| AbortReachable String st st
| InvariantPre String st
| InvariantMaintain String st st
| MeasureBound String (st, [m])
| MeasureDecrease String (st, [m]) (st, [m])
dispVC :: String -> [(String, String)] -> String
dispVC :: String -> [(String, String)] -> String
dispVC String
tag [(String, String)]
flds = forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" forall a b. (a -> b) -> a -> b
$ String -> String
col String
tag forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map (String, String) -> String
showField [(String, String)]
flds
where col :: String -> String
col String
"" = String
""
col String
t = String
t forall a. [a] -> [a] -> [a]
++ String
":"
showField :: (String, String) -> String
showField (String
t, String
c) = forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall {a}. (Eq a, Num a) => a -> String -> String
mark [(Int
1::Int)..] (String -> [String]
lines String
c)
where tt :: String
tt = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
t then String
"" else String -> String
col String
t forall a. [a] -> [a] -> [a]
++ String
" "
sp :: String
sp = forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length String
tt) Char
' '
mark :: a -> String -> String
mark a
i String
s = String
" " forall a. [a] -> [a] -> [a]
++ (if a
i forall a. Eq a => a -> a -> Bool
== a
1 then String
tt else String
sp) forall a. [a] -> [a] -> [a]
++ String
s
showMeasure :: Show a => [a] -> String
showMeasure :: forall a. Show a => [a] -> String
showMeasure [a
x] = forall a. Show a => a -> String
show a
x
showMeasure [a]
xs = forall a. Show a => a -> String
show [a]
xs
instance (Show st, Show m) => Show (VC st m) where
show :: VC st m -> String
show (BadPrecondition st
s) = String -> [(String, String)] -> String
dispVC String
"Precondition fails"
[(String
"", forall a. Show a => a -> String
show st
s)]
show (BadPostcondition st
s1 st
s2) = String -> [(String, String)] -> String
dispVC String
"Postcondition fails"
[ (String
"Start", forall a. Show a => a -> String
show st
s1)
, (String
"End ", forall a. Show a => a -> String
show st
s2)
]
show (Unstable String
m st
s1 st
s2) = String -> [(String, String)] -> String
dispVC (String
"Stability fails for " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
m)
[ (String
"Before", forall a. Show a => a -> String
show st
s1)
, (String
"After ", forall a. Show a => a -> String
show st
s2)
]
show (AbortReachable String
nm st
s1 st
s2) = String -> [(String, String)] -> String
dispVC (String
"Abort " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm forall a. [a] -> [a] -> [a]
++ String
" condition is satisfiable")
[ (String
"Before", forall a. Show a => a -> String
show st
s1)
, (String
"After ", forall a. Show a => a -> String
show st
s2)
]
show (InvariantPre String
nm st
s) = String -> [(String, String)] -> String
dispVC (String
"Invariant for loop " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm forall a. [a] -> [a] -> [a]
++ String
" fails upon entry")
[(String
"", forall a. Show a => a -> String
show st
s)]
show (InvariantMaintain String
nm st
s1 st
s2) = String -> [(String, String)] -> String
dispVC (String
"Invariant for loop " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm forall a. [a] -> [a] -> [a]
++ String
" is not maintained by the body")
[ (String
"Before", forall a. Show a => a -> String
show st
s1)
, (String
"After ", forall a. Show a => a -> String
show st
s2)
]
show (MeasureBound String
nm (st
s, [m]
m)) = String -> [(String, String)] -> String
dispVC (String
"Measure for loop " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm forall a. [a] -> [a] -> [a]
++ String
" is negative")
[ (String
"State ", forall a. Show a => a -> String
show st
s)
, (String
"Measure", forall a. Show a => [a] -> String
showMeasure [m]
m )
]
show (MeasureDecrease String
nm (st
s1, [m]
m1) (st
s2, [m]
m2)) = String -> [(String, String)] -> String
dispVC (String
"Measure for loop " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm forall a. [a] -> [a] -> [a]
++ String
" does not decrease")
[ (String
"Before ", forall a. Show a => a -> String
show st
s1)
, (String
"Measure", forall a. Show a => [a] -> String
showMeasure [m]
m1)
, (String
"After ", forall a. Show a => a -> String
show st
s2)
, (String
"Measure", forall a. Show a => [a] -> String
showMeasure [m]
m2)
]
data ProofResult res = Proven Bool
| Indeterminate String
| Failed [VC res Integer]
instance Show res => Show (ProofResult res) where
show :: ProofResult res -> String
show (Proven Bool
True) = String
"Q.E.D."
show (Proven Bool
False) = String
"Q.E.D. [Partial: not all termination measures were provided.]"
show (Indeterminate String
s) = String
"Indeterminate: " forall a. [a] -> [a] -> [a]
++ String
s
show (Failed [VC res Integer]
vcs) = forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" forall a b. (a -> b) -> a -> b
$ (String
"Proof failure. Failing verification condition" forall a. [a] -> [a] -> [a]
++ if forall (t :: * -> *) a. Foldable t => t a -> Int
length [VC res Integer]
vcs forall a. Ord a => a -> a -> Bool
> Int
1 then String
"s:" else String
":")
forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map (\VC res Integer
vc -> forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" [String
" " forall a. [a] -> [a] -> [a]
++ String
l | String
l <- String -> [String]
lines (forall a. Show a => a -> String
show VC res Integer
vc)]) [VC res Integer]
vcs
wpProveWith :: forall st res. (Show res, Mergeable st, Queriable IO st res) => WPConfig -> Program st -> IO (ProofResult res)
wpProveWith :: forall st res.
(Show res, Mergeable st, Queriable IO st res) =>
WPConfig -> Program st -> IO (ProofResult res)
wpProveWith cfg :: WPConfig
cfg@WPConfig{Bool
wpVerbose :: WPConfig -> Bool
wpVerbose :: Bool
wpVerbose} Program{Symbolic ()
setup :: Symbolic ()
setup :: forall st. Program st -> Symbolic ()
setup, st -> SBool
precondition :: st -> SBool
precondition :: forall st. Program st -> st -> SBool
precondition, Stmt st
program :: Stmt st
program :: forall st. Program st -> Stmt st
program, st -> SBool
postcondition :: st -> SBool
postcondition :: forall st. Program st -> st -> SBool
postcondition, Stable st
stability :: Stable st
stability :: forall st. Program st -> Stable st
stability} =
forall a. SMTConfig -> Symbolic a -> IO a
runSMTWith (WPConfig -> SMTConfig
wpSolver WPConfig
cfg) forall a b. (a -> b) -> a -> b
$ do Symbolic ()
setup
forall a. Query a -> Symbolic a
query QueryT IO (ProofResult res)
q
where q :: QueryT IO (ProofResult res)
q = do st
start <- forall (m :: * -> *) a b. Queriable m a b => QueryT m a
create
st -> [(SBool, VC st SInteger)]
weakestPrecondition <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start Stmt st
program (\st
st -> [(st -> SBool
postcondition st
st, forall st m. st -> st -> VC st m
BadPostcondition st
start st
st)])
let vcs :: [(SBool, VC st SInteger)]
vcs = st -> [(SBool, VC st SInteger)]
weakestPrecondition st
start
forall (m :: * -> *). SolverContext m => SBool -> m ()
constrain forall a b. (a -> b) -> a -> b
$ SBool -> SBool
sNot forall a b. (a -> b) -> a -> b
$ st -> SBool
precondition st
start SBool -> SBool -> SBool
.=> [SBool] -> SBool
sAnd (forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst [(SBool, VC st SInteger)]
vcs)
CheckSatResult
cs <- Query CheckSatResult
checkSat
case CheckSatResult
cs of
CheckSatResult
Unk -> forall res. String -> ProofResult res
Indeterminate forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Query SMTReasonUnknown
getUnknownReason
CheckSatResult
Unsat -> do let t :: Bool
t = forall st. Stmt st -> Bool
isTotal Stmt st
program
if Bool
t then String -> QueryT IO ()
msg String
"Total correctness is established."
else String -> QueryT IO ()
msg String
"Partial correctness is established."
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall res. Bool -> ProofResult res
Proven Bool
t
DSat{} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall res. String -> ProofResult res
Indeterminate String
"Unsupported: Solver returned a delta-satisfiable answer."
CheckSatResult
Sat -> do let checkVC :: (SBool, VC st SInteger) -> Query [VC res Integer]
checkVC :: (SBool, VC st SInteger) -> Query [VC res Integer]
checkVC (SBool
cond, VC st SInteger
vc) = do Bool
c <- forall a. SymVal a => SBV a -> Query a
getValue SBool
cond
if Bool
c
then forall (m :: * -> *) a. Monad m => a -> m a
return []
else do VC res Integer
vc' <- case VC st SInteger
vc of
BadPrecondition st
s -> forall st m. st -> VC st m
BadPrecondition forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s
BadPostcondition st
s1 st
s2 -> forall st m. st -> st -> VC st m
BadPostcondition forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s2
Unstable String
l st
s1 st
s2 -> forall st m. String -> st -> st -> VC st m
Unstable String
l forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s2
AbortReachable String
l st
s1 st
s2 -> forall st m. String -> st -> st -> VC st m
AbortReachable String
l forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s2
InvariantPre String
l st
s -> forall st m. String -> st -> VC st m
InvariantPre String
l forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s
InvariantMaintain String
l st
s1 st
s2 -> forall st m. String -> st -> st -> VC st m
InvariantMaintain String
l forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s2
MeasureBound String
l (st
s, [SInteger]
m) -> do res
r <- forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s
[Integer]
v <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. SymVal a => SBV a -> Query a
getValue [SInteger]
m
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall st m. String -> (st, [m]) -> VC st m
MeasureBound String
l (res
r, [Integer]
v)
MeasureDecrease String
l (st
s1, [SInteger]
i1) (st
s2, [SInteger]
i2) -> do res
r1 <- forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s1
[Integer]
v1 <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. SymVal a => SBV a -> Query a
getValue [SInteger]
i1
res
r2 <- forall (m :: * -> *) a b. Queriable m a b => a -> QueryT m b
project st
s2
[Integer]
v2 <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. SymVal a => SBV a -> Query a
getValue [SInteger]
i2
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall st m. String -> (st, [m]) -> (st, [m]) -> VC st m
MeasureDecrease String
l (res
r1, [Integer]
v1) (res
r2, [Integer]
v2)
forall (m :: * -> *) a. Monad m => a -> m a
return [VC res Integer
vc']
[VC res Integer]
badVCs <- forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (SBool, VC st SInteger) -> Query [VC res Integer]
checkVC [(SBool, VC st SInteger)]
vcs
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (forall (t :: * -> *) a. Foldable t => t a -> Bool
null [VC res Integer]
badVCs) forall a b. (a -> b) -> a -> b
$ forall a. HasCallStack => String -> a
error String
"Data.SBV.proveWP: Impossible happened. Proof failed, but no failing VC found!"
let plu :: String -> [a] -> String
plu String
w (a
_:a
_:[a]
_) = String
w forall a. [a] -> [a] -> [a]
++ String
"s"
plu String
w [a]
_ = String
w
m :: String
m = String
"Following proof " forall a. [a] -> [a] -> [a]
++ forall {a}. String -> [a] -> String
plu String
"obligation" [VC res Integer]
badVCs forall a. [a] -> [a] -> [a]
++ String
" failed:"
String -> QueryT IO ()
msg String
m
String -> QueryT IO ()
msg forall a b. (a -> b) -> a -> b
$ forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length String
m) Char
'='
let disp :: a -> QueryT IO ()
disp a
c = forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> QueryT IO ()
msg [String
" " forall a. [a] -> [a] -> [a]
++ String
l | String
l <- String -> [String]
lines (forall a. Show a => a -> String
show a
c)]
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ forall {a}. Show a => a -> QueryT IO ()
disp [VC res Integer]
badVCs
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall res. [VC res Integer] -> ProofResult res
Failed [VC res Integer]
badVCs
msg :: String -> QueryT IO ()
msg = forall a. IO a -> Query a
io forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
wpVerbose forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO ()
putStrLn
wp :: st -> Stmt st -> (st -> [(SBool, VC st SInteger)]) -> Query (st -> [(SBool, VC st SInteger)])
wp :: st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
_ Stmt st
Skip st -> [(SBool, VC st SInteger)]
post = forall (m :: * -> *) a. Monad m => a -> m a
return st -> [(SBool, VC st SInteger)]
post
wp st
start (Abort String
nm) st -> [(SBool, VC st SInteger)]
_ = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \st
st -> [(SBool
sFalse, forall st m. String -> st -> st -> VC st m
AbortReachable String
nm st
start st
st)]
wp st
_ (Assign st -> st
f) st -> [(SBool, VC st SInteger)]
post = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \st
st -> let st' :: st
st' = st -> st
f st
st
vcs :: [(SBool, VC st m)]
vcs = forall a b. (a -> b) -> [a] -> [b]
map (\st -> st -> (String, SBool)
s -> let (String
nm, SBool
b) = st -> st -> (String, SBool)
s st
st st
st' in (SBool
b, forall st m. String -> st -> st -> VC st m
Unstable String
nm st
st st
st')) Stable st
stability
in forall {m}. [(SBool, VC st m)]
vcs forall a. [a] -> [a] -> [a]
++ st -> [(SBool, VC st SInteger)]
post st
st'
wp st
start (If st -> SBool
c Stmt st
tb Stmt st
fb) st -> [(SBool, VC st SInteger)]
post = do st -> [(SBool, VC st SInteger)]
tWP <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start Stmt st
tb st -> [(SBool, VC st SInteger)]
post
st -> [(SBool, VC st SInteger)]
fWP <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start Stmt st
fb st -> [(SBool, VC st SInteger)]
post
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \st
st -> let cond :: SBool
cond = st -> SBool
c st
st
in [( SBool
cond SBool -> SBool -> SBool
.=> SBool
b, VC st SInteger
v) | (SBool
b, VC st SInteger
v) <- st -> [(SBool, VC st SInteger)]
tWP st
st]
forall a. [a] -> [a] -> [a]
++ [(SBool -> SBool
sNot SBool
cond SBool -> SBool -> SBool
.=> SBool
b, VC st SInteger
v) | (SBool
b, VC st SInteger
v) <- st -> [(SBool, VC st SInteger)]
fWP st
st]
wp st
_ (Seq []) st -> [(SBool, VC st SInteger)]
post = forall (m :: * -> *) a. Monad m => a -> m a
return st -> [(SBool, VC st SInteger)]
post
wp st
start (Seq (Stmt st
s:[Stmt st]
ss)) st -> [(SBool, VC st SInteger)]
post = st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start Stmt st
s forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start (forall st. [Stmt st] -> Stmt st
Seq [Stmt st]
ss) st -> [(SBool, VC st SInteger)]
post
wp st
start (While String
nm st -> SBool
inv Maybe (Measure st)
mm st -> SBool
cond Stmt st
body) st -> [(SBool, VC st SInteger)]
post = do
st
st' <- forall (m :: * -> *) a b. Queriable m a b => QueryT m a
create
let noMeasure :: Bool
noMeasure = forall a. Maybe a -> Bool
isNothing Maybe (Measure st)
mm
m :: Measure st
m = forall a. HasCallStack => Maybe a -> a
fromJust Maybe (Measure st)
mm
curM :: [SInteger]
curM = Measure st
m st
st'
zero :: [SInteger]
zero = forall a b. (a -> b) -> [a] -> [b]
map (forall a b. a -> b -> a
const SInteger
0) [SInteger]
curM
iterates :: SBool
iterates = st -> SBool
inv st
st' SBool -> SBool -> SBool
.&& st -> SBool
cond st
st'
terminates :: SBool
terminates = st -> SBool
inv st
st' SBool -> SBool -> SBool
.&& SBool -> SBool
sNot (st -> SBool
cond st
st')
st -> [(SBool, VC st SInteger)]
invHoldsPrior <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
start forall st. Stmt st
Skip (\st
st -> [(st -> SBool
inv st
st, forall st m. String -> st -> VC st m
InvariantPre String
nm st
st)])
st -> [(SBool, VC st SInteger)]
invMaintained <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
st' Stmt st
body (\st
st -> [(SBool
iterates SBool -> SBool -> SBool
.=> st -> SBool
inv st
st, forall st m. String -> st -> st -> VC st m
InvariantMaintain String
nm st
st' st
st)])
st -> [(SBool, VC st SInteger)]
invEstablish <- st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
st' Stmt st
body (forall a b. a -> b -> a
const [(SBool
terminates SBool -> SBool -> SBool
.=> SBool
b, VC st SInteger
v) | (SBool
b, VC st SInteger
v) <- st -> [(SBool, VC st SInteger)]
post st
st'])
st -> [(SBool, VC st SInteger)]
measureNonNegative <- if Bool
noMeasure
then forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. a -> b -> a
const [])
else st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
st' forall st. Stmt st
Skip (forall a b. a -> b -> a
const [(SBool
iterates SBool -> SBool -> SBool
.=> [SInteger]
curM forall a. OrdSymbolic a => a -> a -> SBool
.>= [SInteger]
zero, forall st m. String -> (st, [m]) -> VC st m
MeasureBound String
nm (st
st', [SInteger]
curM))])
st -> [(SBool, VC st SInteger)]
measureDecreases <- if Bool
noMeasure
then forall (m :: * -> *) a. Monad m => a -> m a
return (forall a b. a -> b -> a
const [])
else st
-> Stmt st
-> (st -> [(SBool, VC st SInteger)])
-> Query (st -> [(SBool, VC st SInteger)])
wp st
st' Stmt st
body (\st
st -> let prevM :: [SInteger]
prevM = Measure st
m st
st in [(SBool
iterates SBool -> SBool -> SBool
.=> [SInteger]
prevM forall a. OrdSymbolic a => a -> a -> SBool
.< [SInteger]
curM, forall st m. String -> (st, [m]) -> (st, [m]) -> VC st m
MeasureDecrease String
nm (st
st', [SInteger]
curM) (st
st, [SInteger]
prevM))])
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \st
st -> st -> [(SBool, VC st SInteger)]
invHoldsPrior st
st
forall a. [a] -> [a] -> [a]
++ st -> [(SBool, VC st SInteger)]
invMaintained st
st'
forall a. [a] -> [a] -> [a]
++ st -> [(SBool, VC st SInteger)]
invEstablish st
st'
forall a. [a] -> [a] -> [a]
++ st -> [(SBool, VC st SInteger)]
measureNonNegative st
st'
forall a. [a] -> [a] -> [a]
++ st -> [(SBool, VC st SInteger)]
measureDecreases st
st'
wpProve :: (Show res, Mergeable st, Queriable IO st res) => Program st -> IO (ProofResult res)
wpProve :: forall res st.
(Show res, Mergeable st, Queriable IO st res) =>
Program st -> IO (ProofResult res)
wpProve = forall st res.
(Show res, Mergeable st, Queriable IO st res) =>
WPConfig -> Program st -> IO (ProofResult res)
wpProveWith WPConfig
defaultWPCfg
data WPConfig = WPConfig { WPConfig -> SMTConfig
wpSolver :: SMTConfig
, WPConfig -> Bool
wpVerbose :: Bool
}
defaultWPCfg :: WPConfig
defaultWPCfg :: WPConfig
defaultWPCfg = WPConfig { wpSolver :: SMTConfig
wpSolver = SMTConfig
defaultSMTCfg
, wpVerbose :: Bool
wpVerbose = Bool
False
}
data Location = Line Int
| Iteration Int
type Loc = [Location]
data Status st = Good st
| Stuck (VC st Integer)
instance Show st => Show (Status st) where
show :: Status st -> String
show (Good st
st) = String
"Program terminated successfully. Final state:\n" forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" [String
" " forall a. [a] -> [a] -> [a]
++ String
l | String
l <- String -> [String]
lines (forall a. Show a => a -> String
show st
st)]
show (Stuck VC st Integer
vc) = String
"Program is stuck.\n" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show VC st Integer
vc
traceExecution :: forall st. Show st
=> Program st
-> st
-> IO (Status st)
traceExecution :: forall st. Show st => Program st -> st -> IO (Status st)
traceExecution Program{st -> SBool
precondition :: st -> SBool
precondition :: forall st. Program st -> st -> SBool
precondition, Stmt st
program :: Stmt st
program :: forall st. Program st -> Stmt st
program, st -> SBool
postcondition :: st -> SBool
postcondition :: forall st. Program st -> st -> SBool
postcondition, Stable st
stability :: Stable st
stability :: forall st. Program st -> Stable st
stability} st
start = do
Status st
status <- if forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap [] String
"checking precondition" (st -> SBool
precondition st
start)
then Loc -> Stmt st -> Status st -> IO (Status st)
go [Int -> Location
Line Int
1] Stmt st
program forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Loc -> st -> String -> IO (Status st)
step [] st
start String
"*** Precondition holds, starting execution:"
else st -> VC st Integer -> String -> IO (Status st)
giveUp st
start (forall st m. st -> VC st m
BadPrecondition st
start) String
"*** Initial state does not satisfy the precondition:"
case Status st
status of
s :: Status st
s@Stuck{} -> forall (m :: * -> *) a. Monad m => a -> m a
return Status st
s
Good st
end -> if forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap [] String
"checking postcondition" (st -> SBool
postcondition st
end)
then Loc -> st -> String -> IO (Status st)
step [] st
end String
"*** Program successfully terminated, post condition holds of the final state:"
else st -> VC st Integer -> String -> IO (Status st)
giveUp st
end (forall st m. st -> st -> VC st m
BadPostcondition st
start st
end) String
"*** Failed, final state does not satisfy the postcondition:"
where sLoc :: Loc -> String -> String
sLoc :: Loc -> String -> String
sLoc Loc
l String
m
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null Loc
l = String
m
| Bool
True = String
"===> [" forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
"." (forall a b. (a -> b) -> [a] -> [b]
map Location -> String
sh (forall a. [a] -> [a]
reverse Loc
l)) forall a. [a] -> [a] -> [a]
++ String
"] " forall a. [a] -> [a] -> [a]
++ String
m
where sh :: Location -> String
sh (Line Int
i) = forall a. Show a => a -> String
show Int
i
sh (Iteration Int
i) = String
"{" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Int
i forall a. [a] -> [a] -> [a]
++ String
"}"
step :: Loc -> st -> String -> IO (Status st)
step :: Loc -> st -> String -> IO (Status st)
step Loc
l st
st String
m = do String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ Loc -> String -> String
sLoc Loc
l String
m
st -> IO ()
printST st
st
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall st. st -> Status st
Good st
st
stop :: Loc -> VC st Integer -> String -> IO (Status st)
stop :: Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
l VC st Integer
vc String
m = do String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ Loc -> String -> String
sLoc Loc
l String
m
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall st. VC st Integer -> Status st
Stuck VC st Integer
vc
giveUp :: st -> VC st Integer -> String -> IO (Status st)
giveUp :: st -> VC st Integer -> String -> IO (Status st)
giveUp st
st VC st Integer
vc String
m = do Status st
r <- Loc -> VC st Integer -> String -> IO (Status st)
stop [] VC st Integer
vc String
m
st -> IO ()
printST st
st
forall (m :: * -> *) a. Monad m => a -> m a
return Status st
r
dispST :: st -> String
dispST :: st -> String
dispST st
st = forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" [String
" " forall a. [a] -> [a] -> [a]
++ String
l | String
l <- String -> [String]
lines (forall a. Show a => a -> String
show st
st)]
printST :: st -> IO ()
printST :: st -> IO ()
printST = String -> IO ()
putStrLn forall b c a. (b -> c) -> (a -> b) -> a -> c
. st -> String
dispST
unwrap :: SymVal a => Loc -> String -> SBV a -> a
unwrap :: forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
l String
m SBV a
v = case forall a. SymVal a => SBV a -> Maybe a
unliteral SBV a
v of
Just a
c -> a
c
Maybe a
Nothing -> forall a. HasCallStack => String -> a
error forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines [ String
""
, String
"*** Data.SBV.WeakestPreconditions.traceExecution:"
, String
"***"
, String
"*** Unable to extract concrete value:"
, String
"*** " forall a. [a] -> [a] -> [a]
++ Loc -> String -> String
sLoc Loc
l String
m
, String
"***"
, String
"*** Make sure the starting state is fully concrete and"
, String
"*** there are no uninterpreted functions in play!"
]
go :: Loc -> Stmt st -> Status st -> IO (Status st)
go :: Loc -> Stmt st -> Status st -> IO (Status st)
go Loc
_ Stmt st
_ s :: Status st
s@Stuck{} = forall (m :: * -> *) a. Monad m => a -> m a
return Status st
s
go Loc
loc Stmt st
p (Good st
st) = Stmt st -> IO (Status st)
analyze Stmt st
p
where analyze :: Stmt st -> IO (Status st)
analyze Stmt st
Skip = Loc -> st -> String -> IO (Status st)
step Loc
loc st
st String
"Skip"
analyze (Abort String
nm) = Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc (forall st m. String -> st -> st -> VC st m
AbortReachable String
nm st
start st
st) forall a b. (a -> b) -> a -> b
$ String
"Abort command executed, labeled: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm
analyze (Assign st -> st
f) = case [String
nm | st -> st -> (String, SBool)
s <- Stable st
stability, let (String
nm, SBool
b) = st -> st -> (String, SBool)
s st
st st
st', Bool -> Bool
not (forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
loc (String
"evaluation stability condition " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
nm) SBool
b)] of
[] -> Loc -> st -> String -> IO (Status st)
step Loc
loc st
st' String
"Assign"
[String]
nms -> let comb :: String
comb = forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
nms
bad :: VC st m
bad = forall st m. String -> st -> st -> VC st m
Unstable String
comb st
st st
st'
in Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc forall {m}. VC st m
bad forall a b. (a -> b) -> a -> b
$ String
"Stability condition fails for: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
comb
where st' :: st
st' = st -> st
f st
st
analyze (If st -> SBool
c Stmt st
tb Stmt st
eb)
| Bool
branchTrue = Loc -> Stmt st -> Status st -> IO (Status st)
go (Int -> Location
Line Int
1 forall a. a -> [a] -> [a]
: Loc
loc) Stmt st
tb forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Loc -> st -> String -> IO (Status st)
step Loc
loc st
st String
"Conditional, taking the \"then\" branch"
| Bool
True = Loc -> Stmt st -> Status st -> IO (Status st)
go (Int -> Location
Line Int
2 forall a. a -> [a] -> [a]
: Loc
loc) Stmt st
eb forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Loc -> st -> String -> IO (Status st)
step Loc
loc st
st String
"Conditional, taking the \"else\" branch"
where branchTrue :: Bool
branchTrue = forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
loc String
"evaluating the test condition" (st -> SBool
c st
st)
analyze (Seq [Stmt st]
stmts) = [Stmt st] -> Int -> Status st -> IO (Status st)
walk [Stmt st]
stmts Int
1 (forall st. st -> Status st
Good st
st)
where walk :: [Stmt st] -> Int -> Status st -> IO (Status st)
walk [] Int
_ Status st
is = forall (m :: * -> *) a. Monad m => a -> m a
return Status st
is
walk (Stmt st
s:[Stmt st]
ss) Int
c Status st
is = [Stmt st] -> Int -> Status st -> IO (Status st)
walk [Stmt st]
ss (Int
cforall a. Num a => a -> a -> a
+Int
1) forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Loc -> Stmt st -> Status st -> IO (Status st)
go (Int -> Location
Line Int
c forall a. a -> [a] -> [a]
: Loc
loc) Stmt st
s Status st
is
analyze (While String
loopName st -> SBool
invariant Maybe (Measure st)
mbMeasure st -> SBool
condition Stmt st
body)
| st -> Bool
currentInvariant st
st
= Int -> st -> Maybe [Integer] -> Status st -> IO (Status st)
while Int
1 st
st forall a. Maybe a
Nothing (forall st. st -> Status st
Good st
st)
| Bool
True
= Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc (forall st m. String -> st -> VC st m
InvariantPre String
loopName st
st) forall a b. (a -> b) -> a -> b
$ String -> String
tag String
"invariant fails to hold prior to loop entry"
where tag :: String -> String
tag String
s = String
"Loop " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
loopName forall a. [a] -> [a] -> [a]
++ String
": " forall a. [a] -> [a] -> [a]
++ String
s
hasMeasure :: Bool
hasMeasure = forall a. Maybe a -> Bool
isJust Maybe (Measure st)
mbMeasure
measure :: Measure st
measure = forall a. HasCallStack => Maybe a -> a
fromJust Maybe (Measure st)
mbMeasure
currentCondition :: st -> Bool
currentCondition = forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
loc (String -> String
tag String
"evaluating the while condition") forall b c a. (b -> c) -> (a -> b) -> a -> c
. st -> SBool
condition
currentMeasure :: st -> [Integer]
currentMeasure = forall a b. (a -> b) -> [a] -> [b]
map (forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
loc (String -> String
tag String
"evaluating the measure")) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Measure st
measure
currentInvariant :: st -> Bool
currentInvariant = forall a. SymVal a => Loc -> String -> SBV a -> a
unwrap Loc
loc (String -> String
tag String
"evaluating the invariant") forall b c a. (b -> c) -> (a -> b) -> a -> c
. st -> SBool
invariant
while :: Int -> st -> Maybe [Integer] -> Status st -> IO (Status st)
while Int
_ st
_ Maybe [Integer]
_ s :: Status st
s@Stuck{} = forall (m :: * -> *) a. Monad m => a -> m a
return Status st
s
while Int
c st
prevST Maybe [Integer]
mbPrev (Good st
is)
| Bool -> Bool
not (st -> Bool
currentCondition st
is)
= Loc -> st -> String -> IO (Status st)
step Loc
loc st
is forall a b. (a -> b) -> a -> b
$ String -> String
tag String
"condition fails, terminating"
| Bool -> Bool
not (st -> Bool
currentInvariant st
is)
= Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc (forall st m. String -> st -> st -> VC st m
InvariantMaintain String
loopName st
prevST st
is) forall a b. (a -> b) -> a -> b
$ String -> String
tag String
"invariant fails to hold in iteration " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Int
c
| Bool
hasMeasure Bool -> Bool -> Bool
&& [Integer]
mCur forall a. Ord a => a -> a -> Bool
< [Integer]
zero
= Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc (forall st m. String -> (st, [m]) -> VC st m
MeasureBound String
loopName (st
is, [Integer]
mCur)) forall a b. (a -> b) -> a -> b
$ String -> String
tag String
"measure must be non-negative, evaluated to: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [Integer]
mCur
| Bool
hasMeasure, Just [Integer]
mPrev <- Maybe [Integer]
mbPrev, [Integer]
mCur forall a. Ord a => a -> a -> Bool
>= [Integer]
mPrev
= Loc -> VC st Integer -> String -> IO (Status st)
stop Loc
loc (forall st m. String -> (st, [m]) -> (st, [m]) -> VC st m
MeasureDecrease String
loopName (st
prevST, [Integer]
mPrev) (st
is, [Integer]
mCur)) forall a b. (a -> b) -> a -> b
$ String -> String
tag forall a b. (a -> b) -> a -> b
$ String
"measure failed to decrease, prev = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [Integer]
mPrev forall a. [a] -> [a] -> [a]
++ String
", current = " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show [Integer]
mCur
| Bool
True
= do Status st
nextState <- Loc -> Stmt st -> Status st -> IO (Status st)
go (Int -> Location
Iteration Int
c forall a. a -> [a] -> [a]
: Loc
loc) Stmt st
body forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Loc -> st -> String -> IO (Status st)
step Loc
loc st
is (String -> String
tag String
"condition holds, executing the body")
Int -> st -> Maybe [Integer] -> Status st -> IO (Status st)
while (Int
cforall a. Num a => a -> a -> a
+Int
1) st
is (forall a. a -> Maybe a
Just [Integer]
mCur) Status st
nextState
where mCur :: [Integer]
mCur = st -> [Integer]
currentMeasure st
is
zero :: [Integer]
zero = forall a b. (a -> b) -> [a] -> [b]
map (forall a b. a -> b -> a
const Integer
0) [Integer]
mCur
{-# ANN traceExecution ("HLint: ignore Use fromMaybe" :: String) #-}