summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsheaf <sam.derbyshire@gmail.com>2023-03-23 18:35:20 +0100
committersheaf <sam.derbyshire@gmail.com>2023-03-29 13:57:33 +0200
commit4f1940f05a9c0353ed12afe12a547a01878abd15 (patch)
treed5f6f6920232f7aa13e23abbfffe57c40efe7636
parent3f374399e2dbebcdfe5bc31f94fc502b46d0cf4f (diff)
downloadhaskell-4f1940f05a9c0353ed12afe12a547a01878abd15.tar.gz
Avoid repeatedly shadowing in shadowNames
This commit refactors GHC.Type.Name.Reader.shadowNames to first accumulate all the shadowing arising from the introduction of a new set of GREs, and then applies all the shadowing to the old GlobalRdrEnv in one go.
-rw-r--r--compiler/GHC/Types/Name/Reader.hs189
-rw-r--r--compiler/GHC/Types/Unique/FM.hs5
2 files changed, 154 insertions, 40 deletions
diff --git a/compiler/GHC/Types/Name/Reader.hs b/compiler/GHC/Types/Name/Reader.hs
index 4b05eedb39..dd7a612d22 100644
--- a/compiler/GHC/Types/Name/Reader.hs
+++ b/compiler/GHC/Types/Name/Reader.hs
@@ -112,8 +112,10 @@ import GHC.Types.Name.Env
( NameEnv, nonDetNameEnvElts, emptyNameEnv, extendNameEnv_Acc )
import GHC.Types.Name.Set
import GHC.Types.SrcLoc as SrcLoc
+import GHC.Types.Unique
import GHC.Types.Unique.FM
import GHC.Types.Unique.Set
+import GHC.Builtin.Uniques ( isFldNSUnique )
import GHC.Unit.Module
@@ -1413,29 +1415,42 @@ Wrinkle [Shadowing namespaces]
shadowNames :: GlobalRdrEnv -> GlobalRdrEnv -> GlobalRdrEnv
-- Remove certain old GREs that share the same OccName as this new Name.
-- See Note [GlobalRdrEnv shadowing] for details
-shadowNames env new_gres =
- minusOccEnv_C_Ns (nonDetStrictFoldUFM shadow_many) env new_gres
+shadowNames env new_gres = minusOccEnv_C_Ns do_shadowing env new_gres
where
- shadow_many :: [GlobalRdrElt]
- -> UniqFM NameSpace [GlobalRdrElt]
- -> UniqFM NameSpace [GlobalRdrElt]
- shadow_many news olds_map =
- ( `mapMaybeUFM` olds_map ) $ \ olds ->
- case foldl' shadow_one olds news of
- res | null res
- -> Nothing
- | otherwise
- -> Just res
-
- shadow_one :: [GlobalRdrElt] -> GlobalRdrElt -> [GlobalRdrElt]
- shadow_one olds new =
- ( `mapMaybe` olds ) $ \ old ->
- if new `greClashesWith` old
- then shadow old
- else Just old
-
- shadow :: GlobalRdrElt -> Maybe GlobalRdrElt
+ do_shadowing :: UniqFM NameSpace [GlobalRdrElt]
+ -> UniqFM NameSpace [GlobalRdrElt]
+ -> UniqFM NameSpace [GlobalRdrElt]
+ do_shadowing olds news =
+ -- Start off by accumulating all 'NameSpace's shadowed
+ -- by the entire collection of new GREs.
+ let shadowed_gres :: ShadowedGREs
+ shadowed_gres =
+ nonDetFoldUFM (\ gres shads -> foldMap greShadowedNameSpaces gres S.<> shads)
+ mempty news
+
+ -- Then shadow the old 'GlobalRdrElt's, now that we know which 'NameSpace's
+ -- should be shadowed.
+ shadow_list :: Unique -> [GlobalRdrElt] -> Maybe [GlobalRdrElt]
+ shadow_list old_ns old_gres =
+ case namespace_is_shadowed old_ns shadowed_gres of
+ IsNotShadowed -> Just old_gres
+ IsShadowed -> guard_nonEmpty $ mapMaybe shadow old_gres
+ IsShadowedIfFieldSelector ->
+ guard_nonEmpty $
+ mapMaybe (\ old_gre -> if isFieldSelectorGRE old_gre then shadow old_gre else Just old_gre)
+ old_gres
+
+ -- Now do all of the shadowing in a single go. This avoids traversing
+ -- the old GlobalRdrEnv multiple times over.
+ in mapMaybeWithKeyUFM shadow_list olds
+
+ guard_nonEmpty :: [a] -> Maybe [a]
+ guard_nonEmpty xs | null xs = Nothing
+ | otherwise = Just xs
+
+ -- Shadow a single GRE, by either qualifying it or removing it entirely.
+ shadow :: GlobalRdrElt-> Maybe GlobalRdrElt
shadow old_gre@(GRE { gre_lcl = lcl, gre_imp = iss }) =
case greDefinitionModule old_gre of
Nothing -> Just old_gre -- Old name is Internal; do not shadow
@@ -1463,29 +1478,125 @@ shadowNames env new_gres =
set_qual :: ImportSpec -> ImportSpec
set_qual is = is { is_decl = (is_decl is) { is_qual = True } }
-
--- | @greClashesWith gre old_gre@ computes whether @gre@ clashes with @old_gre@
--- (assuming they both have the same underlying 'occNameFS').
+-- | @greClashesWith new_gre old_gre@ computes whether @new_gre@ clashes
+-- with @old_gre@ (assuming they both have the same underlying 'occNameFS').
greClashesWith :: GlobalRdrElt -> (GlobalRdrElt -> Bool)
-greClashesWith gre old_gre
- | ns == old_ns
- = True
+greClashesWith new_gre old_gre =
+ old_gre `greIsShadowed` greShadowedNameSpaces new_gre
+
+-- | Is the given 'GlobalRdrElt' shadowed, as specified by the 'ShadowedNameSpace's?
+greIsShadowed :: GlobalRdrElt -> ShadowedGREs -> Bool
+greIsShadowed old_gre shadowed =
+ case getUnique old_ns `namespace_is_shadowed` shadowed of
+ IsShadowed -> True
+ IsNotShadowed -> False
+ IsShadowedIfFieldSelector -> isFieldSelectorGRE old_gre
+ where
+ old_ns = occNameSpace $ greOccName old_gre
+
- -- A new variable shadows record fields with field selectors.
- | ns == varName
- = isFieldSelectorGRE old_gre
+-- | Whether a 'GlobalRdrElt' is definitely shadowed, definitely not shadowed,
+-- or conditionally shadowed based on more information beyond the 'NameSpace'.
+data IsShadowed
+ = IsNotShadowed
+ | IsShadowed
+ | IsShadowedIfFieldSelector
- -- A new record field...
- | isFieldNameSpace ns
- -- ... shadows variables if it defines a field selector.
- = ( old_ns == varName && isFieldSelectorGRE gre )
- -- ... shadows record fields unless it is a duplicate record field.
- || ( isFieldNameSpace old_ns && not (isDuplicateRecFldGRE gre) )
+-- | Internal function: is a 'GlobalRdrElt' with the 'NameSpace' with given
+-- 'Unique' shadowed by the specified 'ShadowedGREs'?
+--
+-- - @Just b@ means: definitely @b@.
+-- - @Nothing@ means: the GRE is shadowed iff it is a record field GRE
+-- with FieldSelectors enabled.
+namespace_is_shadowed :: Unique -> ShadowedGREs -> IsShadowed
+namespace_is_shadowed old_ns (ShadowedGREs shadowed_nonflds shadowed_flds)
+ | isFldNSUnique old_ns
+ = case shadowed_flds of
+ ShadowAllFieldGREs -> IsShadowed
+ ShadowFieldSelectorsAnd shadowed
+ | old_ns `elemUniqSet_Directly` shadowed
+ -> IsShadowed
+ | otherwise
+ -> IsShadowedIfFieldSelector
+ ShadowFieldNameSpaces shadowed
+ | old_ns `elemUniqSet_Directly` shadowed
+ -> IsShadowed
+ | otherwise
+ -> IsNotShadowed
+ | old_ns `elemUniqSet_Directly` shadowed_nonflds
+ = IsShadowed
| otherwise
- = False
+ = IsNotShadowed
+
+-- | What are all the 'GlobalRdrElt's that are shadowed by this new 'GlobalRdrElt'?
+greShadowedNameSpaces :: GlobalRdrElt -> ShadowedGREs
+greShadowedNameSpaces gre = ShadowedGREs shadowed_nonflds shadowed_flds
where
- ns = occNameSpace $ greOccName gre
- old_ns = occNameSpace $ greOccName old_gre
+ ns = occNameSpace $ greOccName gre
+ !shadowed_nonflds
+ | isFieldNameSpace ns
+ -- A new record field shadows variables if it defines a field selector.
+ = if isFieldSelectorGRE gre
+ then unitUniqSet varName
+ else emptyUniqSet
+ | otherwise
+ = unitUniqSet ns
+ !shadowed_flds
+ | ns == varName
+ -- A new variable shadows record fields with field selectors.
+ = ShadowFieldSelectorsAnd emptyUniqSet
+ | isFieldNameSpace ns
+ -- A new record field shadows record fields unless it is a duplicate record field.
+ = if isDuplicateRecFldGRE gre
+ then ShadowFieldNameSpaces (unitUniqSet ns)
+ -- NB: we must still shadow fields with the same constructor name.
+ else ShadowAllFieldGREs
+ | otherwise
+ = ShadowFieldNameSpaces emptyUniqSet
+
+-- | A description of which 'GlobalRdrElt's are shadowed.
+data ShadowedGREs
+ = ShadowedGREs
+ { shadowedNonFieldNameSpaces :: !(UniqSet NameSpace)
+ -- ^ These specific non-field 'NameSpace's are shadowed.
+ , shadowedFieldGREs :: !ShadowedFieldGREs
+ -- ^ These field 'GlobalRdrElt's are shadowed.
+ }
+
+-- | A description of which record field 'GlobalRdrElt's are shadowed.
+data ShadowedFieldGREs
+ -- | All field 'GlobalRdrElt's are shadowed.
+ = ShadowAllFieldGREs
+ -- | Record field GREs defining field selectors, as well as those
+ -- with the explicitly specified field 'NameSpace's, are shadowed.
+ | ShadowFieldSelectorsAnd { shadowedFieldNameSpaces :: !(UniqSet NameSpace) }
+ -- | These specific field 'NameSpace's are shadowed.
+ | ShadowFieldNameSpaces { shadowedFieldNameSpaces :: !(UniqSet NameSpace) }
+
+instance Monoid ShadowedFieldGREs where
+ mempty = ShadowFieldNameSpaces { shadowedFieldNameSpaces = emptyUniqSet }
+
+instance Semigroup ShadowedFieldGREs where
+ ShadowAllFieldGREs <> _ = ShadowAllFieldGREs
+ _ <> ShadowAllFieldGREs = ShadowAllFieldGREs
+ ShadowFieldSelectorsAnd ns1 <> ShadowFieldSelectorsAnd ns2 =
+ ShadowFieldSelectorsAnd (ns1 S.<> ns2)
+ ShadowFieldSelectorsAnd ns1 <> ShadowFieldNameSpaces ns2 =
+ ShadowFieldSelectorsAnd (ns1 S.<> ns2)
+ ShadowFieldNameSpaces ns1 <> ShadowFieldSelectorsAnd ns2 =
+ ShadowFieldSelectorsAnd (ns1 S.<> ns2)
+ ShadowFieldNameSpaces ns1 <> ShadowFieldNameSpaces ns2 =
+ ShadowFieldNameSpaces (ns1 S.<> ns2)
+
+instance Monoid ShadowedGREs where
+ mempty =
+ ShadowedGREs
+ { shadowedNonFieldNameSpaces = emptyUniqSet
+ , shadowedFieldGREs = mempty }
+
+instance Semigroup ShadowedGREs where
+ ShadowedGREs nonflds1 flds1 <> ShadowedGREs nonflds2 flds2 =
+ ShadowedGREs (nonflds1 S.<> nonflds2) (flds1 S.<> flds2)
{-
************************************************************************
diff --git a/compiler/GHC/Types/Unique/FM.hs b/compiler/GHC/Types/Unique/FM.hs
index bdd14156dd..2da9aad3cb 100644
--- a/compiler/GHC/Types/Unique/FM.hs
+++ b/compiler/GHC/Types/Unique/FM.hs
@@ -69,7 +69,7 @@ module GHC.Types.Unique.FM (
nonDetStrictFoldUFM_Directly,
anyUFM, allUFM, seqEltsUFM,
mapUFM, mapUFM_Directly, strictMapUFM,
- mapMaybeUFM,
+ mapMaybeUFM, mapMaybeWithKeyUFM,
elemUFM, elemUFM_Directly,
filterUFM, filterUFM_Directly, partitionUFM,
sizeUFM,
@@ -383,6 +383,9 @@ mapUFM f (UFM m) = UFM (M.map f m)
mapMaybeUFM :: (elt1 -> Maybe elt2) -> UniqFM key elt1 -> UniqFM key elt2
mapMaybeUFM f (UFM m) = UFM (M.mapMaybe f m)
+mapMaybeWithKeyUFM :: (Unique -> elt1 -> Maybe elt2) -> UniqFM key elt1 -> UniqFM key elt2
+mapMaybeWithKeyUFM f (UFM m) = UFM (M.mapMaybeWithKey (f . getUnique) m)
+
mapUFM_Directly :: (Unique -> elt1 -> elt2) -> UniqFM key elt1 -> UniqFM key elt2
mapUFM_Directly f (UFM m) = UFM (M.mapWithKey (f . getUnique) m)