summaryrefslogtreecommitdiff
path: root/compiler/specialise/Rules.lhs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/specialise/Rules.lhs')
-rw-r--r--compiler/specialise/Rules.lhs633
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}
+