summaryrefslogtreecommitdiff
path: root/compiler/coreSyn/CoreSyn.hs
diff options
context:
space:
mode:
authorEdward Z. Yang <ezyang@cs.stanford.edu>2015-06-03 14:33:05 -0700
committerEdward Z. Yang <ezyang@cs.stanford.edu>2015-06-20 15:13:42 -0700
commit0cb1f5cf26fae946ca745abc5e302e62a8f66feb (patch)
tree07744de6d51cea9bde926d3ea88c1fda2b138974 /compiler/coreSyn/CoreSyn.hs
parent85d539754ac07286ef5fed714ad42451bd5a1d28 (diff)
downloadhaskell-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.hs116
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