{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
module What4.Solver.Adapter
( SolverAdapter(..)
, defaultWriteSMTLIB2Features
, defaultSolverAdapter
, solverAdapterOptions
, LogData(..)
, logCallback
, defaultLogData
, smokeTest
) where
import qualified Control.Exception as X
import Data.Bits
import Data.IORef
import qualified Data.Map as Map
import qualified Data.Text as T
import System.IO
import qualified Text.PrettyPrint.ANSI.Leijen as PP
import What4.BaseTypes
import What4.Config
import What4.Concrete
import What4.Interface
import What4.SatResult
import What4.ProblemFeatures
import What4.Expr.Builder
import What4.Expr.GroundEval
data SolverAdapter st =
SolverAdapter
{ solver_adapter_name :: !String
, solver_adapter_config_options
:: ![ConfigDesc]
, solver_adapter_check_sat
:: !(forall t fs a.
ExprBuilder t st fs
-> LogData
-> [BoolExpr t]
-> (SatResult (GroundEvalFn t, Maybe (ExprRangeBindings t)) () -> IO a)
-> IO a)
, solver_adapter_write_smt2 :: !(forall t fs . ExprBuilder t st fs -> Handle -> [BoolExpr t] -> IO ())
}
data LogData = LogData { logCallbackVerbose :: Int -> String -> IO ()
, logVerbosity :: Int
, logReason :: String
, logHandle :: Maybe Handle
}
logCallback :: LogData -> (String -> IO ())
logCallback logData = logCallbackVerbose logData (logVerbosity logData)
defaultLogData :: LogData
defaultLogData = LogData { logCallbackVerbose = \_ _ -> return ()
, logVerbosity = 2
, logReason = "defaultReason"
, logHandle = Nothing }
instance Show (SolverAdapter st) where
show = solver_adapter_name
instance Eq (SolverAdapter st) where
x == y = solver_adapter_name x == solver_adapter_name y
instance Ord (SolverAdapter st) where
compare x y = compare (solver_adapter_name x) (solver_adapter_name y)
defaultWriteSMTLIB2Features :: ProblemFeatures
defaultWriteSMTLIB2Features
= useComputableReals
.|. useIntegerArithmetic
.|. useBitvectors
.|. useQuantifiers
.|. useSymbolicArrays
defaultSolverAdapter :: ConfigOption (BaseStringType Unicode)
defaultSolverAdapter = configOption (BaseStringRepr UnicodeRepr) "default_solver"
solverAdapterOptions ::
[SolverAdapter st] ->
IO ([ConfigDesc], IO (SolverAdapter st))
solverAdapterOptions [] = fail "No solver adapters specified!"
solverAdapterOptions xs@(def:_) =
do ref <- newIORef def
let opts = sty ref : concatMap solver_adapter_config_options xs
return (opts, readIORef ref)
where
f ref x = (T.pack (solver_adapter_name x), writeIORef ref x >> return optOK)
vals ref = Map.fromList (map (f ref) xs)
sty ref = mkOpt defaultSolverAdapter
(listOptSty (vals ref))
(Just (PP.text "Indicates which solver to use for check-sat queries"))
(Just (ConcreteString (UnicodeLiteral (T.pack (solver_adapter_name def)))))
smokeTest :: ExprBuilder t st fs -> SolverAdapter st -> IO (Maybe X.SomeException)
smokeTest sym adpt = test `X.catch` (pure . Just)
where
test :: IO (Maybe X.SomeException)
test =
solver_adapter_check_sat adpt sym defaultLogData [falsePred sym] $ \case
Unsat{} -> pure Nothing
_ -> fail "Smoke test failed: expected UNSAT"