{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
module GHC.HsToCore.Pmc.Check (
CheckAction(..),
checkMatchGroup, checkGRHSs, checkPatBind, checkEmptyCase, checkRecSel
) where
import GHC.Prelude
import GHC.Builtin.Names ( hasKey, considerAccessibleIdKey, trueDataConKey )
import GHC.HsToCore.Monad ( DsM )
import GHC.HsToCore.Pmc.Types
import GHC.HsToCore.Pmc.Utils
import GHC.HsToCore.Pmc.Solver
import GHC.Driver.DynFlags
import GHC.Utils.Outputable
import GHC.Tc.Utils.TcType (evVarPred)
import GHC.Data.OrdList
import GHC.Data.Bag
import qualified Data.Semigroup as Semi
import Data.List.NonEmpty ( NonEmpty(..) )
import qualified Data.List.NonEmpty as NE
import Data.Coerce
import GHC.Types.Var
import GHC.Core
import GHC.Core.Utils
newtype CheckAction a = CA { forall a. CheckAction a -> Nablas -> DsM (CheckResult a)
unCA :: Nablas -> DsM (CheckResult a) }
deriving (forall a b. (a -> b) -> CheckAction a -> CheckAction b)
-> (forall a b. a -> CheckAction b -> CheckAction a)
-> Functor CheckAction
forall a b. a -> CheckAction b -> CheckAction a
forall a b. (a -> b) -> CheckAction a -> CheckAction b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> CheckAction a -> CheckAction b
fmap :: forall a b. (a -> b) -> CheckAction a -> CheckAction b
$c<$ :: forall a b. a -> CheckAction b -> CheckAction a
<$ :: forall a b. a -> CheckAction b -> CheckAction a
Functor
topToBottom :: (top -> bot -> ret)
-> CheckAction top
-> CheckAction bot
-> CheckAction ret
topToBottom :: forall top bot ret.
(top -> bot -> ret)
-> CheckAction top -> CheckAction bot -> CheckAction ret
topToBottom top -> bot -> ret
f (CA Nablas -> DsM (CheckResult top)
top) (CA Nablas -> DsM (CheckResult bot)
bot) = (Nablas -> DsM (CheckResult ret)) -> CheckAction ret
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult ret)) -> CheckAction ret)
-> (Nablas -> DsM (CheckResult ret)) -> CheckAction ret
forall a b. (a -> b) -> a -> b
$ \Nablas
inc -> do
t <- Nablas -> DsM (CheckResult top)
top Nablas
inc
b <- bot (cr_uncov t)
pure CheckResult { cr_ret = f (cr_ret t) (cr_ret b)
, cr_uncov = cr_uncov b
, cr_approx = cr_approx t Semi.<> cr_approx b }
leftToRight :: (RedSets -> right -> ret)
-> CheckAction RedSets
-> CheckAction right
-> CheckAction ret
leftToRight :: forall right ret.
(Post -> right -> ret)
-> CheckAction Post -> CheckAction right -> CheckAction ret
leftToRight Post -> right -> ret
f (CA Nablas -> DsM (CheckResult Post)
left) (CA Nablas -> DsM (CheckResult right)
right) = (Nablas -> DsM (CheckResult ret)) -> CheckAction ret
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult ret)) -> CheckAction ret)
-> (Nablas -> DsM (CheckResult ret)) -> CheckAction ret
forall a b. (a -> b) -> a -> b
$ \Nablas
inc -> do
l <- Nablas -> DsM (CheckResult Post)
left Nablas
inc
r <- right (rs_cov (cr_ret l))
limit <- maxPmCheckModels <$> getDynFlags
let uncov = CheckResult Post -> Nablas
forall a. CheckResult a -> Nablas
cr_uncov CheckResult Post
l Nablas -> Nablas -> Nablas
forall a. Semigroup a => a -> a -> a
Semi.<> CheckResult right -> Nablas
forall a. CheckResult a -> Nablas
cr_uncov CheckResult right
r
let (prec', uncov') = throttle limit inc uncov
pure CheckResult { cr_ret = f (cr_ret l) (cr_ret r)
, cr_uncov = uncov'
, cr_approx = prec' Semi.<> cr_approx l Semi.<> cr_approx r }
throttle :: Int -> Nablas -> Nablas -> (Precision, Nablas)
throttle :: Int -> Nablas -> Nablas -> (Precision, Nablas)
throttle Int
limit old :: Nablas
old@(MkNablas Bag Nabla
old_ds) new :: Nablas
new@(MkNablas Bag Nabla
new_ds)
| Bag Nabla -> Int
forall a. Bag a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length Bag Nabla
new_ds Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
limit (Bag Nabla -> Int
forall a. Bag a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length Bag Nabla
old_ds) = (Precision
Approximate, Nablas
old)
| Bool
otherwise = (Precision
Precise, Nablas
new)
checkSequence :: (grdtree -> CheckAction anntree) -> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
checkSequence :: forall grdtree anntree.
(grdtree -> CheckAction anntree)
-> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
checkSequence grdtree -> CheckAction anntree
act (grdtree
t :| []) = (anntree -> [anntree] -> NonEmpty anntree
forall a. a -> [a] -> NonEmpty a
:| []) (anntree -> NonEmpty anntree)
-> CheckAction anntree -> CheckAction (NonEmpty anntree)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> grdtree -> CheckAction anntree
act grdtree
t
checkSequence grdtree -> CheckAction anntree
act (grdtree
t1 :| (grdtree
t2:[grdtree]
ts)) =
(anntree -> NonEmpty anntree -> NonEmpty anntree)
-> CheckAction anntree
-> CheckAction (NonEmpty anntree)
-> CheckAction (NonEmpty anntree)
forall top bot ret.
(top -> bot -> ret)
-> CheckAction top -> CheckAction bot -> CheckAction ret
topToBottom anntree -> NonEmpty anntree -> NonEmpty anntree
forall a. a -> NonEmpty a -> NonEmpty a
(NE.<|) (grdtree -> CheckAction anntree
act grdtree
t1) ((grdtree -> CheckAction anntree)
-> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
forall grdtree anntree.
(grdtree -> CheckAction anntree)
-> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
checkSequence grdtree -> CheckAction anntree
act (grdtree
t2grdtree -> [grdtree] -> NonEmpty grdtree
forall a. a -> [a] -> NonEmpty a
:|[grdtree]
ts))
emptyRedSets :: RedSets
emptyRedSets :: Post
emptyRedSets = Nablas -> Nablas -> OrdList (Nablas, SrcInfo) -> Post
RedSets Nablas
forall a. Monoid a => a
mempty Nablas
forall a. Monoid a => a
mempty OrdList (Nablas, SrcInfo)
forall a. Monoid a => a
mempty
checkGrd :: PmGrd -> CheckAction RedSets
checkGrd :: PmGrd -> CheckAction Post
checkGrd PmGrd
grd = (Nablas -> DsM (CheckResult Post)) -> CheckAction Post
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult Post)) -> CheckAction Post)
-> (Nablas -> DsM (CheckResult Post)) -> CheckAction Post
forall a b. (a -> b) -> a -> b
$ \Nablas
inc -> case PmGrd
grd of
PmLet Id
x CoreExpr
e -> do
matched <- Nablas -> PhiCt -> DsM Nablas
addPhiCtNablas Nablas
inc (Id -> CoreExpr -> PhiCt
PhiCoreCt Id
x CoreExpr
e)
tracePm "check:Let" (ppr x <+> char '=' <+> ppr e)
pure CheckResult { cr_ret = emptyRedSets { rs_cov = matched }
, cr_uncov = mempty
, cr_approx = Precise }
PmBang Id
x Maybe SrcInfo
mb_info -> do
div <- Nablas -> PhiCt -> DsM Nablas
addPhiCtNablas Nablas
inc (Id -> PhiCt
PhiBotCt Id
x)
matched <- addPhiCtNablas inc (PhiNotBotCt x)
let bangs | Just SrcInfo
info <- Maybe SrcInfo
mb_info = (Nablas, SrcInfo) -> OrdList (Nablas, SrcInfo)
forall a. a -> OrdList a
unitOL (Nablas
div, SrcInfo
info)
| Bool
otherwise = OrdList (Nablas, SrcInfo)
forall a. OrdList a
NilOL
tracePm "check:Bang" (ppr x <+> ppr div)
pure CheckResult { cr_ret = RedSets { rs_cov = matched, rs_div = div, rs_bangs = bangs }
, cr_uncov = mempty
, cr_approx = Precise }
PmCon Id
x (PmAltConLike ConLike
con) [Id]
_ [Id]
_ [Id]
_
| Id
x Id -> Unique -> Bool
forall a. Uniquable a => a -> Unique -> Bool
`hasKey` Unique
considerAccessibleIdKey
, ConLike
con ConLike -> Unique -> Bool
forall a. Uniquable a => a -> Unique -> Bool
`hasKey` Unique
trueDataConKey
-> CheckResult Post -> DsM (CheckResult Post)
forall a. a -> IOEnv (Env DsGblEnv DsLclEnv) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CheckResult { cr_ret :: Post
cr_ret = Post
emptyRedSets { rs_cov = initNablas }
, cr_uncov :: Nablas
cr_uncov = Nablas
forall a. Monoid a => a
mempty
, cr_approx :: Precision
cr_approx = Precision
Precise }
PmCon Id
x PmAltCon
con [Id]
tvs [Id]
dicts [Id]
args -> do
!div <- if PmAltCon -> Bool
isPmAltConMatchStrict PmAltCon
con
then Nablas -> PhiCt -> DsM Nablas
addPhiCtNablas Nablas
inc (Id -> PhiCt
PhiBotCt Id
x)
else Nablas -> DsM Nablas
forall a. a -> IOEnv (Env DsGblEnv DsLclEnv) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Nablas
forall a. Monoid a => a
mempty
!matched <- addPhiCtNablas inc (PhiConCt x con tvs (map evVarPred dicts) args)
!uncov <- addPhiCtNablas inc (PhiNotConCt x con)
tracePm "check:Con" $ vcat
[ ppr grd
, ppr inc
, hang (text "div") 2 (ppr div)
, hang (text "matched") 2 (ppr matched)
, hang (text "uncov") 2 (ppr uncov)
]
pure CheckResult { cr_ret = emptyRedSets { rs_cov = matched, rs_div = div }
, cr_uncov = uncov
, cr_approx = Precise }
checkGrds :: [PmGrd] -> CheckAction RedSets
checkGrds :: [PmGrd] -> CheckAction Post
checkGrds [] = (Nablas -> DsM (CheckResult Post)) -> CheckAction Post
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult Post)) -> CheckAction Post)
-> (Nablas -> DsM (CheckResult Post)) -> CheckAction Post
forall a b. (a -> b) -> a -> b
$ \Nablas
inc ->
CheckResult Post -> DsM (CheckResult Post)
forall a. a -> IOEnv (Env DsGblEnv DsLclEnv) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CheckResult { cr_ret :: Post
cr_ret = Post
emptyRedSets { rs_cov = inc }
, cr_uncov :: Nablas
cr_uncov = Nablas
forall a. Monoid a => a
mempty
, cr_approx :: Precision
cr_approx = Precision
Precise }
checkGrds (PmGrd
g:[PmGrd]
grds) = (Post -> Post -> Post)
-> CheckAction Post -> CheckAction Post -> CheckAction Post
forall right ret.
(Post -> right -> ret)
-> CheckAction Post -> CheckAction right -> CheckAction ret
leftToRight Post -> Post -> Post
merge (PmGrd -> CheckAction Post
checkGrd PmGrd
g) ([PmGrd] -> CheckAction Post
checkGrds [PmGrd]
grds)
where
merge :: Post -> Post -> Post
merge Post
ri_g Post
ri_grds =
RedSets { rs_cov :: Nablas
rs_cov = Post -> Nablas
rs_cov Post
ri_grds
, rs_div :: Nablas
rs_div = Post -> Nablas
rs_div Post
ri_g Nablas -> Nablas -> Nablas
forall a. Semigroup a => a -> a -> a
Semi.<> Post -> Nablas
rs_div Post
ri_grds
, rs_bangs :: OrdList (Nablas, SrcInfo)
rs_bangs = Post -> OrdList (Nablas, SrcInfo)
rs_bangs Post
ri_g OrdList (Nablas, SrcInfo)
-> OrdList (Nablas, SrcInfo) -> OrdList (Nablas, SrcInfo)
forall a. Semigroup a => a -> a -> a
Semi.<> Post -> OrdList (Nablas, SrcInfo)
rs_bangs Post
ri_grds }
checkMatchGroup :: PmMatchGroup Pre -> CheckAction (PmMatchGroup Post)
checkMatchGroup :: PmMatchGroup Pre -> CheckAction (PmMatchGroup Post)
checkMatchGroup (PmMatchGroup NonEmpty (PmMatch Pre)
matches) =
NonEmpty (PmMatch Post) -> PmMatchGroup Post
forall p. NonEmpty (PmMatch p) -> PmMatchGroup p
PmMatchGroup (NonEmpty (PmMatch Post) -> PmMatchGroup Post)
-> CheckAction (NonEmpty (PmMatch Post))
-> CheckAction (PmMatchGroup Post)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (PmMatch Pre -> CheckAction (PmMatch Post))
-> NonEmpty (PmMatch Pre) -> CheckAction (NonEmpty (PmMatch Post))
forall grdtree anntree.
(grdtree -> CheckAction anntree)
-> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
checkSequence PmMatch Pre -> CheckAction (PmMatch Post)
checkMatch NonEmpty (PmMatch Pre)
matches
checkMatch :: PmMatch Pre -> CheckAction (PmMatch Post)
checkMatch :: PmMatch Pre -> CheckAction (PmMatch Post)
checkMatch (PmMatch { pm_pats :: forall p. PmMatch p -> p
pm_pats = GrdVec [PmGrd]
grds, pm_grhss :: forall p. PmMatch p -> PmGRHSs p
pm_grhss = PmGRHSs Pre
grhss }) =
(Post -> PmGRHSs Post -> PmMatch Post)
-> CheckAction Post
-> CheckAction (PmGRHSs Post)
-> CheckAction (PmMatch Post)
forall right ret.
(Post -> right -> ret)
-> CheckAction Post -> CheckAction right -> CheckAction ret
leftToRight Post -> PmGRHSs Post -> PmMatch Post
forall p. p -> PmGRHSs p -> PmMatch p
PmMatch ([PmGrd] -> CheckAction Post
checkGrds [PmGrd]
grds) (PmGRHSs Pre -> CheckAction (PmGRHSs Post)
checkGRHSs PmGRHSs Pre
grhss)
checkGRHSs :: PmGRHSs Pre -> CheckAction (PmGRHSs Post)
checkGRHSs :: PmGRHSs Pre -> CheckAction (PmGRHSs Post)
checkGRHSs (PmGRHSs { pgs_lcls :: forall p. PmGRHSs p -> p
pgs_lcls = GrdVec [PmGrd]
lcls, pgs_grhss :: forall p. PmGRHSs p -> NonEmpty (PmGRHS p)
pgs_grhss = NonEmpty (PmGRHS Pre)
grhss }) =
(Post -> NonEmpty (PmGRHS Post) -> PmGRHSs Post)
-> CheckAction Post
-> CheckAction (NonEmpty (PmGRHS Post))
-> CheckAction (PmGRHSs Post)
forall right ret.
(Post -> right -> ret)
-> CheckAction Post -> CheckAction right -> CheckAction ret
leftToRight Post -> NonEmpty (PmGRHS Post) -> PmGRHSs Post
forall p. p -> NonEmpty (PmGRHS p) -> PmGRHSs p
PmGRHSs ([PmGrd] -> CheckAction Post
checkGrds [PmGrd]
lcls) ((PmGRHS Pre -> CheckAction (PmGRHS Post))
-> NonEmpty (PmGRHS Pre) -> CheckAction (NonEmpty (PmGRHS Post))
forall grdtree anntree.
(grdtree -> CheckAction anntree)
-> NonEmpty grdtree -> CheckAction (NonEmpty anntree)
checkSequence PmGRHS Pre -> CheckAction (PmGRHS Post)
checkGRHS NonEmpty (PmGRHS Pre)
grhss)
checkGRHS :: PmGRHS Pre -> CheckAction (PmGRHS Post)
checkGRHS :: PmGRHS Pre -> CheckAction (PmGRHS Post)
checkGRHS (PmGRHS { pg_grds :: forall p. PmGRHS p -> p
pg_grds = GrdVec [PmGrd]
grds, pg_rhs :: forall p. PmGRHS p -> SrcInfo
pg_rhs = SrcInfo
rhs_info }) =
(Post -> SrcInfo -> PmGRHS Post) -> SrcInfo -> Post -> PmGRHS Post
forall a b c. (a -> b -> c) -> b -> a -> c
flip Post -> SrcInfo -> PmGRHS Post
forall p. p -> SrcInfo -> PmGRHS p
PmGRHS SrcInfo
rhs_info (Post -> PmGRHS Post)
-> CheckAction Post -> CheckAction (PmGRHS Post)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [PmGrd] -> CheckAction Post
checkGrds [PmGrd]
grds
checkEmptyCase :: PmEmptyCase -> CheckAction PmEmptyCase
checkEmptyCase :: PmEmptyCase -> CheckAction PmEmptyCase
checkEmptyCase pe :: PmEmptyCase
pe@(PmEmptyCase { pe_var :: PmEmptyCase -> Id
pe_var = Id
var }) = (Nablas -> DsM (CheckResult PmEmptyCase))
-> CheckAction PmEmptyCase
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult PmEmptyCase))
-> CheckAction PmEmptyCase)
-> (Nablas -> DsM (CheckResult PmEmptyCase))
-> CheckAction PmEmptyCase
forall a b. (a -> b) -> a -> b
$ \Nablas
inc -> do
unc <- Nablas -> PhiCt -> DsM Nablas
addPhiCtNablas Nablas
inc (Id -> PhiCt
PhiNotBotCt Id
var)
pure CheckResult { cr_ret = pe, cr_uncov = unc, cr_approx = mempty }
checkPatBind :: (PmPatBind Pre) -> CheckAction (PmPatBind Post)
checkPatBind :: PmPatBind Pre -> CheckAction (PmPatBind Post)
checkPatBind = (PmGRHS Pre -> CheckAction (PmGRHS Post))
-> PmPatBind Pre -> CheckAction (PmPatBind Post)
forall a b. Coercible a b => a -> b
coerce PmGRHS Pre -> CheckAction (PmGRHS Post)
checkGRHS
checkRecSel :: PmRecSel () -> CheckAction (PmRecSel Id)
checkRecSel :: PmRecSel () -> CheckAction (PmRecSel Id)
checkRecSel pr :: PmRecSel ()
pr@(PmRecSel { pr_arg :: forall v. PmRecSel v -> CoreExpr
pr_arg = CoreExpr
arg, pr_cons :: forall v. PmRecSel v -> [ConLike]
pr_cons = [ConLike]
cons }) = (Nablas -> DsM (CheckResult (PmRecSel Id)))
-> CheckAction (PmRecSel Id)
forall a. (Nablas -> DsM (CheckResult a)) -> CheckAction a
CA ((Nablas -> DsM (CheckResult (PmRecSel Id)))
-> CheckAction (PmRecSel Id))
-> (Nablas -> DsM (CheckResult (PmRecSel Id)))
-> CheckAction (PmRecSel Id)
forall a b. (a -> b) -> a -> b
$ \Nablas
inc -> do
arg_id <- case CoreExpr
arg of
Var Id
arg_id -> Id -> IOEnv (Env DsGblEnv DsLclEnv) Id
forall a. a -> IOEnv (Env DsGblEnv DsLclEnv) a
forall (m :: * -> *) a. Monad m => a -> m a
return Id
arg_id
CoreExpr
_ -> PredType -> IOEnv (Env DsGblEnv DsLclEnv) Id
mkPmId (PredType -> IOEnv (Env DsGblEnv DsLclEnv) Id)
-> PredType -> IOEnv (Env DsGblEnv DsLclEnv) Id
forall a b. (a -> b) -> a -> b
$ HasDebugCallStack => CoreExpr -> PredType
CoreExpr -> PredType
exprType CoreExpr
arg
let con_cts = (ConLike -> PhiCt) -> [ConLike] -> [PhiCt]
forall a b. (a -> b) -> [a] -> [b]
map (Id -> PmAltCon -> PhiCt
PhiNotConCt Id
arg_id (PmAltCon -> PhiCt) -> (ConLike -> PmAltCon) -> ConLike -> PhiCt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConLike -> PmAltCon
PmAltConLike) [ConLike]
cons
arg_ct = Id -> CoreExpr -> PhiCt
PhiCoreCt Id
arg_id CoreExpr
arg
phi_cts = [PhiCt] -> Bag PhiCt
forall a. [a] -> Bag a
listToBag (PhiCt
arg_ct PhiCt -> [PhiCt] -> [PhiCt]
forall a. a -> [a] -> [a]
: [PhiCt]
con_cts)
unc <- addPhiCtsNablas inc phi_cts
pure CheckResult { cr_ret = pr{ pr_arg_var = arg_id }, cr_uncov = unc, cr_approx = mempty }