+lookupRule :: (Activation -> Bool)
+ -> InScopeSet
+ -> RuleBase -- Ids from other modules
+ -> Id -> [CoreExpr] -> Maybe (RuleName, CoreExpr)
+lookupRule is_active in_scope rules fn args
+ = case idSpecialisation fn' of
+ Rules rules _ -> matchRules is_active in_scope rules args
+ where
+ fn' | isLocalId fn = fn
+ | Just ext_fn <- lookupVarSet (ruleBaseIds rules) fn = ext_fn
+ | otherwise = fn
+\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 = case idSpecialisation fn of
+ Rules rules _ -> filter match rules
+ 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 ..]
+
+ check_rule rule = rule_herald rule <> colon <+> rule_info rule
+
+ rule_herald (BuiltinRule name _) =
+ ptext SLIT("Builtin rule") <+> doubleQuotes (ftext name)
+ rule_herald (Rule name _ _ _ _) =
+ ptext SLIT("Rule") <+> doubleQuotes (ftext name)
+
+ rule_info rule
+ | Just (name,_) <- matchRule noBlackList emptyInScopeSet rule args
+ = text "matches (which is very peculiar!)"
+
+ rule_info (BuiltinRule name fn) = text "does not match"
+
+ rule_info (Rule name act rule_bndrs 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 }