g2-0.2.0.0: src/G2/Interface/Interface.hs
{-# OPTIONS_GHC -Wno-orphans #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module G2.Interface.Interface ( MkCurrExpr
, MkArgTypes
, IT.SimpleState
, doTimeout
, maybeDoTimeout
, initStateWithCall
, initStateWithCall'
, initStateFromSimpleState
, initStateFromSimpleState'
, initStateFromSimpleStateWithCall
, initSimpleState
, mkArgTys
, initRedHaltOrd
, initSolver
, initSolverInfinite
, initialStateFromFileSimple
, initialStateFromFile
, initialStateNoStartFunc
, runG2FromFile
, runG2WithConfig
, runG2WithSomes
, runG2Pre
, runG2Post
, runG2ThroughExecution
, runExecution
, runG2SolvingResult
, runG2Solving
, runG2
, Config) where
import G2.Config.Config
import G2.Language
import G2.Initialization.Interface
import G2.Initialization.KnownValues
import G2.Initialization.MkCurrExpr
import qualified G2.Initialization.Types as IT
import G2.Preprocessing.Interface
import G2.Execution.HPC
import G2.Execution.Interface
import G2.Execution.Reducer
import G2.Execution.Rules
import G2.Execution.PrimitiveEval
import G2.Execution.Memory
import G2.Interface.OutputTypes
import G2.Lib.Printers
import G2.Translation
import G2.Solver
import G2.Postprocessing.Interface
import qualified G2.Language.ExprEnv as E
import qualified G2.Language.PathConds as PC
import qualified G2.Language.Stack as Stack
import Control.Monad.IO.Class
import qualified Control.Monad.State as SM
import qualified Data.HashMap.Lazy as HM
import qualified Data.HashSet as S
import Data.Maybe
import qualified Data.Sequence as Seq
import qualified Data.Text as T
import System.Timeout
type AssumeFunc = T.Text
type AssertFunc = T.Text
type ReachFunc = T.Text
type StartFunc = T.Text
type ModuleName = Maybe T.Text
type MkCurrExpr = TypeClasses -> NameGen -> ExprEnv -> TypeEnv -> Walkers
-> KnownValues -> Config -> (Expr, [Id], [Expr], NameGen)
doTimeout :: Int -> IO a -> IO (Maybe a)
doTimeout secs action = do
res <- timeout (secs * 1000 * 1000) action -- timeout takes micros.
case res of
Just _ -> return res
Nothing -> do
putStrLn "Timeout!"
return Nothing
maybeDoTimeout :: Maybe Int -> IO a -> IO (Maybe a)
maybeDoTimeout (Just secs) = doTimeout secs
maybeDoTimeout Nothing = fmap Just
{-# INLINE initStateWithCall #-}
initStateWithCall :: ExtractedG2
-> Bool
-> StartFunc
-> ModuleName
-> (Id -> MkCurrExpr)
-> (Expr -> MkArgTypes)
-> Config
-> (State (), Bindings)
initStateWithCall exg2 useAssert f m_mod mkCurr argTys config =
let
s = initSimpleState exg2
(ie, fe) = case findFunc f m_mod (IT.expr_env s) of
Left ie' -> ie'
Right errs -> error errs
in
initStateFromSimpleState s m_mod useAssert (mkCurr ie) (argTys fe) config
{-# INLINE initStateWithCall' #-}
initStateWithCall' :: ExtractedG2
-> StartFunc
-> ModuleName
-> (Id -> MkCurrExpr)
-> (Expr -> MkArgTypes)
-> Config
-> (State (), Bindings)
initStateWithCall' exg2 =
initStateWithCall exg2 False
{-# INLINE initStateFromSimpleStateWithCall #-}
initStateFromSimpleStateWithCall :: IT.SimpleState
-> Bool
-> StartFunc
-> ModuleName
-> (Id -> MkCurrExpr)
-> (Expr -> MkArgTypes)
-> Config
-> (State (), Id, Bindings)
initStateFromSimpleStateWithCall simp_s useAssert f m_mod mkCurr argTys config =
let
(ie, fe) = case findFunc f m_mod (IT.expr_env simp_s) of
Left ie' -> ie'
Right errs -> error errs
(s, b) = initStateFromSimpleState simp_s m_mod useAssert (mkCurr ie) (argTys fe) config
in
(s, ie, b)
initStateFromSimpleState :: IT.SimpleState
-> Maybe T.Text
-> Bool
-> MkCurrExpr
-> MkArgTypes
-> Config
-> (State (), Bindings)
initStateFromSimpleState s m_mod useAssert mkCurr argTys config =
let
(s', ds_walkers) = runInitialization2 s argTys
eenv' = IT.expr_env s'
tenv' = IT.type_env s'
ng' = IT.name_gen s'
kv' = IT.known_values s'
tc' = IT.type_classes s'
(ce, is, f_i, ng'') = mkCurr tc' ng' eenv' tenv' ds_walkers kv' config
in
(State {
expr_env = foldr E.insertSymbolic eenv' is
, type_env = tenv'
, curr_expr = CurrExpr Evaluate ce
, path_conds = PC.fromList []
, non_red_path_conds = []
, true_assert = if useAssert then False else True
, assert_ids = Nothing
, type_classes = tc'
, exec_stack = Stack.empty
, model = HM.empty
, known_values = kv'
, rules = []
, num_steps = 0
, track = ()
, sym_gens = Seq.empty
, tags = S.empty
}
, Bindings {
deepseq_walkers = ds_walkers
, fixed_inputs = f_i
, arb_value_gen = arbValueInit
, cleaned_names = HM.empty
, input_names = map idName is
, higher_order_inst = S.filter (\n -> nameModule n == m_mod) . S.fromList $ IT.exports s
, rewrite_rules = IT.rewrite_rules s
, name_gen = ng''
, exported_funcs = IT.exports s })
mkArgTys :: Expr -> MkArgTypes
mkArgTys e simp_s =
snd $ instantiateArgTypes (IT.type_classes simp_s) (IT.known_values simp_s) e
{-# INLINE initStateFromSimpleState' #-}
initStateFromSimpleState' :: IT.SimpleState
-> StartFunc
-> ModuleName
-> Config
-> (State (), Bindings)
initStateFromSimpleState' s sf m_mod =
let
(ie, fe) = case findFunc sf m_mod (IT.expr_env s) of
Left ie' -> ie'
Right errs -> error errs
in
initStateFromSimpleState s m_mod False (mkCurrExpr Nothing Nothing ie) (mkArgTys fe)
{-# INLINE initSimpleState #-}
initSimpleState :: ExtractedG2
-> IT.SimpleState
initSimpleState (ExtractedG2 { exg2_binds = prog
, exg2_tycons = prog_typ
, exg2_classes = cls
, exg2_exports = es
, exg2_rules = rs }) =
let
eenv = E.fromExprMap prog
tenv = mkTypeEnv prog_typ
tc = initTypeClasses cls
kv = initKnownValues eenv tenv tc
ng = mkNameGen (prog, prog_typ, rs)
s = IT.SimpleState { IT.expr_env = eenv
, IT.type_env = tenv
, IT.name_gen = ng
, IT.known_values = kv
, IT.type_classes = tc
, IT.rewrite_rules = rs
, IT.exports = es }
in
runInitialization1 s
initCheckReaches :: State t -> ModuleName -> Maybe ReachFunc -> State t
initCheckReaches s@(State { expr_env = eenv
, known_values = kv }) m_mod reaches =
s {expr_env = checkReaches eenv kv reaches m_mod }
type RHOStack m = SM.StateT LengthNTrack (SM.StateT PrettyGuide (SM.StateT HpcTracker m))
{-# SPECIALIZE runReducer :: Ord b =>
Reducer (RHOStack IO) rv ()
-> Halter (RHOStack IO) hv ()
-> Orderer (RHOStack IO) sov b ()
-> State ()
-> Bindings
-> (RHOStack IO) (Processed (State ()), Bindings)
#-}
{-# SPECIALIZE
initRedHaltOrd :: (Solver solver, Simplifier simplifier) =>
Maybe T.Text
-> solver
-> simplifier
-> Config
-> (SomeReducer (RHOStack IO) (), SomeHalter (RHOStack IO) (), SomeOrderer (RHOStack IO) ())
#-}
initRedHaltOrd :: (MonadIO m, Solver solver, Simplifier simplifier) =>
Maybe T.Text
-> solver
-> simplifier
-> Config
-> (SomeReducer (RHOStack m) (), SomeHalter (RHOStack m) (), SomeOrderer (RHOStack m) ())
initRedHaltOrd mod_name solver simplifier config =
let
share = sharing config
state_name = Name "state" Nothing 0 Nothing
m_logger = fmap SomeReducer $ getLogger config
hpc_red f = case hpc config of
True -> SomeReducer (hpcReducer mod_name ~> stdRed share f solver simplifier)
False -> SomeReducer (stdRed share f solver simplifier)
logger_std_red f = case m_logger of
Just logger -> liftSomeReducer (logger .~> liftSomeReducer (hpc_red f))
Nothing -> liftSomeReducer (liftSomeReducer (hpc_red f))
halter = switchEveryNHalter 20
<~> maxOutputsHalter (maxOutputs config)
<~> zeroHalter (steps config)
<~> acceptIfViolatedHalter
orderer = case search_strat config of
Subpath -> SomeOrderer $ lengthNSubpathOrderer (subpath_length config)
Iterative -> SomeOrderer $ pickLeastUsedOrderer
in
case higherOrderSolver config of
AllFuncs ->
( logger_std_red retReplaceSymbFuncVar .== Finished .--> SomeReducer nonRedPCRed
, SomeHalter halter
, orderer)
SingleFunc ->
( logger_std_red retReplaceSymbFuncVar .== Finished .--> taggerRed state_name :== Finished --> nonRedPCRed
, SomeHalter (discardIfAcceptedTagHalter state_name <~> halter)
, orderer)
SymbolicFunc ->
( logger_std_red retReplaceSymbFuncTemplate .== Finished .--> SomeReducer nonRedPCRed
, SomeHalter halter
, orderer)
SymbolicFuncTemplate ->
( logger_std_red retReplaceSymbFuncVar .== Finished .--> taggerRed state_name :== Finished --> nonRedPCTemplates
, SomeHalter (discardIfAcceptedTagHalter state_name <~> halter)
, orderer)
initSolver :: Config -> IO SomeSolver
initSolver = initSolver' arbValue
initSolverInfinite :: Config -> IO SomeSolver
initSolverInfinite con = initSolver' arbValueInfinite con
initSolver' :: ArbValueFunc -> Config -> IO SomeSolver
initSolver' avf config = do
SomeSMTSolver con <- getSMTAV avf config
let con' = GroupRelated avf (UndefinedHigherOrder :?> (ADTNumericalSolver avf con))
return (SomeSolver con')
mkTypeEnv :: HM.HashMap Name AlgDataTy -> TypeEnv
mkTypeEnv = id
{-# INLINE initialStateFromFileSimple #-}
initialStateFromFileSimple :: [FilePath]
-> [FilePath]
-> StartFunc
-> (Id -> MkCurrExpr)
-> (Expr -> MkArgTypes)
-> Config
-> IO (State (), Id, Bindings)
initialStateFromFileSimple proj src f mkCurr argTys config =
initialStateFromFile proj src Nothing False f mkCurr argTys simplTranslationConfig config
initialStateNoStartFunc :: [FilePath]
-> [FilePath]
-> TranslationConfig
-> Config
-> IO (State (), Bindings)
initialStateNoStartFunc proj src transConfig config = do
(m_mod, exg2) <- translateLoaded proj src transConfig config
let simp_state = initSimpleState exg2
(init_s, bindings) = initStateFromSimpleState simp_state m_mod False
(\_ ng _ _ _ _ _ -> (Prim Undefined TyBottom, [], [], ng))
(E.higherOrderExprs . IT.expr_env)
config
return (init_s, bindings)
initialStateFromFile :: [FilePath]
-> [FilePath]
-> Maybe ReachFunc
-> Bool
-> StartFunc
-> (Id -> MkCurrExpr)
-> (Expr -> MkArgTypes)
-> TranslationConfig
-> Config
-> IO (State (), Id, Bindings)
initialStateFromFile proj src m_reach def_assert f mkCurr argTys transConfig config = do
(mb_modname, exg2) <- translateLoaded proj src transConfig config
let simp_state = initSimpleState exg2
(ie, fe) = case findFunc f mb_modname (IT.expr_env simp_state) of
Left ie' -> ie'
Right errs -> error errs
(init_s, bindings) = initStateFromSimpleState simp_state mb_modname def_assert
(mkCurr ie) (argTys fe) config
-- let (init_s, ent_f, bindings) = initState exg2 def_assert
-- f mb_modname mkCurr argTys config
reaches_state = initCheckReaches init_s mb_modname m_reach
return (reaches_state, ie, bindings)
runG2FromFile :: [FilePath]
-> [FilePath]
-> Maybe AssumeFunc
-> Maybe AssertFunc
-> Maybe ReachFunc
-> Bool
-> StartFunc
-> TranslationConfig
-> Config
-> IO (([ExecRes ()], Bindings), Id)
runG2FromFile proj src m_assume m_assert m_reach def_assert f transConfig config = do
(init_state, entry_f, bindings) <- initialStateFromFile proj src
m_reach def_assert f (mkCurrExpr m_assume m_assert) (mkArgTys)
transConfig config
r <- runG2WithConfig (nameModule $ idName entry_f) init_state config bindings
return (r, entry_f)
runG2WithConfig :: Maybe T.Text -> State () -> Config -> Bindings -> IO ([ExecRes ()], Bindings)
runG2WithConfig mod_name state config bindings = do
SomeSolver solver <- initSolver config
let simplifier = IdSimplifier
hpc_t <- hpcTracker
(in_out, bindings') <- case initRedHaltOrd mod_name solver simplifier config of
(red, hal, ord) ->
SM.evalStateT
(SM.evalStateT
(SM.evalStateT
(runG2WithSomes red hal ord solver simplifier emptyMemConfig state bindings)
lnt
)
(mkPrettyGuide ())
)
hpc_t
close solver
return (in_out, bindings')
{-# SPECIALIZE
runG2WithSomes :: ( Solver solver
, Simplifier simplifier)
=> SomeReducer (RHOStack IO) ()
-> SomeHalter (RHOStack IO) ()
-> SomeOrderer (RHOStack IO) ()
-> solver
-> simplifier
-> MemConfig
-> State ()
-> Bindings
-> RHOStack IO ([ExecRes ()], Bindings)
#-}
runG2WithSomes :: ( MonadIO m
, Named t
, ASTContainer t Expr
, ASTContainer t Type
, Solver solver
, Simplifier simplifier)
=> SomeReducer m t
-> SomeHalter m t
-> SomeOrderer m t
-> solver
-> simplifier
-> MemConfig
-> State t
-> Bindings
-> m ([ExecRes t], Bindings)
runG2WithSomes red hal ord solver simplifier mem state bindings =
case (red, hal, ord) of
(SomeReducer red', SomeHalter hal', SomeOrderer ord') ->
runG2 red' hal' ord' solver simplifier mem state bindings
runG2Pre :: ( Named t
, ASTContainer t Expr
, ASTContainer t Type) => MemConfig -> State t -> Bindings -> (State t, Bindings)
runG2Pre mem s bindings =
let
(swept, bindings') = markAndSweepPreserving mem s bindings
in
runPreprocessing swept bindings'
runG2Post :: ( MonadIO m
, Named t
, ASTContainer t Expr
, ASTContainer t Type
, Solver solver
, Simplifier simplifier
, Ord b) => Reducer m rv t -> Halter m hv t -> Orderer m sov b t ->
solver -> simplifier -> State t -> Bindings -> m ([ExecRes t], Bindings)
runG2Post red hal ord solver simplifier is bindings = do
(exec_states, bindings') <- runExecution red hal ord is bindings
sol_states <- mapM (runG2Solving solver simplifier bindings') exec_states
return (catMaybes sol_states, bindings')
{-# SPECIALISE runG2ThroughExecution ::
( Named t
, ASTContainer t Expr
, ASTContainer t Type
, Ord b) => Reducer IO rv t -> Halter IO hv t -> Orderer IO sov b t ->
MemConfig -> State t -> Bindings -> IO ([State t], Bindings) #-}
{-# SPECIALISE runG2ThroughExecution ::
( Named t
, ASTContainer t Expr
, ASTContainer t Type
, Ord b) => Reducer (SM.StateT PrettyGuide IO) rv t -> Halter (SM.StateT PrettyGuide IO) hv t -> Orderer (SM.StateT PrettyGuide IO) sov b t ->
MemConfig -> State t -> Bindings -> SM.StateT PrettyGuide IO ([State t], Bindings) #-}
runG2ThroughExecution ::
( Monad m
, Named t
, ASTContainer t Expr
, ASTContainer t Type
, Ord b) => Reducer m rv t -> Halter m hv t -> Orderer m sov b t ->
MemConfig -> State t -> Bindings -> m ([State t], Bindings)
runG2ThroughExecution red hal ord mem is bindings = do
let (is', bindings') = runG2Pre mem is bindings
runExecution red hal ord is' bindings'
{-# INLINABLE runG2ThroughExecution #-}
runG2SolvingResult :: ( Named t
, Solver solver
, Simplifier simplifier) =>
solver
-> simplifier
-> Bindings
-> State t
-> IO (Result (ExecRes t) () ())
runG2SolvingResult solver simplifier bindings s
| true_assert s = do
r <- solve solver s bindings (E.symbolicIds . expr_env $ s) (path_conds s)
case r of
SAT m -> do
let m' = reverseSimplification simplifier s bindings m
return . SAT $ runG2SubstModel m' s bindings
UNSAT _ -> return $ UNSAT ()
Unknown reason _ -> return $ Unknown reason ()
| otherwise = return $ UNSAT ()
runG2Solving :: ( MonadIO m
, Named t
, Solver solver
, Simplifier simplifier) =>
solver
-> simplifier
-> Bindings
-> State t
-> m (Maybe (ExecRes t))
runG2Solving solver simplifier bindings s = do
res <- liftIO $ runG2SolvingResult solver simplifier bindings s
case res of
SAT m -> return $ Just m
_ -> return Nothing
runG2SubstModel :: Named t =>
Model
-> State t
-> Bindings
-> ExecRes t
runG2SubstModel m s@(State { type_env = tenv, known_values = kv }) bindings =
let
s' = s { model = m }
(es, e, ais, gens) = subModel s' bindings
sm = ExecRes { final_state = s'
, conc_args = es
, conc_out = e
, conc_sym_gens = gens
, violated = ais}
sm' = runPostprocessing bindings sm
sm'' = ExecRes { final_state = final_state sm'
, conc_args = fixed_inputs bindings ++ conc_args sm'
, conc_out = evalPrims tenv kv (conc_out sm')
, conc_sym_gens = gens
, violated = evalPrims tenv kv (violated sm')}
in
sm''
{-# SPECIALIZE runG2 :: ( Solver solver
, Simplifier simplifier
, Ord b) => Reducer (RHOStack IO) rv () -> Halter (RHOStack IO) hv () -> Orderer (RHOStack IO) sov b () ->
solver -> simplifier -> MemConfig -> State () -> Bindings -> RHOStack IO ([ExecRes ()], Bindings)
#-}
-- | Runs G2, returning both fully executed states,
-- and states that have only been partially executed.
runG2 :: ( MonadIO m
, Named t
, ASTContainer t Expr
, ASTContainer t Type
, Solver solver
, Simplifier simplifier
, Ord b) => Reducer m rv t -> Halter m hv t -> Orderer m sov b t ->
solver -> simplifier -> MemConfig -> State t -> Bindings -> m ([ExecRes t], Bindings)
runG2 red hal ord solver simplifier mem is bindings = do
(exec_states, bindings') <- runG2ThroughExecution red hal ord mem is bindings
sol_states <- mapM (runG2Solving solver simplifier bindings') exec_states
return (catMaybes sol_states, bindings')