\begin{code}
module Rules (
- RuleBase, emptyRuleBase,
- extendRuleBase, extendRuleBaseList, addRuleBaseFVs,
- ruleBaseIds, ruleBaseFVs,
- pprRuleBase, ruleCheckProgram,
+ RuleBase, emptyRuleBase, mkRuleBase, extendRuleBaseList,
+ unionRuleBase, pprRuleBase, ruleCheckProgram,
- lookupRule, addRule, addIdSpecialisations
+ mkSpecInfo, extendSpecInfo, addSpecInfo,
+ rulesOfBinds, addIdSpecialisations,
+
+ lookupRule, mkLocalRule, roughTopNames
) where
#include "HsVersions.h"
import CoreSyn -- All of it
-import OccurAnal ( occurAnalyseRule )
-import CoreFVs ( exprFreeVars, ruleRhsFreeVars, ruleLhsFreeIds )
+import OccurAnal ( occurAnalyseExpr )
+import CoreFVs ( exprFreeVars, exprsFreeVars, rulesRhsFreeVars )
import CoreUnfold ( isCheapUnfolding, unfoldingTemplate )
-import CoreUtils ( eqExpr )
-import CoreTidy ( pprTidyIdRules )
-import Subst ( Subst, InScopeSet, mkInScopeSet, lookupSubst, extendSubst,
- substEnv, setSubstEnv, emptySubst, isInScope, emptyInScopeSet,
- bindSubstList, unBindSubstList, substInScope, uniqAway
- )
-import Id ( Id, idUnfolding, idSpecialisation, setIdSpecialisation )
-import Var ( isId )
+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 VarEnv
-import TcType ( mkTyVarTy )
-import qualified TcType ( match )
+import Name ( Name, NamedThing(..), nameOccName )
+import NameEnv
+import Unify ( ruleMatchTyX, MatchEnv(..) )
import BasicTypes ( Activation, CompilerPhase, isActive )
-
import Outputable
-import Maybe ( isJust, isNothing, fromMaybe )
-import Util ( sortLt )
+import FastString
+import Maybes ( isJust, orElse )
import Bag
+import Util ( singleton )
import List ( isPrefixOf )
\end{code}
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}
+
%************************************************************************
%* *
%************************************************************************
\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
- -> [CoreRule] -> [CoreExpr] -> Maybe (RuleName, CoreExpr)
+ -> Id -> [CoreExpr]
+ -> [CoreRule] -> Maybe (RuleName, CoreExpr)
-- See comments on matchRule
-matchRules is_active in_scope [] args = Nothing
-matchRules is_active in_scope (rule:rules) args
- = case matchRule is_active in_scope rule args of
- Just result -> Just result
- Nothing -> matchRules is_active in_scope rules args
+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
- -> CoreRule -> [CoreExpr] -> Maybe (RuleName, CoreExpr)
+ -> [CoreExpr] -> [Maybe Name]
+ -> CoreRule -> Maybe CoreExpr
-- If (matchRule rule args) returns Just (name,rhs)
-- then (f args) matches the rule, and the corresponding
--
-- Any 'surplus' arguments in the input are simply put on the end
-- of the output.
---
--- ASSUMPTION (A):
--- A1. No top-level variable is bound in the target
--- A2. No template variable is bound in the target
--- A3. No lambda bound template variable is free in any subexpression of the target
---
--- To see why A1 is necessary, consider matching
--- \x->f against \f->f
--- When we meet the lambdas we substitute [f/x] in the template (a no-op),
--- and then erroneously succeed in matching f against f.
---
--- To see why A2 is needed consider matching
--- forall a. \b->b against \a->3
--- When we meet the lambdas we substitute [a/b] in the template, and then
--- erroneously succeed in matching what looks like the template variable 'a' against 3.
---
--- A3 is needed to validate the rule that says
--- (\x->E) matches F
--- if
--- (\x->E) matches (\x->F x)
-
-matchRule is_active in_scope rule@(BuiltinRule name match_fn) args
+matchRule is_active in_scope args rough_args
+ (BuiltinRule { ru_name = name, ru_try = match_fn })
= case match_fn args of
- Just expr -> Just (name,expr)
+ Just expr -> Just expr
Nothing -> Nothing
-matchRule is_active in_scope rule@(Rule rn act tpl_vars tpl_args rhs) args
- | not (is_active act)
- = 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
- = go tpl_args args emptySubst
- -- We used to use the in_scope set, but I don't think that's necessary
- -- After all, the result is going to be simplified again with that in_scope set
- where
- tpl_var_set = mkVarSet tpl_vars
-
- -----------------------
- -- Do the business
- go (tpl_arg:tpl_args) (arg:args) subst = match tpl_arg arg tpl_var_set (go tpl_args args) subst
-
- -- Two easy ways to terminate
- go [] [] subst = Just (rn, app_match subst (mkLams tpl_vars rhs) tpl_vars)
- go [] args subst = Just (rn, app_match subst (mkLams tpl_vars rhs) tpl_vars `mkApps` args)
-
- -- One tiresome way to terminate: check for excess unmatched
- -- template arguments
- go tpl_args [] subst = Nothing -- Failure
-
-
- -----------------------
- app_match subst fn vs = foldl go fn vs
- where
- senv = substEnv subst
- go fn v = case lookupSubstEnv senv v of
- Just (DoneEx ex) -> fn `App` ex
- Just (DoneTy ty) -> fn `App` Type ty
- -- Substitution should bind them all!
-
-
- -----------------------
-{- The code below tries 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
-
- = case eta_complete tpl_args (mkVarSet leftovers) of
- Just leftovers' -> Just (rn, mkLams done (mkLams leftovers' rhs),
- mk_result_args subst done)
- Nothing -> Nothing -- Failure
- where
- (done, leftovers) = partition (\v -> isJust (lookupSubstEnv subst_env v))
- (map zapOccInfo tpl_vars)
- -- Zap the occ info
- subst_env = substEnv subst
-
- -----------------------
- eta_complete [] vars = ASSERT( isEmptyVarSet vars )
- Just []
- eta_complete (Type ty:tpl_args) vars
- = case getTyVar_maybe ty of
- Just tv | tv `elemVarSet` vars
- -> case eta_complete tpl_args (vars `delVarSet` tv) of
- Just vars' -> Just (tv:vars')
- Nothing -> Nothing
- other -> Nothing
-
- eta_complete (Var v:tpl_args) vars
- | v `elemVarSet` vars
- = case eta_complete tpl_args (vars `delVarSet` v) of
- Just vars' -> Just (v:vars')
- Nothing -> Nothing
-
- eta_complete other vars = Nothing
-
-
-zapOccInfo bndr | isTyVar bndr = bndr
- | otherwise = zapLamIdInfo bndr
--}
+ = 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}
-type Matcher result = VarSet -- Template variables
- -> (Subst -> Maybe result) -- Continuation if success
- -> Subst -> Maybe result -- Substitution so far -> result
--- The *SubstEnv* in these Substs apply to the TEMPLATE only
+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 *InScopeSet* in these Substs gives variables bound so far in the
--- target term. So when matching forall a. (\x. a x) against (\y. y y)
--- while processing the body of the lambdas, the in-scope set will be {y}.
--- That lets us do the occurs-check when matching 'a' against 'y'
-match :: CoreExpr -- Template
- -> CoreExpr -- Target
- -> Matcher result
+ ---------------------------------------------
+ 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)
-match_fail = Nothing
-match (Var v1) e2 tpl_vars kont subst
- = case lookupSubst subst v1 of
- Nothing | v1 `elemVarSet` tpl_vars -- v1 is a template variable
- -> if (any (`isInScope` subst) (varSetElems (exprFreeVars e2))) then
- match_fail -- Occurs check failure
- -- e.g. match forall a. (\x-> a x) against (\y. y y)
- else
- kont (extendSubst subst v1 (DoneEx e2))
+-- 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
- | eqExpr (Var v1) e2 -> kont subst
- -- v1 is not a template variable, so it must be a global constant
- Just (DoneEx e2') | eqExpr e2' e2 -> kont subst
+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
- other -> match_fail
+-- 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 (Lit lit1) (Lit lit2) tpl_vars kont subst
+match menv subst (Lit lit1) (Lit lit2)
| lit1 == lit2
- = kont subst
+ = Just subst
-match (App f1 a1) (App f2 a2) tpl_vars kont subst
- = match f1 f2 tpl_vars (match a1 a2 tpl_vars kont) subst
+match menv subst (App f1 a1) (App f2 a2)
+ = do { subst' <- match menv subst f1 f2
+ ; match menv subst' a1 a2 }
-match (Lam x1 e1) (Lam x2 e2) tpl_vars kont subst
- = bind [x1] [x2] (match e1 e2) tpl_vars kont subst
+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
--- See assumption A3
-match (Lam x1 e1) e2 tpl_vars kont subst
- = bind [x1] [x1] (match e1 (App e2 (mkVarArg x1))) tpl_vars kont subst
+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 \y.M y ~ \y.N
--- iff M y ~ N
--- Remembering that by (A), y can't be free in M, we get this
-match e1 (Lam x2 e2) tpl_vars kont subst
- = bind [new_id] [x2] (match (App e1 (mkVarArg new_id)) e2) tpl_vars kont subst
- where
- new_id = uniqAway (substInScope subst) x2
- -- This uniqAway is actually needed. Here's the example:
- -- rule: foldr (mapFB (:) f) [] = mapList
- -- target: foldr (\x. mapFB k f x) []
- -- where
- -- k = \x. mapFB ... x
- -- The first \x is ok, but when we inline k, hoping it might
- -- match (:) we find a second \x.
-
-match (Case e1 x1 alts1) (Case e2 x2 alts2) tpl_vars kont subst
- = match e1 e2 tpl_vars case_kont subst
+-- M ~ (\y.N) iff M y ~ N
+match menv subst e1 (Lam x2 e2)
+ = match menv' subst (App e1 (varToCoreExpr new_x)) e2
where
- case_kont subst = bind [x1] [x2] (match_alts alts1 (sortLt lt_alt alts2))
- tpl_vars kont subst
-
-match (Type ty1) (Type ty2) tpl_vars kont subst
- = match_ty ty1 ty2 tpl_vars kont subst
+ (rn_env', new_x) = rnBndrR (me_env menv) x2
+ menv' = menv { me_env = rn_env' }
-match (Note (Coerce to1 from1) e1) (Note (Coerce to2 from2) e2)
- tpl_vars kont subst
- = (match_ty to1 to2 tpl_vars $
- match_ty from1 from2 tpl_vars $
- match e1 e2 tpl_vars kont) subst
+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
-{- I don't buy this let-rule any more
- The let rule fails on matching
- forall f,x,xs. f (x:xs)
- against
- f (let y = e in (y:[]))
- because we just get x->y, which is bogus.
+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. Meanwhile, we can't get false matches because
--- (also by assumption) the term being matched has no shadowing.
-match e1 (Let bind e2) tpl_vars kont subst
- = match e1 e2 tpl_vars kont subst
--}
-
--- 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 e1 (Var v2) tpl_vars kont subst
- | isCheapUnfolding unfolding
- = match e1 (unfoldingTemplate unfolding) tpl_vars kont subst
+-- them when we encounter them.
+match menv subst e1 (Let (NonRec x2 r2) e2)
+ = match menv' subst e1 e2
where
- unfolding = idUnfolding v2
-
-
--- We can't cope with lets in the template
-
-match e1 e2 tpl_vars kont subst = match_fail
-
+ 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 [] [] tpl_vars kont subst
- = kont subst
-match_alts ((c1,vs1,r1):alts1) ((c2,vs2,r2):alts2) tpl_vars kont subst
+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
- = bind vs1 vs2 (match r1 r2) tpl_vars
- (match_alts alts1 alts2 tpl_vars kont)
- subst
-match_alts alts1 alts2 tpl_vars kont subst = match_fail
-
-lt_alt (con1, _, _) (con2, _, _) = con1 < con2
-
-----------------------------------------
-bind :: [CoreBndr] -- Template binders
- -> [CoreBndr] -- Target binders
- -> Matcher result
- -> Matcher result
--- This makes uses of assumption (A) above. For example,
--- this would fail:
--- Template: (\x.y) (y is free)
--- Target : (\y.y) (y is bound)
--- We rename x to y in the template... but then erroneously
--- match y against y. But this can't happen because of (A)
-bind vs1 vs2 matcher tpl_vars kont subst
- = WARN( not (all not_in_subst vs1), bug_msg )
- matcher tpl_vars kont' subst'
+ = do { subst1 <- match menv' subst r1 r2
+ ; match_alts menv subst1 alts1 alts2 }
where
- kont' subst'' = kont (unBindSubstList subst'' vs1 vs2)
- subst' = bindSubstList subst vs1 vs2
-
- -- The unBindSubst relies on no shadowing in the template
- not_in_subst v = isNothing (lookupSubst subst v)
- bug_msg = sep [ppr vs1, ppr vs2]
-
-----------------------------------------
-matches [] [] tpl_vars kont subst
- = kont subst
-matches (e:es) (e':es') tpl_vars kont subst
- = match e e' tpl_vars (matches es es' tpl_vars kont) subst
-matches es es' tpl_vars kont subst
- = match_fail
-
-----------------------------------------
-mkVarArg :: CoreBndr -> CoreArg
-mkVarArg v | isId v = Var v
- | otherwise = Type (mkTyVarTy v)
+ 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.
We only want to replace (f T) with f', not (f Int).
\begin{code}
-----------------------------------------
-match_ty ty1 ty2 tpl_vars kont subst
- = TcType.match ty1 ty2 tpl_vars kont' (substEnv subst)
- where
- kont' senv = kont (setSubstEnv subst senv)
-\end{code}
-
-
-
-%************************************************************************
-%* *
-\subsection{Adding a new rule}
-%* *
-%************************************************************************
-
-\begin{code}
-addRule :: Id -> CoreRules -> CoreRule -> CoreRules
-
--- Add a new rule to an existing bunch of rules.
--- The rules are for the given Id; the Id argument is needed only
--- so that we can exclude the Id from its own RHS free-var set
-
--- Insert the new rule just before a rule that is *less specific*
--- than the new one; or at the end if there isn't such a one.
--- In this way we make sure that when looking up, the first match
--- is the most specific.
---
--- We make no check for rules that unify without one dominating
--- the other. Arguably this would be a bug.
-
-addRule id (Rules rules rhs_fvs) rule@(BuiltinRule _ _)
- = Rules (rule:rules) rhs_fvs
- -- Put it at the start for lack of anything better
-
-addRule id (Rules rules rhs_fvs) rule
- = Rules (insertRule rules new_rule) (rhs_fvs `unionVarSet` new_rhs_fvs)
- where
- new_rule = occurAnalyseRule rule
- new_rhs_fvs = ruleRhsFreeVars new_rule `delVarSet` id
- -- Hack alert!
- -- Don't include the Id in its own rhs free-var set.
- -- Otherwise the occurrence analyser makes bindings recursive
- -- that shoudn't be. E.g.
- -- RULE: f (f x y) z ==> f x (f y z)
-
-insertRule rules new_rule@(Rule _ _ tpl_vars tpl_args _)
- = go rules
- where
- tpl_var_set = mkInScopeSet (mkVarSet tpl_vars)
- -- Actually we should probably include the free vars of tpl_args,
- -- but I can't be bothered
-
- go [] = [new_rule]
- go (rule:rules) | new_is_more_specific rule = (new_rule:rule:rules)
- | otherwise = rule : go rules
-
- new_is_more_specific rule = isJust (matchRule noBlackList tpl_var_set rule tpl_args)
-
-addIdSpecialisations :: Id -> [CoreRule] -> Id
-addIdSpecialisations id rules
- = setIdSpecialisation id new_specs
- where
- new_specs = foldl (addRule id) (idSpecialisation id) rules
-\end{code}
-
-
-%************************************************************************
-%* *
-\subsection{Looking up a rule}
-%* *
-%************************************************************************
-
-\begin{code}
-lookupRule :: (Activation -> Bool) -> InScopeSet
- -> Id -> [CoreExpr] -> Maybe (RuleName, CoreExpr)
-lookupRule is_active in_scope fn args
- = case idSpecialisation fn of
- Rules rules _ -> matchRules is_active in_scope rules args
+------------------------------------------
+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}
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]
+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
| 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` _UNPK_ (ruleName rule)
+ 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
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 name _) = text "Builtin rule" <+> doubleQuotes (ptext name)
- rule_herald (Rule name _ _ _ _) = text "Rule" <+> doubleQuotes (ptext name)
+ 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 (name,_) <- matchRule noBlackList emptyInScopeSet rule args
+ | Just _ <- matchRule noBlackList emptyInScopeSet args rough_args rule
= text "matches (which is very peculiar!)"
- rule_info (BuiltinRule name fn) = text "does not match"
+ rule_info (BuiltinRule {}) = text "does not match"
- rule_info (Rule name act rule_bndrs rule_args _)
+ 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 the rule as a whole does not"
+ | 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
mismatches = [i | (rule_arg, (arg,i)) <- rule_args `zip` i_args,
not (isJust (match_fn rule_arg arg))]
- bndr_set = mkVarSet rule_bndrs
- match_fn rule_arg arg = match rule_arg arg bndr_set (\s -> Just ()) emptySubst
+ 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}
-
-%************************************************************************
-%* *
-\subsection{Getting the rules ready}
-%* *
-%************************************************************************
-
-\begin{code}
-data RuleBase = RuleBase
- IdSet -- Ids with their rules in their specialisations
- -- Held as a set, so that it can simply be the initial
- -- in-scope set in the simplifier
-
- IdSet -- Ids (whether local or imported) mentioned on
- -- LHS of some rule; these should be black listed
-
- -- This representation is a bit cute, and I wonder if we should
- -- change it to use (IdEnv CoreRule) which seems a bit more natural
-
-ruleBaseIds (RuleBase ids _) = ids
-ruleBaseFVs (RuleBase _ fvs) = fvs
-
-emptyRuleBase = RuleBase emptyVarSet emptyVarSet
-
-addRuleBaseFVs :: RuleBase -> IdSet -> RuleBase
-addRuleBaseFVs (RuleBase rules fvs) extra_fvs
- = RuleBase rules (fvs `unionVarSet` extra_fvs)
-
-extendRuleBaseList :: RuleBase -> [(Id,CoreRule)] -> RuleBase
-extendRuleBaseList rule_base new_guys
- = foldl extendRuleBase rule_base new_guys
-
-extendRuleBase :: RuleBase -> (Id,CoreRule) -> RuleBase
-extendRuleBase (RuleBase rule_ids rule_fvs) (id, rule)
- = RuleBase (extendVarSet rule_ids new_id)
- (rule_fvs `unionVarSet` extendVarSet lhs_fvs id)
- where
- new_id = setIdSpecialisation id (addRule id old_rules rule)
-
- old_rules = idSpecialisation (fromMaybe id (lookupVarSet rule_ids id))
- -- Get the old rules from rule_ids if the Id is already there, but
- -- if not, use the Id from the incoming rule. If may be a PrimOpId,
- -- in which case it may have rules in its belly already. Seems
- -- dreadfully hackoid.
-
- lhs_fvs = ruleLhsFreeIds rule
- -- Finds *all* the free Ids of the LHS, not just
- -- locally defined ones!!
-
-pprRuleBase :: RuleBase -> SDoc
-pprRuleBase (RuleBase rules _) = vcat [ pprTidyIdRules id | id <- varSetElems rules ]
-\end{code}