diff options
Diffstat (limited to 'compiler/specialise/Rules.lhs')
-rw-r--r-- | compiler/specialise/Rules.lhs | 633 |
1 files changed, 633 insertions, 0 deletions
diff --git a/compiler/specialise/Rules.lhs b/compiler/specialise/Rules.lhs new file mode 100644 index 0000000000..4d743140ea --- /dev/null +++ b/compiler/specialise/Rules.lhs @@ -0,0 +1,633 @@ +% +% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998 +% +\section[CoreRules]{Transformation rules} + +\begin{code} +module Rules ( + RuleBase, emptyRuleBase, mkRuleBase, extendRuleBaseList, + unionRuleBase, pprRuleBase, ruleCheckProgram, + + mkSpecInfo, extendSpecInfo, addSpecInfo, + rulesOfBinds, addIdSpecialisations, + + lookupRule, mkLocalRule, roughTopNames + ) where + +#include "HsVersions.h" + +import CoreSyn -- All of it +import OccurAnal ( occurAnalyseExpr ) +import CoreFVs ( exprFreeVars, exprsFreeVars, rulesRhsFreeVars ) +import CoreUnfold ( isCheapUnfolding, unfoldingTemplate ) +import CoreUtils ( tcEqExprX ) +import PprCore ( pprRules ) +import Type ( TvSubstEnv ) +import TcType ( tcSplitTyConApp_maybe ) +import CoreTidy ( tidyRules ) +import Id ( Id, idUnfolding, isLocalId, isGlobalId, idName, + idSpecialisation, idCoreRules, setIdSpecialisation ) +import IdInfo ( SpecInfo( SpecInfo ) ) +import Var ( Var ) +import VarEnv ( IdEnv, InScopeSet, emptyTidyEnv, + emptyInScopeSet, mkInScopeSet, extendInScopeSetList, + emptyVarEnv, lookupVarEnv, extendVarEnv, + nukeRnEnvL, mkRnEnv2, rnOccR, rnOccL, inRnEnvR, + rnBndrR, rnBndr2, rnBndrL, rnBndrs2 ) +import VarSet +import Name ( Name, NamedThing(..), nameOccName ) +import NameEnv +import Unify ( ruleMatchTyX, MatchEnv(..) ) +import BasicTypes ( Activation, CompilerPhase, isActive ) +import Outputable +import FastString +import Maybes ( isJust, orElse ) +import Bag +import Util ( singleton ) +import List ( isPrefixOf ) +\end{code} + + +%************************************************************************ +%* * +\subsection[specialisation-IdInfo]{Specialisation info about an @Id@} +%* * +%************************************************************************ + +A @CoreRule@ holds details of one rule for an @Id@, which +includes its specialisations. + +For example, if a rule for @f@ contains the mapping: +\begin{verbatim} + forall a b d. [Type (List a), Type b, Var d] ===> f' a b +\end{verbatim} +then when we find an application of f to matching types, we simply replace +it by the matching RHS: +\begin{verbatim} + f (List Int) Bool dict ===> f' Int Bool +\end{verbatim} +All the stuff about how many dictionaries to discard, and what types +to apply the specialised function to, are handled by the fact that the +Rule contains a template for the result of the specialisation. + +There is one more exciting case, which is dealt with in exactly the same +way. If the specialised value is unboxed then it is lifted at its +definition site and unlifted at its uses. For example: + + pi :: forall a. Num a => a + +might have a specialisation + + [Int#] ===> (case pi' of Lift pi# -> pi#) + +where pi' :: Lift Int# is the specialised version of pi. + +\begin{code} +mkLocalRule :: RuleName -> Activation + -> Name -> [CoreBndr] -> [CoreExpr] -> CoreExpr -> CoreRule +-- Used to make CoreRule for an Id defined in this module +mkLocalRule name act fn bndrs args rhs + = Rule { ru_name = name, ru_fn = fn, ru_act = act, + ru_bndrs = bndrs, ru_args = args, + ru_rhs = rhs, ru_rough = roughTopNames args, + ru_orph = Just (nameOccName fn), ru_local = True } + +-------------- +roughTopNames :: [CoreExpr] -> [Maybe Name] +roughTopNames args = map roughTopName args + +roughTopName :: CoreExpr -> Maybe Name +-- Find the "top" free name of an expression +-- a) the function in an App chain (if a GlobalId) +-- b) the TyCon in a type +-- This is used for the fast-match-check for rules; +-- if the top names don't match, the rest can't +roughTopName (Type ty) = case tcSplitTyConApp_maybe ty of + Just (tc,_) -> Just (getName tc) + Nothing -> Nothing +roughTopName (App f a) = roughTopName f +roughTopName (Var f) | isGlobalId f = Just (idName f) + | otherwise = Nothing +roughTopName other = Nothing + +ruleCantMatch :: [Maybe Name] -> [Maybe Name] -> Bool +-- (ruleCantMatch tpl actual) returns True only if 'actual' +-- definitely can't match 'tpl' by instantiating 'tpl'. +-- It's only a one-way match; unlike instance matching we +-- don't consider unification +ruleCantMatch (Just n1 : ts) (Just n2 : as) = n1 /= n2 || ruleCantMatch ts as +ruleCantMatch (Just n1 : ts) (Nothing : as) = True +ruleCantMatch (t : ts) (a : as) = ruleCantMatch ts as +ruleCantMatch ts as = False +\end{code} + + +%************************************************************************ +%* * + SpecInfo: the rules in an IdInfo +%* * +%************************************************************************ + +\begin{code} +mkSpecInfo :: [CoreRule] -> SpecInfo +mkSpecInfo rules = SpecInfo rules (rulesRhsFreeVars rules) + +extendSpecInfo :: SpecInfo -> [CoreRule] -> SpecInfo +extendSpecInfo (SpecInfo rs1 fvs1) rs2 + = SpecInfo (rs2 ++ rs1) (rulesRhsFreeVars rs2 `unionVarSet` fvs1) + +addSpecInfo :: SpecInfo -> SpecInfo -> SpecInfo +addSpecInfo (SpecInfo rs1 fvs1) (SpecInfo rs2 fvs2) + = SpecInfo (rs1 ++ rs2) (fvs1 `unionVarSet` fvs2) + +addIdSpecialisations :: Id -> [CoreRule] -> Id +addIdSpecialisations id rules + = setIdSpecialisation id $ + extendSpecInfo (idSpecialisation id) rules + +rulesOfBinds :: [CoreBind] -> [CoreRule] +rulesOfBinds binds = concatMap (concatMap idCoreRules . bindersOf) binds +\end{code} + + +%************************************************************************ +%* * + RuleBase +%* * +%************************************************************************ + +\begin{code} +type RuleBase = NameEnv [CoreRule] + -- Maps (the name of) an Id to its rules + -- The rules are are unordered; + -- we sort out any overlaps on lookup + +emptyRuleBase = emptyNameEnv + +mkRuleBase :: [CoreRule] -> RuleBase +mkRuleBase rules = extendRuleBaseList emptyRuleBase rules + +extendRuleBaseList :: RuleBase -> [CoreRule] -> RuleBase +extendRuleBaseList rule_base new_guys + = foldl extendRuleBase rule_base new_guys + +unionRuleBase :: RuleBase -> RuleBase -> RuleBase +unionRuleBase rb1 rb2 = plusNameEnv_C (++) rb1 rb2 + +extendRuleBase :: RuleBase -> CoreRule -> RuleBase +extendRuleBase rule_base rule + = extendNameEnv_Acc (:) singleton rule_base (ruleIdName rule) rule + +pprRuleBase :: RuleBase -> SDoc +pprRuleBase rules = vcat [ pprRules (tidyRules emptyTidyEnv rs) + | rs <- nameEnvElts rules ] +\end{code} + + +%************************************************************************ +%* * +\subsection{Matching} +%* * +%************************************************************************ + +\begin{code} +lookupRule :: (Activation -> Bool) -> InScopeSet + -> RuleBase -- Imported rules + -> Id -> [CoreExpr] -> Maybe (RuleName, CoreExpr) +lookupRule is_active in_scope rule_base fn args + = matchRules is_active in_scope fn args rules + where + -- The rules for an Id come from two places: + -- (a) the ones it is born with (idCoreRules fn) + -- (b) rules added in subsequent modules (extra_rules) + -- PrimOps, for example, are born with a bunch of rules under (a) + rules = extra_rules ++ idCoreRules fn + extra_rules | isLocalId fn = [] + | otherwise = lookupNameEnv rule_base (idName fn) `orElse` [] + +matchRules :: (Activation -> Bool) -> InScopeSet + -> Id -> [CoreExpr] + -> [CoreRule] -> Maybe (RuleName, CoreExpr) +-- See comments on matchRule +matchRules is_active in_scope fn args rules + = case go [] rules of + [] -> Nothing + (m:ms) -> Just (case findBest (fn,args) m ms of + (rule, ans) -> (ru_name rule, ans)) + where + rough_args = map roughTopName args + + go :: [(CoreRule,CoreExpr)] -> [CoreRule] -> [(CoreRule,CoreExpr)] + go ms [] = ms + go ms (r:rs) = case (matchRule is_active in_scope args rough_args r) of + Just e -> go ((r,e):ms) rs + Nothing -> go ms rs + +findBest :: (Id, [CoreExpr]) + -> (CoreRule,CoreExpr) -> [(CoreRule,CoreExpr)] -> (CoreRule,CoreExpr) +-- All these pairs matched the expression +-- Return the pair the the most specific rule +-- The (fn,args) is just for overlap reporting + +findBest target (rule,ans) [] = (rule,ans) +findBest target (rule1,ans1) ((rule2,ans2):prs) + | rule1 `isMoreSpecific` rule2 = findBest target (rule1,ans1) prs + | rule2 `isMoreSpecific` rule1 = findBest target (rule1,ans1) prs +#ifdef DEBUG + | otherwise = pprTrace "Rules.findBest: rule overlap (Rule 1 wins)" + (vcat [ptext SLIT("Expression to match:") <+> ppr fn <+> sep (map ppr args), + ptext SLIT("Rule 1:") <+> ppr rule1, + ptext SLIT("Rule 2:") <+> ppr rule2]) $ + findBest target (rule1,ans1) prs +#else + | otherwise = findBest target (rule1,ans1) prs +#endif + where + (fn,args) = target + +isMoreSpecific :: CoreRule -> CoreRule -> Bool +isMoreSpecific (BuiltinRule {}) r2 = True +isMoreSpecific r1 (BuiltinRule {}) = False +isMoreSpecific (Rule { ru_bndrs = bndrs1, ru_args = args1 }) + (Rule { ru_bndrs = bndrs2, ru_args = args2 }) + = isJust (matchN in_scope bndrs2 args2 args1) + where + in_scope = mkInScopeSet (mkVarSet bndrs1) + -- Actually we should probably include the free vars + -- of rule1's args, but I can't be bothered + +noBlackList :: Activation -> Bool +noBlackList act = False -- Nothing is black listed + +matchRule :: (Activation -> Bool) -> InScopeSet + -> [CoreExpr] -> [Maybe Name] + -> CoreRule -> Maybe CoreExpr + +-- If (matchRule rule args) returns Just (name,rhs) +-- then (f args) matches the rule, and the corresponding +-- rewritten RHS is rhs +-- +-- The bndrs and rhs is occurrence-analysed +-- +-- Example +-- +-- The rule +-- forall f g x. map f (map g x) ==> map (f . g) x +-- is stored +-- CoreRule "map/map" +-- [f,g,x] -- tpl_vars +-- [f,map g x] -- tpl_args +-- map (f.g) x) -- rhs +-- +-- Then the call: matchRule the_rule [e1,map e2 e3] +-- = Just ("map/map", (\f,g,x -> rhs) e1 e2 e3) +-- +-- Any 'surplus' arguments in the input are simply put on the end +-- of the output. + +matchRule is_active in_scope args rough_args + (BuiltinRule { ru_name = name, ru_try = match_fn }) + = case match_fn args of + Just expr -> Just expr + Nothing -> Nothing + +matchRule is_active in_scope args rough_args + (Rule { ru_name = rn, ru_act = act, ru_rough = tpl_tops, + ru_bndrs = tpl_vars, ru_args = tpl_args, + ru_rhs = rhs }) + | not (is_active act) = Nothing + | ruleCantMatch tpl_tops rough_args = Nothing + | otherwise + = case matchN in_scope tpl_vars tpl_args args of + Nothing -> Nothing + Just (tpl_vals, leftovers) -> Just (rule_fn + `mkApps` tpl_vals + `mkApps` leftovers) + where + rule_fn = occurAnalyseExpr (mkLams tpl_vars rhs) + -- We could do this when putting things into the rulebase, I guess +\end{code} + +\begin{code} +matchN :: InScopeSet + -> [Var] -- Template tyvars + -> [CoreExpr] -- Template + -> [CoreExpr] -- Target; can have more elts than template + -> Maybe ([CoreExpr], -- What is substituted for each template var + [CoreExpr]) -- Leftover target exprs + +matchN in_scope tmpl_vars tmpl_es target_es + = do { (subst, leftover_es) <- go init_menv emptySubstEnv tmpl_es target_es + ; return (map (lookup_tmpl subst) tmpl_vars, leftover_es) } + where + init_menv = ME { me_tmpls = mkVarSet tmpl_vars, me_env = init_rn_env } + init_rn_env = mkRnEnv2 (extendInScopeSetList in_scope tmpl_vars) + + go menv subst [] es = Just (subst, es) + go menv subst ts [] = Nothing -- Fail if too few actual args + go menv subst (t:ts) (e:es) = do { subst1 <- match menv subst t e + ; go menv subst1 ts es } + + lookup_tmpl :: (TvSubstEnv, IdSubstEnv) -> Var -> CoreExpr + lookup_tmpl (tv_subst, id_subst) tmpl_var + | isTyVar tmpl_var = case lookupVarEnv tv_subst tmpl_var of + Just ty -> Type ty + Nothing -> unbound tmpl_var + | otherwise = case lookupVarEnv id_subst tmpl_var of + Just e -> e + other -> unbound tmpl_var + + unbound var = pprPanic "Template variable unbound in rewrite rule" (ppr var) +\end{code} + + + --------------------------------------------- + The inner workings of matching + --------------------------------------------- + +\begin{code} +-- These two definitions are not the same as in Subst, +-- but they simple and direct, and purely local to this module +-- The third, for TvSubstEnv, is the same as in VarEnv, but repeated here +-- for uniformity with IdSubstEnv +type SubstEnv = (TvSubstEnv, IdSubstEnv) +type IdSubstEnv = IdEnv CoreExpr + +emptySubstEnv :: SubstEnv +emptySubstEnv = (emptyVarEnv, emptyVarEnv) + + +-- At one stage I tried to match even if there are more +-- template args than real args. + +-- I now think this is probably a bad idea. +-- Should the template (map f xs) match (map g)? I think not. +-- For a start, in general eta expansion wastes work. +-- SLPJ July 99 + + +match :: MatchEnv + -> SubstEnv + -> CoreExpr -- Template + -> CoreExpr -- Target + -> Maybe SubstEnv + +-- See the notes with Unify.match, which matches types +-- Everything is very similar for terms + +-- Interesting examples: +-- Consider matching +-- \x->f against \f->f +-- When we meet the lambdas we must remember to rename f to f' in the +-- second expresion. The RnEnv2 does that. +-- +-- Consider matching +-- forall a. \b->b against \a->3 +-- We must rename the \a. Otherwise when we meet the lambdas we +-- might substitute [a/b] in the template, and then erroneously +-- succeed in matching what looks like the template variable 'a' against 3. + +-- The Var case follows closely what happens in Unify.match +match menv subst@(tv_subst, id_subst) (Var v1) e2 + | v1 `elemVarSet` me_tmpls menv + = case lookupVarEnv id_subst v1' of + Nothing | any (inRnEnvR rn_env) (varSetElems (exprFreeVars e2)) + -> Nothing -- Occurs check failure + -- e.g. match forall a. (\x-> a x) against (\y. y y) + + | otherwise + -> Just (tv_subst, extendVarEnv id_subst v1 e2) + + Just e2' | tcEqExprX (nukeRnEnvL rn_env) e2' e2 + -> Just subst + + other -> Nothing + + | otherwise -- v1 is not a template variable + = case e2 of + Var v2 | v1' == rnOccR rn_env v2 -> Just subst + other -> Nothing + where + rn_env = me_env menv + v1' = rnOccL rn_env v1 + +-- Here is another important rule: if the term being matched is a +-- variable, we expand it so long as its unfolding is a WHNF +-- (Its occurrence information is not necessarily up to date, +-- so we don't use it.) +match menv subst e1 (Var v2) + | isCheapUnfolding unfolding + = match menv subst e1 (unfoldingTemplate unfolding) + where + unfolding = idUnfolding v2 + +match menv subst (Lit lit1) (Lit lit2) + | lit1 == lit2 + = Just subst + +match menv subst (App f1 a1) (App f2 a2) + = do { subst' <- match menv subst f1 f2 + ; match menv subst' a1 a2 } + +match menv subst (Lam x1 e1) (Lam x2 e2) + = match menv' subst e1 e2 + where + menv' = menv { me_env = rnBndr2 (me_env menv) x1 x2 } + +-- This rule does eta expansion +-- (\x.M) ~ N iff M ~ N x +match menv subst (Lam x1 e1) e2 + = match menv' subst e1 (App e2 (varToCoreExpr new_x)) + where + (rn_env', new_x) = rnBndrL (me_env menv) x1 + menv' = menv { me_env = rn_env' } + +-- Eta expansion the other way +-- M ~ (\y.N) iff M y ~ N +match menv subst e1 (Lam x2 e2) + = match menv' subst (App e1 (varToCoreExpr new_x)) e2 + where + (rn_env', new_x) = rnBndrR (me_env menv) x2 + menv' = menv { me_env = rn_env' } + +match menv subst (Case e1 x1 ty1 alts1) (Case e2 x2 ty2 alts2) + = do { subst1 <- match_ty menv subst ty1 ty2 + ; subst2 <- match menv subst1 e1 e2 + ; let menv' = menv { me_env = rnBndr2 (me_env menv) x2 x2 } + ; match_alts menv' subst2 alts1 alts2 -- Alts are both sorted + } + +match menv subst (Type ty1) (Type ty2) + = match_ty menv subst ty1 ty2 + +match menv subst (Note (Coerce to1 from1) e1) (Note (Coerce to2 from2) e2) + = do { subst1 <- match_ty menv subst to1 to2 + ; subst2 <- match_ty menv subst1 from1 from2 + ; match menv subst2 e1 e2 } + +-- This is an interesting rule: we simply ignore lets in the +-- term being matched against! The unfolding inside it is (by assumption) +-- already inside any occurrences of the bound variables, so we'll expand +-- them when we encounter them. +match menv subst e1 (Let (NonRec x2 r2) e2) + = match menv' subst e1 e2 + where + menv' = menv { me_env = fst (rnBndrR (me_env menv) x2) } + -- It's important to do this renaming. For example: + -- Matching + -- forall f,x,xs. f (x:xs) + -- against + -- f (let y = e in (y:[])) + -- We must not get success with x->y! Instead, we + -- need an occurs check. + +-- Everything else fails +match menv subst e1 e2 = Nothing + +------------------------------------------ +match_alts :: MatchEnv + -> SubstEnv + -> [CoreAlt] -- Template + -> [CoreAlt] -- Target + -> Maybe SubstEnv +match_alts menv subst [] [] + = return subst +match_alts menv subst ((c1,vs1,r1):alts1) ((c2,vs2,r2):alts2) + | c1 == c2 + = do { subst1 <- match menv' subst r1 r2 + ; match_alts menv subst1 alts1 alts2 } + where + menv' :: MatchEnv + menv' = menv { me_env = rnBndrs2 (me_env menv) vs1 vs2 } + +match_alts menv subst alts1 alts2 + = Nothing +\end{code} + +Matching Core types: use the matcher in TcType. +Notice that we treat newtypes as opaque. For example, suppose +we have a specialised version of a function at a newtype, say + newtype T = MkT Int +We only want to replace (f T) with f', not (f Int). + +\begin{code} +------------------------------------------ +match_ty menv (tv_subst, id_subst) ty1 ty2 + = do { tv_subst' <- Unify.ruleMatchTyX menv tv_subst ty1 ty2 + ; return (tv_subst', id_subst) } +\end{code} + + +%************************************************************************ +%* * +\subsection{Checking a program for failing rule applications} +%* * +%************************************************************************ + +----------------------------------------------------- + Game plan +----------------------------------------------------- + +We want to know what sites have rules that could have fired but didn't. +This pass runs over the tree (without changing it) and reports such. + +NB: we assume that this follows a run of the simplifier, so every Id +occurrence (including occurrences of imported Ids) is decorated with +all its (active) rules. No need to construct a rule base or anything +like that. + +\begin{code} +ruleCheckProgram :: CompilerPhase -> String -> [CoreBind] -> SDoc +-- Report partial matches for rules beginning +-- with the specified string +ruleCheckProgram phase rule_pat binds + | isEmptyBag results + = text "Rule check results: no rule application sites" + | otherwise + = vcat [text "Rule check results:", + line, + vcat [ p $$ line | p <- bagToList results ] + ] + where + results = unionManyBags (map (ruleCheckBind (phase, rule_pat)) binds) + line = text (replicate 20 '-') + +type RuleCheckEnv = (CompilerPhase, String) -- Phase and Pattern + +ruleCheckBind :: RuleCheckEnv -> CoreBind -> Bag SDoc + -- The Bag returned has one SDoc for each call site found +ruleCheckBind env (NonRec b r) = ruleCheck env r +ruleCheckBind env (Rec prs) = unionManyBags [ruleCheck env r | (b,r) <- prs] + +ruleCheck :: RuleCheckEnv -> CoreExpr -> Bag SDoc +ruleCheck env (Var v) = emptyBag +ruleCheck env (Lit l) = emptyBag +ruleCheck env (Type ty) = emptyBag +ruleCheck env (App f a) = ruleCheckApp env (App f a) [] +ruleCheck env (Note n e) = ruleCheck env e +ruleCheck env (Let bd e) = ruleCheckBind env bd `unionBags` ruleCheck env e +ruleCheck env (Lam b e) = ruleCheck env e +ruleCheck env (Case e _ _ as) = ruleCheck env e `unionBags` + unionManyBags [ruleCheck env r | (_,_,r) <- as] + +ruleCheckApp env (App f a) as = ruleCheck env a `unionBags` ruleCheckApp env f (a:as) +ruleCheckApp env (Var f) as = ruleCheckFun env f as +ruleCheckApp env other as = ruleCheck env other +\end{code} + +\begin{code} +ruleCheckFun :: RuleCheckEnv -> Id -> [CoreExpr] -> Bag SDoc +-- Produce a report for all rules matching the predicate +-- saying why it doesn't match the specified application + +ruleCheckFun (phase, pat) fn args + | null name_match_rules = emptyBag + | otherwise = unitBag (ruleAppCheck_help phase fn args name_match_rules) + where + name_match_rules = filter match (idCoreRules fn) + match rule = pat `isPrefixOf` unpackFS (ruleName rule) + +ruleAppCheck_help :: CompilerPhase -> Id -> [CoreExpr] -> [CoreRule] -> SDoc +ruleAppCheck_help phase fn args rules + = -- The rules match the pattern, so we want to print something + vcat [text "Expression:" <+> ppr (mkApps (Var fn) args), + vcat (map check_rule rules)] + where + n_args = length args + i_args = args `zip` [1::Int ..] + rough_args = map roughTopName args + + check_rule rule = rule_herald rule <> colon <+> rule_info rule + + rule_herald (BuiltinRule { ru_name = name }) + = ptext SLIT("Builtin rule") <+> doubleQuotes (ftext name) + rule_herald (Rule { ru_name = name }) + = ptext SLIT("Rule") <+> doubleQuotes (ftext name) + + rule_info rule + | Just _ <- matchRule noBlackList emptyInScopeSet args rough_args rule + = text "matches (which is very peculiar!)" + + rule_info (BuiltinRule {}) = text "does not match" + + rule_info (Rule { ru_name = name, ru_act = act, + ru_bndrs = rule_bndrs, ru_args = rule_args}) + | not (isActive phase act) = text "active only in later phase" + | n_args < n_rule_args = text "too few arguments" + | n_mismatches == n_rule_args = text "no arguments match" + | n_mismatches == 0 = text "all arguments match (considered individually), but rule as a whole does not" + | otherwise = text "arguments" <+> ppr mismatches <+> text "do not match (1-indexing)" + where + n_rule_args = length rule_args + n_mismatches = length mismatches + mismatches = [i | (rule_arg, (arg,i)) <- rule_args `zip` i_args, + not (isJust (match_fn rule_arg arg))] + + lhs_fvs = exprsFreeVars rule_args -- Includes template tyvars + match_fn rule_arg arg = match menv emptySubstEnv rule_arg arg + where + in_scope = lhs_fvs `unionVarSet` exprFreeVars arg + menv = ME { me_env = mkRnEnv2 (mkInScopeSet in_scope) + , me_tmpls = mkVarSet rule_bndrs } +\end{code} + |