{-# LANGUAGE OverloadedStrings #-} module G2.Execution.Rules ( module G2.Execution.RuleTypes , stdReduce , evalVar , evalApp , evalLam , retLam , evalLet , evalCase , evalCast , evalTick , evalNonDet , evalSymGen , evalAssume , evalAssert , isExecValueForm ) where import G2.Execution.NormalForms import G2.Execution.PrimitiveEval import G2.Execution.RuleTypes import G2.Language import qualified G2.Language.ExprEnv as E import qualified G2.Language.KnownValues as KV import qualified G2.Language.PathConds as PC import qualified G2.Language.Stack as S import G2.Solver hiding (Assert) import Control.Monad.Extra import Data.Maybe stdReduce :: Solver solver => solver -> State t -> Bindings -> IO (Rule, [(State t, ())], Bindings) stdReduce solver s b@(Bindings {name_gen = ng}) = do (r, s', ng') <- stdReduce' solver s ng let s'' = map (\ss -> ss { rules = r:rules ss }) s' return (r, zip s'' (repeat ()), b { name_gen = ng'}) stdReduce' :: Solver solver => solver -> State t -> NameGen -> IO (Rule, [State t], NameGen) stdReduce' solver s@(State { curr_expr = CurrExpr Evaluate ce }) ng | Var i <- ce = return $ evalVar s ng i | App e1 e2 <- ce = return $ evalApp s ng e1 e2 | Let b e <- ce = return $ evalLet s ng b e | Case e i a <- ce = do let (r, xs, ng') = evalCase s ng e i a xs' <- mapMaybeM (reduceNewPC solver) xs return (r, xs', ng') | Cast e c <- ce = return $ evalCast s ng e c | Tick t e <- ce = return $ evalTick s ng t e | NonDet es <- ce = return $ evalNonDet s ng es | SymGen t <- ce = return $ evalSymGen s ng t | Assume fc e1 e2 <- ce = return $ evalAssume s ng fc e1 e2 | Assert fc e1 e2 <- ce = return $ evalAssert s ng fc e1 e2 | otherwise = return (RuleReturn, [s { curr_expr = CurrExpr Return ce }], ng) stdReduce' solver s@(State { curr_expr = CurrExpr Return ce , exec_stack = stck }) ng | Prim Error _ <- ce , Just (AssertFrame is _, stck') <- S.pop stck = return (RuleError, [s { exec_stack = stck' , true_assert = True , assert_ids = is }], ng) | Prim Error _ <- ce , Just (_, stck') <- S.pop stck = return (RuleError, [s { exec_stack = stck' }], ng) | Just (UpdateFrame n, stck') <- frstck = return $ retUpdateFrame s ng n stck' | Lam u i e <- ce = return $ retLam s ng u i e | Just (ApplyFrame e, stck') <- S.pop stck = return $ retApplyFrame s ng ce e stck' | Just rs <- retReplaceSymbFunc s ng ce = return rs | Just (CaseFrame i a, stck') <- frstck = return $ retCaseFrame s ng ce i a stck' | Just (CastFrame c, stck') <- frstck = return $ retCastFrame s ng ce c stck' | Just (AssumeFrame e, stck') <- frstck = do let (r, xs, ng') = retAssumeFrame s ng ce e stck' xs' <- mapMaybeM (reduceNewPC solver) xs return (r, xs', ng') | Just (AssertFrame ais e, stck') <- frstck = do let (r, xs, ng') = retAssertFrame s ng ce ais e stck' xs' <- mapMaybeM (reduceNewPC solver) xs return (r, xs', ng') | Just (CurrExprFrame e, stck') <- frstck = do let (r, xs) = retCurrExpr s ce e stck' xs' <- mapMaybeM (reduceNewPC solver) xs return (r, xs', ng) | Nothing <- frstck = return (RuleIdentity, [s], ng) | otherwise = error $ "stdReduce': Unknown Expr" ++ show ce ++ show (S.pop stck) where frstck = S.pop stck data NewPC t = NewPC { state :: State t , new_pcs :: [PathCond] } newPCEmpty :: State t -> NewPC t newPCEmpty s = NewPC { state = s, new_pcs = []} reduceNewPC :: Solver solver => solver -> NewPC t -> IO (Maybe (State t)) reduceNewPC solver (NewPC { state = s@(State { known_values = kv , path_conds = spc }) , new_pcs = pc }) | not (null pc) = do -- In the case of newtypes, the PC exists we get may have the correct name -- but incorrect type. -- We do not want to add these to the State -- This is a bit ugly, but not a huge deal, since the State already has PCExists let pc' = filter (not . PC.isPCExists) pc -- Optimization -- We replace the path_conds with only those that are directly -- affected by the new path constraints -- This allows for more efficient solving, and in some cases may -- change an Unknown into a SAT or UNSAT let new_pc = foldr (PC.insert kv) spc $ pc' s' = s { path_conds = new_pc} let rel_pc = PC.filter (not . PC.isPCExists) $ PC.relevant kv pc new_pc res <- check solver s rel_pc if res == SAT then return $ Just s' else return Nothing | otherwise = return $ Just s evalVar :: State t -> NameGen -> Id -> (Rule, [State t], NameGen) evalVar s@(State { expr_env = eenv , exec_stack = stck }) ng i | E.isSymbolic (idName i) eenv = (RuleEvalVal, [s { curr_expr = CurrExpr Return (Var i)}], ng) | Just e <- E.lookup (idName i) eenv = -- If the target in our environment is already a value form, we do not -- need to push additional redirects for updating later on. -- If our variable is not in value form, we first push the -- current name of the variable onto the stack and evaluate the -- expression that it points to. After the evaluation, -- we pop the stack to add a redirection pointer into the heap. let (r, stck') = if isExprValueForm eenv e then ( RuleEvalVarVal (idName i), stck) else ( RuleEvalVarNonVal (idName i) , S.push (UpdateFrame (idName i)) stck) in (r, [s { curr_expr = CurrExpr Evaluate e , exec_stack = stck' }], ng) | otherwise = error $ "evalVar: bad input." ++ show i -- | If we have a primitive operator, we are at a point where either: -- (1) We can concretely evaluate the operator, or -- (2) We have a symbolic value, and no evaluation is possible, so we return -- If we do not have a primitive operator, we go into the center of the apps, -- to evaluate the function call evalApp :: State t -> NameGen -> Expr -> Expr -> (Rule, [State t], NameGen) evalApp s@(State { expr_env = eenv , known_values = kv , exec_stack = stck }) ng e1 e2 | (App (Prim BindFunc _) v) <- e1 , Var i1 <- findSym v , v2 <- e2 = ( RuleBind , [s { expr_env = E.insert (idName i1) v2 eenv , curr_expr = CurrExpr Return (mkTrue kv) }] , ng) | isExprValueForm eenv (App e1 e2) = ( RuleReturnAppSWHNF , [s { curr_expr = CurrExpr Return (App e1 e2) }] , ng) | (Prim prim ty):ar <- unApp (App e1 e2) = let ar' = map (lookupForPrim eenv) ar appP = mkApp (Prim prim ty : ar') exP = evalPrims kv appP in ( RuleEvalPrimToNorm , [s { curr_expr = CurrExpr Return exP }] , ng) | otherwise = let frame = ApplyFrame e2 stck' = S.push frame stck in ( RuleEvalApp e2 , [s { curr_expr = CurrExpr Evaluate e1 , exec_stack = stck' }] , ng) where findSym v@(Var (Id n _)) | E.isSymbolic n eenv = v | Just e <- E.lookup n eenv = findSym e findSym _ = error "findSym: No symbolic variable" lookupForPrim :: ExprEnv -> Expr -> Expr lookupForPrim eenv v@(Var (Id _ _)) = repeatedLookup eenv v lookupForPrim eenv (App e e') = App (lookupForPrim eenv e) (lookupForPrim eenv e') lookupForPrim _ e = e repeatedLookup :: ExprEnv -> Expr -> Expr repeatedLookup eenv v@(Var (Id n _)) | E.isSymbolic n eenv = v | otherwise = case E.lookup n eenv of Just v'@(Var _) -> repeatedLookup eenv v' Just e -> e Nothing -> v repeatedLookup _ e = e evalLam :: State t -> LamUse -> Id -> Expr -> (Rule, [State t]) evalLam = undefined retLam :: State t -> NameGen -> LamUse -> Id -> Expr -> (Rule, [State t], NameGen) retLam s@(State { expr_env = eenv , exec_stack = stck }) ng u i e | TypeL <- u , Just (ApplyFrame tf, stck') <- S.pop stck = case traceType eenv tf of Just t -> let e' = retype i t e binds = [(i, Type t)] (eenv', e'', ng', news) = liftBinds binds eenv e' ng in ( RuleReturnEApplyLamType news , [s { expr_env = eenv' , curr_expr = CurrExpr Evaluate e'' , exec_stack = stck' }] , ng') Nothing -> error "retLam: Bad type" | TermL <- u , Just (ApplyFrame ae, stck') <- S.pop stck = let binds = [(i, ae)] (eenv', e', ng', news) = liftBinds binds eenv e ng in ( RuleReturnEApplyLamExpr news , [s { expr_env = eenv' , curr_expr = CurrExpr Evaluate e' , exec_stack = stck' }] ,ng') | otherwise = error "retLam: Bad type" traceType :: E.ExprEnv -> Expr -> Maybe Type traceType _ (Type t) = Just t traceType eenv (Var (Id n _)) = traceType eenv =<< E.lookup n eenv traceType _ _ = Nothing evalLet :: State t -> NameGen -> Binds -> Expr -> (Rule, [State t], NameGen) evalLet s@(State { expr_env = eenv }) ng binds e = let (binds_lhs, binds_rhs) = unzip binds olds = map idName binds_lhs (news, ng') = freshSeededNames olds ng e' = renameExprs (zip olds news) e binds_rhs' = renameExprs (zip olds news) binds_rhs eenv' = E.insertExprs (zip news binds_rhs') eenv in (RuleEvalLet news, [s { expr_env = eenv' , curr_expr = CurrExpr Evaluate e'}] , ng') -- | Handle the Case forms of Evaluate. evalCase :: State t -> NameGen -> Expr -> Id -> [Alt] -> (Rule, [NewPC t], NameGen) evalCase s@(State { expr_env = eenv , exec_stack = stck }) ng mexpr bind alts -- Is the current expression able to match with a literal based `Alt`? If -- so, we do the cvar binding, and proceed with evaluation of the body. | (Lit lit) <- unsafeElimOuterCast mexpr , (Alt (LitAlt _) expr):_ <- matchLitAlts lit alts = let binds = [(bind, Lit lit)] expr' = liftCaseBinds binds expr in ( RuleEvalCaseLit , [newPCEmpty $ s { expr_env = eenv , curr_expr = CurrExpr Evaluate expr' }], ng) -- Is the current expression able to match a data consturctor based `Alt`? -- If so, then we bind all the parameters to the appropriate arguments and -- proceed with the evaluation of the `Alt`'s expression. We also make sure -- to perform the cvar binding. -- We unwrap the outermost cast from the mexpr. It must be being cast -- to the DataCon type, so this is safe, and needed for our pattern matching. -- We do not want to remove casting from any of the arguments since this could -- mess up there types later | (Data dcon):ar <- unApp $ exprInCasts mexpr , (DataCon _ _) <- dcon , ar' <- removeTypes ar eenv , (Alt (DataAlt _ params) expr):_ <- matchDataAlts dcon alts , length params == length ar' = let dbind = [(bind, mexpr)] expr' = liftCaseBinds dbind expr pbinds = zip params ar' (eenv', expr'', ng', news) = liftBinds pbinds eenv expr' ng in ( RuleEvalCaseData news , [newPCEmpty $ s { expr_env = eenv' , curr_expr = CurrExpr Evaluate expr''}] , ng') -- We are not able to match any constructor but don't have a symbolic variable? -- We hit a DEFAULT instead. -- We perform the cvar binding and proceed with the alt -- expression. | (Data _):_ <- unApp $ unsafeElimOuterCast mexpr , (Alt _ expr):_ <- matchDefaultAlts alts = let binds = [(bind, mexpr)] expr' = liftCaseBinds binds expr in ( RuleEvalCaseDefault , [newPCEmpty $ s { expr_env = eenv , curr_expr = CurrExpr Evaluate expr' }], ng) -- If we are pointing to something in expr value form, that is not addressed -- by some previous case, we handle it by branching on every `Alt`, and adding -- path constraints. | isExprValueForm eenv mexpr , dalts <- dataAlts alts , lalts <- litAlts alts , defs <- defaultAlts alts , (length dalts + length lalts + length defs) > 0 = let (cast, expr) = case mexpr of (Cast e c) -> (Just c, e) _ -> (Nothing, mexpr) (dsts_cs, ng') = case unApp $ unsafeElimOuterCast expr of (Var i@(Id _ _)):_ -> concretizeVarExpr s ng i bind dalts cast (Prim _ _):_ -> createExtConds s ng expr bind dalts (Lit _):_ -> ([], ng) (Data _):_ -> ([], ng) _ -> error $ "unmatched expr" ++ show (unApp $ unsafeElimOuterCast mexpr) lsts_cs = liftSymLitAlt s mexpr bind lalts def_sts = liftSymDefAlt s mexpr bind alts in (RuleEvalCaseSym, dsts_cs ++ lsts_cs ++ def_sts, ng') -- Case evaluation also uses the stack in graph reduction based evaluation -- semantics. The case's binding variable and alts are pushed onto the stack -- as a `CaseFrame` along with their appropriate `ExecExprEnv`. However this -- is only done when the matching expression is NOT in value form. Value -- forms should be handled by other RuleEvalCase* rules. | not (isExprValueForm eenv mexpr) = let frame = CaseFrame bind alts in ( RuleEvalCaseNonVal , [newPCEmpty $ s { expr_env = eenv , curr_expr = CurrExpr Evaluate mexpr , exec_stack = S.push frame stck }], ng) | otherwise = error $ "reduceCase: bad case passed in\n" ++ show mexpr ++ "\n" ++ show alts -- | Remove everything from an [Expr] that are actually Types. removeTypes :: [Expr] -> E.ExprEnv -> [Expr] removeTypes ((Type _):es) eenv = removeTypes es eenv removeTypes ((Var (Id n ty)):es) eenv = case E.lookup n eenv of Just (Type _) -> removeTypes es eenv _ -> (Var (Id n ty)) : removeTypes es eenv removeTypes (e:es) eenv = e : removeTypes es eenv removeTypes [] _ = [] -- | DEFAULT `Alt`s. matchDefaultAlts :: [Alt] -> [Alt] matchDefaultAlts alts = [a | a @ (Alt Default _) <- alts] -- | Match data constructor based `Alt`s. matchDataAlts :: DataCon -> [Alt] -> [Alt] matchDataAlts (DataCon n _) alts = [a | a @ (Alt (DataAlt (DataCon n' _) _) _) <- alts, n == n'] -- | Match literal constructor based `Alt`s. matchLitAlts :: Lit -> [Alt] -> [Alt] matchLitAlts lit alts = [a | a @ (Alt (LitAlt alit) _) <- alts, lit == alit] liftCaseBinds :: [(Id, Expr)] -> Expr -> Expr liftCaseBinds [] expr = expr liftCaseBinds ((b, e):xs) expr = liftCaseBinds xs $ replaceASTs (Var b) e expr -- | `DataCon` `Alt`s. dataAlts :: [Alt] -> [(DataCon, [Id], Expr)] dataAlts alts = [(dcon, ps, aexpr) | Alt (DataAlt dcon ps) aexpr <- alts] -- | `Lit` `Alt`s. litAlts :: [Alt] -> [(Lit, Expr)] litAlts alts = [(lit, aexpr) | Alt (LitAlt lit) aexpr <- alts] -- | DEFAULT `Alt`s. defaultAlts :: [Alt] -> [Alt] defaultAlts alts = [a | a @ (Alt Default _) <- alts] -- | Lift positive datacon `State`s from symbolic alt matching. This in -- part involves erasing all of the parameters from the environment by rename -- their occurrence in the aexpr to something fresh. concretizeVarExpr :: State t -> NameGen -> Id -> Id -> [(DataCon, [Id], Expr)] -> Maybe Coercion -> ([NewPC t], NameGen) concretizeVarExpr _ ng _ _ [] _ = ([], ng) concretizeVarExpr s ng mexpr_id cvar (x:xs) maybeC = (x':newPCs, ng'') where (x', ng') = concretizeVarExpr' s ng mexpr_id cvar x maybeC (newPCs, ng'') = concretizeVarExpr s ng' mexpr_id cvar xs maybeC concretizeVarExpr' :: State t -> NameGen -> Id -> Id -> (DataCon, [Id], Expr) -> Maybe Coercion -> (NewPC t, NameGen) concretizeVarExpr' s@(State {expr_env = eenv, type_env = tenv, symbolic_ids = syms}) ngen mexpr_id cvar (dcon, params, aexpr) maybeC = (NewPC { state = s { expr_env = eenv'' , symbolic_ids = syms' , curr_expr = CurrExpr Evaluate aexpr''} -- It is VERY important that we insert a PCExists with the mexpr_id -- This forces reduceNewPC to check that the concretized data constructor does -- not violate any path constraints from default cases. , new_pcs = [PCExists mexpr_id] }, ngen') where -- Make sure that the parameters do not conflict in their symbolic reps. olds = map idName params -- [ChildrenNames] -- Optimization -- We use the same names repeatedly for the children of the same ADT -- Haskell is purely functional, so this is OK! The children can't change -- Then, in the constraint solver, we can consider fewer constraints at once -- (see note [AltCond] in Language/PathConds.hs) mexpr_n = idName mexpr_id (news, ngen') = childrenNames mexpr_n olds ngen --Update the expr environment newIds = map (\(Id _ t, n) -> (n, Id n t)) (zip params news) eenv' = foldr (uncurry E.insertSymbolic) eenv newIds (dcon', aexpr') = renameExprs (zip olds news) (Data dcon, aexpr) newparams = map (uncurry Id) $ zip news (map typeOf params) dConArgs = (map (Var) newparams) -- Get list of Types to concretize polymorphic data constructor and concatenate with other arguments mexpr_t = (\(Id _ t) -> t) (mexpr_id) exprs = [dcon'] ++ (mexprTyToExpr mexpr_t tenv) ++ dConArgs -- Apply list of types (if present) and DataCon children to DataCon dcon'' = mkApp exprs -- Apply cast, in opposite direction of unsafeElimOuterCast dcon''' = case maybeC of (Just (t1 :~ t2)) -> Cast dcon'' (t2 :~ t1) Nothing -> dcon'' syms' = newparams ++ (filter (/= mexpr_id) syms) -- concretizes the mexpr to have same form as the DataCon specified eenv'' = E.insert mexpr_n dcon''' eenv' -- Now do a round of rename for binding the cvar. binds = [(cvar, (Var mexpr_id))] aexpr'' = liftCaseBinds binds aexpr' -- | Given the Type of the matched Expr, looks for Type in the TypeEnv, and returns Expr level representation of the Type mexprTyToExpr :: Type -> TypeEnv -> [Expr] mexprTyToExpr mexpr_t tenv -- special case for NewTyCon, involves looking up tyVars and binding them to concrete types specified by mexpr_t | Just (algDataTy, bindings) <- getAlgDataTy mexpr_t tenv , (isNewTyCon algDataTy) = dconTyToExpr (data_con algDataTy) bindings | otherwise = typeToExpr mexpr_t -- | Given a DataCon, and an (Id, Type) mapping, returns list of Expression level Type Arguments to DataCon dconTyToExpr :: DataCon -> [(Id, Type)] -> [Expr] dconTyToExpr (DataCon _ t) bindings = case (getTyApps t) of (Just tApps) -> tyAppsToExpr tApps bindings Nothing -> [] createExtConds :: State t -> NameGen -> Expr -> Id -> [(DataCon, [Id], Expr)] -> ([NewPC t], NameGen) createExtConds _ ng _ _ [] = ([], ng) createExtConds s ng mexpr cvar (x:xs) = (x':newPCs, ng'') where (x', ng') = createExtCond s ng mexpr cvar x (newPCs, ng'') = createExtConds s ng' mexpr cvar xs createExtCond :: State t -> NameGen -> Expr -> Id -> (DataCon, [Id], Expr) -> (NewPC t, NameGen) createExtCond s ngen mexpr cvar (dcon, _, aexpr) = (NewPC { state = res, new_pcs = [cond] }, ngen) where -- Get the Bool value specified by the matching DataCon -- Throws an error if dcon is not a Bool Data Constructor boolValue = getBoolFromDataCon s dcon cond = ExtCond mexpr boolValue -- Now do a round of rename for binding the cvar. binds = [(cvar, mexpr)] aexpr' = liftCaseBinds binds aexpr res = s {curr_expr = CurrExpr Evaluate aexpr'} getBoolFromDataCon :: State t -> DataCon -> Bool getBoolFromDataCon (State {known_values = kv}) dcon | (DataCon dconName dconType) <- dcon , dconType == (tyBool kv) , dconName == (KV.dcTrue kv) = True | (DataCon dconName dconType) <- dcon , dconType == (tyBool kv) , dconName == (KV.dcFalse kv) = False | otherwise = error $ "getBoolFromDataCon: invalid DataCon passed in\n" ++ show dcon ++ "\n" liftSymLitAlt :: State t -> Expr -> Id -> [(Lit, Expr)] -> [NewPC t] liftSymLitAlt s mexpr cvar = map (liftSymLitAlt' s mexpr cvar) -- | Lift literal alts found in symbolic case matching. liftSymLitAlt' :: State t -> Expr -> Id -> (Lit, Expr) -> NewPC t liftSymLitAlt' s mexpr cvar (lit, aexpr) = NewPC { state = res, new_pcs = [cond] } where -- Condition that was matched. cond = AltCond lit mexpr True -- Bind the cvar. binds = [(cvar, Lit lit)] aexpr' = liftCaseBinds binds aexpr res = s { curr_expr = CurrExpr Evaluate aexpr' } liftSymDefAlt :: State t -> Expr -> Id -> [Alt] -> [NewPC t] liftSymDefAlt s mexpr cvar as = let aexpr = defAltExpr as in case aexpr of Just aexpr' -> liftSymDefAlt' s mexpr aexpr' cvar as _ -> [] liftSymDefAlt' :: State t -> Expr -> Expr -> Id -> [Alt] -> [NewPC t] liftSymDefAlt' s mexpr aexpr cvar as = let conds = mapMaybe (liftSymDefAltPCs mexpr) (map altMatch as) binds = [(cvar, mexpr)] aexpr' = liftCaseBinds binds aexpr in [NewPC { state = s { curr_expr = CurrExpr Evaluate aexpr' } , new_pcs = conds }] defAltExpr :: [Alt] -> Maybe Expr defAltExpr [] = Nothing defAltExpr (Alt Default e:_) = Just e defAltExpr (_:xs) = defAltExpr xs liftSymDefAltPCs :: Expr -> AltMatch -> Maybe PathCond liftSymDefAltPCs mexpr (DataAlt dc _) = Just $ ConsCond dc mexpr False liftSymDefAltPCs mexpr (LitAlt lit) = Just $ AltCond lit mexpr False liftSymDefAltPCs _ Default = Nothing evalCast :: State t -> NameGen -> Expr -> Coercion -> (Rule, [State t], NameGen) evalCast s@(State { exec_stack = stck }) ng e c | cast /= cast' = ( RuleEvalCastSplit , [ s { curr_expr = CurrExpr Evaluate $ simplifyCasts cast' }] , ng') | otherwise = ( RuleEvalCast , [s { curr_expr = CurrExpr Evaluate $ simplifyCasts e , exec_stack = S.push frame stck}] , ng) where cast = Cast e c (cast', ng') = splitCast ng cast frame = CastFrame c evalTick :: State t -> NameGen -> Tickish -> Expr -> (Rule, [State t], NameGen) evalTick s ng _ e = (RuleTick, [ s { curr_expr = CurrExpr Evaluate e }], ng) evalNonDet :: State t -> NameGen -> [Expr] -> (Rule, [State t], NameGen) evalNonDet s ng es = let s' = map (\e -> s { curr_expr = CurrExpr Evaluate e }) es in (RuleNonDet, s', ng) evalSymGen :: State t -> NameGen -> Type -> (Rule, [State t], NameGen) evalSymGen s@( State { expr_env = eenv }) ng t = let (n, ng') = freshSeededString "symG" ng i = Id n t eenv' = E.insertSymbolic n i eenv in (RuleSymGen, [s { expr_env = eenv' , curr_expr = CurrExpr Evaluate (Var i) , symbolic_ids = i:symbolic_ids s }] , ng') evalAssume :: State t -> NameGen -> Maybe FuncCall -> Expr -> Expr -> (Rule, [State t], NameGen) evalAssume s@(State { exec_stack = stck }) ng _ e1 e2 = let fr = AssumeFrame e2 stck' = S.push fr stck in ( RuleEvalAssume , [ s { curr_expr = CurrExpr Evaluate e1 , exec_stack = stck' }] , ng) evalAssert :: State t -> NameGen -> Maybe FuncCall -> Expr -> Expr -> (Rule, [State t], NameGen) evalAssert s@(State { exec_stack = stck }) ng is e1 e2 = let fr = AssertFrame is e2 stck' = S.push fr stck in ( RuleEvalAssert , [ s { curr_expr = CurrExpr Evaluate e1 , exec_stack = stck' }] , ng) retUpdateFrame :: State t -> NameGen -> Name -> S.Stack Frame -> (Rule, [State t], NameGen) retUpdateFrame s@(State { expr_env = eenv , curr_expr = CurrExpr _ e}) ng un stck | Var i@(Id vn _) <- e = ( RuleReturnEUpdateVar un , [s { expr_env = E.redirect un vn eenv , curr_expr = CurrExpr Return (Var i) , exec_stack = stck }] , ng) | otherwise = ( RuleReturnEUpdateNonVar un , [s { expr_env = E.insert un e eenv , exec_stack = stck }] , ng) retApplyFrame :: State t -> NameGen -> Expr -> Expr -> S.Stack Frame -> (Rule, [State t], NameGen) retApplyFrame s@(State { expr_env = eenv }) ng e1 e2 stck' | Var (Id n _):_ <- unApp e1 , E.isSymbolic n eenv = ( RuleReturnEApplySym , [s { curr_expr = CurrExpr Return (App e1 e2) , exec_stack = stck' }], ng) | otherwise = ( RuleReturnEApplySym , [s { curr_expr = CurrExpr Evaluate (App e1 e2) , exec_stack = stck' }], ng) retCaseFrame :: State t -> NameGen -> Expr -> Id -> [Alt] -> S.Stack Frame -> (Rule, [State t], NameGen) retCaseFrame s b e i a stck = ( RuleReturnECase , [s { curr_expr = CurrExpr Evaluate (Case e i a) , exec_stack = stck }] , b) retCastFrame :: State t -> NameGen -> Expr -> Coercion -> S.Stack Frame -> (Rule, [State t], NameGen) retCastFrame s ng e c stck = ( RuleReturnCast , [s { curr_expr = CurrExpr Return $ simplifyCasts $ Cast e c , exec_stack = stck}] , ng) retCurrExpr :: State t -> Expr -> CurrExpr -> S.Stack Frame -> (Rule, [NewPC t]) retCurrExpr s e1 e2 stck = ( RuleReturnCurrExprFr , [NewPC { state = s { curr_expr = e2 , exec_stack = stck} , new_pcs = [ExtCond e1 True]}] ) retAssumeFrame :: State t -> NameGen -> Expr -> Expr -> S.Stack Frame -> (Rule, [NewPC t], NameGen) retAssumeFrame s@(State {known_values = kv , type_env = tenv}) ng e1 e2 stck = let -- Create a True Bool DataCon dalt = case (getDataCon tenv (KV.tyBool kv) (KV.dcTrue kv)) of Just dc -> [dc] _ -> [] -- If Assume is just a Var, concretize the Expr to a True Bool DataCon. Else add an ExtCond (newPCs, ng') = case unApp $ unsafeElimOuterCast e1 of (Var i@(Id _ _)):_ -> concretizeExprToBool s ng i dalt e2 stck _ -> addExtCond s ng e1 e2 True stck in (RuleReturnCAssume, newPCs, ng') retAssertFrame :: State t -> NameGen -> Expr -> Maybe (FuncCall) -> Expr -> S.Stack Frame -> (Rule, [NewPC t], NameGen) retAssertFrame s@(State {known_values = kv , type_env = tenv}) ng e1 ais e2 stck = let -- Create True and False Bool DataCons dalts = case getDataCons (KV.tyBool kv) tenv of Just dcs -> dcs _ -> [] -- If Assert is just a Var, concretize the Expr to a True or False Bool DataCon, else add an ExtCond (newPCs, ng') = case unApp $ unsafeElimOuterCast e1 of (Var i@(Id _ _)):_ -> concretizeExprToBool s ng i dalts e2 stck _ -> addExtConds s ng e1 ais e2 stck in (RuleReturnCAssert, newPCs, ng') concretizeExprToBool :: State t -> NameGen -> Id -> [DataCon] -> Expr -> S.Stack Frame -> ([NewPC t], NameGen) concretizeExprToBool _ ng _ [] _ _ = ([], ng) concretizeExprToBool s ng mexpr_id (x:xs) e2 stck = (x':newPCs, ng'') where (x', ng') = concretizeExprToBool' s ng mexpr_id x e2 stck (newPCs, ng'') = concretizeExprToBool s ng' mexpr_id xs e2 stck concretizeExprToBool' :: State t -> NameGen -> Id -> DataCon -> Expr -> S.Stack Frame -> (NewPC t, NameGen) concretizeExprToBool' s@(State {expr_env = eenv , symbolic_ids = syms , known_values = kv}) ngen mexpr_id dcon@(DataCon dconName _) e2 stck = (newPCEmpty $ s { expr_env = eenv' , symbolic_ids = syms' , exec_stack = stck , curr_expr = CurrExpr Evaluate e2 , true_assert = assertVal} , ngen) where mexpr_n = idName mexpr_id -- concretize the mexpr to the DataCon specified eenv' = E.insert mexpr_n (Data dcon) eenv syms' = filter (/= mexpr_id) syms assertVal = if (dconName == (KV.dcTrue kv)) then False else True addExtCond :: State t -> NameGen -> Expr -> Expr -> Bool -> S.Stack Frame -> ([NewPC t], NameGen) addExtCond s ng e1 e2 boolVal stck = ([NewPC { state = s { curr_expr = CurrExpr Evaluate e2 , exec_stack = stck} , new_pcs = [ExtCond e1 boolVal]}], ng) addExtConds :: State t -> NameGen -> Expr -> Maybe (FuncCall) -> Expr -> S.Stack Frame -> ([NewPC t], NameGen) addExtConds s ng e1 ais e2 stck = let s' = s { curr_expr = CurrExpr Evaluate e2 , exec_stack = stck} condt = [ExtCond e1 True] condf = [ExtCond e1 False] strue = NewPC { state = s' , new_pcs = condt } sfalse = NewPC { state = s' { true_assert = True , assert_ids = ais } , new_pcs = condf } in ([strue, sfalse], ng) -- | Inject binds into the eenv. The LHS of the [(Id, Expr)] are treated as -- seed values for the names. liftBinds :: [(Id, Expr)] -> E.ExprEnv -> Expr -> NameGen -> (E.ExprEnv, Expr, NameGen, [Name]) liftBinds binds eenv expr ngen = (eenv', expr', ngen', news) where (bindsLHS, bindsRHS) = unzip binds olds = map (idName) bindsLHS (news, ngen') = freshSeededNames olds ngen expr' = renameExprs (zip olds news) expr bindsLHS' = renameExprs (zip olds news) bindsLHS binds' = zip bindsLHS' bindsRHS eenv' = E.insertExprs (zip news (map snd binds')) eenv -- If the expression is a symbolic higher order function application, replaces -- it with a symbolic variable of the correct type. -- A non reduced path constraint is added, to force solving for the symbolic -- function later. retReplaceSymbFunc :: State t -> NameGen -> Expr -> Maybe (Rule, [State t], NameGen) retReplaceSymbFunc s@(State { expr_env = eenv , known_values = kv , type_classes = tc , exec_stack = stck }) ng ce | Just (frm, _) <- S.pop stck , not (isApplyFrame frm) , (Var (Id f idt):_) <- unApp ce , E.isSymbolic f eenv , isTyFun idt , t <- typeOf ce , not (isTyFun t) , Just eq_tc <- concreteSatStructEq kv tc t = let (new_sym, ng') = freshSeededString "sym" ng new_sym_id = Id new_sym t s_eq_f = KV.structEqFunc kv nrpc_e = mkApp $ [ Var (Id s_eq_f TyUnknown) , Type t , eq_tc , Var new_sym_id , ce ] in Just (RuleReturnReplaceSymbFunc, [s { expr_env = E.insertSymbolic new_sym new_sym_id eenv , curr_expr = CurrExpr Return (Var new_sym_id) , symbolic_ids = new_sym_id:symbolic_ids s , non_red_path_conds = non_red_path_conds s ++ [nrpc_e] }] , ng') | otherwise = Nothing isApplyFrame :: Frame -> Bool isApplyFrame (ApplyFrame _) = True isApplyFrame _ = False