graph-trace-0.1.0.0: src/Graph/Trace/Internal/Predicates.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
module Graph.Trace.Internal.Predicates
( removeConstraints
, addConstraintToSig
) where
import Control.Monad.Trans.Writer.CPS
import qualified Data.Generics as Syb
import qualified Data.List as L
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Set as S
import qualified Graph.Trace.Internal.GhcFacade as Ghc
import Graph.Trace.Internal.Types
-- | Removes debug predicates from the type signatures in an expression.
-- This is necessary if there are type signatures for pattern bound names and
-- the monomorphism restriction is on.
removeConstraints :: Syb.Data a => DebugNames -> S.Set Ghc.Name -> a -> a
removeConstraints debugNames targetNames thing
| S.null targetNames = thing
| otherwise = Syb.mkT processBind `Syb.everywhere` thing
where
processBind :: Ghc.HsValBinds Ghc.GhcRn -> Ghc.HsValBinds Ghc.GhcRn
processBind (Ghc.XValBindsLR (Ghc.NValBinds binds sigs)) =
Ghc.XValBindsLR (Ghc.NValBinds binds (concatMap removeConstraint sigs))
processBind binds = binds
removeConstraint (Ghc.L loc (Ghc.TypeSig x1 names sig)) =
let (targeted, inert) =
L.partition ((`S.member` targetNames) . Ghc.unLoc) names
in [ Ghc.noLocA' . Ghc.TypeSig x1 targeted
$ Syb.mkT removePred `Syb.everywhere` sig
, Ghc.L loc $ Ghc.TypeSig x1 inert sig
]
removeConstraint s = [s]
removePred (Ghc.HsQualTy' x ctx body) =
let newCtx = (fmap . fmap) (filter (notDebugPred . Ghc.unLoc)) ctx
in Ghc.HsQualTy' x newCtx body
removePred x = x
notDebugPred = isNothing . checkForDebugPred debugNames
-- | Matches on type signatures in order to add the constraint to them.
addConstraintToSig
:: DebugNames
-> Bool -- True <=> Debug all functions
-> Ghc.Sig Ghc.GhcRn
-> Writer (M.Map Ghc.Name (Maybe Ghc.FastString, Propagation))
(Ghc.Sig Ghc.GhcRn)
addConstraintToSig debugNames debugAllFlag
(Ghc.TypeSig x1 lNames (Ghc.HsWC x2 sig)) = do
sig' <- addConstraintToSigType debugNames debugAllFlag (Ghc.unLoc <$> lNames) sig
pure $ Ghc.TypeSig x1 lNames (Ghc.HsWC x2 sig')
addConstraintToSig debugNames debugAllFlag
(Ghc.ClassOpSig x1 b lNames sig) = do
sig' <- addConstraintToSigType debugNames debugAllFlag (Ghc.unLoc <$> lNames) sig
pure $ Ghc.ClassOpSig x1 b lNames sig'
addConstraintToSig _ _ s = pure s
-- | Adds the 'Debug' constraint to a signature if it doesn't already have it
-- as the first constraint in the context.
addConstraintToSigType
:: DebugNames
-> Bool -- True <=> Debug all functions
-> [Ghc.Name]
-> Ghc.LHsSigType Ghc.GhcRn
-> Writer (M.Map Ghc.Name (Maybe Ghc.FastString, Propagation))
(Ghc.LHsSigType Ghc.GhcRn)
addConstraintToSigType debugNames debugAllFlag names sig@(Ghc.HsSig' t) = do
sigBody <- traverse go t
pure $ Ghc.setSigBody sigBody sig
where
prop = if debugAllFlag then Shallow else Inert
predName =
if debugAllFlag
then tracePredName debugNames
else traceInertPredName debugNames
predTy = Ghc.noLocA'
$ Ghc.HsTyVar Ghc.emptyEpAnn Ghc.NotPromoted
(Ghc.noLocA' predName)
go ty =
case ty of
x@Ghc.HsForAllTy { Ghc.hst_body = body } -> do
body' <- traverse go body
pure $ x { Ghc.hst_body = body' }
q@(Ghc.HsQualTy' x ctx body)
| foundPred : _ <-
mapMaybe (checkForDebugPred debugNames)
(Ghc.unLoc <$> foldMap Ghc.unLoc ctx)
-- Note that DebugMuted bindings should still be included because
-- the muted status needs to be inherited by the functions called from it
-> do tell (M.fromList $ names `zip` repeat foundPred)
pure q
| otherwise -> do
tell (M.fromList $ names `zip` repeat (Nothing, prop))
pure $
Ghc.HsQualTy'
x
(Just $ maybe (Ghc.noLocA' [predTy])
(fmap (predTy :))
ctx
)
body
_ -> do
tell (M.fromList $ names `zip` repeat (Nothing, prop))
pure $
Ghc.HsQualTy'
Ghc.NoExtField
(Just $ Ghc.noLocA' [predTy])
(Ghc.noLocA' ty)
addConstraintToSigType _ _ _ x = pure x
-- | Check if a type has a debug predicate in it's context. If so, return the
-- override key if supplied and the propagation strategy.
checkForDebugPred
:: DebugNames
-> Ghc.HsType Ghc.GhcRn
-> Maybe (Maybe Ghc.FastString, Propagation)
checkForDebugPred debugNames
(Ghc.HsTyVar _ _ (Ghc.L _ name))
| name == tracePredName debugNames = Just (Nothing, Shallow)
| name == traceDeepPredName debugNames = Just (Nothing, Deep)
| name == traceMutePredName debugNames = Just (Nothing, Mute)
| name == traceInertPredName debugNames = Just (Nothing, Inert)
checkForDebugPred debugNames
(Ghc.HsAppTy _ (Ghc.L _ (Ghc.HsTyVar _ _ (Ghc.L _ name))) (Ghc.L _ (Ghc.HsTyLit _ (Ghc.HsStrTy _ key))))
| name == traceKeyPredName debugNames = Just (Just key, Shallow)
| name == traceDeepKeyPredName debugNames = Just (Just key, Deep)
checkForDebugPred debugNames Ghc.HsForAllTy { Ghc.hst_body = Ghc.L _ ty }
= checkForDebugPred debugNames ty
checkForDebugPred debugNames (Ghc.HsParTy _ (Ghc.L _ ty))
= checkForDebugPred debugNames ty
checkForDebugPred _ _ = Nothing
-- need a case for nested QualTy?