summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/basicTypes/Avail.hs42
-rw-r--r--compiler/typecheck/TcRnExports.hs52
2 files changed, 68 insertions, 26 deletions
diff --git a/compiler/basicTypes/Avail.hs b/compiler/basicTypes/Avail.hs
index cefa934ab1..291c95abe8 100644
--- a/compiler/basicTypes/Avail.hs
+++ b/compiler/basicTypes/Avail.hs
@@ -47,27 +47,27 @@ import Data.Function
-- -----------------------------------------------------------------------------
-- The AvailInfo type
--- | Records what things are "available", i.e. in scope
-data AvailInfo = Avail Name -- ^ An ordinary identifier in scope
- | AvailTC Name
- [Name]
- [FieldLabel]
- -- ^ A type or class in scope. Parameters:
- --
- -- 1) The name of the type or class
- -- 2) The available pieces of type or class,
- -- excluding field selectors.
- -- 3) The record fields of the type
- -- (see Note [Representing fields in AvailInfo]).
- --
- -- The AvailTC Invariant:
- -- * If the type or class is itself
- -- to be in scope, it must be
- -- *first* in this list. Thus,
- -- typically: @AvailTC Eq [Eq, ==, \/=]@
- deriving( Eq, Data )
- -- Equality used when deciding if the
- -- interface has changed
+-- | Records what things are \"available\", i.e. in scope
+data AvailInfo
+
+ -- | An ordinary identifier in scope
+ = Avail Name
+
+ -- | A type or class in scope
+ --
+ -- The __AvailTC Invariant__: If the type or class is itself to be in scope,
+ -- it must be /first/ in this list. Thus, typically:
+ --
+ -- > AvailTC Eq [Eq, ==, \/=] []
+ | AvailTC
+ Name -- ^ The name of the type or class
+ [Name] -- ^ The available pieces of type or class,
+ -- excluding field selectors.
+ [FieldLabel] -- ^ The record fields of the type
+ -- (see Note [Representing fields in AvailInfo]).
+
+ deriving ( Eq -- ^ Used when deciding if the interface has changed
+ , Data )
-- | A collection of 'AvailInfo' - several things that are \"available\"
type Avails = [AvailInfo]
diff --git a/compiler/typecheck/TcRnExports.hs b/compiler/typecheck/TcRnExports.hs
index 28c1773308..b3baf6c406 100644
--- a/compiler/typecheck/TcRnExports.hs
+++ b/compiler/typecheck/TcRnExports.hs
@@ -89,6 +89,41 @@ At one point I implemented a compromise:
But the compromise seemed too much of a hack, so we backed it out.
You just have to use an explicit export list:
module M( F(..) ) where ...
+
+Note [Avails of associated data families]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose you have (Trac #16077)
+
+ {-# LANGUAGE TypeFamilies #-}
+ module A (module A) where
+
+ class C a where { data T a }
+ instance C () where { data T () = D }
+
+Because @A@ is exported explicitly, GHC tries to produce an export list
+from the @GlobalRdrEnv@. In this case, it pulls out the following:
+
+ [ C defined at A.hs:4:1
+ , T parent:C defined at A.hs:4:23
+ , D parent:T defined at A.hs:5:35 ]
+
+If map these directly into avails, (via 'availFromGRE'), we get
+@[C{C;}, C{T;}, T{D;}]@, which eventually gets merged into @[C{C, T;}, T{D;}]@.
+That's not right, because @T{D;}@ violates the AvailTC invariant: @T@ is
+exported, but it isn't the first entry in the avail!
+
+We work around this issue by expanding GREs where the parent and child
+are both type constructors into two GRES.
+
+ T parent:C defined at A.hs:4:23
+
+ =>
+
+ [ T parent:C defined at A.hs:4:23
+ , T defined at A.hs:4:23 ]
+
+Then, we get @[C{C;}, C{T;}, T{T;}, T{D;}]@, which eventually gets merged
+into @[C{C, T;}, T{T, D;}]@ (which satsifies the AvailTC invariant).
-}
data ExportAccum -- The type of the accumulating parameter of
@@ -175,12 +210,12 @@ tcRnExports explicit_mod exports
; return new_tcg_env }
exports_from_avail :: Maybe (Located [LIE GhcPs])
- -- Nothing => no explicit export list
+ -- ^ 'Nothing' means no explicit export list
-> GlobalRdrEnv
-> ImportAvails
- -- Imported modules; this is used to test if a
- -- 'module Foo' export is valid (it's not valid
- -- if we didn't import Foo!)
+ -- ^ Imported modules; this is used to test if a
+ -- @module Foo@ export is valid (it's not valid
+ -- if we didn't import @Foo@!)
-> Module
-> RnM (Maybe [(LIE GhcRn, Avails)], Avails)
-- (Nothing, _) <=> no explicit export list
@@ -230,6 +265,11 @@ exports_from_avail (Just (dL->L _ rdr_items)) rdr_env imports this_mod
kids_env :: NameEnv [GlobalRdrElt]
kids_env = mkChildEnv (globalRdrEnvElts rdr_env)
+ -- See Note [Avails of associated data families]
+ expand_tyty_gre :: GlobalRdrElt -> [GlobalRdrElt]
+ expand_tyty_gre (gre @ GRE { gre_name = me, gre_par = ParentIs p })
+ | isTyConName p, isTyConName me = [gre, gre{ gre_par = NoParent }]
+ expand_tyty_gre gre = [gre]
imported_modules = [ imv_name imv
| xs <- moduleEnvElts $ imp_mods imports
@@ -248,7 +288,9 @@ exports_from_avail (Just (dL->L _ rdr_items)) rdr_env imports this_mod
= do { let { exportValid = (mod `elem` imported_modules)
|| (moduleName this_mod == mod)
; gre_prs = pickGREsModExp mod (globalRdrEnvElts rdr_env)
- ; new_exports = map (availFromGRE . fst) gre_prs
+ ; new_exports = [ availFromGRE gre'
+ | (gre, _) <- gre_prs
+ , gre' <- expand_tyty_gre gre ]
; all_gres = foldr (\(gre1,gre2) gres -> gre1 : gre2 : gres) [] gre_prs
; mods = addOneToUniqSet earlier_mods mod
}