diff options
Diffstat (limited to 'compiler/GHC/Core/Rules.hs')
-rw-r--r-- | compiler/GHC/Core/Rules.hs | 1832 |
1 files changed, 130 insertions, 1702 deletions
diff --git a/compiler/GHC/Core/Rules.hs b/compiler/GHC/Core/Rules.hs index d9bd0a912c..92fc1665f9 100644 --- a/compiler/GHC/Core/Rules.hs +++ b/compiler/GHC/Core/Rules.hs @@ -1,1715 +1,143 @@ {- +(c) The University of Glasgow 2006 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998 - -\section[CoreRules]{Rewrite rules} -} +{-# LANGUAGE NamedFieldPuns #-} --- | Functions for collecting together and applying rewrite rules to a module. --- The 'CoreRule' datatype itself is declared elsewhere. +-- | The CoreRule type and its friends are dealt with mainly in +-- GHC.Core.Rules, but GHC.Core.FVs, GHC.Core.Subst, GHC.Core.Ppr, +-- GHC.Core.Tidy also inspect the representation. module GHC.Core.Rules ( - -- ** Looking up rules - lookupRule, - - -- ** RuleBase, RuleEnv - RuleBase, RuleEnv(..), mkRuleEnv, emptyRuleEnv, - updExternalPackageRules, addLocalRules, updLocalRules, - emptyRuleBase, mkRuleBase, extendRuleBaseList, - pprRuleBase, - - -- ** Checking rule applications - ruleCheckProgram, - - -- ** Manipulating 'RuleInfo' rules - extendRuleInfo, addRuleInfo, - addIdSpecialisations, - - -- ** RuleBase and RuleEnv - - -- * Misc. CoreRule helpers - rulesOfBinds, getRules, pprRulesForUser, - - -- * Making rules - mkRule, mkSpecRule, roughTopNames - + -- * Core rule data types + CoreRule(..), + RuleName, RuleFun, IdUnfoldingFun, InScopeEnv, RuleOpts, + + -- ** Operations on 'CoreRule's + ruleArity, ruleName, ruleIdName, ruleActivation, + setRuleIdName, ruleModule, + isBuiltinRule, isLocalRule, isAutoRule, ) where import GHC.Prelude -import GHC.Unit.Module ( Module ) -import GHC.Unit.Module.Env -import GHC.Unit.Module.ModGuts( ModGuts(..) ) -import GHC.Unit.Module.Deps( Dependencies(..) ) - -import GHC.Driver.Session( DynFlags ) -import GHC.Driver.Ppr( showSDoc ) - -import GHC.Core -- All of it -import GHC.Core.Subst -import GHC.Core.SimpleOpt ( exprIsLambda_maybe ) -import GHC.Core.FVs ( exprFreeVars, exprsFreeVars, bindFreeVars - , rulesFreeVarsDSet, exprsOrphNames ) -import GHC.Core.Utils ( exprType, mkTick, mkTicks - , stripTicksTopT, stripTicksTopE - , isJoinBind, mkCastMCo ) -import GHC.Core.Ppr ( pprRules ) -import GHC.Core.Unify as Unify ( ruleMatchTyKiX ) -import GHC.Core.Type as Type - ( Type, extendTvSubst, extendCvSubst - , substTy, getTyVar_maybe ) -import GHC.Core.TyCo.Ppr( pprParendType ) -import GHC.Core.Coercion as Coercion -import GHC.Core.Tidy ( tidyRules ) -import GHC.Core.Map.Expr ( eqCoreExpr ) -import GHC.Core.Opt.Arity( etaExpandToJoinPointRule ) - -import GHC.Tc.Utils.TcType ( tcSplitTyConApp_maybe ) -import GHC.Builtin.Types ( anyTypeOfKind ) - -import GHC.Types.Id -import GHC.Types.Id.Info ( RuleInfo( RuleInfo ) ) +import GHC.Types.Var.Env( InScopeSet ) import GHC.Types.Var -import GHC.Types.Var.Env -import GHC.Types.Var.Set -import GHC.Types.Name ( Name, NamedThing(..), nameIsLocalOrFrom ) -import GHC.Types.Name.Set -import GHC.Types.Name.Env -import GHC.Types.Name.Occurrence( occNameFS ) -import GHC.Types.Unique.FM -import GHC.Types.Tickish +import GHC.Core +import GHC.Core.Orphans +import GHC.Core.Unfoldings +import GHC.Core.Rules.Config ( RuleOpts ) +import GHC.Types.Name +import GHC.Unit.Module import GHC.Types.Basic -import GHC.Data.FastString -import GHC.Data.Maybe -import GHC.Data.Bag - -import GHC.Utils.Misc as Utils -import GHC.Utils.Outputable -import GHC.Utils.Panic -import GHC.Utils.Constants (debugIsOn) - -import Data.List (sortBy, mapAccumL, isPrefixOf) -import Data.Function ( on ) -import Control.Monad ( guard ) - -{- -Note [Overall plumbing for rules] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* After the desugarer: - - The ModGuts initially contains mg_rules :: [CoreRule] of - locally-declared rules for imported Ids. - - Locally-declared rules for locally-declared Ids are attached to - the IdInfo for that Id. See Note [Attach rules to local ids] in - GHC.HsToCore.Binds - -* GHC.Iface.Tidy strips off all the rules from local Ids and adds them to - mg_rules, so that the ModGuts has *all* the locally-declared rules. - -* The HomePackageTable contains a ModDetails for each home package - module. Each contains md_rules :: [CoreRule] of rules declared in - that module. The HomePackageTable grows as ghc --make does its - up-sweep. In batch mode (ghc -c), the HPT is empty; all imported modules - are treated by the "external" route, discussed next, regardless of - which package they come from. - -* The ExternalPackageState has a single eps_rule_base :: RuleBase for - Ids in other packages. This RuleBase simply grow monotonically, as - ghc --make compiles one module after another. - - During simplification, interface files may get demand-loaded, - as the simplifier explores the unfoldings for Ids it has in - its hand. (Via an unsafePerformIO; the EPS is really a cache.) - That in turn may make the EPS rule-base grow. In contrast, the - HPT never grows in this way. - -* The result of all this is that during Core-to-Core optimisation - there are four sources of rules: - - (a) Rules in the IdInfo of the Id they are a rule for. These are - easy: fast to look up, and if you apply a substitution then - it'll be applied to the IdInfo as a matter of course. - - (b) Rules declared in this module for imported Ids, kept in the - ModGuts. If you do a substitution, you'd better apply the - substitution to these. There are seldom many of these. - - (c) Rules declared in the HomePackageTable. These never change. - - (d) Rules in the ExternalPackageTable. These can grow in response - to lazy demand-loading of interfaces. - -* At the moment (c) is carried in a reader-monad way by the GHC.Core.Opt.Monad. - The HomePackageTable doesn't have a single RuleBase because technically - we should only be able to "see" rules "below" this module; so we - generate a RuleBase for (c) by combining rules from all the modules - "below" us. That's why we can't just select the home-package RuleBase - from HscEnv. - - [NB: we are inconsistent here. We should do the same for external - packages, but we don't. Same for type-class instances.] - -* So in the outer simplifier loop (simplifyPgmIO), we combine (b & c) into a single - RuleBase, reading - (b) from the ModGuts, - (c) from the GHC.Core.Opt.Monad, and - just before doing rule matching we read - (d) from its mutable variable - and combine it with the results from (b & c). - - In a single simplifier run new rules can be added into the EPS so it matters - to keep an up-to-date view of which rules have been loaded. For examples of - where this went wrong and caused cryptic performance regressions - see T19790 and !6735. - - -************************************************************************ -* * -\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 is - RULE "f" forall @a @b d. f @(List a) @b d = f' a b - -then when we find an application of f to matching types, we simply replace -it by the matching RHS: - f (List Int) Bool dict ===> f' Int Bool -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. --} - -mkRule :: Module -> Bool -> Bool -> RuleName -> Activation - -> Name -> [CoreBndr] -> [CoreExpr] -> CoreExpr -> CoreRule --- ^ Used to make 'CoreRule' for an 'Id' defined in the module being --- compiled. See also 'GHC.Core.CoreRule' -mkRule this_mod is_auto is_local 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_origin = this_mod, - ru_orphan = orph, - ru_auto = is_auto, ru_local = is_local } - where - -- Compute orphanhood. See Note [Orphans] in GHC.Core.InstEnv - -- A rule is an orphan only if none of the variables - -- mentioned on its left-hand side are locally defined - lhs_names = extendNameSet (exprsOrphNames args) fn - - -- Since rules get eventually attached to one of the free names - -- from the definition when compiling the ABI hash, we should make - -- it deterministic. This chooses the one with minimal OccName - -- as opposed to uniq value. - local_lhs_names = filterNameSet (nameIsLocalOrFrom this_mod) lhs_names - orph = chooseOrphanAnchor local_lhs_names - --------------- -mkSpecRule :: DynFlags -> Module -> Bool -> Activation -> SDoc - -> Id -> [CoreBndr] -> [CoreExpr] -> CoreExpr -> CoreRule --- Make a specialisation rule, for Specialise or SpecConstr -mkSpecRule dflags this_mod is_auto inl_act herald fn bndrs args rhs - = case isJoinId_maybe fn of - Just join_arity -> etaExpandToJoinPointRule join_arity rule - Nothing -> rule - where - rule = mkRule this_mod is_auto is_local - rule_name - inl_act -- Note [Auto-specialisation and RULES] - (idName fn) - bndrs args rhs - - is_local = isLocalId fn - rule_name = mkSpecRuleName dflags herald fn args - -mkSpecRuleName :: DynFlags -> SDoc -> Id -> [CoreExpr] -> FastString -mkSpecRuleName dflags herald fn args - = mkFastString $ showSDoc dflags $ - herald <+> ftext (occNameFS (getOccName fn)) - -- This name ends up in interface files, so use occNameFS. - -- Otherwise uniques end up there, making builds - -- less deterministic (See #4012 comment:61 ff) - <+> hsep (mapMaybe ppr_call_key_ty args) - where - ppr_call_key_ty :: CoreExpr -> Maybe SDoc - ppr_call_key_ty (Type ty) = case getTyVar_maybe ty of - Just {} -> Just (text "@_") - Nothing -> Just $ char '@' <> pprParendType ty - ppr_call_key_ty _ = Nothing - - --------------- -roughTopNames :: [CoreExpr] -> [Maybe Name] --- ^ Find the \"top\" free names of several expressions. --- Such names are either: --- --- 1. The function finally being applied to in an application chain --- (if that name is a GlobalId: see "GHC.Types.Var#globalvslocal"), or --- --- 2. The 'TyCon' if the expression is a 'Type' --- --- This is used for the fast-match-check for rules; --- if the top names don't match, the rest can't -roughTopNames args = map roughTopName args - -roughTopName :: CoreExpr -> Maybe Name -roughTopName (Type ty) = case tcSplitTyConApp_maybe ty of - Just (tc,_) -> Just (getName tc) - Nothing -> Nothing -roughTopName (Coercion _) = Nothing -roughTopName (App f _) = roughTopName f -roughTopName (Var f) | isGlobalId f -- Note [Care with roughTopName] - , isDataConWorkId f || idArity f > 0 - = Just (idName f) -roughTopName (Tick t e) | tickishFloatable t - = roughTopName e -roughTopName _ = 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. --- --- Notice that [_$_] --- @ruleCantMatch [Nothing] [Just n2] = False@ --- Reason: a template variable can be instantiated by a constant --- Also: --- @ruleCantMatch [Just n1] [Nothing] = False@ --- Reason: a local variable @v@ in the actuals might [_$_] - -ruleCantMatch (Just n1 : ts) (Just n2 : as) = n1 /= n2 || ruleCantMatch ts as -ruleCantMatch (_ : ts) (_ : as) = ruleCantMatch ts as -ruleCantMatch _ _ = False - -{- -Note [Care with roughTopName] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider this - module M where { x = a:b } - module N where { ...f x... - RULE f (p:q) = ... } -You'd expect the rule to match, because the matcher can -look through the unfolding of 'x'. So we must avoid roughTopName -returning 'M.x' for the call (f x), or else it'll say "can't match" -and we won't even try!! - -However, suppose we have - RULE g (M.h x) = ... - foo = ...(g (M.k v)).... -where k is a *function* exported by M. We never really match -functions (lambdas) except by name, so in this case it seems like -a good idea to treat 'M.k' as a roughTopName of the call. --} - -pprRulesForUser :: [CoreRule] -> SDoc --- (a) tidy the rules --- (b) sort them into order based on the rule name --- (c) suppress uniques (unless -dppr-debug is on) --- This combination makes the output stable so we can use in testing --- It's here rather than in GHC.Core.Ppr because it calls tidyRules -pprRulesForUser rules - = withPprStyle defaultUserStyle $ - pprRules $ - sortBy (lexicalCompareFS `on` ruleName) $ - tidyRules emptyTidyEnv rules - -{- -************************************************************************ -* * - RuleInfo: the rules in an IdInfo -* * -************************************************************************ --} - -extendRuleInfo :: RuleInfo -> [CoreRule] -> RuleInfo -extendRuleInfo (RuleInfo rs1 fvs1) rs2 - = RuleInfo (rs2 ++ rs1) (rulesFreeVarsDSet rs2 `unionDVarSet` fvs1) - -addRuleInfo :: RuleInfo -> RuleInfo -> RuleInfo -addRuleInfo (RuleInfo rs1 fvs1) (RuleInfo rs2 fvs2) - = RuleInfo (rs1 ++ rs2) (fvs1 `unionDVarSet` fvs2) - -addIdSpecialisations :: Id -> [CoreRule] -> Id -addIdSpecialisations id rules - | null rules - = id - | otherwise - = setIdSpecialisation id $ - extendRuleInfo (idSpecialisation id) rules - --- | Gather all the rules for locally bound identifiers from the supplied bindings -rulesOfBinds :: [CoreBind] -> [CoreRule] -rulesOfBinds binds = concatMap (concatMap idCoreRules . bindersOf) binds - - -{- -************************************************************************ -* * - RuleBase -* * -************************************************************************ --} - --- | Gathers a collection of 'CoreRule's. Maps (the name of) an 'Id' to its rules -type RuleBase = NameEnv [CoreRule] - -- The rules are unordered; - -- we sort out any overlaps on lookup - -emptyRuleBase :: RuleBase -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 - -extendRuleBase :: RuleBase -> CoreRule -> RuleBase -extendRuleBase rule_base rule - = extendNameEnv_Acc (:) Utils.singleton rule_base (ruleIdName rule) rule - -pprRuleBase :: RuleBase -> SDoc -pprRuleBase rules = pprUFM rules $ \rss -> - vcat [ pprRules (tidyRules emptyTidyEnv rs) - | rs <- rss ] - --- | A full rule environment which we can apply rules from. Like a 'RuleBase', --- but it also includes the set of visible orphans we use to filter out orphan --- rules which are not visible (even though we can see them...) --- See Note [Orphans] in GHC.Core -data RuleEnv - = RuleEnv { re_local_rules :: !RuleBase -- Rules from this module - , re_home_rules :: !RuleBase -- Rule from the home package - -- (excl this module) - , re_eps_rules :: !RuleBase -- Rules from other packages - -- see Note [External package rules] - , re_visible_orphs :: !ModuleSet - } - -mkRuleEnv :: ModGuts -> RuleBase -> RuleBase -> RuleEnv -mkRuleEnv (ModGuts { mg_module = this_mod - , mg_deps = deps - , mg_rules = local_rules }) - eps_rules hpt_rules - = RuleEnv { re_local_rules = mkRuleBase local_rules - , re_home_rules = hpt_rules - , re_eps_rules = eps_rules - , re_visible_orphs = mkModuleSet vis_orphs } - where - vis_orphs = this_mod : dep_orphs deps - -updExternalPackageRules :: RuleEnv -> RuleBase -> RuleEnv --- Completely over-ride the external rules in RuleEnv -updExternalPackageRules rule_env eps_rules - = rule_env { re_eps_rules = eps_rules } - -updLocalRules :: RuleEnv -> [CoreRule] -> RuleEnv --- Completely over-ride the local rules in RuleEnv -updLocalRules rule_env local_rules - = rule_env { re_local_rules = mkRuleBase local_rules } - -addLocalRules :: RuleEnv -> [CoreRule] -> RuleEnv --- Add new local rules -addLocalRules rule_env rules - = rule_env { re_local_rules = extendRuleBaseList (re_local_rules rule_env) rules } - -emptyRuleEnv :: RuleEnv -emptyRuleEnv = RuleEnv { re_local_rules = emptyNameEnv - , re_home_rules = emptyNameEnv - , re_eps_rules = emptyNameEnv - , re_visible_orphs = emptyModuleSet } - -getRules :: RuleEnv -> Id -> [CoreRule] --- Given a RuleEnv and an Id, find the visible rules for that Id --- See Note [Where rules are found] -getRules (RuleEnv { re_local_rules = local_rules - , re_home_rules = home_rules - , re_eps_rules = eps_rules - , re_visible_orphs = orphs }) fn - - | Just {} <- isDataConId_maybe fn -- Short cut for data constructor workers - = [] -- and wrappers, which never have any rules - - | otherwise - = idCoreRules fn ++ - get local_rules ++ - find_visible home_rules ++ - find_visible eps_rules - - where - fn_name = idName fn - find_visible rb = filter (ruleIsVisible orphs) (get rb) - get rb = lookupNameEnv rb fn_name `orElse` [] - -ruleIsVisible :: ModuleSet -> CoreRule -> Bool -ruleIsVisible _ BuiltinRule{} = True -ruleIsVisible vis_orphs Rule { ru_orphan = orph, ru_origin = origin } - = notOrphan orph || origin `elemModuleSet` vis_orphs - -{- Note [Where rules are found] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The rules for an Id come from two places: - (a) the ones it is born with, stored inside the Id itself (idCoreRules fn), - (b) rules added in other modules, stored in the global RuleBase (imp_rules) - -It's tempting to think that - - LocalIds have only (a) - - non-LocalIds have only (b) - -but that isn't quite right: - - - PrimOps and ClassOps are born with a bunch of rules inside the Id, - even when they are imported - - - The rules in GHC.Core.Opt.ConstantFold.builtinRules should be active even - in the module defining the Id (when it's a LocalId), but - the rules are kept in the global RuleBase - - Note [External package rules] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In Note [Overall plumbing for rules], it is explained that the final -RuleBase which we must consider is combined from 4 different sources. - -During simplifier runs, the fourth source of rules is constantly being updated -as new interfaces are loaded into the EPS. Therefore just before we check to see -if any rules match we get the EPS RuleBase and combine it with the existing RuleBase -and then perform exactly 1 lookup into the new map. - -It is more efficient to avoid combining the environments and store the uncombined -environments as we can instead perform 1 lookup into each environment and then combine -the results. - -Essentially we use the identity: - -> lookupNameEnv n (plusNameEnv_C (++) rb1 rb2) -> = lookupNameEnv n rb1 ++ lookupNameEnv n rb2 - -The latter being more efficient as we don't construct an intermediate -map. --} - -{- -************************************************************************ -* * - Matching -* * -************************************************************************ --} - --- | The main rule matching function. Attempts to apply all (active) --- supplied rules to this instance of an application in a given --- context, returning the rule applied and the resulting expression if --- successful. -lookupRule :: RuleOpts -> InScopeEnv - -> (Activation -> Bool) -- When rule is active - -> Id -- Function head - -> [CoreExpr] -- Args - -> [CoreRule] -- Rules - -> Maybe (CoreRule, CoreExpr) - --- See Note [Extra args in the target] --- See comments on matchRule -lookupRule opts rule_env@(in_scope,_) is_active fn args rules - = -- pprTrace "lookupRule" (ppr fn <+> ppr args $$ ppr rules $$ ppr in_scope) $ - case go [] rules of - [] -> Nothing - (m:ms) -> Just (findBest in_scope (fn,args') m ms) - where - rough_args = map roughTopName args - - -- Strip ticks from arguments, see Note [Tick annotations in RULE - -- matching]. We only collect ticks if a rule actually matches - - -- this matters for performance tests. - args' = map (stripTicksTopE tickishFloatable) args - ticks = concatMap (stripTicksTopT tickishFloatable) args - - go :: [(CoreRule,CoreExpr)] -> [CoreRule] -> [(CoreRule,CoreExpr)] - go ms [] = ms - go ms (r:rs) - | Just e <- matchRule opts rule_env is_active fn args' rough_args r - = go ((r,mkTicks ticks e):ms) rs - | otherwise - = -- pprTrace "match failed" (ppr r $$ ppr args $$ - -- ppr [ (arg_id, unfoldingTemplate unf) - -- | Var arg_id <- args - -- , let unf = idUnfolding arg_id - -- , isCheapUnfolding unf] ) - go ms rs - -findBest :: InScopeSet -> (Id, [CoreExpr]) - -> (CoreRule,CoreExpr) -> [(CoreRule,CoreExpr)] -> (CoreRule,CoreExpr) --- All these pairs matched the expression --- Return the pair the most specific rule --- The (fn,args) is just for overlap reporting - -findBest _ _ (rule,ans) [] = (rule,ans) -findBest in_scope target (rule1,ans1) ((rule2,ans2):prs) - | isMoreSpecific in_scope rule1 rule2 = findBest in_scope target (rule1,ans1) prs - | isMoreSpecific in_scope rule2 rule1 = findBest in_scope target (rule2,ans2) prs - | debugIsOn = let pp_rule rule - = ifPprDebug (ppr rule) - (doubleQuotes (ftext (ruleName rule))) - in pprTrace "Rules.findBest: rule overlap (Rule 1 wins)" - (vcat [ whenPprDebug $ - text "Expression to match:" <+> ppr fn - <+> sep (map ppr args) - , text "Rule 1:" <+> pp_rule rule1 - , text "Rule 2:" <+> pp_rule rule2]) $ - findBest in_scope target (rule1,ans1) prs - | otherwise = findBest in_scope target (rule1,ans1) prs - where - (fn,args) = target - -isMoreSpecific :: InScopeSet -> CoreRule -> CoreRule -> Bool --- The call (rule1 `isMoreSpecific` rule2) --- sees if rule2 can be instantiated to look like rule1 --- See Note [isMoreSpecific] -isMoreSpecific _ (BuiltinRule {}) _ = False -isMoreSpecific _ (Rule {}) (BuiltinRule {}) = True -isMoreSpecific in_scope (Rule { ru_bndrs = bndrs1, ru_args = args1 }) - (Rule { ru_bndrs = bndrs2, ru_args = args2 - , ru_name = rule_name2, ru_rhs = rhs2 }) - = isJust (matchN (full_in_scope, id_unfolding_fun) - rule_name2 bndrs2 args2 args1 rhs2) - where - id_unfolding_fun _ = NoUnfolding -- Don't expand in templates - full_in_scope = in_scope `extendInScopeSetList` bndrs1 - -noBlackList :: Activation -> Bool -noBlackList _ = False -- Nothing is black listed - -{- Note [isMoreSpecific] -~~~~~~~~~~~~~~~~~~~~~~~~ -The call (rule1 `isMoreSpecific` rule2) -sees if rule2 can be instantiated to look like rule1. - -Wrinkle: - -* We take the view that a BuiltinRule is less specific than - anything else, because we want user-defined rules to "win" - In particular, class ops have a built-in rule, but we - prefer any user-specific rules to win: - eg (#4397) - truncate :: (RealFrac a, Integral b) => a -> b - {-# RULES "truncate/Double->Int" truncate = double2Int #-} - double2Int :: Double -> Int - We want the specific RULE to beat the built-in class-op rule - -Note [Extra args in the target] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If we find a matching rule, we return (Just (rule, rhs)), -/but/ the rule firing has only consumed as many of the input args -as the ruleArity says. The unused arguments are handled by the code in -GHC.Core.Opt.Simplify.tryRules, using the arity of the returned rule. - -E.g. Rule "foo": forall a b. f p1 p2 = rhs - Target: f e1 e2 e3 - -Then lookupRule returns Just (Rule "foo", rhs), where Rule "foo" -has ruleArity 2. The real rewrite is - f e1 e2 e3 ==> rhs e3 - -You might think it'd be cleaner for lookupRule to deal with the -leftover arguments, by applying 'rhs' to them, but the main call -in the Simplifier works better as it is. Reason: the 'args' passed -to lookupRule are the result of a lazy substitution - -Historical note: - -At one stage I tried to match even if there are more args in the -/template/ than the target. 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 --} - ------------------------------------- -matchRule :: RuleOpts -> InScopeEnv -> (Activation -> Bool) - -> Id -> [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 returned expression 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 expression --- map e1 (map e2 e3) e4 --- results in a call to --- matchRule the_rule [e1,map e2 e3,e4] --- = Just ("map/map", (\f,g,x -> rhs) e1 e2 e3) --- --- NB: The 'surplus' argument e4 in the input is simply dropped. --- See Note [Extra args in the target] - -matchRule opts rule_env _is_active fn args _rough_args - (BuiltinRule { ru_try = match_fn }) --- Built-in rules can't be switched off, it seems - = case match_fn opts rule_env fn args of - Nothing -> Nothing - Just expr -> Just expr - -matchRule _ rule_env is_active _ args rough_args - (Rule { ru_name = rule_name, 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 = matchN rule_env rule_name tpl_vars tpl_args args rhs - - ---------------------------------------- -matchN :: InScopeEnv - -> RuleName -> [Var] -> [CoreExpr] - -> [CoreExpr] -> CoreExpr -- ^ Target; can have more elements than the template - -> Maybe CoreExpr --- For a given match template and context, find bindings to wrap around --- the entire result and what should be substituted for each template variable. --- --- Fail if there are too few actual arguments from the target to match the template --- --- See Note [Extra args in the target] --- If there are too /many/ actual arguments, we simply ignore the --- trailing ones, returning the result of applying the rule to a prefix --- of the actual arguments. - -matchN (in_scope, id_unf) rule_name tmpl_vars tmpl_es target_es rhs - = do { rule_subst <- match_exprs init_menv emptyRuleSubst tmpl_es target_es - ; let (_, matched_es) = mapAccumL (lookup_tmpl rule_subst) - (mkEmptySubst in_scope) $ - tmpl_vars `zip` tmpl_vars1 - bind_wrapper = rs_binds rule_subst - -- Floated bindings; see Note [Matching lets] - ; return (bind_wrapper $ - mkLams tmpl_vars rhs `mkApps` matched_es) } - where - (init_rn_env, tmpl_vars1) = mapAccumL rnBndrL (mkRnEnv2 in_scope) tmpl_vars - -- See Note [Cloning the template binders] - - init_menv = RV { rv_tmpls = mkVarSet tmpl_vars1 - , rv_lcl = init_rn_env - , rv_fltR = mkEmptySubst (rnInScopeSet init_rn_env) - , rv_unf = id_unf } - - lookup_tmpl :: RuleSubst -> Subst -> (InVar,OutVar) -> (Subst, CoreExpr) - -- Need to return a RuleSubst solely for the benefit of mk_fake_ty - lookup_tmpl (RS { rs_tv_subst = tv_subst, rs_id_subst = id_subst }) - tcv_subst (tmpl_var, tmpl_var1) - | isId tmpl_var1 - = case lookupVarEnv id_subst tmpl_var1 of - Just e | Coercion co <- e - -> (Type.extendCvSubst tcv_subst tmpl_var1 co, Coercion co) - | otherwise - -> (tcv_subst, e) - Nothing | Just refl_co <- isReflCoVar_maybe tmpl_var1 - , let co = Coercion.substCo tcv_subst refl_co - -> -- See Note [Unbound RULE binders] - (Type.extendCvSubst tcv_subst tmpl_var1 co, Coercion co) - | otherwise - -> unbound tmpl_var - - | otherwise - = (Type.extendTvSubst tcv_subst tmpl_var1 ty', Type ty') - where - ty' = case lookupVarEnv tv_subst tmpl_var1 of - Just ty -> ty - Nothing -> fake_ty -- See Note [Unbound RULE binders] - fake_ty = anyTypeOfKind (Type.substTy tcv_subst (tyVarKind tmpl_var1)) - -- This substitution is the sole reason we accumulate - -- TCvSubst in lookup_tmpl - - unbound tmpl_var - = pprPanic "Template variable unbound in rewrite rule" $ - vcat [ text "Variable:" <+> ppr tmpl_var <+> dcolon <+> ppr (varType tmpl_var) - , text "Rule" <+> pprRuleName rule_name - , text "Rule bndrs:" <+> ppr tmpl_vars - , text "LHS args:" <+> ppr tmpl_es - , text "Actual args:" <+> ppr target_es ] - ----------------------- -match_exprs :: RuleMatchEnv -> RuleSubst - -> [CoreExpr] -- Templates - -> [CoreExpr] -- Targets - -> Maybe RuleSubst --- If the targets are longer than templates, succeed, simply ignoring --- the leftover targets. This matters in the call in matchN. --- --- Precondition: corresponding elements of es1 and es2 have the same --- type, assuming earlier elements match. --- Example: f :: forall v. v -> blah --- match_exprs [Type a, y::a] [Type Int, 3] --- Then, after matching Type a against Type Int, --- the type of (y::a) matches that of (3::Int) -match_exprs _ subst [] _ - = Just subst -match_exprs renv subst (e1:es1) (e2:es2) - = do { subst' <- match renv subst e1 e2 MRefl - ; match_exprs renv subst' es1 es2 } -match_exprs _ _ _ _ = Nothing - - -{- Note [Unbound RULE binders] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -It can be the case that the binder in a rule is not actually -bound on the LHS: - -* Type variables. Type synonyms with phantom args can give rise to - unbound template type variables. Consider this (#10689, - simplCore/should_compile/T10689): - - type Foo a b = b - - f :: Eq a => a -> Bool - f x = x==x - - {-# RULES "foo" forall (x :: Foo a Char). f x = True #-} - finkle = f 'c' - - The rule looks like - forall (a::*) (d::Eq Char) (x :: Foo a Char). - f (Foo a Char) d x = True - - Matching the rule won't bind 'a', and legitimately so. We fudge by - pretending that 'a' is bound to (Any :: *). - -* Coercion variables. On the LHS of a RULE for a local binder - we might have - RULE forall (c :: a~b). f (x |> c) = e - Now, if that binding is inlined, so that a=b=Int, we'd get - RULE forall (c :: Int~Int). f (x |> c) = e - and now when we simplify the LHS (Simplify.simplRule) we - optCoercion (look at the CoVarCo case) will turn that 'c' into Refl: - RULE forall (c :: Int~Int). f (x |> <Int>) = e - and then perhaps drop it altogether. Now 'c' is unbound. - - It's tricky to be sure this never happens, so instead I - say it's OK to have an unbound coercion binder in a RULE - provided its type is (c :: t~t). Then, when the RULE - fires we can substitute <t> for c. - - This actually happened (in a RULE for a local function) - in #13410, and also in test T10602. - -Note [Cloning the template binders] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider the following match (example 1): - Template: forall x. f x - Target: f (x+1) -This should succeed, because the template variable 'x' has nothing to -do with the 'x' in the target. - -Likewise this one (example 2): - Template: forall x. f (\x.x) - Target: f (\y.y) - -We achieve this simply by using rnBndrL to clone the template -binders if they are already in scope. - ------- Historical note ------- -At one point I tried simply adding the template binders to the -in-scope set /without/ cloning them, but that failed in a horribly -obscure way in #14777. Problem was that during matching we look -up target-term variables in the in-scope set (see Note [Lookup -in-scope]). If a target-term variable happens to name-clash with a -template variable, that lookup will find the template variable, which -is /utterly/ bogus. In #14777, this transformed a term variable -into a type variable, and then crashed when we wanted its idInfo. ------- End of historical note ------- - - -************************************************************************ -* * - The main matcher -* * -********************************************************************* -} - -data RuleMatchEnv - = RV { rv_lcl :: RnEnv2 -- Renamings for *local bindings* - -- (lambda/case) - , rv_tmpls :: VarSet -- Template variables - -- (after applying envL of rv_lcl) - , rv_fltR :: Subst -- Renamings for floated let-bindings - -- (domain disjoint from envR of rv_lcl) - -- See Note [Matching lets] - -- N.B. The InScopeSet of rv_fltR is always ignored; - -- see (4) in Note [Matching lets]. - , rv_unf :: IdUnfoldingFun - } - -{- Note [rv_lcl in RuleMatchEnv] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider matching - Template: \x->f - Target: \f->f - -where 'f' is free in the template. When we meet the lambdas we must -remember to rename f :-> f' in the target, as well as x :-> f -in the template. The rv_lcl::RnEnv2 does that. - -Similarly, consider matching - Template: {a} \b->b - Target: \a->3 -We must rename the \a. Otherwise when we meet the lambdas we might -substitute [b :-> a] in the template, and then erroneously succeed in -matching what looks like the template variable 'a' against 3. - -So we must add the template vars to the in-scope set before starting; -see `init_menv` in `matchN`. --} - -rvInScopeEnv :: RuleMatchEnv -> InScopeEnv -rvInScopeEnv renv = (rnInScopeSet (rv_lcl renv), rv_unf renv) - --- * The domain of the TvSubstEnv and IdSubstEnv are the template --- variables passed into the match. --- --- * The BindWrapper in a RuleSubst are the bindings floated out --- from nested matches; see the Let case of match, below --- -data RuleSubst = RS { rs_tv_subst :: TvSubstEnv -- Range is the - , rs_id_subst :: IdSubstEnv -- template variables - , rs_binds :: BindWrapper -- Floated bindings - , rs_bndrs :: [Var] -- Variables bound by floated lets - } - -type BindWrapper = CoreExpr -> CoreExpr - -- See Notes [Matching lets] and [Matching cases] - -- we represent the floated bindings as a core-to-core function - -emptyRuleSubst :: RuleSubst -emptyRuleSubst = RS { rs_tv_subst = emptyVarEnv, rs_id_subst = emptyVarEnv - , rs_binds = \e -> e, rs_bndrs = [] } - - -{- Note [Casts in the target] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -As far as possible we don't want casts in the target to get in the way of -matching. E.g. -* (let bind in e) |> co -* (case e of alts) |> co -* (\ a b. f a b) |> co - -In the first two cases we want to float the cast inwards so we can match on -the let/case. This is not important in practice because the Simplifier does -this anyway. - -But the third case /is/ important: we don't want the cast to get in the way -of eta-reduction. See Note [Cancel reflexive casts] for a real life example. - -The most convenient thing is to make 'match' take an MCoercion argument, thus: - -* The main matching function - match env subst template target mco - matches template ~ (target |> mco) - -* Invariant: typeof( subst(template) ) = typeof( target |> mco ) - -Note that for applications - (e1 e2) ~ (d1 d2) |> co -where 'co' is non-reflexive, we simply fail. You might wonder about - (e1 e2) ~ ((d1 |> co1) d2) |> co2 -but the Simplifer pushes the casts in an application to to the -right, if it can, so this doesn't really arise. - -Note [Coercion arguments] -~~~~~~~~~~~~~~~~~~~~~~~~~ -What if we have (f co) in the template, where the 'co' is a coercion -argument to f? Right now we have nothing in place to ensure that a -coercion /argument/ in the template is a variable. We really should, -perhaps by abstracting over that variable. - -C.f. the treatment of dictionaries in GHC.HsToCore.Binds.decompseRuleLhs. - -For now, though, we simply behave badly, by failing in match_co. -We really should never rely on matching the structure of a coercion -(which is just a proof). - -Note [Casts in the template] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider the definition - f x = e, -and SpecConstr on call pattern - f ((e1,e2) |> co) - -We'll make a RULE - RULE forall a,b,g. f ((a,b)|> g) = $sf a b g - $sf a b g = e[ ((a,b)|> g) / x ] - -So here is the invariant: - - In the template, in a cast (e |> co), - the cast `co` is always a /variable/. - -Matching should bind that variable to an actual coercion, so that we -can use it in $sf. So a Cast on the LHS (the template) calls -match_co, which succeeds when the template cast is a variable -- which -it always is. That is why match_co has so few cases. - -See also -* Note [Coercion arguments] -* Note [Matching coercion variables] in GHC.Core.Unify. -* Note [Cast swizzling on rule LHSs] in GHC.Core.Opt.Simplify.Utils: - sm_cast_swizzle is switched off in the template of a RULE --} - ----------------------- -match :: RuleMatchEnv - -> RuleSubst -- Substitution applies to template only - -> CoreExpr -- Template - -> CoreExpr -- Target - -> MCoercion - -> Maybe RuleSubst - --- Postcondition (TypeInv): if matching succeeds, then --- typeof( subst(template) ) = typeof( target |> mco ) --- But this is /not/ a pre-condition! The types of template and target --- may differ, see the (App e1 e2) case --- --- Invariant (CoInv): if mco :: ty ~ ty, then it is MRefl, not MCo co --- See Note [Cancel reflexive casts] --- --- See the notes with Unify.match, which matches types --- Everything is very similar for terms - - ------------------------- Ticks --------------------- --- We look through certain ticks. See Note [Tick annotations in RULE matching] -match renv subst e1 (Tick t e2) mco - | tickishFloatable t - = match renv subst' e1 e2 mco - | otherwise - = Nothing - where - subst' = subst { rs_binds = rs_binds subst . mkTick t } - -match renv subst e@(Tick t e1) e2 mco - | tickishFloatable t -- Ignore floatable ticks in rule template. - = match renv subst e1 e2 mco - | otherwise - = pprPanic "Tick in rule" (ppr e) - ------------------------- Types --------------------- -match renv subst (Type ty1) (Type ty2) _mco - = match_ty renv subst ty1 ty2 - ------------------------- Coercions --------------------- --- See Note [Coercion arguments] for why this isn't really right -match renv subst (Coercion co1) (Coercion co2) MRefl - = match_co renv subst co1 co2 - -- The MCo case corresponds to matching co ~ (co2 |> co3) - -- and I have no idea what to do there -- or even if it can occur - -- Failing seems the simplest thing to do; it's certainly safe. - ------------------------- Casts --------------------- --- See Note [Casts in the template] --- Note [Casts in the target] --- Note [Cancel reflexive casts] - -match renv subst e1 (Cast e2 co2) mco - = match renv subst e1 e2 (checkReflexiveMCo (mkTransMCoR co2 mco)) - -- checkReflexiveMCo: cancel casts if possible - -- This is important: see Note [Cancel reflexive casts] - -match renv subst (Cast e1 co1) e2 mco - = -- See Note [Casts in the template] - do { let co2 = case mco of - MRefl -> mkRepReflCo (exprType e2) - MCo co2 -> co2 - ; subst1 <- match_co renv subst co1 co2 - -- If match_co succeeds, then (exprType e1) = (exprType e2) - -- Hence the MRefl in the next line - ; match renv subst1 e1 e2 MRefl } - ------------------------- Literals --------------------- -match _ subst (Lit lit1) (Lit lit2) mco - | lit1 == lit2 - = assertPpr (isReflMCo mco) (ppr mco) $ - Just subst - ------------------------- Variables --------------------- --- The Var case follows closely what happens in GHC.Core.Unify.match -match renv subst (Var v1) e2 mco - = match_var renv subst v1 (mkCastMCo e2 mco) - -match renv subst e1 (Var v2) mco -- Note [Expanding variables] - | not (inRnEnvR rn_env v2) -- Note [Do not expand locally-bound variables] - , Just e2' <- expandUnfolding_maybe (rv_unf renv v2') - = match (renv { rv_lcl = nukeRnEnvR rn_env }) subst e1 e2' mco - where - v2' = lookupRnInScope rn_env v2 - rn_env = rv_lcl renv - -- Notice that we look up v2 in the in-scope set - -- See Note [Lookup in-scope] - -- No need to apply any renaming first (hence no rnOccR) - -- because of the not-inRnEnvR - ------------------------- Applications --------------------- --- Note the match on MRefl! We fail if there is a cast in the target --- (e1 e2) ~ (d1 d2) |> co --- See Note [Cancel reflexive casts]: in the Cast equations for 'match' --- we aggressively ensure that if MCo is reflective, it really is MRefl. -match renv subst (App f1 a1) (App f2 a2) MRefl - = do { subst' <- match renv subst f1 f2 MRefl - ; match renv subst' a1 a2 MRefl } - ------------------------- Float lets --------------------- -match renv subst e1 (Let bind e2) mco - | -- pprTrace "match:Let" (vcat [ppr bind, ppr $ okToFloat (rv_lcl renv) (bindFreeVars bind)]) $ - not (isJoinBind bind) -- can't float join point out of argument position - , okToFloat (rv_lcl renv) (bindFreeVars bind) -- See Note [Matching lets] - = match (renv { rv_fltR = flt_subst' - , rv_lcl = rv_lcl renv `extendRnInScopeSetList` new_bndrs }) - -- We are floating the let-binding out, as if it had enclosed - -- the entire target from Day 1. So we must add its binders to - -- the in-scope set (#20200) - (subst { rs_binds = rs_binds subst . Let bind' - , rs_bndrs = new_bndrs ++ rs_bndrs subst }) - e1 e2 mco - | otherwise - = Nothing - where - in_scope = rnInScopeSet (rv_lcl renv) `extendInScopeSetList` rs_bndrs subst - -- in_scope: see (4) in Note [Matching lets] - flt_subst = rv_fltR renv `setInScope` in_scope - (flt_subst', bind') = substBind flt_subst bind - new_bndrs = bindersOf bind' - ------------------------- Lambdas --------------------- -match renv subst (Lam x1 e1) e2 mco - | Just (x2, e2', ts) <- exprIsLambda_maybe (rvInScopeEnv renv) (mkCastMCo e2 mco) - -- See Note [Lambdas in the template] - = let renv' = rnMatchBndr2 renv x1 x2 - subst' = subst { rs_binds = rs_binds subst . flip (foldr mkTick) ts } - in match renv' subst' e1 e2' MRefl - -match renv subst e1 e2@(Lam {}) mco - | Just (renv', e2') <- eta_reduce renv e2 -- See Note [Eta reduction in the target] - = match renv' subst e1 e2' mco - -{- Note [Lambdas in the template] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If we match - Template: (\x. blah_template) - Target: (\y. blah_target) -then we want to match inside the lambdas, using rv_lcl to match up -x and y. - -But what about this? - Template (\x. (blah1 |> cv)) - Target (\y. blah2) |> co - -This happens quite readily, because the Simplifier generally moves -casts outside lambdas: see Note [Casts and lambdas] in -GHC.Core.Opt.Simplify.Utils. So, tiresomely, we want to push `co` -back inside, which is what `exprIsLambda_maybe` does. But we've -stripped off that cast, so now we need to put it back, hence mkCastMCo. - -Unlike the target, where we attempt eta-reduction, we do not attempt -to eta-reduce the template, and may therefore fail on - Template: \x. f True x - Target f True - -It's not especially easy to deal with eta reducing the template, -and never happens, because no one write eta-expanded left-hand-sides. --} - ------------------------- Case expression --------------------- -{- Disabled: see Note [Matching cases] below -match renv (tv_subst, id_subst, binds) e1 - (Case scrut case_bndr ty [(con, alt_bndrs, rhs)]) - | exprOkForSpeculation scrut -- See Note [Matching cases] - , okToFloat rn_env bndrs (exprFreeVars scrut) - = match (renv { me_env = rn_env' }) - (tv_subst, id_subst, binds . case_wrap) - e1 rhs - where - rn_env = me_env renv - rn_env' = extendRnInScopeList rn_env bndrs - bndrs = case_bndr : alt_bndrs - case_wrap rhs' = Case scrut case_bndr ty [(con, alt_bndrs, rhs')] --} - -match renv subst (Case e1 x1 ty1 alts1) (Case e2 x2 ty2 alts2) mco - = do { subst1 <- match_ty renv subst ty1 ty2 - ; subst2 <- match renv subst1 e1 e2 MRefl - ; let renv' = rnMatchBndr2 renv x1 x2 - ; match_alts renv' subst2 alts1 alts2 mco -- Alts are both sorted - } - --- Everything else fails -match _ _ _e1 _e2 _mco = -- pprTrace "Failing at" ((text "e1:" <+> ppr _e1) $$ (text "e2:" <+> ppr _e2)) $ - Nothing - -------------- -eta_reduce :: RuleMatchEnv -> CoreExpr -> Maybe (RuleMatchEnv, CoreExpr) --- See Note [Eta reduction in the target] -eta_reduce renv e@(Lam {}) - = go renv id [] e - where - go :: RuleMatchEnv -> BindWrapper -> [Var] -> CoreExpr - -> Maybe (RuleMatchEnv, CoreExpr) - go renv bw vs (Let b e) = go renv (bw . Let b) vs e - - go renv bw vs (Lam v e) = go renv' bw (v':vs) e - where - (rn_env', v') = rnBndrR (rv_lcl renv) v - renv' = renv { rv_lcl = rn_env' } - - go renv bw (v:vs) (App f arg) - | Var a <- arg, v == rnOccR (rv_lcl renv) a - = go renv bw vs f - - | Type ty <- arg, Just tv <- getTyVar_maybe ty - , v == rnOccR (rv_lcl renv) tv - = go renv bw vs f - - go renv bw [] e = Just (renv, bw e) - go _ _ (_:_) _ = Nothing - -eta_reduce _ _ = Nothing - -{- Note [Eta reduction in the target] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Suppose we are faced with this (#19790) - Template {x} f x - Target (\a b c. let blah in f x a b c) - -You might wonder why we have an eta-expanded target (see first subtle -point below), but regardless of how it came about, we'd like -eta-expansion not to impede matching. - -So eta_reduce does on-the-fly eta-reduction of the target expression. -Given (\a b c. let blah in e a b c), it returns (let blah in e). - -Subtle points: -* Consider a target: \x. f <expensive> x - In the main eta-reducer we do not eta-reduce this, because doing so - might reduce the arity of the expression (from 1 to zero, because of - <expensive>). But for rule-matching we /do/ want to match template - (f a) against target (\x. f <expensive> x), with a := <expensive> - - This is a compelling reason for not relying on the Simplifier's - eta-reducer. - -* The Lam case of eta_reduce renames as it goes. Consider - (\x. \x. f x x). We should not eta-reduce this. As we go we rename - the first x to x1, and the second to x2; then both argument x's are x2. - -* eta_reduce does /not/ need to check that the bindings 'blah' - and expression 'e' don't mention a b c; but it /does/ extend the - rv_lcl RnEnv2 (see rn_bndr in eta_reduce). - * If 'blah' mentions the binders, the let-float rule won't - fire; and - * if 'e' mentions the binders we we'll also fail to match - e.g. because of the exprFreeVars test in match_tmpl_var. - - Example: Template: {x} f a -- Some top-level 'a' - Target: (\a b. f a a b) -- The \a shadows top level 'a' - Then eta_reduce will /succeed/, with - (rnEnvR = [a :-> a'], f a) - The returned RnEnv will map [a :-> a'], where a' is fresh. (There is - no need to rename 'b' because (in this example) it is not in scope. - So it's as if we'd returned (f a') from eta_reduce; the renaming applied - to the target is simply deferred. - -Note [Cancel reflexive casts] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Here is an example (from #19790) which we want to catch - (f x) ~ (\a b. (f x |> co) a b) |> sym co -where - f :: Int -> Stream - co :: Stream ~ T1 -> T2 -> T3 - -when we eta-reduce (\a b. blah a b) to 'blah', we'll get - (f x) ~ (f x) |> co |> sym co - -and we really want to spot that the co/sym-co cancels out. -Hence - * We keep an invariant that the MCoercion is always MRefl - if the MCoercion is reflexive - * We maintain this invariant via the call to checkReflexiveMCo - in the Cast case of 'match'. --} - -------------- -match_co :: RuleMatchEnv - -> RuleSubst - -> Coercion - -> Coercion - -> Maybe RuleSubst --- We only match if the template is a coercion variable or Refl: --- see Note [Casts in the template] --- Like 'match' it is /not/ guaranteed that --- coercionKind template = coercionKind target --- But if match_co succeeds, it /is/ guaranteed that --- coercionKind (subst template) = coercionKind target - -match_co renv subst co1 co2 - | Just cv <- getCoVar_maybe co1 - = match_var renv subst cv (Coercion co2) - - | Just (ty1, r1) <- isReflCo_maybe co1 - = do { (ty2, r2) <- isReflCo_maybe co2 - ; guard (r1 == r2) - ; match_ty renv subst ty1 ty2 } - - | debugIsOn - = pprTrace "match_co: needs more cases" (ppr co1 $$ ppr co2) Nothing - -- Currently just deals with CoVarCo and Refl - - | otherwise - = Nothing - -------------- -rnMatchBndr2 :: RuleMatchEnv -> Var -> Var -> RuleMatchEnv -rnMatchBndr2 renv x1 x2 - = renv { rv_lcl = rnBndr2 (rv_lcl renv) x1 x2 - , rv_fltR = delBndr (rv_fltR renv) x2 } - - ------------------------------------------- -match_alts :: RuleMatchEnv - -> RuleSubst - -> [CoreAlt] -- Template - -> [CoreAlt] -> MCoercion -- Target - -> Maybe RuleSubst -match_alts _ subst [] [] _ - = return subst -match_alts renv subst (Alt c1 vs1 r1:alts1) (Alt c2 vs2 r2:alts2) mco - | c1 == c2 - = do { subst1 <- match renv' subst r1 r2 mco - ; match_alts renv subst1 alts1 alts2 mco } - where - renv' = foldl' mb renv (vs1 `zip` vs2) - mb renv (v1,v2) = rnMatchBndr2 renv v1 v2 - -match_alts _ _ _ _ _ - = Nothing - ------------------------------------------- -okToFloat :: RnEnv2 -> VarSet -> Bool -okToFloat rn_env bind_fvs - = allVarSet not_captured bind_fvs - where - not_captured fv = not (inRnEnvR rn_env fv) - ------------------------------------------- -match_var :: RuleMatchEnv - -> RuleSubst - -> Var -- Template - -> CoreExpr -- Target - -> Maybe RuleSubst -match_var renv@(RV { rv_tmpls = tmpls, rv_lcl = rn_env, rv_fltR = flt_env }) - subst v1 e2 - | v1' `elemVarSet` tmpls - = match_tmpl_var renv subst v1' e2 - - | otherwise -- v1' is not a template variable; check for an exact match with e2 - = case e2 of -- Remember, envR of rn_env is disjoint from rv_fltR - Var v2 | Just v2' <- rnOccR_maybe rn_env v2 - -> -- v2 was bound by a nested lambda or case - if v1' == v2' then Just subst - else Nothing - - -- v2 is not bound nestedly; it is free - -- in the whole expression being matched - -- So it will be in the InScopeSet for flt_env (#20200) - | Var v2' <- lookupIdSubst flt_env v2 - , v1' == v2' - -> Just subst - | otherwise - -> Nothing - - _ -> Nothing - - where - v1' = rnOccL rn_env v1 - -- If the template is - -- forall x. f x (\x -> x) = ... - -- Then the x inside the lambda isn't the - -- template x, so we must rename first! - ------------------------------------------- -match_tmpl_var :: RuleMatchEnv - -> RuleSubst - -> Var -- Template - -> CoreExpr -- Target - -> Maybe RuleSubst - -match_tmpl_var renv@(RV { rv_lcl = rn_env, rv_fltR = flt_env }) - subst@(RS { rs_id_subst = id_subst, rs_bndrs = let_bndrs }) - v1' e2 - -- anyInRnEnvR is lazy in the 2nd arg which allows us to avoid computing fvs - -- if the right side of the env is empty. - | anyInRnEnvR rn_env (exprFreeVars e2) - = Nothing -- Skolem-escape failure - -- e.g. match forall a. (\x-> a x) against (\y. y y) - - | Just e1' <- lookupVarEnv id_subst v1' - = if eqCoreExpr e1' e2' - then Just subst - else Nothing - - | otherwise -- See Note [Matching variable types] - = do { subst' <- match_ty renv subst (idType v1') (exprType e2) - ; return (subst' { rs_id_subst = id_subst' }) } - where - -- e2' is the result of applying flt_env to e2 - e2' | null let_bndrs = e2 - | otherwise = substExpr flt_env e2 - - id_subst' = extendVarEnv (rs_id_subst subst) v1' e2' - -- No further renaming to do on e2', - -- because no free var of e2' is in the rnEnvR of the envt - ------------------------------------------- -match_ty :: RuleMatchEnv - -> RuleSubst - -> Type -- Template - -> Type -- Target - -> Maybe RuleSubst --- Matching Core types: use the matcher in GHC.Tc.Utils.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). - -match_ty renv subst ty1 ty2 - = do { tv_subst' - <- Unify.ruleMatchTyKiX (rv_tmpls renv) (rv_lcl renv) tv_subst ty1 ty2 - ; return (subst { rs_tv_subst = tv_subst' }) } - where - tv_subst = rs_tv_subst subst - -{- Note [Matching variable types] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When matching x ~ e, where 'x' is a template variable, we must check that -x's type matches e's type, to establish (TypeInv). For example - forall (c::Char->Int) (x::Char). - f (c x) = "RULE FIRED" -We must not match on, say (f (pred (3::Int))). - -It's actually quite difficult to come up with an example that shows -you need type matching, esp since matching is left-to-right, so type -args get matched first. But it's possible (e.g. simplrun008) and this -is the Right Thing to do. - -An alternative would be to make (TypeInf) into a /pre-condition/. It -is threatened only by the App rule. So when matching an application -(e1 e2) ~ (d1 d2) would be to collect args of the application chain, -match the types of the head, then match arg-by-arg. - -However that alternative seems a bit more complicated. And by -matching types at variables we do one match_ty for each template -variable, rather than one for each application chain. Usually there are -fewer template variables, although for simple rules it could be the other -way around. - -Note [Expanding variables] -~~~~~~~~~~~~~~~~~~~~~~~~~~ -Here is another Very Important rule: if the term being matched is a -variable, we expand it so long as its unfolding is "expandable". (Its -occurrence information is not necessarily up to date, so we don't use -it.) By "expandable" we mean a WHNF or a "constructor-like" application. -This is the key reason for "constructor-like" Ids. If we have - {-# NOINLINE [1] CONLIKE g #-} - {-# RULE f (g x) = h x #-} -then in the term - let v = g 3 in ....(f v).... -we want to make the rule fire, to replace (f v) with (h 3). - -Note [Do not expand locally-bound variables] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Do *not* expand locally-bound variables, else there's a worry that the -unfolding might mention variables that are themselves renamed. -Example - case x of y { (p,q) -> ...y... } -Don't expand 'y' to (p,q) because p,q might themselves have been -renamed. Essentially we only expand unfoldings that are "outside" -the entire match. - -Hence, (a) the guard (not (isLocallyBoundR v2)) - (b) when we expand we nuke the renaming envt (nukeRnEnvR). - -Note [Tick annotations in RULE matching] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We used to unconditionally look through ticks in both template and -expression being matched. This is actually illegal for counting or -cost-centre-scoped ticks, because we have no place to put them without -changing entry counts and/or costs. So now we just fail the match in -these cases. - -On the other hand, where we are allowed to insert new cost into the -tick scope, we can float them upwards to the rule application site. - -Moreover, we may encounter ticks in the template of a rule. There are a few -ways in which these may be introduced (e.g. #18162, #17619). Such ticks are -ignored by the matcher. See Note [Simplifying rules] in -GHC.Core.Opt.Simplify.Utils for details. - -cf Note [Tick annotations in call patterns] in GHC.Core.Opt.SpecConstr - - -Note [Matching lets] -~~~~~~~~~~~~~~~~~~~~ -Matching a let-expression. Consider - RULE forall x. f (g x) = <rhs> -and target expression - f (let { w=R } in g E)) -Then we'd like the rule to match, to generate - let { w=R } in (\x. <rhs>) E -In effect, we want to float the let-binding outward, to enable -the match to happen. This is the WHOLE REASON for accumulating -bindings in the RuleSubst - -We can only do this if the free variables of R are not bound by the -part of the target expression outside the let binding; e.g. - f (\v. let w = v+1 in g E) -Here we obviously cannot float the let-binding for w. Hence the -use of okToFloat. - -There are a couple of tricky points: - (a) What if floating the binding captures a variable that is - free in the entire expression? - f (let v = x+1 in v) v - --> NOT! - let v = x+1 in f (x+1) v - - (b) What if the let shadows a local binding? - f (\v -> (v, let v = x+1 in (v,v)) - --> NOT! - let v = x+1 in f (\v -> (v, (v,v))) - - (c) What if two non-nested let bindings bind the same variable? - f (let v = e1 in b1) (let v = e2 in b2) - --> NOT! - let v = e1 in let v = e2 in (f b2 b2) - See testsuite test `T4814`. - -Our cunning plan is this: - (1) Along with the growing substitution for template variables - we maintain a growing set of floated let-bindings (rs_binds) - plus the set of variables thus bound (rs_bndrs). - - (2) The RnEnv2 in the MatchEnv binds only the local binders - in the term (lambdas, case), not the floated let-bndrs. - - (3) When we encounter a `let` in the term to be matched, in the Let - case of `match`, we use `okToFloat` to check that it does not mention any - locally bound (lambda, case) variables. If so we fail. - - (4) In the Let case of `match`, we use GHC.Core.Subst.substBind to - freshen the binding (which, remember (3), mentions no locally - bound variables), in a lexically-scoped way (via rv_fltR in - MatchEnv). - - The subtle point is that we want an in-scope set for this - substitution that includes /two/ sets: - * The in-scope variables at this point, so that we avoid using - those local names for the floated binding; points (a) and (b) above. - * All "earlier" floated bindings, so that we avoid using the - same name for two different floated bindings; point (c) above. - - Because we have to compute the in-scope set here, the in-scope set - stored in `rv_fltR` is always ignored; we leave it only because it's - convenient to have `rv_fltR :: Subst` (with an always-ignored `InScopeSet`) - rather than storing three separate substitutions. - - (5) We apply that freshening substitution, in a lexically-scoped - way to the term, although lazily; this is the rv_fltR field. - -See #4814, which is an issue resulting from getting this wrong. - -Note [Matching cases] -~~~~~~~~~~~~~~~~~~~~~ -{- NOTE: This idea is currently disabled. It really only works if - the primops involved are OkForSpeculation, and, since - they have side effects readIntOfAddr and touch are not. - Maybe we'll get back to this later . -} - -Consider - f (case readIntOffAddr# p# i# realWorld# of { (# s#, n# #) -> - case touch# fp s# of { _ -> - I# n# } } ) -This happened in a tight loop generated by stream fusion that -Roman encountered. We'd like to treat this just like the let -case, because the primops concerned are ok-for-speculation. -That is, we'd like to behave as if it had been - case readIntOffAddr# p# i# realWorld# of { (# s#, n# #) -> - case touch# fp s# of { _ -> - f (I# n# } } ) - -Note [Lookup in-scope] -~~~~~~~~~~~~~~~~~~~~~~ -Consider this example - foo :: Int -> Maybe Int -> Int - foo 0 (Just n) = n - foo m (Just n) = foo (m-n) (Just n) - -SpecConstr sees this fragment: - - case w_smT of wild_Xf [Just A] { - Data.Maybe.Nothing -> lvl_smf; - Data.Maybe.Just n_acT [Just S(L)] -> - case n_acT of wild1_ams [Just A] { GHC.Base.I# y_amr [Just L] -> - $wfoo_smW (GHC.Prim.-# ds_Xmb y_amr) wild_Xf - }}; - -and correctly generates the rule - - RULES: "SC:$wfoo1" [0] __forall {y_amr [Just L] :: GHC.Prim.Int# - sc_snn :: GHC.Prim.Int#} - $wfoo_smW sc_snn (Data.Maybe.Just @ GHC.Base.Int (GHC.Base.I# y_amr)) - = $s$wfoo_sno y_amr sc_snn ;] - -BUT we must ensure that this rule matches in the original function! -Note that the call to $wfoo is - $wfoo_smW (GHC.Prim.-# ds_Xmb y_amr) wild_Xf - -During matching we expand wild_Xf to (Just n_acT). But then we must also -expand n_acT to (I# y_amr). And we can only do that if we look up n_acT -in the in-scope set, because in wild_Xf's unfolding it won't have an unfolding -at all. - -That is why the 'lookupRnInScope' call in the (Var v2) case of 'match' -is so important. - - -************************************************************************ -* * - Rule-check the program -* * -************************************************************************ - - 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. --} - --- | Report partial matches for rules beginning with the specified --- string for the purposes of error reporting -ruleCheckProgram :: RuleOpts -- ^ Rule options - -> CompilerPhase -- ^ Rule activation test - -> String -- ^ Rule pattern - -> (Id -> [CoreRule]) -- ^ Rules for an Id - -> CoreProgram -- ^ Bindings to check in - -> SDoc -- ^ Resulting check message -ruleCheckProgram ropts phase rule_pat rules 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 - env = RuleCheckEnv { rc_is_active = isActive phase - , rc_id_unf = idUnfolding -- Not quite right - -- Should use activeUnfolding - , rc_pattern = rule_pat - , rc_rules = rules - , rc_ropts = ropts - } - results = unionManyBags (map (ruleCheckBind env) binds) - line = text (replicate 20 '-') - -data RuleCheckEnv = RuleCheckEnv { - rc_is_active :: Activation -> Bool, - rc_id_unf :: IdUnfoldingFun, - rc_pattern :: String, - rc_rules :: Id -> [CoreRule], - rc_ropts :: RuleOpts -} - -ruleCheckBind :: RuleCheckEnv -> CoreBind -> Bag SDoc - -- The Bag returned has one SDoc for each call site found -ruleCheckBind env (NonRec _ r) = ruleCheck env r -ruleCheckBind env (Rec prs) = unionManyBags [ruleCheck env r | (_,r) <- prs] - -ruleCheck :: RuleCheckEnv -> CoreExpr -> Bag SDoc -ruleCheck _ (Var _) = emptyBag -ruleCheck _ (Lit _) = emptyBag -ruleCheck _ (Type _) = emptyBag -ruleCheck _ (Coercion _) = emptyBag -ruleCheck env (App f a) = ruleCheckApp env (App f a) [] -ruleCheck env (Tick _ e) = ruleCheck env e -ruleCheck env (Cast e _) = ruleCheck env e -ruleCheck env (Let bd e) = ruleCheckBind env bd `unionBags` ruleCheck env e -ruleCheck env (Lam _ e) = ruleCheck env e -ruleCheck env (Case e _ _ as) = ruleCheck env e `unionBags` - unionManyBags [ruleCheck env r | Alt _ _ r <- as] - -ruleCheckApp :: RuleCheckEnv -> Expr CoreBndr -> [Arg CoreBndr] -> Bag SDoc -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 _ = ruleCheck env other - -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 env fn args - | null name_match_rules = emptyBag - | otherwise = unitBag (ruleAppCheck_help env fn args name_match_rules) - where - name_match_rules = filter match (rc_rules env fn) - match rule = rc_pattern env `isPrefixOf` unpackFS (ruleName rule) - -ruleAppCheck_help :: RuleCheckEnv -> Id -> [CoreExpr] -> [CoreRule] -> SDoc -ruleAppCheck_help env 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 (rc_ropts env) rule - - rule_herald (BuiltinRule { ru_name = name }) - = text "Builtin rule" <+> doubleQuotes (ftext name) - rule_herald (Rule { ru_name = name }) - = text "Rule" <+> doubleQuotes (ftext name) - - rule_info opts rule - | Just _ <- matchRule opts (emptyInScopeSet, rc_id_unf env) - noBlackList fn args rough_args rule - = text "matches (which is very peculiar!)" - - rule_info _ (BuiltinRule {}) = text "does not match" - - rule_info _ (Rule { ru_act = act, - ru_bndrs = rule_bndrs, ru_args = rule_args}) - | not (rc_is_active env 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 renv emptyRuleSubst rule_arg arg MRefl - where - in_scope = mkInScopeSet (lhs_fvs `unionVarSet` exprFreeVars arg) - renv = RV { rv_lcl = mkRnEnv2 in_scope - , rv_tmpls = mkVarSet rule_bndrs - , rv_fltR = mkEmptySubst in_scope - , rv_unf = rc_id_unf env } +-- | A 'CoreRule' is: +-- +-- * \"Local\" if the function it is a rule for is defined in the +-- same module as the rule itself. +-- +-- * \"Orphan\" if nothing on the LHS is defined in the same module +-- as the rule itself +data CoreRule + = Rule { + ru_name :: RuleName, -- ^ Name of the rule, for communication with the user + ru_act :: Activation, -- ^ When the rule is active + + -- Rough-matching stuff + -- see comments with InstEnv.ClsInst( is_cls, is_rough ) + ru_fn :: !Name, -- ^ Name of the 'GHC.Types.Id.Id' at the head of this rule + ru_rough :: [Maybe Name], -- ^ Name at the head of each argument to the left hand side + + -- Proper-matching stuff + -- see comments with InstEnv.ClsInst( is_tvs, is_tys ) + ru_bndrs :: [CoreBndr], -- ^ Variables quantified over + ru_args :: [CoreExpr], -- ^ Left hand side arguments + + -- And the right-hand side + ru_rhs :: CoreExpr, -- ^ Right hand side of the rule + -- Occurrence info is guaranteed correct + -- See Note [OccInfo in unfoldings and rules] + + -- Locality + ru_auto :: Bool, -- ^ @True@ <=> this rule is auto-generated + -- (notably by Specialise or SpecConstr) + -- @False@ <=> generated at the user's behest + -- See Note [Trimming auto-rules] in "GHC.Iface.Tidy" + -- for the sole purpose of this field. + + ru_origin :: !Module, -- ^ 'Module' the rule was defined in, used + -- to test if we should see an orphan rule. + + ru_orphan :: !IsOrphan, -- ^ Whether or not the rule is an orphan. + + ru_local :: Bool -- ^ @True@ iff the fn at the head of the rule is + -- defined in the same module as the rule + -- and is not an implicit 'Id' (like a record selector, + -- class operation, or data constructor). This + -- is different from 'ru_orphan', where a rule + -- can avoid being an orphan if *any* Name in + -- LHS of the rule was defined in the same + -- module as the rule. + } + + -- | Built-in rules are used for constant folding + -- and suchlike. They have no free variables. + -- A built-in rule is always visible (there is no such thing as + -- an orphan built-in rule.) + | BuiltinRule { + ru_name :: RuleName, -- ^ As above + ru_fn :: Name, -- ^ As above + ru_nargs :: Int, -- ^ Number of arguments that 'ru_try' consumes, + -- if it fires, including type arguments + ru_try :: RuleFun + -- ^ This function does the rewrite. It given too many + -- arguments, it simply discards them; the returned 'CoreExpr' + -- is just the rewrite of 'ru_fn' applied to the first 'ru_nargs' args + } + -- See Note [Extra args in the target] in GHC.Core.Rules + +-- | The 'InScopeSet' in the 'InScopeEnv' is a /superset/ of variables that are +-- currently in scope. See Note [The InScopeSet invariant]. +type RuleFun = RuleOpts -> InScopeEnv -> Id -> [CoreExpr] -> Maybe CoreExpr +type InScopeEnv = (InScopeSet, IdUnfoldingFun) + +type IdUnfoldingFun = Id -> Unfolding +-- A function that embodies how to unfold an Id if you need +-- to do that in the Rule. The reason we need to pass this info in +-- is that whether an Id is unfoldable depends on the simplifier phase + +isBuiltinRule :: CoreRule -> Bool +isBuiltinRule (BuiltinRule {}) = True +isBuiltinRule _ = False + +isAutoRule :: CoreRule -> Bool +isAutoRule (BuiltinRule {}) = False +isAutoRule (Rule { ru_auto = is_auto }) = is_auto + +-- | The number of arguments the 'ru_fn' must be applied +-- to before the rule can match on it +ruleArity :: CoreRule -> Int +ruleArity (BuiltinRule {ru_nargs = n}) = n +ruleArity (Rule {ru_args = args}) = length args + +ruleName :: CoreRule -> RuleName +ruleName = ru_name + +ruleModule :: CoreRule -> Maybe Module +ruleModule Rule { ru_origin } = Just ru_origin +ruleModule BuiltinRule {} = Nothing + +ruleActivation :: CoreRule -> Activation +ruleActivation (BuiltinRule { }) = AlwaysActive +ruleActivation (Rule { ru_act = act }) = act + +-- | The 'Name' of the 'GHC.Types.Id.Id' at the head of the rule left hand side +ruleIdName :: CoreRule -> Name +ruleIdName = ru_fn + +isLocalRule :: CoreRule -> Bool +isLocalRule = ru_local + +-- | Set the 'Name' of the 'GHC.Types.Id.Id' at the head of the rule left hand side +setRuleIdName :: Name -> CoreRule -> CoreRule +setRuleIdName nm ru = ru { ru_fn = nm } |