module Test.Target.Monad
( whenVerbose
, noteUsed
, addDep
, addConstraint
, addConstructor
, addSort
, addVariable
, inModule
, making
, lookupCtor
, guarded
, fresh
, freshChoice
, freshInt
, getValue
, Target, runTarget
, TargetState(..), initState
, TargetOpts(..), defaultOpts
) where
import Control.Applicative
import Control.Arrow (first, second, (***))
import qualified Control.Exception as Ex
import Control.Monad
import Control.Monad.Catch
import Control.Monad.Reader
import Control.Monad.State
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import Data.IORef
import Data.List hiding (sort)
import qualified Data.Text as ST
import System.IO.Unsafe
import Language.Fixpoint.Smt.Interface hiding (SMTLIB2(..))
import Language.Fixpoint.Types
import Language.Fixpoint.Types.Config (SMTSolver(..))
import Language.Haskell.Liquid.Types.PredType
import Language.Haskell.Liquid.Types.RefType
import Language.Haskell.Liquid.Types hiding (var, Target)
import qualified GHC
import qualified Type as GHC
import Test.Target.Types
import Test.Target.Util
newtype Target a = Target (StateT TargetState (ReaderT TargetOpts IO) a)
deriving ( Functor, Applicative, Monad, MonadIO, Alternative
, MonadState TargetState, MonadCatch, MonadReader TargetOpts )
instance MonadThrow Target where
throwM = Ex.throw
runTarget :: TargetOpts -> TargetState -> Target a -> IO a
runTarget opts st (Target x) = runReaderT (evalStateT x st) opts
seed :: IORef Int
seed = unsafePerformIO $ newIORef 0
freshInt :: Target Int
freshInt = liftIO $ do
n <- readIORef seed
modifyIORef' seed (+1)
return n
data TargetOpts = TargetOpts
{ depth :: !Int
, solver :: !SMTSolver
, verbose :: !Bool
, logging :: !Bool
, keepGoing :: !Bool
, maxSuccess :: !(Maybe Int)
, scDepth :: !Bool
, ghcOpts :: ![String]
}
defaultOpts :: TargetOpts
defaultOpts = TargetOpts
{ depth = 3
, solver = Z3
, verbose = False
, logging = True
, keepGoing = False
, maxSuccess = Nothing
, scDepth = True
, ghcOpts = []
}
data TargetState = TargetState
{ variables :: ![Variable]
, choices :: ![Variable]
, constraints :: !Constraint
, deps :: !(M.HashMap Symbol [Symbol])
, realized :: ![(Symbol, Value)]
, ctorEnv :: !DataConEnv
, measEnv :: !MeasureEnv
, embEnv :: !(TCEmb GHC.TyCon)
, tyconInfo :: !(M.HashMap GHC.TyCon RTyCon)
, freesyms :: ![(Symbol,Symbol)]
, constructors :: ![Variable]
, sigs :: ![(Symbol, SpecType)]
, chosen :: !(Maybe Symbol)
, sorts :: !(S.HashSet Sort)
, modName :: !Symbol
, filePath :: !FilePath
, makingTy :: !Sort
, smtContext :: !Context
}
initState :: FilePath -> GhcSpec -> Context -> TargetState
initState fp sp ctx = TargetState
{ variables = []
, choices = []
, constraints = []
, deps = mempty
, realized = []
, ctorEnv = cts
, measEnv = meas
, embEnv = gsTcEmbeds sp
, tyconInfo = tyi
, freesyms = free
, constructors = []
, sigs = sigs
, chosen = Nothing
, sorts = S.empty
, modName = ""
, filePath = fp
, makingTy = FObj ""
, smtContext = ctx
}
where
cts = subst su $ tidyF $ map (symbol *** val) (gsCtors sp)
sigs = subst su $ tidyF $ map (symbol *** val) $ gsTySigs sp
tyi = makeTyConInfo (gsTconsP sp)
free = tidyS $ map (second symbol)
$ gsFreeSyms sp ++ map (\(c,_) -> (symbol c, c)) (gsCtors sp)
meas = gsMeasures sp
tidyF = map (first tidySymbol)
tidyS = map (second tidySymbol)
su = mkSubst (map (second eVar) free)
whenVerbose :: Target () -> Target ()
whenVerbose x
= do v <- asks verbose
when v x
noteUsed :: (Symbol, Value) -> Target ()
noteUsed (v,x) = modify $ \s@(TargetState {..}) -> s { realized = (v,x) : realized }
addDep :: Symbol -> Expr -> Target ()
addDep from (EVar to) = modify $ \s@(TargetState {..}) ->
s { deps = M.insertWith (flip (++)) from [to] deps }
addDep _ _ = return ()
addConstraint :: Expr -> Target ()
addConstraint p = modify $ \s@(TargetState {..}) -> s { constraints = p:constraints }
addConstructor :: Variable -> Target ()
addConstructor c
= modify $ \s@(TargetState {..}) -> s { constructors = nub $ c:constructors }
inModule :: Symbol -> Target a -> Target a
inModule m act
= do m' <- gets modName
modify $ \s -> s { modName = m }
r <- act
modify $ \s -> s { modName = m' }
return r
making :: Sort -> Target a -> Target a
making ty act
= do ty' <- gets makingTy
modify $ \s -> s { makingTy = ty }
r <- act
modify $ \s -> s { makingTy = ty' }
return r
lookupCtor :: Symbol -> SpecType -> Target SpecType
lookupCtor c (toType -> t)
= do mt <- find (\(c', _) -> symbolText c == symbolText c')
<$> gets ctorEnv
case mt of
Just (_, t) -> return t
Nothing -> do
let tc = GHC.tyConAppTyCon t
let dcs = GHC.tyConDataCons tc
let Just dc = find (\d -> c == symbol (GHC.getName d)) dcs
let t = ofType (GHC.dataConUserType dc)
modify $ \s@(TargetState {..}) -> s { ctorEnv = (c,t) : ctorEnv }
return t
guarded :: String -> Target Expr -> Target (Expr, Expr)
guarded cn act
= do c <- freshChoice cn
mc <- gets chosen
modify $ \s -> s { chosen = Just c }
x <- act
modify $ \s -> s { chosen = mc }
return (x, EVar c)
fresh :: Sort -> Target Symbol
fresh sort
= do n <- freshInt
let sorts' = sortTys sort
let x = symbol $ ST.unpack (ST.intercalate "->" $ map (symbolText.unObj) sorts') ++ show n
addVariable (x, sort)
return x
addSort :: Sort -> Target ()
addSort sort = do
let sorts' = sortTys sort
modify $ \s@(TargetState {..}) -> s { sorts = S.union (S.fromList (arrowize sort : sorts')) sorts }
addVariable :: Variable -> Target ()
addVariable (v, sort) = do
addSort sort
modify $ \s@(TargetState {..}) -> s { variables = (v, sort) : variables }
sortTys :: Sort -> [Sort]
sortTys t
| Just (_, ts, t) <- functionSort t
= concatMap sortTys ts ++ [t]
| otherwise
= [t]
arrowize :: Sort -> Sort
arrowize = FObj . symbol . ST.intercalate "->" . map (symbolText . unObj) . sortTys
unObj :: Sort -> Symbol
unObj FInt = "Int"
unObj (FObj s) = s
unObj s = error $ "unObj: " ++ show s
freshChoice :: String -> Target Symbol
freshChoice cn
= do n <- freshInt
let x = intSymbol (unObj choicesort) (cn ++ "-" ++ show n)
modify $ \s@(TargetState {..}) -> s { variables = (x, choicesort) : variables }
return x
getValue :: Symbol -> Target Value
getValue v = do
ctx <- gets smtContext
Values [x] <- io $ ensureValues $ command ctx (GetValue [v])
noteUsed x
return (snd x)