liquid-fixpoint-8.10.7: src/Language/Fixpoint/Solver/Monad.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
-- | This is a wrapper around IO that permits SMT queries
module Language.Fixpoint.Solver.Monad
( -- * Type
SolveM
-- * Execution
, runSolverM
-- * Get Binds
, getBinds
-- * SMT Query
, filterRequired
, filterValid
, filterValidGradual
, checkSat
, smtEnablembqi
, sendConcreteBindingsToSMT
-- * Debug
, Stats
, tickIter
, stats
, numIter
, SolverState(..)
)
where
import Language.Fixpoint.Utils.Progress
import qualified Language.Fixpoint.Types.Config as C
import Language.Fixpoint.Types.Config (Config)
import qualified Language.Fixpoint.Types as F
-- import qualified Language.Fixpoint.Misc as Misc
-- import Language.Fixpoint.SortCheck
import qualified Language.Fixpoint.Types.Solutions as F
import qualified Language.Fixpoint.Types.Visitor as F
-- import qualified Language.Fixpoint.Types.Errors as E
import Language.Fixpoint.Smt.Serialize ()
import Language.Fixpoint.Types.PrettyPrint ()
import Language.Fixpoint.Smt.Interface
-- import qualified Language.Fixpoint.Smt.Theories as Thy
import Language.Fixpoint.Solver.Sanitize
import Language.Fixpoint.Solver.Stats
import Language.Fixpoint.Graph.Types (SolverInfo (..))
-- import Language.Fixpoint.Solver.Solution
-- import Data.Maybe (catMaybes)
import Data.List (partition)
-- import Data.Char (isUpper)
import Control.Monad.State.Strict
import qualified Data.HashMap.Strict as M
import Data.Maybe (catMaybes)
import Control.Exception.Base (bracket)
--------------------------------------------------------------------------------
-- | Solver Monadic API --------------------------------------------------------
--------------------------------------------------------------------------------
type SolveM = StateT SolverState IO
data SolverState = SS
{ ssCtx :: !Context -- ^ SMT Solver Context
, ssBinds :: !F.BindEnv -- ^ All variables and types
, ssStats :: !Stats -- ^ Solver Statistics
}
stats0 :: F.GInfo c b -> Stats
stats0 fi = Stats nCs 0 0 0 0
where
nCs = M.size $ F.cm fi
--------------------------------------------------------------------------------
runSolverM :: Config -> SolverInfo b c -> SolveM a -> IO a
--------------------------------------------------------------------------------
runSolverM cfg sI act =
bracket acquire release $ \ctx -> do
res <- runStateT act' (s0 ctx)
smtExit ctx
return (fst res)
where
s0 ctx = SS ctx be (stats0 fi)
act' = assumesAxioms (F.asserts fi) >> act
release = cleanupContext
acquire = makeContextWithSEnv cfg file initEnv
initEnv = symbolEnv cfg fi
be = F.bs fi
file = C.srcFile cfg
-- only linear arithmentic when: linear flag is on or solver /= Z3
-- lar = linear cfg || Z3 /= solver cfg
fi = (siQuery sI) {F.hoInfo = F.HOI (C.allowHO cfg) (C.allowHOqs cfg)}
--------------------------------------------------------------------------------
getBinds :: SolveM F.BindEnv
--------------------------------------------------------------------------------
getBinds = ssBinds <$> get
--------------------------------------------------------------------------------
getIter :: SolveM Int
--------------------------------------------------------------------------------
getIter = numIter . ssStats <$> get
--------------------------------------------------------------------------------
incIter, incBrkt :: SolveM ()
--------------------------------------------------------------------------------
incIter = modifyStats $ \s -> s {numIter = 1 + numIter s}
incBrkt = modifyStats $ \s -> s {numBrkt = 1 + numBrkt s}
--------------------------------------------------------------------------------
incChck, incVald :: Int -> SolveM ()
--------------------------------------------------------------------------------
incChck n = modifyStats $ \s -> s {numChck = n + numChck s}
incVald n = modifyStats $ \s -> s {numVald = n + numVald s}
withContext :: (Context -> IO a) -> SolveM a
withContext k = (lift . k) =<< getContext
getContext :: SolveM Context
getContext = ssCtx <$> get
modifyStats :: (Stats -> Stats) -> SolveM ()
modifyStats f = modify $ \s -> s { ssStats = f (ssStats s) }
--------------------------------------------------------------------------------
-- | SMT Interface -------------------------------------------------------------
--------------------------------------------------------------------------------
-- | Takes the environment of bindings already known to the SMT,
-- and the environment of all bindings that need to be known.
--
-- Yields the ids of bindings known to the SMT
sendConcreteBindingsToSMT
:: F.IBindEnv -> (F.IBindEnv -> SolveM a) -> SolveM a
sendConcreteBindingsToSMT known act = do
be <- getBinds
let concretePreds =
[ (i, F.subst1 p (v, F.EVar s))
| (i, s, F.RR _ (F.Reft (v, p))) <- F.bindEnvToList be
, F.isConc p
, not (isShortExpr p)
, not (F.memberIBindEnv i known)
]
st <- get
(a, st') <- withContext $ \me -> do
smtBracket me "" $ do
forM_ concretePreds $ \(i, e) ->
smtDefineFunc me (F.bindSymbol (fromIntegral i)) [] F.boolSort e
flip runStateT st $ act $ F.unionIBindEnv known $ F.fromListIBindEnv $ map fst concretePreds
put st'
return a
where
isShortExpr F.PTrue = True
isShortExpr F.PTop = True
isShortExpr _ = False
-- | `filterRequired [(x1, p1),...,(xn, pn)] q` returns a minimal list [xi] s.t.
-- /\ [pi] => q
--------------------------------------------------------------------------------
filterRequired :: F.Cand a -> F.Expr -> SolveM [a]
--------------------------------------------------------------------------------
filterRequired = error "TBD:filterRequired"
{-
(set-option :produce-unsat-cores true)
(declare-fun x () Int)
(declare-fun y () Int)
(declare-fun z () Int)
; Z3 will only track assertions that are named.
(assert (< 0 x))
(assert (! (< 0 y) :named b2))
(assert (! (< x 10) :named b3))
(assert (! (< y 10) :named b4))
(assert (! (< (+ x y) 0) :named bR))
(check-sat)
(get-unsat-core)
> unsat (b2 bR)
-}
--------------------------------------------------------------------------------
-- | `filterValid p [(x1, q1),...,(xn, qn)]` returns the list `[ xi | p => qi]`
--------------------------------------------------------------------------------
{-# SCC filterValid #-}
filterValid :: F.SrcSpan -> F.Expr -> F.Cand a -> SolveM [a]
--------------------------------------------------------------------------------
filterValid sp p qs = do
qs' <- withContext $ \me ->
smtBracket me "filterValidLHS" $
filterValid_ sp p qs me
-- stats
incBrkt
incChck (length qs)
incVald (length qs')
return qs'
{-# SCC filterValid_ #-}
filterValid_ :: F.SrcSpan -> F.Expr -> F.Cand a -> Context -> IO [a]
filterValid_ sp p qs me = catMaybes <$> do
smtAssertAsync me p
forM_ qs $ \(q, _x) ->
smtBracketAsyncAt sp me "filterValidRHS" $ do
smtAssertAsync me (F.PNot q)
smtCheckUnsatAsync me
forM qs $ \(_, x) -> do
valid <- readCheckUnsat me
return $ if valid then Just x else Nothing
--------------------------------------------------------------------------------
-- | `filterValidGradual ps [(x1, q1),...,(xn, qn)]` returns the list `[ xi | p => qi]`
-- | for some p in the list ps
--------------------------------------------------------------------------------
filterValidGradual :: [F.Expr] -> F.Cand a -> SolveM [a]
--------------------------------------------------------------------------------
filterValidGradual p qs = do
qs' <- withContext $ \me ->
smtBracket me "filterValidGradualLHS" $
filterValidGradual_ p qs me
-- stats
incBrkt
incChck (length qs)
incVald (length qs')
return qs'
filterValidGradual_ :: [F.Expr] -> F.Cand a -> Context -> IO [a]
filterValidGradual_ ps qs me
= (map snd . fst) <$> foldM partitionCandidates ([], qs) ps
where
partitionCandidates :: (F.Cand a, F.Cand a) -> F.Expr -> IO (F.Cand a, F.Cand a)
partitionCandidates (ok, candidates) p = do
(valids', invalids') <- partition snd <$> filterValidOne_ p candidates me
let (valids, invalids) = (fst <$> valids', fst <$> invalids')
return (ok ++ valids, invalids)
filterValidOne_ :: F.Expr -> F.Cand a -> Context -> IO [((F.Expr, a), Bool)]
filterValidOne_ p qs me = do
smtAssert me p
forM qs $ \(q, x) ->
smtBracket me "filterValidRHS" $ do
smtAssert me (F.PNot q)
valid <- smtCheckUnsat me
return $ ((q, x), valid)
smtEnablembqi :: SolveM ()
smtEnablembqi
= withContext smtSetMbqi
--------------------------------------------------------------------------------
checkSat :: F.Expr -> SolveM Bool
--------------------------------------------------------------------------------
checkSat p
= withContext $ \me ->
smtBracket me "checkSat" $
smtCheckSat me p
--------------------------------------------------------------------------------
assumesAxioms :: [F.Triggered F.Expr] -> SolveM ()
--------------------------------------------------------------------------------
assumesAxioms es = withContext $ \me -> forM_ es $ smtAssertAxiom me
---------------------------------------------------------------------------
stats :: SolveM Stats
---------------------------------------------------------------------------
stats = ssStats <$> get
---------------------------------------------------------------------------
tickIter :: Bool -> SolveM Int
---------------------------------------------------------------------------
tickIter newScc = progIter newScc >> incIter >> getIter
progIter :: Bool -> SolveM ()
progIter newScc = lift $ when newScc progressTick