g2-0.1.0.0: src/G2/Solver/ADTSolver.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
module G2.Solver.ADTSolver ( ADTSolver (..)
, adtSolverFinite
, adtSolverInfinite
, checkConsistency
, findConsistent) where
import G2.Language.ArbValueGen
import G2.Language.Casts
import G2.Language.Expr
import qualified G2.Language.ExprEnv as E
import G2.Language.Naming
import G2.Language.Support
import G2.Language.Syntax
import G2.Language.PathConds hiding (map, filter, null)
import qualified G2.Language.PathConds as PC
import G2.Language.Typing
import G2.Solver.Solver
import Data.List
import qualified Data.Map as M
import Data.Maybe
import Prelude hiding (null)
import qualified Prelude as Pre
import Data.Tuple
data ADTSolver = ADTSolver ArbValueFunc
adtSolverFinite :: ADTSolver
adtSolverFinite = ADTSolver arbValue
adtSolverInfinite :: ADTSolver
adtSolverInfinite = ADTSolver arbValueInfinite
instance Solver ADTSolver where
check _ s = return .checkConsistency (known_values s) (expr_env s) (type_env s)
solve (ADTSolver avf) s b is = solveADTs avf s b (nub is)
-- | Attempts to detemine if the given PathConds are consistent.
-- Returns Just True if they are, Just False if they are not,
-- and Nothing if it can't decide.
checkConsistency :: KnownValues -> ExprEnv -> TypeEnv -> PathConds -> Result
checkConsistency kv eenv tenv pc
| all PC.isPCExists $ PC.toList pc = SAT
| otherwise =
maybe (Unknown "Non-ADT path constraints")
(\me -> if not (Pre.null me) then SAT else UNSAT)
$ findConsistent kv eenv tenv $ PC.filter (not . PC.isPCExists) pc
-- | Attempts to find expressions (Data d) or (Coercion (Data d), (t1 :~ t2)) consistent with the given path
-- constraints. Returns Just [...] if it can determine [...] are consistent.
-- Just [] means there are no consistent Expr. Nothing nmeans it could not be
-- determined if there were any consistent data constructors.
-- In practice, the result should always be Just [...] if all the path conds
-- are about ADTs.
findConsistent :: KnownValues -> ExprEnv -> TypeEnv -> PathConds -> Maybe [Expr]
findConsistent kv eenv tenv = fmap fst . findConsistent' kv eenv tenv
head' :: [a] -> Maybe a
head' (x:_) = Just x
head' _ = Nothing
findConsistent' :: KnownValues -> ExprEnv -> TypeEnv -> PathConds -> Maybe ([Expr], [(Id, Type)])
findConsistent' kv eenv tenv pc =
let
pc' = unsafeElimCast $ toList pc
-- Adding Coercions
pcNT = fmap (pcInCastType tenv) . head' $ toList pc
cons = findConsistent'' kv tenv eenv pc'
in
case cons of
Just (cons', bi) ->
let
cons'' = simplifyCasts . map (castReturnType $ fromJust pcNT) $ cons'
in
-- We can't use the ADT solver when we have a Boolean, because the RHS of the
-- DataAlt might be a primitive.
if any isExtCond pc' || pcNT == Just (tyBool kv) then Nothing else Just (cons'', bi)
Nothing -> Nothing
findConsistent'' :: KnownValues -> TypeEnv -> ExprEnv -> [PathCond] -> Maybe ([Expr], [(Id, Type)])
findConsistent'' kv tenv eenv pc =
let
is = nub . map (\(Id n t') -> Id n (typeStripCastType tenv t')) $ concatMap (varIdsInPC kv) pc
t = pcVarType tenv pc
cons = maybe Nothing (flip getCastedAlgDataTy tenv) t
in
case (cons, is) of
(Just (DataTyCon _ dc, bi), [i]) ->
let
dc' = case E.lookup (idName i) eenv of
Just e
| Data spec_dc:_ <- unApp e -> [spec_dc]
_ -> dc
cons' = fmap (map Data) $ findConsistent''' dc' pc
in
maybe Nothing (Just . (, bi)) cons'
_ -> Nothing
findConsistent''' :: [DataCon] -> [PathCond] -> Maybe [DataCon]
findConsistent''' dcs ((ConsCond dc _ True):pc) =
findConsistent''' (filter ((==) (dcName dc) . dcName) dcs) pc
findConsistent''' dcs ((ConsCond dc _ False):pc) =
findConsistent''' (filter ((/=) (dcName dc) . dcName) dcs) pc
-- findConsistent''' dcs (PCExists _:pc) = findConsistent''' dcs pc
findConsistent''' dcs [] = Just dcs
findConsistent''' _ _ = Nothing
solveADTs :: ArbValueFunc -> State t -> Bindings -> [Id] -> PathConds -> IO (Result, Maybe Model)
solveADTs avf s@(State { expr_env = eenv, model = m }) b [Id n t] pc
| not $ E.isSymbolic n eenv
, Just e <- E.lookup n eenv = return (SAT, Just . liftCasts $ M.insert n e m )
-- We can't use the ADT solver when we have a Boolean, because the RHS of the
-- DataAlt might be a primitive.
| TyCon tn k <- tyAppCenter t
, ts <- tyAppArgs t
, t /= tyBool (known_values s) =
do
let (r, s', _) = addADTs avf n tn ts k s b (PC.filter (not . isPCExists) pc)
case r of
SAT -> return (r, Just . liftCasts $ model s')
r' -> return (r', Nothing)
solveADTs _ _ _ _ _ = return (Unknown "Unhandled path constraints in ADTSolver", Nothing)
-- | Determines an ADT based on the path conds. The path conds form a witness.
-- In particular, refer to findConsistent in Solver/ADTSolver.hs
addADTs :: ArbValueFunc -> Name -> Name -> [Type] -> Kind -> State t -> Bindings -> PathConds -> (Result, State t, Bindings)
addADTs avf n tn ts k s b pc
| PC.null pc =
let
(bse, av) = avf (mkTyApp (TyCon tn k:ts)) (type_env s) (arb_value_gen b)
m' = M.singleton n bse
in
(SAT, (s {model = M.union m' (model s)}), (b {arb_value_gen = av}))
| Just (dcs@(fdc:_), bi) <- findConsistent' (known_values s) (expr_env s) (type_env s) pc =
let
eenv = expr_env s
ts2 = map snd bi
-- We map names over the arguments of a DataCon, to make sure we have the correct
-- number of undefined's.
ts'' = case exprInCasts fdc of
Data (DataCon _ ts') -> anonArgumentTypes $ PresType ts'
_ -> [] -- [Name "b" Nothing 0 Nothing]
(ns, _) = childrenNames n (map (const $ Name "a" Nothing 0 Nothing) ts'') (name_gen b)
(av, vs) = mapAccumL (\av_ (n', t') ->
case E.lookup n' eenv of
Just e -> (av_, e)
Nothing -> swap $ avf t' (type_env s) av_) (arb_value_gen b) $ zip ns ts''
dc = mkApp $ fdc:map Type ts2 ++ vs
m = M.insert n dc (model s)
in
case not . Pre.null $ dcs of
True -> (SAT, s { model = M.union m (model s) }, b { arb_value_gen = av })
False -> (UNSAT, s, b)
| otherwise = (UNSAT, s, b)
-- Various helper functions
isExtCond :: PathCond -> Bool
isExtCond (ExtCond _ _) = True
isExtCond _ = False
pcVarType :: TypeEnv -> [PathCond] -> Maybe Type
pcVarType tenv (AltCond _ (Var (Id _ t)) _:pc) = pcVarType' t tenv pc
pcVarType tenv (ConsCond _ (Var (Id _ t)) _:pc) = pcVarType' t tenv pc
pcVarType _ _ = Nothing
pcVarType' :: Type -> TypeEnv -> [PathCond] -> Maybe Type
pcVarType' t tenv (AltCond _ (Var (Id _ t')) _:pc) =
if t == t' then pcVarType' t tenv pc else Nothing
pcVarType' t tenv (ConsCond _ (Var (Id _ t')) _:pc) =
if t == t' then pcVarType' t tenv pc else Nothing
pcVarType' n _ [] = Just n
pcVarType' _ _ _ = Nothing
pcInCastType :: TypeEnv -> PathCond -> Type
pcInCastType _ (AltCond _ e _) = typeInCasts e
pcInCastType _ (ExtCond e _) = typeInCasts e
pcInCastType _ (ConsCond _ e _) = typeInCasts e
pcInCastType tenv (PCExists (Id _ t)) = typeStripCastType tenv t
castReturnType :: Type -> Expr -> Expr
castReturnType t e =
let
te = typeOf e
tr = replaceReturnType te t
in
Cast e (te :~ tr)
replaceReturnType :: Type -> Type -> Type
replaceReturnType (TyForAll b t) r = TyForAll b $ replaceReturnType t r
replaceReturnType (TyFun t1 t2@(TyFun _ _)) r = TyFun t1 $ replaceReturnType t2 r
replaceReturnType (TyFun t _) r = TyFun t r
replaceReturnType _ r = r