diff options
author | Edward Z. Yang <ezyang@cs.stanford.edu> | 2015-06-03 14:33:05 -0700 |
---|---|---|
committer | Edward Z. Yang <ezyang@cs.stanford.edu> | 2015-06-20 15:13:42 -0700 |
commit | 0cb1f5cf26fae946ca745abc5e302e62a8f66feb (patch) | |
tree | 07744de6d51cea9bde926d3ea88c1fda2b138974 /compiler/coreSyn/CoreSyn.hs | |
parent | 85d539754ac07286ef5fed714ad42451bd5a1d28 (diff) | |
download | haskell-0cb1f5cf26fae946ca745abc5e302e62a8f66feb.tar.gz |
Filter orphan rules based on imports, fixes #10294 and #10420.
Summary:
If we have an orphan rule in our database, don't apply it
unless the defining module is transitively imported by the
module we are processing. We do this by defining a new RuleEnv
data type which includes both the RuleBase as well as the set
of visible orphan modules, and threading this through the
relevant environments (CoreReader, RuleCheckEnv and ScEnv).
This is analogous to the instances fix we applied in #2182
4c834fdddf4d44d12039da4d6a2c63a660975b95, but done for RULES.
An important knock-on effect is that we can remove some buggy
code in LoadInterface which tried to avoid loading interfaces
that were loaded by plugins (which sometimes caused instances
and rules to NEVER become visible).
One note about tests: I renamed the old plugins07 test to T10420
and replaced plugins07 with a test to ensure that a plugin
import did not cause new rules to be loaded in.
Signed-off-by: Edward Z. Yang <ezyang@cs.stanford.edu>
Test Plan: validate
Reviewers: simonpj, austin, goldfire
Subscribers: bgamari, thomie
Differential Revision: https://phabricator.haskell.org/D950
GHC Trac Issues: #10420
Diffstat (limited to 'compiler/coreSyn/CoreSyn.hs')
-rw-r--r-- | compiler/coreSyn/CoreSyn.hs | 116 |
1 files changed, 111 insertions, 5 deletions
diff --git a/compiler/coreSyn/CoreSyn.hs b/compiler/coreSyn/CoreSyn.hs index 98400c42a3..c641d88f65 100644 --- a/compiler/coreSyn/CoreSyn.hs +++ b/compiler/coreSyn/CoreSyn.hs @@ -67,9 +67,13 @@ module CoreSyn ( -- ** Operations on annotations deAnnotate, deAnnotate', deAnnAlt, collectAnnBndrs, + -- * Orphanhood + IsOrphan(..), isOrphan, notOrphan, + -- * Core rule data types CoreRule(..), RuleBase, RuleName, RuleFun, IdUnfoldingFun, InScopeEnv, + RuleEnv(..), mkRuleEnv, emptyRuleEnv, -- ** Operations on 'CoreRule's ruleArity, ruleName, ruleIdName, ruleActivation, @@ -88,7 +92,7 @@ import Var import Type import Coercion import Name -import NameEnv( NameEnv ) +import NameEnv( NameEnv, emptyNameEnv ) import Literal import DataCon import Module @@ -99,6 +103,7 @@ import FastString import Outputable import Util import SrcLoc ( RealSrcSpan, containsSpan ) +import Binary import Data.Data hiding (TyCon) import Data.Int @@ -693,6 +698,84 @@ tickishContains t1 t2 {- ************************************************************************ * * + Orphans +* * +************************************************************************ +-} + +-- | Is this instance an orphan? If it is not an orphan, contains an 'OccName' +-- witnessing the instance's non-orphanhood. +-- See Note [Orphans] +data IsOrphan + = IsOrphan + | NotOrphan OccName -- The OccName 'n' witnesses the instance's non-orphanhood + -- In that case, the instance is fingerprinted as part + -- of the definition of 'n's definition + deriving (Data, Typeable) + +-- | Returns true if 'IsOrphan' is orphan. +isOrphan :: IsOrphan -> Bool +isOrphan IsOrphan = True +isOrphan _ = False + +-- | Returns true if 'IsOrphan' is not an orphan. +notOrphan :: IsOrphan -> Bool +notOrphan NotOrphan{} = True +notOrphan _ = False + +instance Binary IsOrphan where + put_ bh IsOrphan = putByte bh 0 + put_ bh (NotOrphan n) = do + putByte bh 1 + put_ bh n + get bh = do + h <- getByte bh + case h of + 0 -> return IsOrphan + _ -> do + n <- get bh + return $ NotOrphan n + +{- +Note [Orphans] +~~~~~~~~~~~~~~ +Class instances, rules, and family instances are divided into orphans +and non-orphans. Roughly speaking, an instance/rule is an orphan if +its left hand side mentions nothing defined in this module. Orphan-hood +has two major consequences + + * A module that contains orphans is called an "orphan module". If + the module being compiled depends (transitively) on an oprhan + module M, then M.hi is read in regardless of whether M is oherwise + needed. This is to ensure that we don't miss any instance decls in + M. But it's painful, because it means we need to keep track of all + the orphan modules below us. + + * A non-orphan is not finger-printed separately. Instead, for + fingerprinting purposes it is treated as part of the entity it + mentions on the LHS. For example + data T = T1 | T2 + instance Eq T where .... + The instance (Eq T) is incorprated as part of T's fingerprint. + + In constrast, orphans are all fingerprinted together in the + mi_orph_hash field of the ModIface. + + See MkIface.addFingerprints. + +Orphan-hood is computed + * For class instances: + when we make a ClsInst + (because it is needed during instance lookup) + + * For rules and family instances: + when we generate an IfaceRule (MkIface.coreRuleToIfaceRule) + or IfaceFamInst (MkIface.instanceToIfaceInst) +-} + +{- +************************************************************************ +* * \subsection{Transformation rules} * * ************************************************************************ @@ -706,6 +789,20 @@ type RuleBase = NameEnv [CoreRule] -- The rules are unordered; -- we sort out any overlaps on lookup +-- | 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...) +data RuleEnv + = RuleEnv { re_base :: RuleBase + , re_visible_orphs :: ModuleSet + } + +mkRuleEnv :: RuleBase -> [Module] -> RuleEnv +mkRuleEnv rules vis_orphs = RuleEnv rules (mkModuleSet vis_orphs) + +emptyRuleEnv :: RuleEnv +emptyRuleEnv = RuleEnv emptyNameEnv emptyModuleSet + -- | A 'CoreRule' is: -- -- * \"Local\" if the function it is a rule for is defined in the @@ -738,17 +835,26 @@ data CoreRule -- @False@ <=> generated at the users behest -- Main effect: reporting of orphan-hood + 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) - - -- NB: ru_local is *not* used to decide orphan-hood - -- c.g. MkIface.coreRuleToIfaceRule + -- 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 |