{-# LANGUAGE CPP #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.SBV.Core.Symbolic
( NodeId(..)
, SV(..), swKind, trueSV, falseSV
, Op(..), PBOp(..), OvOp(..), FPOp(..), StrOp(..), SeqOp(..), SetOp(..), RegExp(..)
, Quantifier(..), needsExistentials
, RoundingMode(..)
, SBVType(..), svUninterpreted, newUninterpreted, addAxiom
, SVal(..)
, svMkSymVar, sWordN, sWordN_, sIntN, sIntN_
, ArrayContext(..), ArrayInfo
, svToSV, svToSymSV, forceSVArg
, SBVExpr(..), newExpr, isCodeGenMode, isSafetyCheckingIStage, isRunIStage, isSetupIStage
, Cached, cache, uncache, modifyState, modifyIncState
, ArrayIndex(..), FArrayIndex(..), uncacheAI, uncacheFAI
, NamedSymVar
, getSValPathCondition, extendSValPathCondition
, getTableIndex
, SBVPgm(..), MonadSymbolic(..), SymbolicT, Symbolic, runSymbolic, State(..), withNewIncState, IncState(..), incrementInternalCounter
, inSMTMode, SBVRunMode(..), IStage(..), Result(..)
, registerKind, registerLabel, recordObservable
, addAssertion, addNewSMTOption, imposeConstraint, internalConstraint, internalVariable
, SMTLibPgm(..), SMTLibVersion(..), smtLibVersionExtension
, SolverCapabilities(..)
, extractSymbolicSimulationState
, OptimizeStyle(..), Objective(..), Penalty(..), objectiveName, addSValOptGoal
, MonadQuery(..), QueryT(..), Query, Queriable(..), Fresh(..), QueryState(..), QueryContext(..)
, SMTScript(..), Solver(..), SMTSolver(..), SMTResult(..), SMTModel(..), SMTConfig(..), SMTEngine
, validationRequested, outputSVal
) where
import Control.Arrow (first, second, (***))
import Control.DeepSeq (NFData(..))
import Control.Monad (when)
import Control.Monad.Except (MonadError, ExceptT)
import Control.Monad.Reader (MonadReader(..), ReaderT, runReaderT,
mapReaderT)
import Control.Monad.State.Lazy (MonadState)
import Control.Monad.Trans (MonadIO(liftIO), MonadTrans(lift))
import Control.Monad.Trans.Maybe (MaybeT)
import Control.Monad.Writer.Strict (MonadWriter)
import Data.Char (isAlpha, isAlphaNum, toLower)
import Data.IORef (IORef, newIORef, readIORef)
import Data.List (intercalate, sortBy)
import Data.Maybe (isJust, fromJust, fromMaybe)
import Data.String (IsString(fromString))
import Data.Time (getCurrentTime, UTCTime)
import GHC.Stack
import qualified Control.Monad.State.Lazy as LS
import qualified Control.Monad.State.Strict as SS
import qualified Control.Monad.Writer.Lazy as LW
import qualified Control.Monad.Writer.Strict as SW
import qualified Data.IORef as R (modifyIORef')
import qualified Data.Generics as G (Data(..))
import qualified Data.IntMap.Strict as IMap (IntMap, empty, toAscList, lookup, insertWith)
import qualified Data.Map.Strict as Map (Map, empty, toList, lookup, insert, size)
import qualified Data.Set as Set (Set, empty, toList, insert, member)
import qualified Data.Foldable as F (toList)
import qualified Data.Sequence as S (Seq, empty, (|>))
import System.Mem.StableName
import Data.SBV.Core.Kind
import Data.SBV.Core.Concrete
import Data.SBV.SMT.SMTLibNames
import Data.SBV.Utils.TDiff (Timing)
import Data.SBV.Utils.Lib (stringToQFS)
import Data.SBV.Control.Types
#if MIN_VERSION_base(4,11,0)
import Control.Monad.Fail as Fail
#endif
newtype NodeId = NodeId Int deriving (Eq, Ord)
data SV = SV !Kind !NodeId deriving (Eq, Ord)
instance HasKind SV where
kindOf (SV k _) = k
instance Show SV where
show (SV _ (NodeId n)) = case n of
-2 -> "false"
-1 -> "true"
_ -> 's' : show n
swKind :: SV -> Kind
swKind (SV k _) = k
forceSVArg :: SV -> IO ()
forceSVArg (SV k n) = k `seq` n `seq` return ()
falseSV :: SV
falseSV = SV KBool $ NodeId (-2)
trueSV :: SV
trueSV = SV KBool $ NodeId (-1)
data Op = Plus
| Times
| Minus
| UNeg
| Abs
| Quot
| Rem
| Equal
| NotEqual
| LessThan
| GreaterThan
| LessEq
| GreaterEq
| Ite
| And
| Or
| XOr
| Not
| Shl
| Shr
| Rol Int
| Ror Int
| Extract Int Int
| Join
| LkUp (Int, Kind, Kind, Int) !SV !SV
| ArrEq ArrayIndex ArrayIndex
| ArrRead ArrayIndex
| KindCast Kind Kind
| Uninterpreted String
| Label String
| IEEEFP FPOp
| PseudoBoolean PBOp
| OverflowOp OvOp
| StrOp StrOp
| SeqOp SeqOp
| SetOp SetOp
| TupleConstructor Int
| TupleAccess Int Int
| EitherConstructor Kind Kind Bool
| EitherIs Kind Kind Bool
| EitherAccess Bool
| MaybeConstructor Kind Bool
| MaybeIs Kind Bool
| MaybeAccess
deriving (Eq, Ord)
data FPOp = FP_Cast Kind Kind SV
| FP_Reinterpret Kind Kind
| FP_Abs
| FP_Neg
| FP_Add
| FP_Sub
| FP_Mul
| FP_Div
| FP_FMA
| FP_Sqrt
| FP_Rem
| FP_RoundToIntegral
| FP_Min
| FP_Max
| FP_ObjEqual
| FP_IsNormal
| FP_IsSubnormal
| FP_IsZero
| FP_IsInfinite
| FP_IsNaN
| FP_IsNegative
| FP_IsPositive
deriving (Eq, Ord)
instance Show FPOp where
show (FP_Cast f t r) = "(FP_Cast: " ++ show f ++ " -> " ++ show t ++ ", using RM [" ++ show r ++ "])"
show (FP_Reinterpret f t) = case (f, t) of
(KBounded False 32, KFloat) -> "(_ to_fp 8 24)"
(KBounded False 64, KDouble) -> "(_ to_fp 11 53)"
_ -> error $ "SBV.FP_Reinterpret: Unexpected conversion: " ++ show f ++ " to " ++ show t
show FP_Abs = "fp.abs"
show FP_Neg = "fp.neg"
show FP_Add = "fp.add"
show FP_Sub = "fp.sub"
show FP_Mul = "fp.mul"
show FP_Div = "fp.div"
show FP_FMA = "fp.fma"
show FP_Sqrt = "fp.sqrt"
show FP_Rem = "fp.rem"
show FP_RoundToIntegral = "fp.roundToIntegral"
show FP_Min = "fp.min"
show FP_Max = "fp.max"
show FP_ObjEqual = "="
show FP_IsNormal = "fp.isNormal"
show FP_IsSubnormal = "fp.isSubnormal"
show FP_IsZero = "fp.isZero"
show FP_IsInfinite = "fp.isInfinite"
show FP_IsNaN = "fp.isNaN"
show FP_IsNegative = "fp.isNegative"
show FP_IsPositive = "fp.isPositive"
data PBOp = PB_AtMost Int
| PB_AtLeast Int
| PB_Exactly Int
| PB_Le [Int] Int
| PB_Ge [Int] Int
| PB_Eq [Int] Int
deriving (Eq, Ord, Show)
data OvOp = Overflow_SMul_OVFL
| Overflow_SMul_UDFL
| Overflow_UMul_OVFL
deriving (Eq, Ord)
instance Show OvOp where
show Overflow_SMul_OVFL = "bvsmul_noovfl"
show Overflow_SMul_UDFL = "bvsmul_noudfl"
show Overflow_UMul_OVFL = "bvumul_noovfl"
data StrOp = StrConcat
| StrLen
| StrUnit
| StrNth
| StrSubstr
| StrIndexOf
| StrContains
| StrPrefixOf
| StrSuffixOf
| StrReplace
| StrStrToNat
| StrNatToStr
| StrInRe RegExp
deriving (Eq, Ord)
data RegExp = Literal String
| All
| None
| Range Char Char
| Conc [RegExp]
| KStar RegExp
| KPlus RegExp
| Opt RegExp
| Loop Int Int RegExp
| Union [RegExp]
| Inter RegExp RegExp
deriving (Eq, Ord)
instance IsString RegExp where
fromString = Literal
instance Num RegExp where
Conc xs * y = Conc (xs ++ [y])
x * Conc ys = Conc (x : ys)
x * y = Conc [x, y]
Union xs + y = Union (xs ++ [y])
x + Union ys = Union (x : ys)
x + y = Union [x, y]
abs = error "Num.RegExp: no abs method"
signum = error "Num.RegExp: no signum method"
fromInteger x
| x == 0 = None
| x == 1 = Literal ""
| True = error $ "Num.RegExp: Only 0 and 1 makes sense as a reg-exp, no meaning for: " ++ show x
negate = error "Num.RegExp: no negate method"
instance Show RegExp where
show (Literal s) = "(str.to.re \"" ++ stringToQFS s ++ "\")"
show All = "re.allchar"
show None = "re.nostr"
show (Range ch1 ch2) = "(re.range \"" ++ stringToQFS [ch1] ++ "\" \"" ++ stringToQFS [ch2] ++ "\")"
show (Conc []) = show (1 :: Integer)
show (Conc [x]) = show x
show (Conc xs) = "(re.++ " ++ unwords (map show xs) ++ ")"
show (KStar r) = "(re.* " ++ show r ++ ")"
show (KPlus r) = "(re.+ " ++ show r ++ ")"
show (Opt r) = "(re.opt " ++ show r ++ ")"
show (Loop lo hi r)
| lo >= 0, hi >= lo = "((_ re.loop " ++ show lo ++ " " ++ show hi ++ ") " ++ show r ++ ")"
| True = error $ "Invalid regular-expression Loop with arguments: " ++ show (lo, hi)
show (Inter r1 r2) = "(re.inter " ++ show r1 ++ " " ++ show r2 ++ ")"
show (Union []) = "re.nostr"
show (Union [x]) = show x
show (Union xs) = "(re.union " ++ unwords (map show xs) ++ ")"
instance Show StrOp where
show StrConcat = "str.++"
show StrLen = "str.len"
show StrUnit = "seq.unit"
show StrNth = "seq.nth"
show StrSubstr = "str.substr"
show StrIndexOf = "str.indexof"
show StrContains = "str.contains"
show StrPrefixOf = "str.prefixof"
show StrSuffixOf = "str.suffixof"
show StrReplace = "str.replace"
show StrStrToNat = "str.to.int"
show StrNatToStr = "int.to.str"
show (StrInRe s) = "str.in.re " ++ show s
data SeqOp = SeqConcat
| SeqLen
| SeqUnit
| SeqNth
| SeqSubseq
| SeqIndexOf
| SeqContains
| SeqPrefixOf
| SeqSuffixOf
| SeqReplace
deriving (Eq, Ord)
instance Show SeqOp where
show SeqConcat = "seq.++"
show SeqLen = "seq.len"
show SeqUnit = "seq.unit"
show SeqNth = "seq.nth"
show SeqSubseq = "seq.extract"
show SeqIndexOf = "seq.indexof"
show SeqContains = "seq.contains"
show SeqPrefixOf = "seq.prefixof"
show SeqSuffixOf = "seq.suffixof"
show SeqReplace = "seq.replace"
data SetOp = SetEqual
| SetMember
| SetInsert
| SetDelete
| SetIntersect
| SetUnion
| SetSubset
| SetDifference
| SetComplement
| SetHasSize
deriving (Eq, Ord)
instance Show SetOp where
show SetEqual = "=="
show SetMember = "Set.member"
show SetInsert = "Set.insert"
show SetDelete = "Set.delete"
show SetIntersect = "Set.intersect"
show SetUnion = "Set.union"
show SetSubset = "Set.subset"
show SetDifference = "Set.difference"
show SetComplement = "Set.complement"
show SetHasSize = "Set.setHasSize"
instance Show Op where
show Shl = "<<"
show Shr = ">>"
show (Rol i) = "<<<" ++ show i
show (Ror i) = ">>>" ++ show i
show (Extract i j) = "choose [" ++ show i ++ ":" ++ show j ++ "]"
show (LkUp (ti, at, rt, l) i e)
= "lookup(" ++ tinfo ++ ", " ++ show i ++ ", " ++ show e ++ ")"
where tinfo = "table" ++ show ti ++ "(" ++ show at ++ " -> " ++ show rt ++ ", " ++ show l ++ ")"
show (ArrEq i j) = "array_" ++ show i ++ " == array_" ++ show j
show (ArrRead i) = "select array_" ++ show i
show (KindCast fr to) = "cast_" ++ show fr ++ "_" ++ show to
show (Uninterpreted i) = "[uninterpreted] " ++ i
show (Label s) = "[label] " ++ s
show (IEEEFP w) = show w
show (PseudoBoolean p) = show p
show (OverflowOp o) = show o
show (StrOp s) = show s
show (SeqOp s) = show s
show (SetOp s) = show s
show (TupleConstructor 0) = "mkSBVTuple0"
show (TupleConstructor n) = "mkSBVTuple" ++ show n
show (TupleAccess i n) = "proj_" ++ show i ++ "_SBVTuple" ++ show n
show (EitherConstructor k1 k2 False) = "(_ left_SBVEither " ++ show (KEither k1 k2) ++ ")"
show (EitherConstructor k1 k2 True ) = "(_ right_SBVEither " ++ show (KEither k1 k2) ++ ")"
show (EitherIs k1 k2 False) = "(_ is (left_SBVEither (" ++ show k1 ++ ") " ++ show (KEither k1 k2) ++ "))"
show (EitherIs k1 k2 True ) = "(_ is (right_SBVEither (" ++ show k2 ++ ") " ++ show (KEither k1 k2) ++ "))"
show (EitherAccess False) = "get_left_SBVEither"
show (EitherAccess True ) = "get_right_SBVEither"
show (MaybeConstructor k False) = "(_ nothing_SBVMaybe " ++ show (KMaybe k) ++ ")"
show (MaybeConstructor k True) = "(_ just_SBVMaybe " ++ show (KMaybe k) ++ ")"
show (MaybeIs k False) = "(_ is (nothing_SBVMaybe () " ++ show (KMaybe k) ++ "))"
show (MaybeIs k True ) = "(_ is (just_SBVMaybe (" ++ show k ++ ") " ++ show (KMaybe k) ++ "))"
show MaybeAccess = "get_just_SBVMaybe"
show op
| Just s <- op `lookup` syms = s
| True = error "impossible happened; can't find op!"
where syms = [ (Plus, "+"), (Times, "*"), (Minus, "-"), (UNeg, "-"), (Abs, "abs")
, (Quot, "quot")
, (Rem, "rem")
, (Equal, "=="), (NotEqual, "/=")
, (LessThan, "<"), (GreaterThan, ">"), (LessEq, "<="), (GreaterEq, ">=")
, (Ite, "if_then_else")
, (And, "&"), (Or, "|"), (XOr, "^"), (Not, "~")
, (Join, "#")
]
data Quantifier = ALL | EX deriving Eq
instance Show Quantifier where
show ALL = "Forall"
show EX = "Exists"
needsExistentials :: [Quantifier] -> Bool
needsExistentials = (EX `elem`)
newtype SBVType = SBVType [Kind]
deriving (Eq, Ord)
instance Show SBVType where
show (SBVType []) = error "SBV: internal error, empty SBVType"
show (SBVType xs) = intercalate " -> " $ map show xs
data SBVExpr = SBVApp !Op ![SV]
deriving (Eq, Ord)
reorder :: SBVExpr -> SBVExpr
reorder s = case s of
SBVApp op [a, b] | isCommutative op && a > b -> SBVApp op [b, a]
_ -> s
where isCommutative :: Op -> Bool
isCommutative o = o `elem` [Plus, Times, Equal, NotEqual, And, Or, XOr]
instance Show SBVExpr where
show (SBVApp Ite [t, a, b]) = unwords ["if", show t, "then", show a, "else", show b]
show (SBVApp Shl [a, i]) = unwords [show a, "<<", show i]
show (SBVApp Shr [a, i]) = unwords [show a, ">>", show i]
show (SBVApp (Rol i) [a]) = unwords [show a, "<<<", show i]
show (SBVApp (Ror i) [a]) = unwords [show a, ">>>", show i]
show (SBVApp (PseudoBoolean pb) args) = unwords (show pb : map show args)
show (SBVApp (OverflowOp op) args) = unwords (show op : map show args)
show (SBVApp op [a, b]) = unwords [show a, show op, show b]
show (SBVApp op args) = unwords (show op : map show args)
newtype SBVPgm = SBVPgm {pgmAssignments :: S.Seq (SV, SBVExpr)}
type NamedSymVar = (SV, String)
data OptimizeStyle = Lexicographic
| Independent
| Pareto (Maybe Int)
deriving (Eq, Show)
data Penalty = DefaultPenalty
| Penalty Rational (Maybe String)
deriving Show
data Objective a = Minimize String a
| Maximize String a
| AssertWithPenalty String a Penalty
deriving (Show, Functor)
objectiveName :: Objective a -> String
objectiveName (Minimize s _) = s
objectiveName (Maximize s _) = s
objectiveName (AssertWithPenalty s _ _) = s
data QueryState = QueryState { queryAsk :: Maybe Int -> String -> IO String
, querySend :: Maybe Int -> String -> IO ()
, queryRetrieveResponse :: Maybe Int -> IO String
, queryConfig :: SMTConfig
, queryTerminate :: IO ()
, queryTimeOutValue :: Maybe Int
, queryAssertionStackDepth :: Int
, queryTblArrPreserveIndex :: Maybe (Int, Int)
}
class Monad m => MonadQuery m where
queryState :: m State
default queryState :: (MonadTrans t, MonadQuery m', m ~ t m') => m State
queryState = lift queryState
instance MonadQuery m => MonadQuery (ExceptT e m)
instance MonadQuery m => MonadQuery (MaybeT m)
instance MonadQuery m => MonadQuery (ReaderT r m)
instance MonadQuery m => MonadQuery (SS.StateT s m)
instance MonadQuery m => MonadQuery (LS.StateT s m)
instance (MonadQuery m, Monoid w) => MonadQuery (SW.WriterT w m)
instance (MonadQuery m, Monoid w) => MonadQuery (LW.WriterT w m)
newtype QueryT m a = QueryT { runQueryT :: ReaderT State m a }
deriving (Applicative, Functor, Monad, MonadIO, MonadTrans,
MonadError e, MonadState s, MonadWriter w)
instance Monad m => MonadQuery (QueryT m) where
queryState = QueryT ask
mapQueryT :: (ReaderT State m a -> ReaderT State n b) -> QueryT m a -> QueryT n b
mapQueryT f = QueryT . f . runQueryT
{-# INLINE mapQueryT #-}
class Fresh m a where
fresh :: QueryT m a
class Queriable m a b | a -> b where
create :: QueryT m a
project :: a -> QueryT m b
embed :: b -> QueryT m a
instance MonadReader r m => MonadReader r (QueryT m) where
ask = lift ask
local f = mapQueryT $ mapReaderT $ local f
type Query = QueryT IO
instance MonadSymbolic Query where
symbolicEnv = queryState
instance NFData OptimizeStyle where
rnf x = x `seq` ()
instance NFData Penalty where
rnf DefaultPenalty = ()
rnf (Penalty p mbs) = rnf p `seq` rnf mbs
instance NFData a => NFData (Objective a) where
rnf (Minimize s a) = rnf s `seq` rnf a
rnf (Maximize s a) = rnf s `seq` rnf a
rnf (AssertWithPenalty s a p) = rnf s `seq` rnf a `seq` rnf p
data Result = Result { reskinds :: Set.Set Kind
, resTraces :: [(String, CV)]
, resObservables :: [(String, CV -> Bool, SV)]
, resUISegs :: [(String, [String])]
, resInputs :: ([(Quantifier, NamedSymVar)], [NamedSymVar])
, resConsts :: [(SV, CV)]
, resTables :: [((Int, Kind, Kind), [SV])]
, resArrays :: [(Int, ArrayInfo)]
, resUIConsts :: [(String, SBVType)]
, resAxioms :: [(String, [String])]
, resAsgns :: SBVPgm
, resConstraints :: S.Seq (Bool, [(String, String)], SV)
, resAssertions :: [(String, Maybe CallStack, SV)]
, resOutputs :: [SV]
}
instance Show Result where
show Result{resConsts=cs, resOutputs=[r]}
| Just c <- r `lookup` cs
= show c
show (Result kinds _ _ cgs is cs ts as uis axs xs cstrs asserts os) = intercalate "\n" $
(if null usorts then [] else "SORTS" : map (" " ++) usorts)
++ ["INPUTS"]
++ map shn (fst is)
++ (if null (snd is) then [] else "TRACKER VARS" : map (shn . (EX,)) (snd is))
++ ["CONSTANTS"]
++ concatMap shc cs
++ ["TABLES"]
++ map sht ts
++ ["ARRAYS"]
++ map sha as
++ ["UNINTERPRETED CONSTANTS"]
++ map shui uis
++ ["USER GIVEN CODE SEGMENTS"]
++ concatMap shcg cgs
++ ["AXIOMS"]
++ map shax axs
++ ["DEFINE"]
++ map (\(s, e) -> " " ++ shs s ++ " = " ++ show e) (F.toList (pgmAssignments xs))
++ ["CONSTRAINTS"]
++ map ((" " ++) . shCstr) (F.toList cstrs)
++ ["ASSERTIONS"]
++ map ((" "++) . shAssert) asserts
++ ["OUTPUTS"]
++ sh2 os
where sh2 :: Show a => [a] -> [String]
sh2 = map ((" "++) . show)
usorts = [sh s t | KUninterpreted s t <- Set.toList kinds]
where sh s (Left _) = s
sh s (Right es) = s ++ " (" ++ intercalate ", " es ++ ")"
shs sv = show sv ++ " :: " ++ show (swKind sv)
sht ((i, at, rt), es) = " Table " ++ show i ++ " : " ++ show at ++ "->" ++ show rt ++ " = " ++ show es
shc (sv, cv)
| sv == falseSV || sv == trueSV
= []
| True
= [" " ++ show sv ++ " = " ++ show cv]
shcg (s, ss) = ("Variable: " ++ s) : map (" " ++) ss
shn (q, (sv, nm)) = " " ++ ni ++ " :: " ++ show (swKind sv) ++ ex ++ alias
where ni = show sv
ex | q == ALL = ""
| True = ", existential"
alias | ni == nm = ""
| True = ", aliasing " ++ show nm
sha (i, (nm, (ai, bi), ctx)) = " " ++ ni ++ " :: " ++ show ai ++ " -> " ++ show bi ++ alias
++ "\n Context: " ++ show ctx
where ni = "array_" ++ show i
alias | ni == nm = ""
| True = ", aliasing " ++ show nm
shui (nm, t) = " [uninterpreted] " ++ nm ++ " :: " ++ show t
shax (nm, ss) = " -- user defined axiom: " ++ nm ++ "\n " ++ intercalate "\n " ss
shCstr (isSoft, [], c) = soft isSoft ++ show c
shCstr (isSoft, [(":named", nm)], c) = soft isSoft ++ nm ++ ": " ++ show c
shCstr (isSoft, attrs, c) = soft isSoft ++ show c ++ " (attributes: " ++ show attrs ++ ")"
soft True = "[SOFT] "
soft False = ""
shAssert (nm, stk, p) = " -- assertion: " ++ nm ++ " " ++ maybe "[No location]"
#if MIN_VERSION_base(4,9,0)
prettyCallStack
#else
showCallStack
#endif
stk ++ ": " ++ show p
data ArrayContext = ArrayFree (Maybe SV)
| ArrayMutate ArrayIndex SV SV
| ArrayMerge SV ArrayIndex ArrayIndex
instance Show ArrayContext where
show (ArrayFree Nothing) = " initialized with random elements"
show (ArrayFree (Just sv)) = " initialized with " ++ show sv
show (ArrayMutate i a b) = " cloned from array_" ++ show i ++ " with " ++ show a ++ " :: " ++ show (swKind a) ++ " |-> " ++ show b ++ " :: " ++ show (swKind b)
show (ArrayMerge s i j) = " merged arrays " ++ show i ++ " and " ++ show j ++ " on condition " ++ show s
type ExprMap = Map.Map SBVExpr SV
type CnstMap = Map.Map CV SV
type KindSet = Set.Set Kind
type TableMap = Map.Map (Kind, Kind, [SV]) Int
type ArrayInfo = (String, (Kind, Kind), ArrayContext)
type ArrayMap = IMap.IntMap ArrayInfo
type FArrayMap = IMap.IntMap (SVal -> SVal, IORef (IMap.IntMap SV))
type UIMap = Map.Map String SBVType
type CgMap = Map.Map String [String]
type Cache a = IMap.IntMap [(StableName (State -> IO a), a)]
data IStage = ISetup
| ISafe
| IRun
isSafetyCheckingIStage :: IStage -> Bool
isSafetyCheckingIStage s = case s of
ISetup -> False
ISafe -> True
IRun -> False
isSetupIStage :: IStage -> Bool
isSetupIStage s = case s of
ISetup -> True
ISafe -> False
IRun -> True
isRunIStage :: IStage -> Bool
isRunIStage s = case s of
ISetup -> False
ISafe -> False
IRun -> True
data SBVRunMode = SMTMode QueryContext IStage Bool SMTConfig
| CodeGen
| Concrete (Maybe (Bool, [((Quantifier, NamedSymVar), Maybe CV)]))
instance Show SBVRunMode where
show (SMTMode qc ISetup True _) = "Satisfiability setup (" ++ show qc ++ ")"
show (SMTMode qc ISafe True _) = "Safety setup (" ++ show qc ++ ")"
show (SMTMode qc IRun True _) = "Satisfiability (" ++ show qc ++ ")"
show (SMTMode qc ISetup False _) = "Proof setup (" ++ show qc ++ ")"
show (SMTMode qc ISafe False _) = error $ "ISafe-False is not an expected/supported combination for SBVRunMode! (" ++ show qc ++ ")"
show (SMTMode qc IRun False _) = "Proof (" ++ show qc ++ ")"
show CodeGen = "Code generation"
show (Concrete Nothing) = "Concrete evaluation with random values"
show (Concrete (Just (True, _))) = "Concrete evaluation during model validation for sat"
show (Concrete (Just (False, _))) = "Concrete evaluation during model validation for prove"
isCodeGenMode :: State -> IO Bool
isCodeGenMode State{runMode} = do rm <- readIORef runMode
return $ case rm of
Concrete{} -> False
SMTMode{} -> False
CodeGen -> True
data IncState = IncState { rNewInps :: IORef [NamedSymVar]
, rNewKinds :: IORef KindSet
, rNewConsts :: IORef CnstMap
, rNewArrs :: IORef ArrayMap
, rNewTbls :: IORef TableMap
, rNewUIs :: IORef UIMap
, rNewAsgns :: IORef SBVPgm
, rNewConstraints :: IORef (S.Seq (Bool, [(String, String)], SV))
}
newIncState :: IO IncState
newIncState = do
is <- newIORef []
ks <- newIORef Set.empty
nc <- newIORef Map.empty
am <- newIORef IMap.empty
tm <- newIORef Map.empty
ui <- newIORef Map.empty
pgm <- newIORef (SBVPgm S.empty)
cstrs <- newIORef S.empty
return IncState { rNewInps = is
, rNewKinds = ks
, rNewConsts = nc
, rNewArrs = am
, rNewTbls = tm
, rNewUIs = ui
, rNewAsgns = pgm
, rNewConstraints = cstrs
}
withNewIncState :: State -> (State -> IO a) -> IO (IncState, a)
withNewIncState st cont = do
is <- newIncState
R.modifyIORef' (rIncState st) (const is)
r <- cont st
finalIncState <- readIORef (rIncState st)
return (finalIncState, r)
data State = State { pathCond :: SVal
, startTime :: UTCTime
, runMode :: IORef SBVRunMode
, rIncState :: IORef IncState
, rCInfo :: IORef [(String, CV)]
, rObservables :: IORef [(String, CV -> Bool, SV)]
, rctr :: IORef Int
, rUsedKinds :: IORef KindSet
, rUsedLbls :: IORef (Set.Set String)
, rinps :: IORef ([(Quantifier, NamedSymVar)], [NamedSymVar])
, rConstraints :: IORef (S.Seq (Bool, [(String, String)], SV))
, routs :: IORef [SV]
, rtblMap :: IORef TableMap
, spgm :: IORef SBVPgm
, rconstMap :: IORef CnstMap
, rexprMap :: IORef ExprMap
, rArrayMap :: IORef ArrayMap
, rFArrayMap :: IORef FArrayMap
, rUIMap :: IORef UIMap
, rCgMap :: IORef CgMap
, raxioms :: IORef [(String, [String])]
, rSMTOptions :: IORef [SMTOption]
, rOptGoals :: IORef [Objective (SV, SV)]
, rAsserts :: IORef [(String, Maybe CallStack, SV)]
, rSVCache :: IORef (Cache SV)
, rAICache :: IORef (Cache ArrayIndex)
, rFAICache :: IORef (Cache FArrayIndex)
, rQueryState :: IORef (Maybe QueryState)
}
instance NFData State where
rnf State{} = ()
getSValPathCondition :: State -> SVal
getSValPathCondition = pathCond
extendSValPathCondition :: State -> (SVal -> SVal) -> State
extendSValPathCondition st f = st{pathCond = f (pathCond st)}
inSMTMode :: State -> IO Bool
inSMTMode State{runMode} = do rm <- readIORef runMode
return $ case rm of
CodeGen -> False
Concrete{} -> False
SMTMode{} -> True
data SVal = SVal !Kind !(Either CV (Cached SV))
instance HasKind SVal where
kindOf (SVal k _) = k
instance Show SVal where
show (SVal KBool (Left c)) = showCV False c
show (SVal k (Left c)) = showCV False c ++ " :: " ++ show k
show (SVal k (Right _)) = "<symbolic> :: " ++ show k
instance Eq SVal where
a == b = noEquals "==" ".==" (show a, show b)
a /= b = noEquals "/=" "./=" (show a, show b)
noEquals :: String -> String -> (String, String) -> a
noEquals o n (l, r) = error $ unlines [ ""
, "*** Data.SBV: Comparing symbolic values using Haskell's Eq class!"
, "***"
, "*** Received: " ++ l ++ " " ++ o ++ " " ++ r
, "*** Instead use: " ++ l ++ " " ++ n ++ " " ++ r
, "***"
, "*** The Eq instance for symbolic values are necessiated only because"
, "*** of the Bits class requirement. You must use symbolic equality"
, "*** operators instead. (And complain to Haskell folks that they"
, "*** remove the 'Eq' superclass from 'Bits'!.)"
]
noInteractive :: [String] -> a
noInteractive ss = error $ unlines $ ""
: "*** Data.SBV: Unsupported interactive/query mode feature."
: map ("*** " ++) ss
++ ["*** Data.SBV: Please report this as a feature request!"]
modifyState :: State -> (State -> IORef a) -> (a -> a) -> IO () -> IO ()
modifyState st@State{runMode} field update interactiveUpdate = do
R.modifyIORef' (field st) update
rm <- readIORef runMode
case rm of
SMTMode _ IRun _ _ -> interactiveUpdate
_ -> return ()
modifyIncState :: State -> (IncState -> IORef a) -> (a -> a) -> IO ()
modifyIncState State{rIncState} field update = do
incState <- readIORef rIncState
R.modifyIORef' (field incState) update
recordObservable :: State -> String -> (CV -> Bool) -> SV -> IO ()
recordObservable st nm chk sv = modifyState st rObservables ((nm, chk, sv):) (return ())
incrementInternalCounter :: State -> IO Int
incrementInternalCounter st = do ctr <- readIORef (rctr st)
modifyState st rctr (+1) (return ())
return ctr
svUninterpreted :: Kind -> String -> Maybe [String] -> [SVal] -> SVal
svUninterpreted k nm code args = SVal k $ Right $ cache result
where result st = do let ty = SBVType (map kindOf args ++ [k])
newUninterpreted st nm ty code
sws <- mapM (svToSV st) args
mapM_ forceSVArg sws
newExpr st k $ SBVApp (Uninterpreted nm) sws
newUninterpreted :: State -> String -> SBVType -> Maybe [String] -> IO ()
newUninterpreted st nm t mbCode
| null nm || not enclosed && (not (isAlpha (head nm)) || not (all validChar (tail nm)))
= error $ "Bad uninterpreted constant name: " ++ show nm ++ ". Must be a valid identifier."
| True = do uiMap <- readIORef (rUIMap st)
case nm `Map.lookup` uiMap of
Just t' -> checkType t' (return ())
Nothing -> do modifyState st rUIMap (Map.insert nm t)
$ modifyIncState st rNewUIs (\newUIs -> case nm `Map.lookup` newUIs of
Just t' -> checkType t' newUIs
Nothing -> Map.insert nm t newUIs)
when (isJust mbCode) $ modifyState st rCgMap (Map.insert nm (fromJust mbCode)) (return ())
where checkType :: SBVType -> r -> r
checkType t' cont
| t /= t' = error $ "Uninterpreted constant " ++ show nm ++ " used at incompatible types\n"
++ " Current type : " ++ show t ++ "\n"
++ " Previously used at: " ++ show t'
| True = cont
validChar x = isAlphaNum x || x `elem` "_"
enclosed = head nm == '|' && last nm == '|' && length nm > 2 && not (any (`elem` "|\\") (tail (init nm)))
addAssertion :: State -> Maybe CallStack -> String -> SV -> IO ()
addAssertion st cs msg cond = modifyState st rAsserts ((msg, cs, cond):)
$ noInteractive [ "Named assertions (sAssert):"
, " Tag: " ++ msg
, " Loc: " ++ maybe "Unknown" show cs
]
internalVariable :: State -> Kind -> IO SV
internalVariable st k = do (sv, nm) <- newSV st k
rm <- readIORef (runMode st)
let q = case rm of
SMTMode _ _ True _ -> EX
SMTMode _ _ False _ -> ALL
CodeGen -> ALL
Concrete{} -> ALL
n = "__internal_sbv_" ++ nm
v = (sv, n)
modifyState st rinps (first ((q, v) :))
$ modifyIncState st rNewInps (\newInps -> case q of
EX -> v : newInps
ALL -> noInteractive [ "Internal universally quantified variable creation:"
, " Named: " ++ nm
])
return sv
{-# INLINE internalVariable #-}
newSV :: State -> Kind -> IO (SV, String)
newSV st k = do ctr <- incrementInternalCounter st
let sv = SV k (NodeId ctr)
registerKind st k
return (sv, 's' : show ctr)
{-# INLINE newSV #-}
registerKind :: State -> Kind -> IO ()
registerKind st k
| KUninterpreted sortName _ <- k, map toLower sortName `elem` smtLibReservedNames
= error $ "SBV: " ++ show sortName ++ " is a reserved sort; please use a different name."
| True
= do
existingKinds <- readIORef (rUsedKinds st)
modifyState st rUsedKinds (Set.insert k) $ do
let needsAdding = case k of
KUninterpreted{} -> k `notElem` existingKinds
KList{} -> k `notElem` existingKinds
KTuple nks -> length nks `notElem` [length oks | KTuple oks <- Set.toList existingKinds]
KMaybe{} -> k `notElem` existingKinds
KEither{} -> k `notElem` existingKinds
_ -> False
when needsAdding $ modifyIncState st rNewKinds (Set.insert k)
case k of
KBool {} -> return ()
KBounded {} -> return ()
KUnbounded {} -> return ()
KReal {} -> return ()
KUninterpreted {} -> return ()
KFloat {} -> return ()
KDouble {} -> return ()
KChar {} -> return ()
KString {} -> return ()
KList ek -> registerKind st ek
KSet ek -> registerKind st ek
KTuple eks -> mapM_ (registerKind st) eks
KMaybe ke -> registerKind st ke
KEither k1 k2 -> mapM_ (registerKind st) [k1, k2]
registerLabel :: String -> State -> String -> IO ()
registerLabel whence st nm
| map toLower nm `elem` smtLibReservedNames
= err "is a reserved string; please use a different name."
| '|' `elem` nm
= err "contains the character `|', which is not allowed!"
| '\\' `elem` nm
= err "contains the character `\', which is not allowed!"
| True
= do old <- readIORef $ rUsedLbls st
if nm `Set.member` old
then err "is used multiple times. Please do not use duplicate names!"
else modifyState st rUsedLbls (Set.insert nm) (return ())
where err w = error $ "SBV (" ++ whence ++ "): " ++ show nm ++ " " ++ w
newConst :: State -> CV -> IO SV
newConst st c = do
constMap <- readIORef (rconstMap st)
case c `Map.lookup` constMap of
Just sv -> return sv
Nothing -> do (sv, _) <- newSV st (kindOf c)
let ins = Map.insert c sv
modifyState st rconstMap ins $ modifyIncState st rNewConsts ins
return sv
{-# INLINE newConst #-}
getTableIndex :: State -> Kind -> Kind -> [SV] -> IO Int
getTableIndex st at rt elts = do
let key = (at, rt, elts)
tblMap <- readIORef (rtblMap st)
case key `Map.lookup` tblMap of
Just i -> return i
_ -> do let i = Map.size tblMap
upd = Map.insert key i
modifyState st rtblMap upd $ modifyIncState st rNewTbls upd
return i
newExpr :: State -> Kind -> SBVExpr -> IO SV
newExpr st k app = do
let e = reorder app
exprMap <- readIORef (rexprMap st)
case e `Map.lookup` exprMap of
Just sv | kindOf sv == k -> return sv
_ -> do (sv, _) <- newSV st k
let append (SBVPgm xs) = SBVPgm (xs S.|> (sv, e))
modifyState st spgm append $ modifyIncState st rNewAsgns append
modifyState st rexprMap (Map.insert e sv) (return ())
return sv
{-# INLINE newExpr #-}
svToSV :: State -> SVal -> IO SV
svToSV st (SVal _ (Left c)) = newConst st c
svToSV st (SVal _ (Right f)) = uncache f st
svToSymSV :: MonadSymbolic m => SVal -> m SV
svToSymSV sbv = do st <- symbolicEnv
liftIO $ svToSV st sbv
class MonadIO m => MonadSymbolic m where
symbolicEnv :: m State
default symbolicEnv :: (MonadTrans t, MonadSymbolic m', m ~ t m') => m State
symbolicEnv = lift symbolicEnv
instance MonadSymbolic m => MonadSymbolic (ExceptT e m)
instance MonadSymbolic m => MonadSymbolic (MaybeT m)
instance MonadSymbolic m => MonadSymbolic (ReaderT r m)
instance MonadSymbolic m => MonadSymbolic (SS.StateT s m)
instance MonadSymbolic m => MonadSymbolic (LS.StateT s m)
instance (MonadSymbolic m, Monoid w) => MonadSymbolic (SW.WriterT w m)
instance (MonadSymbolic m, Monoid w) => MonadSymbolic (LW.WriterT w m)
newtype SymbolicT m a = SymbolicT { runSymbolicT :: ReaderT State m a }
deriving ( Applicative, Functor, Monad, MonadIO, MonadTrans
, MonadError e, MonadState s, MonadWriter w
#if MIN_VERSION_base(4,11,0)
, Fail.MonadFail
#endif
)
instance MonadIO m => MonadSymbolic (SymbolicT m) where
symbolicEnv = SymbolicT ask
mapSymbolicT :: (ReaderT State m a -> ReaderT State n b) -> SymbolicT m a -> SymbolicT n b
mapSymbolicT f = SymbolicT . f . runSymbolicT
{-# INLINE mapSymbolicT #-}
instance MonadReader r m => MonadReader r (SymbolicT m) where
ask = lift ask
local f = mapSymbolicT $ mapReaderT $ local f
type Symbolic = SymbolicT IO
svMkSymVar :: Maybe Quantifier -> Kind -> Maybe String -> State -> IO SVal
svMkSymVar = svMkSymVarGen False
svMkTrackerVar :: Kind -> String -> State -> IO SVal
svMkTrackerVar k nm = svMkSymVarGen True (Just EX) k (Just nm)
sWordN :: MonadSymbolic m => Int -> String -> m SVal
sWordN w nm = symbolicEnv >>= liftIO . svMkSymVar Nothing (KBounded False w) (Just nm)
sWordN_ :: MonadSymbolic m => Int -> m SVal
sWordN_ w = symbolicEnv >>= liftIO . svMkSymVar Nothing (KBounded False w) Nothing
sIntN :: MonadSymbolic m => Int -> String -> m SVal
sIntN w nm = symbolicEnv >>= liftIO . svMkSymVar Nothing (KBounded True w) (Just nm)
sIntN_ :: MonadSymbolic m => Int -> m SVal
sIntN_ w = symbolicEnv >>= liftIO . svMkSymVar Nothing (KBounded True w) Nothing
svMkSymVarGen :: Bool -> Maybe Quantifier -> Kind -> Maybe String -> State -> IO SVal
svMkSymVarGen isTracker mbQ k mbNm st = do
rm <- readIORef (runMode st)
let varInfo = case mbNm of
Nothing -> ", of type " ++ show k
Just nm -> ", while defining " ++ nm ++ " :: " ++ show k
disallow what = error $ "Data.SBV: Unsupported: " ++ what ++ varInfo ++ " in mode: " ++ show rm
noUI cont
| isUninterpreted k = disallow "Uninterpreted sorts"
| True = cont
mkS q = do (sv, internalName) <- newSV st k
let nm = fromMaybe internalName mbNm
introduceUserName st isTracker nm k q sv
mkC cv = do registerKind st k
modifyState st rCInfo ((fromMaybe "_" mbNm, cv):) (return ())
return $ SVal k (Left cv)
case (mbQ, rm) of
(Just q, SMTMode{} ) -> mkS q
(Nothing, SMTMode _ _ isSAT _) -> mkS (if isSAT then EX else ALL)
(Just EX, CodeGen{}) -> disallow "Existentially quantified variables"
(_ , CodeGen) -> noUI $ mkS ALL
(Just EX, Concrete Nothing) -> disallow "Existentially quantified variables"
(_ , Concrete Nothing) -> noUI (randomCV k >>= mkC)
(_ , Concrete (Just (_isSat, env))) ->
let bad why conc = error $ unlines [ ""
, "*** Data.SBV: " ++ why
, "***"
, "*** To turn validation off, use `cfg{validateModel = False}`"
, "***"
, "*** " ++ conc
]
cant = "Validation engine is not capable of handling this case. Failed to validate."
report = "Please report this as a bug in SBV!"
in if isUninterpreted k
then bad ("Cannot validate models in the presence of uninterpeted kinds, saw: " ++ show k) cant
else do (sv, internalName) <- newSV st k
let nm = fromMaybe internalName mbNm
nsv = (sv, nm)
cv = case [(q, v) | ((q, nsv'), v) <- env, nsv == nsv'] of
[] -> if isTracker
then
mkConstCV k (0::Integer)
else bad ("Cannot locate variable: " ++ show (nsv, k)) report
[(ALL, _)] -> bad ("Cannot validate models in the presence of universally quantified variable " ++ show (snd nsv)) cant
[(EX, Nothing)] -> bad ("Cannot locate model value of variable: " ++ show (snd nsv)) report
[(EX, Just c)] -> c
r -> bad ( "Found multiple matching values for variable: " ++ show nsv
++ "\n*** " ++ show r) report
mkC cv
introduceUserName :: State -> Bool -> String -> Kind -> Quantifier -> SV -> IO SVal
introduceUserName st isTracker nmOrig k q sv = do
(is, ints) <- readIORef (rinps st)
let old = [n | (_, (_, n)) <- is] ++ [n | (_, n) <- ints]
nm = mkUnique nmOrig old
if isTracker && q == ALL
then error $ "SBV: Impossible happened! A universally quantified tracker variable is being introduced: " ++ show nm
else do let newInp olds = case q of
EX -> (sv, nm) : olds
ALL -> noInteractive [ "Adding a new universally quantified variable: "
, " Name : " ++ show nm
, " Kind : " ++ show k
, " Quantifier: Universal"
, " Node : " ++ show sv
, "Only existential variables are supported in query mode."
]
if isTracker
then modifyState st rinps (second ((:) (sv, nm)))
$ noInteractive ["Adding a new tracker variable in interactive mode: " ++ show nm]
else modifyState st rinps (first ((:) (q, (sv, nm))))
$ modifyIncState st rNewInps newInp
return $ SVal k $ Right $ cache (const (return sv))
where
mkUnique prefix names = head $ dropWhile (`elem` names) (prefix : [prefix ++ "_" ++ show i | i <- [(0::Int)..]])
addAxiom :: MonadSymbolic m => String -> [String] -> m ()
addAxiom nm ax = do
st <- symbolicEnv
liftIO $ modifyState st raxioms ((nm, ax) :)
$ noInteractive [ "Adding a new axiom:"
, " Named: " ++ show nm
, " Axiom: " ++ unlines ax
]
runSymbolic :: MonadIO m => SBVRunMode -> SymbolicT m a -> m (a, Result)
runSymbolic currentRunMode (SymbolicT c) = do
st <- liftIO $ do
currTime <- getCurrentTime
rm <- newIORef currentRunMode
ctr <- newIORef (-2)
cInfo <- newIORef []
observes <- newIORef []
pgm <- newIORef (SBVPgm S.empty)
emap <- newIORef Map.empty
cmap <- newIORef Map.empty
inps <- newIORef ([], [])
outs <- newIORef []
tables <- newIORef Map.empty
arrays <- newIORef IMap.empty
fArrays <- newIORef IMap.empty
uis <- newIORef Map.empty
cgs <- newIORef Map.empty
axioms <- newIORef []
swCache <- newIORef IMap.empty
aiCache <- newIORef IMap.empty
faiCache <- newIORef IMap.empty
usedKinds <- newIORef Set.empty
usedLbls <- newIORef Set.empty
cstrs <- newIORef S.empty
smtOpts <- newIORef []
optGoals <- newIORef []
asserts <- newIORef []
istate <- newIORef =<< newIncState
qstate <- newIORef Nothing
pure $ State { runMode = rm
, startTime = currTime
, pathCond = SVal KBool (Left trueCV)
, rIncState = istate
, rCInfo = cInfo
, rObservables = observes
, rctr = ctr
, rUsedKinds = usedKinds
, rUsedLbls = usedLbls
, rinps = inps
, routs = outs
, rtblMap = tables
, spgm = pgm
, rconstMap = cmap
, rArrayMap = arrays
, rFArrayMap = fArrays
, rexprMap = emap
, rUIMap = uis
, rCgMap = cgs
, raxioms = axioms
, rSVCache = swCache
, rAICache = aiCache
, rFAICache = faiCache
, rConstraints = cstrs
, rSMTOptions = smtOpts
, rOptGoals = optGoals
, rAsserts = asserts
, rQueryState = qstate
}
_ <- liftIO $ newConst st falseCV
_ <- liftIO $ newConst st trueCV
r <- runReaderT c st
res <- liftIO $ extractSymbolicSimulationState st
qs <- liftIO $ readIORef $ rQueryState st
case qs of
Nothing -> return ()
Just QueryState{queryTerminate} -> liftIO queryTerminate
return (r, res)
extractSymbolicSimulationState :: State -> IO Result
extractSymbolicSimulationState st@State{ spgm=pgm, rinps=inps, routs=outs, rtblMap=tables, rArrayMap=arrays, rUIMap=uis, raxioms=axioms
, rAsserts=asserts, rUsedKinds=usedKinds, rCgMap=cgs, rCInfo=cInfo, rConstraints=cstrs
, rObservables=observes
} = do
SBVPgm rpgm <- readIORef pgm
inpsO <- (reverse *** reverse) <$> readIORef inps
outsO <- reverse <$> readIORef outs
let swap (a, b) = (b, a)
cmp (a, _) (b, _) = a `compare` b
arrange (i, (at, rt, es)) = ((i, at, rt), es)
cnsts <- sortBy cmp . map swap . Map.toList <$> readIORef (rconstMap st)
tbls <- map arrange . sortBy cmp . map swap . Map.toList <$> readIORef tables
arrs <- IMap.toAscList <$> readIORef arrays
unint <- Map.toList <$> readIORef uis
axs <- reverse <$> readIORef axioms
knds <- readIORef usedKinds
cgMap <- Map.toList <$> readIORef cgs
traceVals <- reverse <$> readIORef cInfo
observables <- reverse <$> readIORef observes
extraCstrs <- readIORef cstrs
assertions <- reverse <$> readIORef asserts
return $ Result knds traceVals observables cgMap inpsO cnsts tbls arrs unint axs (SBVPgm rpgm) extraCstrs assertions outsO
addNewSMTOption :: MonadSymbolic m => SMTOption -> m ()
addNewSMTOption o = do st <- symbolicEnv
liftIO $ modifyState st rSMTOptions (o:) (return ())
imposeConstraint :: MonadSymbolic m => Bool -> [(String, String)] -> SVal -> m ()
imposeConstraint isSoft attrs c = do st <- symbolicEnv
rm <- liftIO $ readIORef (runMode st)
case rm of
CodeGen -> error "SBV: constraints are not allowed in code-generation"
_ -> liftIO $ do mapM_ (registerLabel "Constraint" st) [nm | (":named", nm) <- attrs]
internalConstraint st isSoft attrs c
internalConstraint :: State -> Bool -> [(String, String)] -> SVal -> IO ()
internalConstraint st isSoft attrs b = do v <- svToSV st b
rm <- liftIO $ readIORef (runMode st)
let isValidating = case rm of
SMTMode _ _ _ cfg -> validationRequested cfg
CodeGen -> False
Concrete Nothing -> False
Concrete (Just _) -> True
let c = (isSoft, attrs, v)
interesting = v /= trueSV || not (null attrs)
when (isValidating || interesting) $
modifyState st rConstraints (S.|> c)
$ modifyIncState st rNewConstraints (S.|> c)
addSValOptGoal :: MonadSymbolic m => Objective SVal -> m ()
addSValOptGoal obj = do st <- symbolicEnv
let mkGoal nm orig = liftIO $ do origSV <- svToSV st orig
track <- svMkTrackerVar (kindOf orig) nm st
trackSV <- svToSV st track
return (origSV, trackSV)
let walk (Minimize nm v) = Minimize nm <$> mkGoal nm v
walk (Maximize nm v) = Maximize nm <$> mkGoal nm v
walk (AssertWithPenalty nm v mbP) = flip (AssertWithPenalty nm) mbP <$> mkGoal nm v
obj' <- walk obj
liftIO $ modifyState st rOptGoals (obj' :)
$ noInteractive [ "Adding an optimization objective:"
, " Objective: " ++ show obj
]
outputSVal :: MonadSymbolic m => SVal -> m ()
outputSVal (SVal _ (Left c)) = do
st <- symbolicEnv
sv <- liftIO $ newConst st c
liftIO $ modifyState st routs (sv:) (return ())
outputSVal (SVal _ (Right f)) = do
st <- symbolicEnv
sv <- liftIO $ uncache f st
liftIO $ modifyState st routs (sv:) (return ())
newtype Cached a = Cached (State -> IO a)
cache :: (State -> IO a) -> Cached a
cache = Cached
uncache :: Cached SV -> State -> IO SV
uncache = uncacheGen rSVCache
newtype ArrayIndex = ArrayIndex { unArrayIndex :: Int } deriving (Eq, Ord)
instance Show ArrayIndex where
show (ArrayIndex i) = show i
newtype FArrayIndex = FArrayIndex { unFArrayIndex :: Int } deriving (Eq, Ord)
instance Show FArrayIndex where
show (FArrayIndex i) = show i
uncacheAI :: Cached ArrayIndex -> State -> IO ArrayIndex
uncacheAI = uncacheGen rAICache
uncacheFAI :: Cached FArrayIndex -> State -> IO FArrayIndex
uncacheFAI = uncacheGen rFAICache
uncacheGen :: (State -> IORef (Cache a)) -> Cached a -> State -> IO a
uncacheGen getCache (Cached f) st = do
let rCache = getCache st
stored <- readIORef rCache
sn <- f `seq` makeStableName f
let h = hashStableName sn
case (h `IMap.lookup` stored) >>= (sn `lookup`) of
Just r -> return r
Nothing -> do r <- f st
r `seq` R.modifyIORef' rCache (IMap.insertWith (++) h [(sn, r)])
return r
data SMTLibVersion = SMTLib2
deriving (Bounded, Enum, Eq, Show)
smtLibVersionExtension :: SMTLibVersion -> String
smtLibVersionExtension SMTLib2 = "smt2"
data SMTLibPgm = SMTLibPgm SMTLibVersion [String]
instance NFData SMTLibVersion where rnf a = a `seq` ()
instance NFData SMTLibPgm where rnf (SMTLibPgm v p) = rnf v `seq` rnf p
instance Show SMTLibPgm where
show (SMTLibPgm _ pre) = intercalate "\n" pre
instance NFData CV where
rnf (CV x y) = x `seq` y `seq` ()
instance NFData GeneralizedCV where
rnf (ExtendedCV e) = e `seq` ()
rnf (RegularCV c) = c `seq` ()
#if MIN_VERSION_base(4,9,0)
#else
instance NFData CallStack where
rnf _ = ()
#endif
instance NFData Result where
rnf (Result kindInfo qcInfo obs cgs inps consts tbls arrs uis axs pgm cstr asserts outs)
= rnf kindInfo `seq` rnf qcInfo `seq` rnf obs `seq` rnf cgs
`seq` rnf inps `seq` rnf consts `seq` rnf tbls
`seq` rnf arrs `seq` rnf uis `seq` rnf axs
`seq` rnf pgm `seq` rnf cstr `seq` rnf asserts
`seq` rnf outs
instance NFData Kind where rnf a = seq a ()
instance NFData ArrayContext where rnf a = seq a ()
instance NFData SV where rnf a = seq a ()
instance NFData SBVExpr where rnf a = seq a ()
instance NFData Quantifier where rnf a = seq a ()
instance NFData SBVType where rnf a = seq a ()
instance NFData SBVPgm where rnf a = seq a ()
instance NFData (Cached a) where rnf (Cached f) = f `seq` ()
instance NFData SVal where rnf (SVal x y) = rnf x `seq` rnf y
instance NFData SMTResult where
rnf (Unsatisfiable _ xs ) = rnf xs
rnf (Satisfiable _ xs ) = rnf xs
rnf (SatExtField _ xs ) = rnf xs
rnf (Unknown _ xs ) = rnf xs
rnf (ProofError _ xs mr) = rnf xs `seq` rnf mr
instance NFData SMTModel where
rnf (SMTModel objs bndgs assocs uifuns) = rnf objs `seq` rnf bndgs `seq` rnf assocs `seq` rnf uifuns
instance NFData SMTScript where
rnf (SMTScript b m) = rnf b `seq` rnf m
data SolverCapabilities = SolverCapabilities {
supportsQuantifiers :: Bool
, supportsUninterpretedSorts :: Bool
, supportsUnboundedInts :: Bool
, supportsReals :: Bool
, supportsApproxReals :: Bool
, supportsIEEE754 :: Bool
, supportsSets :: Bool
, supportsOptimization :: Bool
, supportsPseudoBooleans :: Bool
, supportsCustomQueries :: Bool
, supportsGlobalDecls :: Bool
, supportsDataTypes :: Bool
, supportsFlattenedModels :: Maybe [String]
}
data RoundingMode = RoundNearestTiesToEven
| RoundNearestTiesToAway
| RoundTowardPositive
| RoundTowardNegative
| RoundTowardZero
deriving (Eq, Ord, Show, Read, G.Data, Bounded, Enum)
instance HasKind RoundingMode
data SMTConfig = SMTConfig {
verbose :: Bool
, timing :: Timing
, printBase :: Int
, printRealPrec :: Int
, satCmd :: String
, allSatMaxModelCount :: Maybe Int
, allSatPrintAlong :: Bool
, satTrackUFs :: Bool
, isNonModelVar :: String -> Bool
, validateModel :: Bool
, optimizeValidateConstraints :: Bool
, transcript :: Maybe FilePath
, smtLibVersion :: SMTLibVersion
, solver :: SMTSolver
, allowQuantifiedQueries :: Bool
, roundingMode :: RoundingMode
, solverSetOptions :: [SMTOption]
, ignoreExitCode :: Bool
, redirectVerbose :: Maybe FilePath
}
validationRequested :: SMTConfig -> Bool
validationRequested SMTConfig{validateModel, optimizeValidateConstraints} = validateModel || optimizeValidateConstraints
instance NFData SMTConfig where
rnf SMTConfig{} = ()
data SMTModel = SMTModel {
modelObjectives :: [(String, GeneralizedCV)]
, modelBindings :: Maybe [((Quantifier, NamedSymVar), Maybe CV)]
, modelAssocs :: [(String, CV)]
, modelUIFuns :: [(String, (SBVType, ([([CV], CV)], CV)))]
}
deriving Show
data SMTResult = Unsatisfiable SMTConfig (Maybe [String])
| Satisfiable SMTConfig SMTModel
| SatExtField SMTConfig SMTModel
| Unknown SMTConfig SMTReasonUnknown
| ProofError SMTConfig [String] (Maybe SMTResult)
data SMTScript = SMTScript {
scriptBody :: String
, scriptModel :: [String]
}
type SMTEngine = forall res.
SMTConfig
-> State
-> String
-> (State -> IO res)
-> IO res
data Solver = Z3
| Yices
| Boolector
| CVC4
| MathSAT
| ABC
deriving (Show, Enum, Bounded)
data SMTSolver = SMTSolver {
name :: Solver
, executable :: String
, preprocess :: String -> String
, options :: SMTConfig -> [String]
, engine :: SMTEngine
, capabilities :: SolverCapabilities
}
data QueryContext = QueryInternal
| QueryExternal
instance Show QueryContext where
show QueryInternal = "Internal Query"
show QueryExternal = "User Query"
{-# ANN type FPOp ("HLint: ignore Use camelCase" :: String) #-}
{-# ANN type PBOp ("HLint: ignore Use camelCase" :: String) #-}
{-# ANN type OvOp ("HLint: ignore Use camelCase" :: String) #-}