{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ExistentialQuantification #-}
module Language.Fixpoint.Solver.Instantiate (instantiate) where
import Language.Fixpoint.Types
import Language.Fixpoint.Types.Config as FC
import qualified Language.Fixpoint.Types.Visitor as Vis
import qualified Language.Fixpoint.Misc as Misc
import qualified Language.Fixpoint.Smt.Interface as SMT
import Language.Fixpoint.Defunctionalize
import qualified Language.Fixpoint.Utils.Trie as T
import Language.Fixpoint.Utils.Progress
import Language.Fixpoint.SortCheck
import Language.Fixpoint.Graph.Deps (isTarget)
import Language.Fixpoint.Solver.Sanitize (symbolEnv)
import qualified Language.Fixpoint.Solver.PLE as PLE (instantiate)
import Control.Monad.State
import qualified Data.Text as T
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import qualified Data.List as L
import qualified Data.Maybe as Mb
import Data.Char (isUpper)
mytracepp :: (PPrint a) => String -> a -> a
mytracepp = notracepp
instantiate :: (Loc a) => Config -> SInfo a -> IO (SInfo a)
instantiate cfg fi
| rewriteAxioms cfg && not (oldPLE cfg)
= PLE.instantiate cfg fi
| rewriteAxioms cfg && noIncrPle cfg
= instantiate' cfg fi
| rewriteAxioms cfg
= incrInstantiate' cfg fi
| otherwise
= return fi
incrInstantiate' :: (Loc a) => Config -> SInfo a -> IO (SInfo a)
incrInstantiate' cfg fi = do
let cs = [ (i, c) | (i, c) <- M.toList (cm fi), isPleCstr aEnv i c ]
let t = mkCTrie cs
res <- withProgress (1 + length cs) $
withCtx cfg file sEnv (pleTrie t . instEnv cfg fi cs)
return $ resSInfo cfg sEnv fi res
where
file = srcFile cfg ++ ".evals"
sEnv = symbolEnv cfg fi
aEnv = ae fi
instEnv :: (Loc a) => Config -> SInfo a -> [(SubcId, SimpC a)] -> SMT.Context -> InstEnv a
instEnv cfg fi cs ctx = InstEnv cfg ctx bEnv aEnv (M.fromList cs) γ s0
where
bEnv = bs fi
aEnv = ae fi
γ = knowledge cfg ctx aEnv
s0 = EvalEnv 0 [] aEnv (SMT.ctxSymEnv ctx) cfg
mkCTrie :: [(SubcId, SimpC a)] -> CTrie
mkCTrie ics = mytracepp "TRIE" $ T.fromList [ (cBinds c, i) | (i, c) <- ics ]
where
cBinds = L.sort . elemsIBindEnv . senv
pleTrie :: CTrie -> InstEnv a -> IO InstRes
pleTrie t env = loopT env ctx0 diff0 Nothing res0 t
where
diff0 = []
res0 = M.empty
ctx0 = initCtx es0
es0 = eqBody <$> L.filter (null . eqArgs) (aenvEqs . ieAenv $ env)
loopT :: InstEnv a -> ICtx -> Diff -> Maybe BindId -> InstRes -> CTrie -> IO InstRes
loopT env ctx delta i res t = case t of
T.Node [] -> return res
T.Node [b] -> loopB env ctx delta i res b
T.Node bs -> withAssms env ctx delta Nothing $ \ctx' -> do
(ctx'', res') <- ple1 env ctx' i Nothing res
foldM (loopB env ctx'' [] i) res' bs
loopB :: InstEnv a -> ICtx -> Diff -> Maybe BindId -> InstRes -> CBranch -> IO InstRes
loopB env ctx delta iMb res b = case b of
T.Bind i t -> loopT env ctx (i:delta) (Just i) res t
T.Val cid -> withAssms env ctx delta (Just cid) $ \ctx' -> do
progressTick
(snd <$> ple1 env ctx' iMb (Just cid) res)
withAssms :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> (ICtx -> IO b) -> IO b
withAssms env@(InstEnv {..}) ctx delta cidMb act = do
let ctx' = updCtx env ctx delta cidMb
let assms = mytracepp ("ple1-assms: " ++ show (cidMb, delta)) (icAssms ctx')
SMT.smtBracket ieSMT "PLE.evaluate" $ do
forM_ assms (SMT.smtAssert ieSMT)
act ctx'
ple1 :: InstEnv a -> ICtx -> Maybe BindId -> Maybe SubcId -> InstRes -> IO (ICtx, InstRes)
ple1 env@(InstEnv {..}) ctx i cidMb res = do
let cands = mytracepp ("ple1-cands: " ++ show cidMb) $ S.toList (icCands ctx)
unfolds <- evalCandsLoop ieCfg ieSMT ieKnowl ieEvEnv cands
return $ updCtxRes env ctx res i cidMb (mytracepp ("ple1-cands-unfolds: " ++ show cidMb) unfolds)
_evalCands :: Knowledge -> EvalEnv -> [Expr] -> IO [Unfold]
_evalCands _ _ [] = return []
_evalCands γ s0 cands = do eqs <- mapM (evalOne γ s0) cands
return $ mkUnfolds (zip (Just <$> cands) eqs)
unfoldPred :: Config -> SMT.Context -> [Unfold] -> Pred
unfoldPred cfg ctx = toSMT cfg ctx [] . pAnd . concatMap snd
evalCandsLoop :: Config -> SMT.Context -> Knowledge -> EvalEnv -> [Expr] -> IO [Unfold]
evalCandsLoop cfg ctx γ s0 cands = go [] cands
where
go acc [] = return acc
go acc cands = do eqss <- SMT.smtBracket ctx "PLE.evaluate" $ do
SMT.smtAssert ctx (unfoldPred cfg ctx acc)
mapM (evalOne γ s0) cands
let us = zip (Just <$> cands) eqss
case mkUnfolds us of
[] -> return acc
us' -> do let acc' = acc ++ us'
let oks = S.fromList [ e | (Just e, _) <- us' ]
let cands' = [ e | e <- cands, not (S.member e oks) ]
go acc' cands'
resSInfo :: Config -> SymEnv -> SInfo a -> InstRes -> SInfo a
resSInfo cfg env fi res = strengthenBinds fi res'
where
res' = M.fromList $ mytracepp "ELAB-INST: " $ zip is ps''
ps'' = zipWith (\i -> elaborate (atLoc dummySpan ("PLE1 " ++ show i)) env) is ps'
ps' = defuncAny cfg env ps
(is, ps) = unzip (M.toList res)
data InstEnv a = InstEnv
{ ieCfg :: !Config
, ieSMT :: !SMT.Context
, ieBEnv :: !BindEnv
, ieAenv :: !AxiomEnv
, ieCstrs :: !(M.HashMap SubcId (SimpC a))
, ieKnowl :: !Knowledge
, ieEvEnv :: !EvalEnv
}
data ICtx = ICtx
{ icAssms :: ![Pred]
, icCands :: S.HashSet Expr
, icEquals :: ![Expr]
, icSolved :: S.HashSet Expr
}
type InstRes = M.HashMap BindId Expr
type Unfold = (Maybe Expr, [Expr])
type CTrie = T.Trie SubcId
type CBranch = T.Branch SubcId
type Diff = [BindId]
initCtx :: [Expr] -> ICtx
initCtx es = ICtx
{ icAssms = []
, icCands = mempty
, icEquals = mytracepp "INITIAL-STUFF-INCR" es
, icSolved = mempty
}
equalitiesPred :: [(Expr, Expr)] -> [Expr]
equalitiesPred eqs = [ EEq e1 e2 | (e1, e2) <- eqs, e1 /= e2 ]
updCtxRes :: InstEnv a -> ICtx -> InstRes -> Maybe BindId -> Maybe SubcId -> [Unfold] -> (ICtx, InstRes)
updCtxRes env ctx res iMb cidMb us
=
( ctx { icSolved = solved', icEquals = mempty}
, res'
)
where
_msg = Mb.maybe "nuttin\n" (debugResult env res') cidMb
res' = updRes res iMb (pAnd solvedEqs)
_cands' = ((icCands ctx) `S.union` newCands) `S.difference` solved'
solved' = S.union (icSolved ctx) solvedCands
newCands = S.fromList (concatMap topApps newEqs)
solvedCands = S.fromList [ e | (Just e, _) <- okUnfolds ]
solvedEqs = icEquals ctx ++ newEqs
newEqs = concatMap snd okUnfolds
okUnfolds = mytracepp _str [ (eMb, ps) | (eMb, ps) <- us, not (null ps) ]
_str = "okUnfolds " ++ showpp (iMb, cidMb)
mkUnfolds :: [(a, [(Expr, Expr)])] -> [(a, [Expr])]
mkUnfolds us = [ (eMb, ps) | (eMb, eqs) <- us
, let ps = equalitiesPred eqs
, not (null ps)
]
debugResult :: InstEnv a -> InstRes -> SubcId -> String
debugResult (InstEnv {..}) res i = msg
where
msg = "INCR-INSTANTIATE i = " ++ show i ++ ": " ++ showpp cidEqs
cidEqs = pAnd [ e | i <- cBinds, e <- Mb.maybeToList $ M.lookup i res ]
cBinds = L.sort . elemsIBindEnv . senv . getCstr ieCstrs $ i
updRes :: InstRes -> Maybe BindId -> Expr -> InstRes
updRes res (Just i) e = M.insert i e res
updRes res Nothing _ = res
updCtx :: InstEnv a -> ICtx -> Diff -> Maybe SubcId -> ICtx
updCtx InstEnv {..} ctx delta cidMb
= ctx { icAssms = ctxEqs
, icCands = cands <> icCands ctx
, icEquals = initEqs <> icEquals ctx }
where
initEqs = equalitiesPred (initEqualities ieSMT ieAenv bs)
cands = (S.fromList (concatMap topApps es0)) `S.difference` (icSolved ctx)
ctxEqs = toSMT ieCfg ieSMT [] <$> concat
[ initEqs
, [ expr xr | xr@(_, r) <- bs, null (Vis.kvars r) ]
]
(bs, es0) = (unElab <$> binds, unElab <$> es)
es = eRhs : (expr <$> binds)
eRhs = maybe PTrue crhs subMb
binds = [ lookupBindEnv i ieBEnv | i <- delta ]
subMb = getCstr ieCstrs <$> cidMb
getCstr :: M.HashMap SubcId (SimpC a) -> SubcId -> SimpC a
getCstr env cid = Misc.safeLookup "Instantiate.getCstr" cid env
instantiate' :: (Loc a) => Config -> SInfo a -> IO (SInfo a)
instantiate' cfg fi = sInfo cfg env fi <$> withCtx cfg file env act
where
act ctx = forM cstrs $ \(i, c) ->
((i,srcSpan c),) . mytracepp ("INSTANTIATE i = " ++ show i) <$> instSimpC cfg ctx (bs fi) aenv i c
cstrs = [ (i, c) | (i, c) <- M.toList (cm fi) , isPleCstr aenv i c]
file = srcFile cfg ++ ".evals"
env = symbolEnv cfg fi
aenv = (ae fi)
sInfo :: Config -> SymEnv -> SInfo a -> [((SubcId, SrcSpan), Expr)] -> SInfo a
sInfo cfg env fi ips = strengthenHyp fi (mytracepp "ELAB-INST: " $ zip (fst <$> is) ps'')
where
(is, ps) = unzip ips
ps' = defuncAny cfg env ps
ps'' = zipWith (\(i, sp) -> elaborate (atLoc sp ("PLE1 " ++ show i)) env) is ps'
instSimpC :: Config -> SMT.Context -> BindEnv -> AxiomEnv -> SubcId -> SimpC a -> IO Expr
instSimpC cfg ctx bds aenv sid sub
| isPleCstr aenv sid sub = do
let is0 = mytracepp "INITIAL-STUFF" $ eqBody <$> L.filter (null . eqArgs) (aenvEqs aenv)
let (bs, es0) = cstrExprs bds sub
equalities <- evaluate cfg ctx aenv bs es0 sid
let evalEqs = [ EEq e1 e2 | (e1, e2) <- equalities, e1 /= e2 ]
return $ pAnd (is0 ++ evalEqs)
| otherwise = return PTrue
isPleCstr :: AxiomEnv -> SubcId -> SimpC a -> Bool
isPleCstr aenv sid c = isTarget c && M.lookupDefault False sid (aenvExpand aenv)
cstrExprs :: BindEnv -> SimpC a -> ([(Symbol, SortedReft)], [Expr])
cstrExprs bds sub = (unElab <$> binds, unElab <$> es)
where
es = (crhs sub) : (expr <$> binds)
binds = envCs bds (senv sub)
evaluate :: Config -> SMT.Context -> AxiomEnv
-> [(Symbol, SortedReft)]
-> [Expr]
-> SubcId
-> IO [(Expr, Expr)]
evaluate cfg ctx aenv facts es sid = do
let eqs = initEqualities ctx aenv facts
let γ = knowledge cfg ctx aenv
let cands = mytracepp ("evaluate-cands " ++ showpp sid) $ Misc.hashNub (concatMap topApps es)
let s0 = EvalEnv 0 [] aenv (SMT.ctxSymEnv ctx) cfg
let ctxEqs = [ toSMT cfg ctx [] (EEq e1 e2) | (e1, e2) <- eqs ]
++ [ toSMT cfg ctx [] (expr xr) | xr@(_, r) <- facts, null (Vis.kvars r) ]
eqss <- _evalLoop cfg ctx γ s0 ctxEqs cands
return $ eqs ++ eqss
_evalLoop :: Config -> SMT.Context -> Knowledge -> EvalEnv -> [Pred] -> [Expr] -> IO [(Expr, Expr)]
_evalLoop cfg ctx γ s0 ctxEqs cands = loop 0 [] cands
where
loop _ acc [] = return acc
loop i acc cands = do let eqp = toSMT cfg ctx [] $ pAnd $ equalitiesPred acc
eqss <- SMT.smtBracket ctx "PLE.evaluate" $ do
forM_ (eqp : ctxEqs) (SMT.smtAssert ctx)
mapM (evalOne γ s0) cands
case concat eqss of
[] -> return acc
eqs' -> do let acc' = acc ++ eqs'
let oks = S.fromList (fst <$> eqs')
let cands' = [ e | e <- cands, not (S.member e oks) ]
loop (i+1) acc' cands'
data EvalEnv = EvalEnv
{ evId :: !Int
, evSequence :: [(Expr,Expr)]
, _evAEnv :: !AxiomEnv
, evEnv :: !SymEnv
, _evCfg :: !Config
}
type EvalST a = StateT EvalEnv IO a
evalOne :: Knowledge -> EvalEnv -> Expr -> IO [(Expr, Expr)]
evalOne γ s0 e = do
(e', st) <- runStateT (eval γ initCS (mytracepp "evalOne: " e)) s0
if e' == e then return [] else return ((e, e') : evSequence st)
data Recur = Ok | Stop deriving (Eq, Show)
type CStack = ([Symbol], Recur)
instance PPrint Recur where
pprintTidy _ = Misc.tshow
initCS :: CStack
initCS = ([], Ok)
pushCS :: CStack -> Symbol -> CStack
pushCS (fs, r) f = (f:fs, r)
recurCS :: CStack -> Symbol -> Bool
recurCS (_, Ok) _ = True
recurCS (fs, _) f = not (f `elem` fs)
noRecurCS :: CStack -> CStack
noRecurCS (fs, _) = (fs, Stop)
topApps :: Expr -> [Expr]
topApps = go
where
go (PAnd es) = concatMap go es
go (POr es) = concatMap go es
go (PAtom _ e1 e2) = go e1 ++ go e2
go (PIff e1 e2) = go e1 ++ go e2
go (PImp e1 e2) = go e1 ++ go e2
go (EBin _ e1 e2) = go e1 ++ go e2
go (PNot e) = go e
go (ENeg e) = go e
go e@(EApp _ _) = [e]
go _ = []
makeLam :: Knowledge -> Expr -> Expr
makeLam γ e = L.foldl' (flip ELam) e (knLams γ)
eval :: Knowledge -> CStack -> Expr -> EvalST Expr
eval γ stk = go
where
go (ELam (x,s) e) = ELam (x, s) <$> eval γ' stk e where γ' = γ { knLams = (x, s) : knLams γ }
go e@(EIte b e1 e2) = go b >>= \b' -> evalIte γ stk e b' e1 e2
go (ECoerc s t e) = ECoerc s t <$> go e
go e@(EApp _ _) = evalArgs γ stk e >>= evalApp γ stk e
go e@(EVar _) = evalApp γ stk e (e, [])
go (PAtom r e1 e2) = PAtom r <$> go e1 <*> go e2
go (ENeg e) = ENeg <$> go e
go (EBin o e1 e2) = EBin o <$> go e1 <*> go e2
go (ETApp e t) = flip ETApp t <$> go e
go (ETAbs e s) = flip ETAbs s <$> go e
go (PNot e) = PNot <$> go e
go (PImp e1 e2) = PImp <$> go e1 <*> go e2
go (PIff e1 e2) = PIff <$> go e1 <*> go e2
go (PAnd es) = PAnd <$> (go <$$> es)
go (POr es) = POr <$> (go <$$> es)
go e = return e
(<$$>) :: (Monad m) => (a -> m b) -> [a] -> m [b]
f <$$> xs = f Misc.<$$> xs
evalArgs :: Knowledge -> CStack -> Expr -> EvalST (Expr, [Expr])
evalArgs γ stk e = go [] e
where
go acc (EApp f e)
= do f' <- evalOk γ stk f
e' <- eval γ stk e
go (e':acc) f'
go acc e
= (,acc) <$> eval γ stk e
evalOk :: Knowledge -> CStack -> Expr -> EvalST Expr
evalOk γ stk@(_, Ok) e = eval γ stk e
evalOk _ _ e = pure e
evalApp :: Knowledge -> CStack -> Expr -> (Expr, [Expr]) -> EvalST Expr
evalApp γ stk e (e1, es) = do
res <- evalAppAc γ stk e (e1, es)
let diff = (res /= (eApps e1 es))
return $ mytracepp ("evalApp:END:" ++ showpp diff) res
evalAppAc :: Knowledge -> CStack -> Expr -> (Expr, [Expr]) -> EvalST Expr
evalAppAc γ stk e (EVar f, [ex])
| (EVar dc, es) <- splitEApp ex
, Just simp <- L.find (\simp -> (smName simp == f) && (smDC simp == dc)) (knSims γ)
, length (smArgs simp) == length es
= do let msg = "evalAppAc:ePop: " ++ showpp (f, dc, es)
let ePopIf = mytracepp msg $ substPopIf (zip (smArgs simp) es) (smBody simp)
e' <- eval γ stk ePopIf
(e, "Rewrite -" ++ showpp f) ~> e'
evalAppAc γ stk _ (EVar f, es)
| Just eq <- L.find (( == f) . eqName) (knAms γ)
, Just bd <- getEqBody eq
, length (eqArgs eq) == length es
, f `notElem` syms bd
, recurCS stk f
= do env <- seSort <$> gets evEnv
let ee = substEq env PopIf eq es bd
assertSelectors γ ee
eval γ (pushCS stk f) ee
evalAppAc γ stk _e (EVar f, es)
| Just eq <- L.find ((== f) . eqName) (knAms γ)
, Just bd <- getEqBody eq
, length (eqArgs eq) == length es
, recurCS stk f
= do env <- seSort <$> gets evEnv
mytracepp ("EVAL-REC-APP" ++ showpp (stk, _e))
<$> evalRecApplication γ (pushCS stk f) (eApps (EVar f) es) (substEq env Normal eq es bd)
evalAppAc _ _ _ (f, es)
= return (eApps f es)
substEq :: SEnv Sort -> SubstOp -> Equation -> [Expr] -> Expr -> Expr
substEq env o eq es bd = substEqVal o eq es (substEqCoerce env eq es bd)
data SubstOp = PopIf | Normal
substEqVal :: SubstOp -> Equation -> [Expr] -> Expr -> Expr
substEqVal o eq es bd = case o of
PopIf -> substPopIf xes bd
Normal -> subst (mkSubst xes) bd
where
xes = zip xs es
xs = eqArgNames eq
substEqCoerce :: SEnv Sort -> Equation -> [Expr] -> Expr -> Expr
substEqCoerce env eq es bd = Vis.applyCoSub coSub bd
where
ts = snd <$> eqArgs eq
sp = panicSpan "mkCoSub"
eTs = sortExpr sp env <$> es
coSub = mytracepp ("substEqCoerce" ++ showpp (eqName eq, es, eTs, ts)) $ mkCoSub env eTs ts
mkCoSub :: SEnv Sort -> [Sort] -> [Sort] -> Vis.CoSub
mkCoSub env eTs xTs = M.fromList [ (x, unite ys) | (x, ys) <- Misc.groupList xys ]
where
unite ts = mytracepp ("UNITE: " ++ showpp ts) $ Mb.fromMaybe (uError ts) (unifyTo1 senv ts)
senv = mkSearchEnv env
uError ts = panic ("mkCoSub: cannot build CoSub for " ++ showpp xys ++ " cannot unify " ++ showpp ts)
xys = mytracepp "mkCoSubXXX" $ Misc.sortNub $ concat $ zipWith matchSorts _xTs _eTs
(_xTs,_eTs) = mytracepp "mkCoSub:MATCH" $ (xTs, eTs)
matchSorts :: Sort -> Sort -> [(Symbol, Sort)]
matchSorts s1 s2 = mytracepp ("matchSorts :" ++ showpp (s1, s2)) $ go s1 s2
where
go (FObj x) y = [(x, y)]
go (FAbs _ t1) (FAbs _ t2) = go t1 t2
go (FFunc s1 t1) (FFunc s2 t2) = go s1 s2 ++ go t1 t2
go (FApp s1 t1) (FApp s2 t2) = go s1 s2 ++ go t1 t2
go _ _ = []
getEqBody :: Equation -> Maybe Expr
getEqBody (Equ x xts b _ _)
| Just (fxs, e) <- getEqBodyPred b
, (EVar f, es) <- splitEApp fxs
, f == x
, es == (EVar . fst <$> xts)
= Just e
getEqBody _
= Nothing
getEqBodyPred :: Expr -> Maybe (Expr, Expr)
getEqBodyPred (PAtom Eq fxs e)
= Just (fxs, e)
getEqBodyPred (PAnd ((PAtom Eq fxs e):_))
= Just (fxs, e)
getEqBodyPred _
= Nothing
eqArgNames :: Equation -> [Symbol]
eqArgNames = map fst . eqArgs
substPopIf :: [(Symbol, Expr)] -> Expr -> Expr
substPopIf xes e = L.foldl' go e xes
where
go e (x, EIte b e1 e2) = EIte b (subst1 e (x, e1)) (subst1 e (x, e2))
go e (x, ex) = subst1 e (x, ex)
evalRecApplication :: Knowledge -> CStack -> Expr -> Expr -> EvalST Expr
evalRecApplication γ stk e (EIte b e1 e2) = do
contra <- liftIO (isValid γ PFalse)
if contra
then return e
else do b' <- eval γ stk (mytracepp "REC-APP-COND" b)
b1 <- liftIO (isValid γ b')
if b1
then addEquality γ e e1 >>
({-# SCC "assertSelectors-1" #-} assertSelectors γ e1) >>
eval γ stk (mytracepp ("evalREC-1: " ++ showpp stk) e1) >>=
((e, "App1: ") ~>)
else do
b2 <- liftIO (isValid γ (PNot b'))
if b2
then addEquality γ e e2 >>
({-# SCC "assertSelectors-2" #-} assertSelectors γ e2) >>
eval γ stk (mytracepp ("evalREC-2: " ++ showpp stk) e2) >>=
((e, ("App2: " ++ showpp stk ) ) ~>)
else return e
evalRecApplication _ _ _ e
= return e
addEquality :: Knowledge -> Expr -> Expr -> EvalST ()
addEquality γ e1 e2 =
modify (\st -> st{evSequence = (makeLam γ e1, makeLam γ e2):evSequence st})
evalIte :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> EvalST Expr
evalIte γ stk e b e1 e2 = mytracepp "evalIte:END: " <$>
evalIteAc γ stk e b e1 (mytracepp msg e2)
where
msg = "evalIte:BEGINS: " ++ showpp (stk, e)
evalIteAc :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> EvalST Expr
evalIteAc γ stk e b e1 e2
= join $ evalIte' γ stk e b e1 e2 <$> liftIO (isValid γ b) <*> liftIO (isValid γ (PNot b))
evalIte' :: Knowledge -> CStack -> Expr -> Expr -> Expr -> Expr -> Bool -> Bool -> EvalST Expr
evalIte' γ stk e _ e1 _ b _
| b
= do e' <- eval γ stk e1
(e, "If-True of:" ++ showpp b) ~> e'
evalIte' γ stk e _ _ e2 _ b'
| b'
= do e' <- eval γ stk e2
(e, "If-False") ~> e'
evalIte' γ stk _ b e1 e2 _ _
= EIte b <$> eval γ stk' e1 <*> eval γ stk' e2
where stk' = mytracepp "evalIte'" $ noRecurCS stk
data Knowledge = KN
{ knSims :: ![Rewrite]
, knAms :: ![Equation]
, knContext :: SMT.Context
, knPreds :: SMT.Context -> [(Symbol, Sort)] -> Expr -> IO Bool
, knLams :: [(Symbol, Sort)]
}
isValid :: Knowledge -> Expr -> IO Bool
isValid γ e = mytracepp ("isValid: " ++ showpp e) <$>
knPreds γ (knContext γ) (knLams γ) e
isProof :: (a, SortedReft) -> Bool
isProof (_, RR s _) = showpp s == "Tuple"
knowledge :: Config -> SMT.Context -> AxiomEnv -> Knowledge
knowledge cfg ctx aenv = KN
{ knSims = aenvSimpl aenv
, knAms = aenvEqs aenv
, knContext = ctx
, knPreds = askSMT cfg
, knLams = []
}
initEqualities :: SMT.Context -> AxiomEnv -> [(Symbol, SortedReft)] -> [(Expr, Expr)]
initEqualities ctx aenv es = concatMap (makeSimplifications (aenvSimpl aenv)) dcEqs
where
dcEqs = Misc.hashNub (Mb.catMaybes [getDCEquality senv e1 e2 | EEq e1 e2 <- atoms])
atoms = splitPAnd =<< (expr <$> filter isProof es)
senv = SMT.ctxSymEnv ctx
askSMT :: Config -> SMT.Context -> [(Symbol, Sort)] -> Expr -> IO Bool
askSMT cfg ctx bs e
| isTautoPred e = return True
| null (Vis.kvars e) = SMT.checkValidWithContext ctx [] PTrue e'
| otherwise = return False
where
e' = toSMT cfg ctx bs e
toSMT :: Config -> SMT.Context -> [(Symbol, Sort)] -> Expr -> Pred
toSMT cfg ctx bs = defuncAny cfg senv . elaborate "makeKnowledge" (elabEnv bs)
where
elabEnv = insertsSymEnv senv
senv = SMT.ctxSymEnv ctx
makeSimplifications :: [Rewrite] -> (Symbol, [Expr], Expr) -> [(Expr, Expr)]
makeSimplifications sis (dc, es, e)
= go =<< sis
where
go (SMeasure f dc' xs bd)
| dc == dc', length xs == length es
= [(EApp (EVar f) e, subst (mkSubst $ zip xs es) bd)]
go _
= []
getDCEquality :: SymEnv -> Expr -> Expr -> Maybe (Symbol, [Expr], Expr)
getDCEquality senv e1 e2
| Just dc1 <- f1
, Just dc2 <- f2
= if dc1 == dc2
then Nothing
else error ("isDCEquality on" ++ showpp e1 ++ "\n" ++ showpp e2)
| Just dc1 <- f1
= Just (dc1, es1, e2)
| Just dc2 <- f2
= Just (dc2, es2, e1)
| otherwise
= Nothing
where
(f1, es1) = Misc.mapFst (getDC senv) (splitEApp e1)
(f2, es2) = Misc.mapFst (getDC senv) (splitEApp e2)
getDC :: SymEnv -> Expr -> Maybe Symbol
getDC senv (EVar x)
| isUpperSymbol x && Mb.isNothing (symEnvTheory x senv)
= Just x
getDC _ _
= Nothing
isUpperSymbol :: Symbol -> Bool
isUpperSymbol x = (0 < lengthSym x') && (isUpper $ headSym x')
where
x' = dropModuleNames x
dropModuleNames :: Symbol -> Symbol
dropModuleNames = mungeNames (symbol . last) "."
where
mungeNames _ _ "" = ""
mungeNames f d s'@(symbolText -> s)
| s' == tupConName = tupConName
| otherwise = f $ T.splitOn d $ stripParens s
stripParens t = Mb.fromMaybe t ((T.stripPrefix "(" >=> T.stripSuffix ")") t)
assertSelectors :: Knowledge -> Expr -> EvalST ()
assertSelectors γ e = do
sims <- aenvSimpl <$> gets _evAEnv
forM_ sims $ \s -> Vis.mapMExpr (go s) e
return ()
where
go :: Rewrite -> Expr -> EvalST Expr
go (SMeasure f dc xs bd) e@(EApp _ _)
| (EVar dc', es) <- splitEApp e
, dc == dc'
, length xs == length es
= do let e1 = EApp (EVar f) e
let e2 = subst (mkSubst $ zip xs es) bd
addEquality γ e1 e2
return e
go _ e
= return e
withCtx :: Config -> FilePath -> SymEnv -> (SMT.Context -> IO a) -> IO a
withCtx cfg file env k = do
ctx <- SMT.makeContextWithSEnv cfg file env
_ <- SMT.smtPush ctx
res <- k ctx
_ <- SMT.cleanupContext ctx
return res
(~>) :: (Expr, String) -> Expr -> EvalST Expr
(e, _str) ~> e' = do
let msg = "PLE: " ++ _str ++ showpp (e, e')
modify (\st -> st {evId = (mytracepp msg $ evId st) + 1})
return e'