hermit-0.2.0.0: src/Language/HERMIT/Context.hs
{-# LANGUAGE InstanceSigs, ConstraintKinds, MultiParamTypeClasses #-}
module Language.HERMIT.Context
( -- * HERMIT Contexts
-- ** The Standard Context
HermitC
, initHermitC
-- ** Bindings
, HermitBindingSite(..)
, BindingDepth
, HermitBinding
, hermitBindingSiteExpr
, hermitBindingExpr
-- ** Adding bindings to contexts
, AddBindings(..)
, addBindingGroup
, addLambdaBinding
, addAltBindings
, addCaseWildBinding
, addForallBinding
-- ** Reading bindings from the context
, BoundVars(..)
, boundIn
, findBoundVars
, ReadBindings(..)
, lookupHermitBinding
-- ** Accessing the Global Reader Environment from the context
, HasGlobalRdrEnv(..)
-- ** Accessing GHC rewrite rules from the context
, HasCoreRules(..)
) where
import Prelude hiding (lookup)
import GhcPlugins hiding (empty)
import Data.Monoid (mempty)
import Data.Map hiding (map, foldr, filter)
import qualified Data.Set as S
import qualified Language.Haskell.TH as TH
import Language.KURE
import Language.HERMIT.Core
import Language.HERMIT.GHC
------------------------------------------------------------------------
-- | The depth of a binding. Used, for example, to detect shadowing when inlining.
type BindingDepth = Int
-- | HERMIT\'s representation of variable bindings.
-- Bound expressions cannot be inlined without checking for shadowing issues (using the depth information).
data HermitBindingSite
= REC CoreExpr
| NONREC CoreExpr
| LAM
| CASEALT
| FORALL
| CASEWILD CoreExpr (AltCon,[Var]) -- ^ We store both the scrutinised expression, and the case alternative 'AltCon' (which can be converted to Constructor or Literal) and variables.
type HermitBinding = (BindingDepth, HermitBindingSite)
-- | Retrieve the expression in a 'HermitBindingSite', if there is one.
hermitBindingSiteExpr :: HermitBindingSite -> Maybe CoreExpr
hermitBindingSiteExpr b = case b of
REC e -> Just e
NONREC e -> Just e
CASEWILD e _ -> Just e
_ -> Nothing
-- | Retrieve the expression in a 'HermitBinding', if there is one.
hermitBindingExpr :: HermitBinding -> Maybe CoreExpr
hermitBindingExpr = hermitBindingSiteExpr . snd
------------------------------------------------------------------------
-- | A class of contexts that can have HERMIT bindings added to them.
class AddBindings c where
-- | Add a complete set of parrallel bindings to the context.
-- (Parallel bindings occur in recursive let bindings and case alternatives.)
-- This can also be used for solitary bindings (e.g. lambdas).
-- Bindings are added in parallel sets to help with shadowing issues.
addHermitBindings :: [(Var,HermitBindingSite)] -> c -> c
-- | Here the bindings are just discarded.
instance AddBindings (SnocPath crumb) where
addHermitBindings :: [(Var,HermitBindingSite)] -> SnocPath crumb -> SnocPath crumb
addHermitBindings _ = id
-------------------------------------------
-- | Add a single binding to the context.
addHermitBinding :: AddBindings c => Var -> HermitBindingSite -> c -> c
addHermitBinding v bd = addHermitBindings [(v,bd)]
-- | Add all bindings in a binding group to a context.
addBindingGroup :: AddBindings c => CoreBind -> c -> c
addBindingGroup (NonRec v e) = addHermitBinding v (NONREC e)
addBindingGroup (Rec ies) = addHermitBindings [ (i, REC e) | (i,e) <- ies ]
-- | Add a wildcard binding for a specific case alternative.
addCaseWildBinding :: AddBindings c => (Id,CoreExpr,CoreAlt) -> c -> c
addCaseWildBinding (i,e,(con,vs,_)) = addHermitBinding i (CASEWILD e (con,vs))
-- | Add a lambda bound variable to a context.
-- All that is known is the variable, which may shadow something.
-- If so, we don't worry about that here, it is instead checked during inlining.
addLambdaBinding :: AddBindings c => Var -> c -> c
addLambdaBinding v = addHermitBinding v LAM
-- | Add the variables bound by a 'DataCon' in a case.
-- They are all bound at the same depth.
addAltBindings :: AddBindings c => [Var] -> c -> c
addAltBindings vs = addHermitBindings [ (v, CASEALT) | v <- vs ]
-- | Add a universally quantified type variable to a context.
addForallBinding :: AddBindings c => TyVar -> c -> c
addForallBinding v = addHermitBinding v FORALL
------------------------------------------------------------------------
-- | A class of contexts that stores the set of variables in scope that have been bound during the traversal.
class BoundVars c where
boundVars :: c -> S.Set Var
-- | List all variables bound in the context that match the given name.
findBoundVars :: BoundVars c => TH.Name -> c -> [Var]
findBoundVars nm = filter (cmpTHName2Var nm) . S.toList . boundVars
-- | A class of contexts from which HERMIT bindings can be retrieved.
class BoundVars c => ReadBindings c where
hermitDepth :: c -> BindingDepth
hermitBindings :: c -> Map Var HermitBinding
-- | Determine if a variable is bound in a context.
boundIn :: ReadBindings c => Var -> c -> Bool
boundIn i c = i `member` hermitBindings c
-- | Lookup the binding for a variable in a context.
lookupHermitBinding :: ReadBindings c => Var -> c -> Maybe HermitBinding
lookupHermitBinding v = lookup v . hermitBindings
------------------------------------------------------------------------
-- | A class of contexts that store GHC rewrite rules.
class HasCoreRules c where
hermitCoreRules :: c -> [CoreRule]
------------------------------------------------------------------------
-- | A class of contexts that store the Global Reader Environment.
class HasGlobalRdrEnv c where
hermitGlobalRdrEnv :: c -> GlobalRdrEnv
------------------------------------------------------------------------
-- | The HERMIT context, containing all bindings in scope and the current location in the AST.
-- The bindings here are lazy by choice, so that we can avoid the cost
-- of building the context if we never use it.
data HermitC = HermitC
{ hermitC_bindings :: Map Var HermitBinding -- ^ All (important) bindings in scope.
, hermitC_depth :: BindingDepth -- ^ The depth of the bindings.
, hermitC_path :: AbsolutePath Crumb -- ^ The 'AbsolutePath' to the current node from the root.
, hermitC_globalRdrEnv :: GlobalRdrEnv -- ^ The top-level lexical environment.
, hermitC_coreRules :: [CoreRule] -- ^ GHC rewrite RULES.
}
------------------------------------------------------------------------
-- | Create the initial HERMIT 'HermitC' by providing a 'ModGuts'.
initHermitC :: ModGuts -> HermitC
initHermitC modGuts = HermitC
{ hermitC_bindings = empty
, hermitC_depth = 0
, hermitC_path = mempty
, hermitC_globalRdrEnv = mg_rdr_env modGuts
, hermitC_coreRules = mg_rules modGuts ++ other_rules
}
where other_rules :: [CoreRule]
other_rules = mg_binds modGuts >>= bindToIdExprs >>= (idCoreRules . fst)
------------------------------------------------------------------------
-- | Retrieve the 'AbsolutePath' to the current node, from the HERMIT context.
instance ReadPath HermitC Crumb where
absPath :: HermitC -> AbsolutePath Crumb
absPath = hermitC_path
-- | Extend the 'AbsolutePath' stored in the HERMIT context.
instance ExtendPath HermitC Crumb where
(@@) :: HermitC -> Crumb -> HermitC
c @@ n = c { hermitC_path = hermitC_path c @@ n }
------------------------------------------------------------------------
instance AddBindings HermitC where
addHermitBindings :: [(Var,HermitBindingSite)] -> HermitC -> HermitC
addHermitBindings vbs c = let nextDepth = succ (hermitC_depth c)
vhbs = [ (v, (nextDepth,b)) | (v,b) <- vbs ]
in c { hermitC_bindings = fromList vhbs `union` hermitC_bindings c
, hermitC_depth = nextDepth
}
------------------------------------------------------------------------
instance BoundVars HermitC where
boundVars :: HermitC -> S.Set Var
boundVars = keysSet . hermitC_bindings
instance ReadBindings HermitC where
hermitDepth :: HermitC -> BindingDepth
hermitDepth = hermitC_depth
hermitBindings :: HermitC -> Map Var HermitBinding
hermitBindings = hermitC_bindings
------------------------------------------------------------------------
instance HasCoreRules HermitC where
hermitCoreRules :: HermitC -> [CoreRule]
hermitCoreRules = hermitC_coreRules
------------------------------------------------------------------------
instance HasGlobalRdrEnv HermitC where
hermitGlobalRdrEnv :: HermitC -> GlobalRdrEnv
hermitGlobalRdrEnv = hermitC_globalRdrEnv
------------------------------------------------------------------------