summaryrefslogtreecommitdiff
path: root/compiler/GHC/Hs
diff options
context:
space:
mode:
authorsheaf <sam.derbyshire@gmail.com>2023-03-17 12:48:21 +0100
committersheaf <sam.derbyshire@gmail.com>2023-03-29 13:57:33 +0200
commit3f374399e2dbebcdfe5bc31f94fc502b46d0cf4f (patch)
treea5103e3d597c2d724173e070a22759ce50a9d2e7 /compiler/GHC/Hs
parent76bb4c586084d7fdcf0e5ce52623abbfca527c55 (diff)
downloadhaskell-3f374399e2dbebcdfe5bc31f94fc502b46d0cf4f.tar.gz
Handle records in the renamer
This patch moves the field-based logic for disambiguating record updates to the renamer. The type-directed logic, scheduled for removal, remains in the typechecker. To do this properly (and fix the myriad of bugs surrounding the treatment of duplicate record fields), we took the following main steps: 1. Create GREInfo, a renamer-level equivalent to TyThing which stores information pertinent to the renamer. This allows us to uniformly treat imported and local Names in the renamer, as described in Note [GREInfo]. 2. Remove GreName. Instead of a GlobalRdrElt storing GreNames, which distinguished between normal names and field names, we now store simple Names in GlobalRdrElt, along with the new GREInfo information which allows us to recover the FieldLabel for record fields. 3. Add namespacing for record fields, within the OccNames themselves. This allows us to remove the mangling of duplicate field selectors. This change ensures we don't print mangled names to the user in error messages, and allows us to handle duplicate record fields in Template Haskell. 4. Move record disambiguation to the renamer, and operate on the level of data constructors instead, to handle #21443. The error message text for ambiguous record updates has also been changed to reflect that type-directed disambiguation is on the way out. (3) means that OccEnv is now a bit more complex: we first key on the textual name, which gives an inner map keyed on NameSpace: OccEnv a ~ FastStringEnv (UniqFM NameSpace a) Note that this change, along with (2), both increase the memory residency of GlobalRdrEnv = OccEnv [GlobalRdrElt], which causes a few tests to regress somewhat in compile-time allocation. Even though (3) simplified a lot of code (in particular the treatment of field selectors within Template Haskell and in error messages), it came with one important wrinkle: in the situation of -- M.hs-boot module M where { data A; foo :: A -> Int } -- M.hs module M where { data A = MkA { foo :: Int } } we have that M.hs-boot exports a variable foo, which is supposed to match with the record field foo that M exports. To solve this issue, we add a new impedance-matching binding to M foo{var} = foo{fld} This mimics the logic that existed already for impedance-binding DFunIds, but getting it right was a bit tricky. See Note [Record field impedance matching] in GHC.Tc.Module. We also needed to be careful to avoid introducing space leaks in GHCi. So we dehydrate the GlobalRdrEnv before storing it anywhere, e.g. in ModIface. This means stubbing out all the GREInfo fields, with the function forceGlobalRdrEnv. When we read it back in, we rehydrate with rehydrateGlobalRdrEnv. This robustly avoids any space leaks caused by retaining old type environments. Fixes #13352 #14848 #17381 #17551 #19664 #21443 #21444 #21720 #21898 #21946 #21959 #22125 #22160 #23010 #23062 #23063 Updates haddock submodule ------------------------- Metric Increase: MultiComponentModules MultiLayerModules MultiLayerModulesDefsGhci MultiLayerModulesNoCode T13701 T14697 hard_hole_fits -------------------------
Diffstat (limited to 'compiler/GHC/Hs')
-rw-r--r--compiler/GHC/Hs/Decls.hs5
-rw-r--r--compiler/GHC/Hs/Expr.hs36
-rw-r--r--compiler/GHC/Hs/ImpExp.hs60
-rw-r--r--compiler/GHC/Hs/Instances.hs8
-rw-r--r--compiler/GHC/Hs/Pat.hs4
-rw-r--r--compiler/GHC/Hs/Type.hs20
-rw-r--r--compiler/GHC/Hs/Utils.hs250
7 files changed, 251 insertions, 132 deletions
diff --git a/compiler/GHC/Hs/Decls.hs b/compiler/GHC/Hs/Decls.hs
index fa62c6a49c..201adc5467 100644
--- a/compiler/GHC/Hs/Decls.hs
+++ b/compiler/GHC/Hs/Decls.hs
@@ -124,7 +124,6 @@ import GHC.Utils.Panic
import GHC.Types.SrcLoc
import GHC.Types.SourceText
import GHC.Core.Type
-import GHC.Core.TyCon (TyConFlavour(NewtypeFlavour,DataTypeFlavour))
import GHC.Types.ForeignCall
import GHC.Unit.Module.Warnings (WarningTxt(..))
@@ -704,7 +703,7 @@ ppDataDefnHeader pp_hdr HsDataDefn
| isTypeDataDefnCons condecls = text "type"
| otherwise = empty
pp_ct = case mb_ct of
- Nothing -> empty
+ Nothing -> empty
Just ct -> ppr ct
pp_sig = case mb_sig of
Nothing -> empty
@@ -935,7 +934,7 @@ instDeclDataFamInsts inst_decls
do_one (L _ (TyFamInstD {})) = []
-- | Convert a 'NewOrData' to a 'TyConFlavour'
-newOrDataToFlavour :: NewOrData -> TyConFlavour
+newOrDataToFlavour :: NewOrData -> TyConFlavour tc
newOrDataToFlavour NewType = NewtypeFlavour
newOrDataToFlavour DataType = DataTypeFlavour
diff --git a/compiler/GHC/Hs/Expr.hs b/compiler/GHC/Hs/Expr.hs
index 621848920d..4a8abe8404 100644
--- a/compiler/GHC/Hs/Expr.hs
+++ b/compiler/GHC/Hs/Expr.hs
@@ -44,6 +44,7 @@ import GHC.Parser.Annotation
-- others:
import GHC.Tc.Types.Evidence
+import GHC.Types.Id.Info ( RecSelParent )
import GHC.Types.Name
import GHC.Types.Name.Reader
import GHC.Types.Name.Set
@@ -52,7 +53,8 @@ import GHC.Types.Fixity
import GHC.Types.SourceText
import GHC.Types.SrcLoc
import GHC.Types.Tickish (CoreTickish)
-import GHC.Core.ConLike
+import GHC.Types.Unique.Set (UniqSet)
+import GHC.Core.ConLike ( conLikeName, ConLike )
import GHC.Unit.Module (ModuleName)
import GHC.Utils.Misc
import GHC.Utils.Outputable
@@ -74,6 +76,7 @@ import qualified Data.Kind
import Data.Maybe (isJust)
import Data.Foldable ( toList )
import Data.List (uncons)
+import Data.List.NonEmpty (NonEmpty)
import Data.Bifunctor (first)
{- *********************************************************************
@@ -324,6 +327,31 @@ type instance XRecordUpd GhcTc = DataConCantHappen
-- See [Handling overloaded and rebindable constructs],
-- and [Record Updates] in GHC.Tc.Gen.Expr.
+-- | Information about the parent of a record update:
+--
+-- - the parent type constructor or pattern synonym,
+-- - the relevant con-likes,
+-- - the field labels.
+data family HsRecUpdParent x
+
+data instance HsRecUpdParent GhcPs
+data instance HsRecUpdParent GhcRn
+ = RnRecUpdParent
+ { rnRecUpdLabels :: NonEmpty FieldGlobalRdrElt
+ , rnRecUpdCons :: UniqSet ConLikeName }
+data instance HsRecUpdParent GhcTc
+ = TcRecUpdParent
+ { tcRecUpdParent :: RecSelParent
+ , tcRecUpdLabels :: NonEmpty FieldGlobalRdrElt
+ , tcRecUpdCons :: UniqSet ConLike }
+
+type instance XLHsRecUpdLabels GhcPs = NoExtField
+type instance XLHsRecUpdLabels GhcRn = NonEmpty (HsRecUpdParent GhcRn)
+ -- Possible parents for the record update.
+type instance XLHsRecUpdLabels GhcTc = DataConCantHappen
+
+type instance XLHsOLRecUpdLabels p = NoExtField
+
type instance XGetField GhcPs = EpAnnCO
type instance XGetField GhcRn = NoExtField
type instance XGetField GhcTc = DataConCantHappen
@@ -625,8 +653,10 @@ ppr_expr (RecordCon { rcon_con = con, rcon_flds = rbinds })
ppr_expr (RecordUpd { rupd_expr = L _ aexp, rupd_flds = flds })
= case flds of
- Left rbinds -> hang (ppr aexp) 2 (braces (fsep (punctuate comma (map ppr rbinds))))
- Right pbinds -> hang (ppr aexp) 2 (braces (fsep (punctuate comma (map ppr pbinds))))
+ RegularRecUpdFields { recUpdFields= rbinds } ->
+ hang (ppr aexp) 2 (braces (fsep (punctuate comma (map ppr rbinds))))
+ OverloadedRecUpdFields { olRecUpdFields = pbinds } ->
+ hang (ppr aexp) 2 (braces (fsep (punctuate comma (map ppr pbinds))))
ppr_expr (HsGetField { gf_expr = L _ fexp, gf_field = field })
= ppr fexp <> dot <> ppr field
diff --git a/compiler/GHC/Hs/ImpExp.hs b/compiler/GHC/Hs/ImpExp.hs
index c25a72c079..83f5cfbb88 100644
--- a/compiler/GHC/Hs/ImpExp.hs
+++ b/compiler/GHC/Hs/ImpExp.hs
@@ -21,25 +21,26 @@ module GHC.Hs.ImpExp
, module GHC.Hs.ImpExp
) where
+import Language.Haskell.Syntax.Extension
+import Language.Haskell.Syntax.Module.Name
+import Language.Haskell.Syntax.ImpExp
+
import GHC.Prelude
import GHC.Types.SourceText ( SourceText(..) )
-import GHC.Types.FieldLabel ( FieldLabel )
-
-import GHC.Utils.Outputable
-import GHC.Utils.Panic
import GHC.Types.SrcLoc
-import GHC.Parser.Annotation
-import GHC.Hs.Extension
import GHC.Types.Name
import GHC.Types.PkgQual
+import GHC.Parser.Annotation
+import GHC.Hs.Extension
+
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+
import Data.Data
import Data.Maybe
-import Language.Haskell.Syntax.Extension
-import Language.Haskell.Syntax.Module.Name
-import Language.Haskell.Syntax.ImpExp
{-
************************************************************************
@@ -203,11 +204,7 @@ type instance XIEVar GhcTc = NoExtField
type instance XIEThingAbs (GhcPass _) = EpAnn [AddEpAnn]
type instance XIEThingAll (GhcPass _) = EpAnn [AddEpAnn]
-
--- See Note [IEThingWith]
-type instance XIEThingWith (GhcPass 'Parsed) = EpAnn [AddEpAnn]
-type instance XIEThingWith (GhcPass 'Renamed) = [Located FieldLabel]
-type instance XIEThingWith (GhcPass 'Typechecked) = NoExtField
+type instance XIEThingWith (GhcPass _) = EpAnn [AddEpAnn]
type instance XIEModuleContents GhcPs = EpAnn [AddEpAnn]
type instance XIEModuleContents GhcRn = NoExtField
@@ -220,32 +217,6 @@ type instance XXIE (GhcPass _) = DataConCantHappen
type instance Anno (LocatedA (IE (GhcPass p))) = SrcSpanAnnA
-{-
-Note [IEThingWith]
-~~~~~~~~~~~~~~~~~~
-A definition like
-
- {-# LANGUAGE DuplicateRecordFields #-}
- module M ( T(MkT, x) ) where
- data T = MkT { x :: Int }
-
-gives rise to this in the output of the parser:
-
- IEThingWith NoExtField T [MkT, x] NoIEWildcard
-
-But in the renamer we need to attach the correct field label,
-because the selector Name is mangled (see Note [FieldLabel] in
-GHC.Types.FieldLabel). Hence we change this to:
-
- IEThingWith [FieldLabel "x" True $sel:x:MkT)] T [MkT] NoIEWildcard
-
-using the TTG extension field to store the list of fields in renamed syntax
-only. (Record fields always appear in this list, regardless of whether
-DuplicateRecordFields was in use at the definition site or not.)
-
-See Note [Representing fields in AvailInfo] in GHC.Types.Avail for more details.
--}
-
ieName :: IE (GhcPass p) -> IdP (GhcPass p)
ieName (IEVar _ (L _ n)) = ieWrappedName n
ieName (IEThingAbs _ (L _ n)) = ieWrappedName n
@@ -292,9 +263,8 @@ instance OutputableBndrId p => Outputable (IE (GhcPass p)) where
ppr (IEVar _ var) = ppr (unLoc var)
ppr (IEThingAbs _ thing) = ppr (unLoc thing)
ppr (IEThingAll _ thing) = hcat [ppr (unLoc thing), text "(..)"]
- ppr (IEThingWith flds thing wc withs)
- = ppr (unLoc thing) <> parens (fsep (punctuate comma
- (ppWiths ++ ppFields) ))
+ ppr (IEThingWith _ thing wc withs)
+ = ppr (unLoc thing) <> parens (fsep (punctuate comma ppWiths))
where
ppWiths =
case wc of
@@ -303,10 +273,6 @@ instance OutputableBndrId p => Outputable (IE (GhcPass p)) where
IEWildcard pos ->
let (bs, as) = splitAt pos (map (ppr . unLoc) withs)
in bs ++ [text ".."] ++ as
- ppFields =
- case ghcPass @p of
- GhcRn -> map ppr flds
- _ -> []
ppr (IEModuleContents _ mod')
= text "module" <+> ppr mod'
ppr (IEGroup _ n _) = text ("<IEGroup: " ++ show n ++ ">")
diff --git a/compiler/GHC/Hs/Instances.hs b/compiler/GHC/Hs/Instances.hs
index 74d75fb7be..5c8e403bb3 100644
--- a/compiler/GHC/Hs/Instances.hs
+++ b/compiler/GHC/Hs/Instances.hs
@@ -287,6 +287,14 @@ deriving instance Data (FieldLabelStrings GhcPs)
deriving instance Data (FieldLabelStrings GhcRn)
deriving instance Data (FieldLabelStrings GhcTc)
+deriving instance Data (HsRecUpdParent GhcPs)
+deriving instance Data (HsRecUpdParent GhcRn)
+deriving instance Data (HsRecUpdParent GhcTc)
+
+deriving instance Data (LHsRecUpdFields GhcPs)
+deriving instance Data (LHsRecUpdFields GhcRn)
+deriving instance Data (LHsRecUpdFields GhcTc)
+
deriving instance Data (DotFieldOcc GhcPs)
deriving instance Data (DotFieldOcc GhcRn)
deriving instance Data (DotFieldOcc GhcTc)
diff --git a/compiler/GHC/Hs/Pat.hs b/compiler/GHC/Hs/Pat.hs
index bc0b51457e..2591efc732 100644
--- a/compiler/GHC/Hs/Pat.hs
+++ b/compiler/GHC/Hs/Pat.hs
@@ -244,8 +244,8 @@ data ConPatTc
hsRecFieldId :: HsRecField GhcTc arg -> Id
hsRecFieldId = hsRecFieldSel
-hsRecUpdFieldRdr :: HsRecUpdField (GhcPass p) -> Located RdrName
-hsRecUpdFieldRdr = fmap rdrNameAmbiguousFieldOcc . reLoc . hfbLHS
+hsRecUpdFieldRdr :: HsRecUpdField (GhcPass p) q -> Located RdrName
+hsRecUpdFieldRdr = fmap ambiguousFieldOccRdrName . reLoc . hfbLHS
hsRecUpdFieldId :: HsFieldBind (LAmbiguousFieldOcc GhcTc) arg -> Located Id
hsRecUpdFieldId = fmap foExt . reLoc . hsRecUpdFieldOcc
diff --git a/compiler/GHC/Hs/Type.hs b/compiler/GHC/Hs/Type.hs
index 313b8e8fe2..968fc99b73 100644
--- a/compiler/GHC/Hs/Type.hs
+++ b/compiler/GHC/Hs/Type.hs
@@ -56,7 +56,8 @@ module GHC.Hs.Type (
FieldOcc(..), LFieldOcc, mkFieldOcc,
AmbiguousFieldOcc(..), LAmbiguousFieldOcc, mkAmbiguousFieldOcc,
- rdrNameAmbiguousFieldOcc, selectorAmbiguousFieldOcc,
+ ambiguousFieldOccRdrName, ambiguousFieldOccLRdrName,
+ selectorAmbiguousFieldOcc,
unambiguousFieldOcc, ambiguousFieldOcc,
mkAnonWildCardTy, pprAnonWildCard,
@@ -104,7 +105,7 @@ import GHC.Parser.Annotation
import GHC.Types.Fixity ( LexicalFixity(..) )
import GHC.Types.Id ( Id )
import GHC.Types.SourceText
-import GHC.Types.Name( Name, NamedThing(getName), tcName, dataName )
+import GHC.Types.Name
import GHC.Types.Name.Reader ( RdrName )
import GHC.Types.Var ( VarBndr, visArgTypeLike )
import GHC.Core.TyCo.Rep ( Type(..) )
@@ -915,11 +916,11 @@ type instance XAmbiguous GhcTc = Id
type instance XXAmbiguousFieldOcc (GhcPass _) = DataConCantHappen
instance Outputable (AmbiguousFieldOcc (GhcPass p)) where
- ppr = ppr . rdrNameAmbiguousFieldOcc
+ ppr = ppr . ambiguousFieldOccRdrName
instance OutputableBndr (AmbiguousFieldOcc (GhcPass p)) where
- pprInfixOcc = pprInfixOcc . rdrNameAmbiguousFieldOcc
- pprPrefixOcc = pprPrefixOcc . rdrNameAmbiguousFieldOcc
+ pprInfixOcc = pprInfixOcc . ambiguousFieldOccRdrName
+ pprPrefixOcc = pprPrefixOcc . ambiguousFieldOccRdrName
instance OutputableBndr (Located (AmbiguousFieldOcc (GhcPass p))) where
pprInfixOcc = pprInfixOcc . unLoc
@@ -928,9 +929,12 @@ instance OutputableBndr (Located (AmbiguousFieldOcc (GhcPass p))) where
mkAmbiguousFieldOcc :: LocatedN RdrName -> AmbiguousFieldOcc GhcPs
mkAmbiguousFieldOcc rdr = Unambiguous noExtField rdr
-rdrNameAmbiguousFieldOcc :: AmbiguousFieldOcc (GhcPass p) -> RdrName
-rdrNameAmbiguousFieldOcc (Unambiguous _ (L _ rdr)) = rdr
-rdrNameAmbiguousFieldOcc (Ambiguous _ (L _ rdr)) = rdr
+ambiguousFieldOccRdrName :: AmbiguousFieldOcc (GhcPass p) -> RdrName
+ambiguousFieldOccRdrName = unLoc . ambiguousFieldOccLRdrName
+
+ambiguousFieldOccLRdrName :: AmbiguousFieldOcc (GhcPass p) -> LocatedN RdrName
+ambiguousFieldOccLRdrName (Unambiguous _ rdr) = rdr
+ambiguousFieldOccLRdrName (Ambiguous _ rdr) = rdr
selectorAmbiguousFieldOcc :: AmbiguousFieldOcc GhcTc -> Id
selectorAmbiguousFieldOcc (Unambiguous sel _) = sel
diff --git a/compiler/GHC/Hs/Utils.hs b/compiler/GHC/Hs/Utils.hs
index 5866243824..008469b458 100644
--- a/compiler/GHC/Hs/Utils.hs
+++ b/compiler/GHC/Hs/Utils.hs
@@ -1,4 +1,6 @@
{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE TupleSections #-}
+
{-|
Module : GHC.Hs.Utils
Description : Generic helpers for the HsSyn type.
@@ -97,6 +99,7 @@ module GHC.Hs.Utils(
collectLStmtBinders, collectStmtBinders,
CollectPass(..), CollectFlag(..),
+ TyDeclBinders(..), LConsWithFields(..),
hsLTyClDeclBinders, hsTyClForeignBinders,
hsPatSynSelectors, getPatSynBinds,
hsForeignDeclsBinders, hsGroupBinders, hsDataFamInstBinders,
@@ -113,6 +116,7 @@ import GHC.Hs.Expr
import GHC.Hs.Pat
import GHC.Hs.Type
import GHC.Hs.Lit
+import Language.Haskell.Syntax.Decls
import Language.Haskell.Syntax.Extension
import GHC.Hs.Extension
import GHC.Parser.Annotation
@@ -146,13 +150,18 @@ import GHC.Utils.Misc
import GHC.Utils.Outputable
import GHC.Utils.Panic
-import Data.Either
+import Control.Arrow ( first )
+import Data.Either ( partitionEithers )
import Data.Foldable ( toList )
-import Data.Function
-import Data.List ( partition, deleteBy )
+import Data.List ( partition )
import Data.List.NonEmpty ( nonEmpty )
import qualified Data.List.NonEmpty as NE
+import Data.IntMap ( IntMap )
+import qualified Data.IntMap.Strict as IntMap
+import Data.Map ( Map )
+import qualified Data.Map.Strict as Map
+
{-
************************************************************************
* *
@@ -1356,17 +1365,31 @@ hsTyClForeignBinders :: [TyClGroup GhcRn]
hsTyClForeignBinders tycl_decls foreign_decls
= map unLoc (hsForeignDeclsBinders foreign_decls)
++ getSelectorNames
- (foldMap (foldMap hsLTyClDeclBinders . group_tyclds) tycl_decls
+ (foldMap (foldMap (tyDeclBinders . hsLTyClDeclBinders) . group_tyclds) tycl_decls
`mappend`
- foldMap (foldMap hsLInstDeclBinders . group_instds) tycl_decls)
+ (foldMap (foldMap hsLInstDeclBinders . group_instds) tycl_decls))
where
getSelectorNames :: ([LocatedA Name], [LFieldOcc GhcRn]) -> [Name]
getSelectorNames (ns, fs) = map unLoc ns ++ map (foExt . unLoc) fs
-------------------
-hsLTyClDeclBinders :: IsPass p
+
+data TyDeclBinders p
+ = TyDeclBinders
+ { tyDeclMainBinder :: !(LocatedA (IdP (GhcPass p)), TyConFlavour ())
+ , tyDeclATs :: ![(LocatedA (IdP (GhcPass p)), TyConFlavour ())]
+ , tyDeclOpSigs :: ![LocatedA (IdP (GhcPass p))]
+ , tyDeclConsWithFields :: !(LConsWithFields p) }
+
+tyDeclBinders :: TyDeclBinders p -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
+tyDeclBinders (TyDeclBinders main ats sigs consWithFields)
+ = (fst main : (fmap fst ats ++ sigs ++ cons), flds)
+ where
+ (cons, flds) = lconsWithFieldsBinders consWithFields
+
+hsLTyClDeclBinders :: (IsPass p, OutputableBndrId p)
=> LocatedA (TyClDecl (GhcPass p))
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
+ -> TyDeclBinders p
-- ^ Returns all the /binding/ names of the decl. The first one is
-- guaranteed to be the name of the decl. The first component
-- represents all binding names except record fields; the second
@@ -1377,27 +1400,40 @@ hsLTyClDeclBinders :: IsPass p
-- See Note [SrcSpan for binders]
hsLTyClDeclBinders (L loc (FamDecl { tcdFam = FamilyDecl
- { fdLName = (L _ name) } }))
- = ([L loc name], [])
+ { fdLName = (L _ name)
+ , fdInfo = fd_info } }))
+ = TyDeclBinders
+ { tyDeclMainBinder = (L loc name, familyInfoTyConFlavour Nothing fd_info)
+ , tyDeclATs = [], tyDeclOpSigs = []
+ , tyDeclConsWithFields = emptyLConsWithFields }
hsLTyClDeclBinders (L loc (SynDecl
{ tcdLName = (L _ name) }))
- = ([L loc name], [])
+ = TyDeclBinders
+ { tyDeclMainBinder = (L loc name, TypeSynonymFlavour)
+ , tyDeclATs = [], tyDeclOpSigs = []
+ , tyDeclConsWithFields = emptyLConsWithFields }
hsLTyClDeclBinders (L loc (ClassDecl
{ tcdLName = (L _ cls_name)
, tcdSigs = sigs
, tcdATs = ats }))
- = (L loc cls_name :
- [ L fam_loc fam_name | (L fam_loc (FamilyDecl
- { fdLName = L _ fam_name })) <- ats ]
- ++
- [ L mem_loc mem_name
- | (L mem_loc (ClassOpSig _ False ns _)) <- sigs
- , (L _ mem_name) <- ns ]
- , [])
+ = TyDeclBinders
+ { tyDeclMainBinder = (L loc cls_name, ClassFlavour)
+ , tyDeclATs = [ (L fam_loc fam_name, familyInfoTyConFlavour (Just ()) fd_info)
+ | (L fam_loc (FamilyDecl { fdLName = L _ fam_name
+ , fdInfo = fd_info })) <- ats ]
+ , tyDeclOpSigs = [ L mem_loc mem_name
+ | (L mem_loc (ClassOpSig _ False ns _)) <- sigs
+ , (L _ mem_name) <- ns ]
+ , tyDeclConsWithFields = emptyLConsWithFields }
hsLTyClDeclBinders (L loc (DataDecl { tcdLName = (L _ name)
, tcdDataDefn = defn }))
- = (\ (xs, ys) -> (L loc name : xs, ys)) $ hsDataDefnBinders defn
-
+ = TyDeclBinders
+ { tyDeclMainBinder = (L loc name, flav )
+ , tyDeclATs = []
+ , tyDeclOpSigs = []
+ , tyDeclConsWithFields = hsDataDefnBinders defn }
+ where
+ flav = newOrDataToFlavour $ dataDefnConsNewOrData $ dd_cons defn
-------------------
hsForeignDeclsBinders :: forall p a. (UnXRec (GhcPass p), IsSrcSpanAnn p a)
@@ -1430,94 +1466,170 @@ getPatSynBinds binds
, (unXRec @id -> (PatSynBind _ psb)) <- bagToList lbinds ]
-------------------
-hsLInstDeclBinders :: IsPass p
+hsLInstDeclBinders :: (IsPass p, OutputableBndrId p)
=> LInstDecl (GhcPass p)
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
+ -> ([(LocatedA (IdP (GhcPass p)))], [LFieldOcc (GhcPass p)])
hsLInstDeclBinders (L _ (ClsInstD
{ cid_inst = ClsInstDecl
{ cid_datafam_insts = dfis }}))
- = foldMap (hsDataFamInstBinders . unLoc) dfis
+ = foldMap (lconsWithFieldsBinders . hsDataFamInstBinders . unLoc) dfis
hsLInstDeclBinders (L _ (DataFamInstD { dfid_inst = fi }))
- = hsDataFamInstBinders fi
+ = lconsWithFieldsBinders $ hsDataFamInstBinders fi
hsLInstDeclBinders (L _ (TyFamInstD {})) = mempty
-------------------
-- | the 'SrcLoc' returned are for the whole declarations, not just the names
-hsDataFamInstBinders :: IsPass p
+hsDataFamInstBinders :: (IsPass p, OutputableBndrId p)
=> DataFamInstDecl (GhcPass p)
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
+ -> LConsWithFields p
hsDataFamInstBinders (DataFamInstDecl { dfid_eqn = FamEqn { feqn_rhs = defn }})
= hsDataDefnBinders defn
-- There can't be repeated symbols because only data instances have binders
-------------------
-- | the 'SrcLoc' returned are for the whole declarations, not just the names
-hsDataDefnBinders :: IsPass p
+hsDataDefnBinders :: (IsPass p, OutputableBndrId p)
=> HsDataDefn (GhcPass p)
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
+ -> LConsWithFields p
hsDataDefnBinders (HsDataDefn { dd_cons = cons })
= hsConDeclsBinders (toList cons)
-- See Note [Binders in family instances]
-------------------
-type Seen p = [LFieldOcc (GhcPass p)] -> [LFieldOcc (GhcPass p)]
- -- Filters out ones that have already been seen
-hsConDeclsBinders :: forall p. IsPass p
+{- Note [Collecting record fields in data declarations]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When renaming a data declaration that includes record constructors, we are, in
+the end, going to to create a mapping from constructor to its field labels,
+to store in 'GREInfo' (see 'IAmConLike'). This allows us to know, in the renamer,
+which constructor has what fields.
+
+In order to achieve this, we return the constructor and field information from
+hsConDeclsBinders in the following format:
+
+ - [(ConRdrName, [Located Int])], a list of the constructors, each associated
+ with its record fields, in the form of a list of Int indices into...
+ - IntMap FieldOcc, an IntMap of record fields.
+
+(In actual fact, we use [(ConRdrName, Maybe [Located Int])], with Nothing indicating
+that the constructor has unlabelled fields: see Note [Local constructor info in the renamer]
+in GHC.Types.GREInfo.)
+
+This allows us to do the following (see GHC.Rename.Names.getLocalNonValBinders.new_tc):
+
+ - create 'Name's for each of the record fields, to get IntMap FieldLabel,
+ - create 'Name's for each of the constructors, to get [(ConName, [Int])],
+ - look up the FieldLabels of each constructor, to get [(ConName, [FieldLabel])].
+
+NB: This can be a bit tricky to get right in the presence of data types with
+duplicate constructors or fields. Storing locations allows us to report an error
+for duplicate field declarations, see test cases T9156 T9156_DF.
+Other relevant test cases: rnfail015.
+
+-}
+
+-- | A mapping from constructors to all of their fields.
+--
+-- See Note [Collecting record fields in data declarations].
+data LConsWithFields p =
+ LConsWithFields
+ { consWithFieldIndices :: [(LocatedA (IdP (GhcPass p)), Maybe [Located Int])]
+ , consFields :: IntMap (LFieldOcc (GhcPass p))
+ }
+
+lconsWithFieldsBinders :: LConsWithFields p
+ -> ([(LocatedA (IdP (GhcPass p)))], [LFieldOcc (GhcPass p)])
+lconsWithFieldsBinders (LConsWithFields cons fields)
+ = (map fst cons, IntMap.elems fields)
+
+emptyLConsWithFields :: LConsWithFields p
+emptyLConsWithFields = LConsWithFields [] IntMap.empty
+
+hsConDeclsBinders :: forall p. (IsPass p, OutputableBndrId p)
=> [LConDecl (GhcPass p)]
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
- -- See hsLTyClDeclBinders for what this does
- -- The function is boringly complicated because of the records
- -- And since we only have equality, we have to be a little careful
-hsConDeclsBinders cons
- = go id cons
+ -> LConsWithFields p
+ -- The function is boringly complicated because of the records
+ -- And since we only have equality, we have to be a little careful
+hsConDeclsBinders cons = go emptyFieldIndices cons
where
- go :: Seen p -> [LConDecl (GhcPass p)]
- -> ([LocatedA (IdP (GhcPass p))], [LFieldOcc (GhcPass p)])
- go _ [] = ([], [])
- go remSeen (r:rs)
+ go :: FieldIndices p -> [LConDecl (GhcPass p)] -> LConsWithFields p
+ go seen [] = LConsWithFields [] (fields seen)
+ go seen (r:rs)
-- Don't re-mangle the location of field names, because we don't
-- have a record of the full location of the field declaration anyway
= let loc = getLoc r
in case unLoc r of
- -- remove only the first occurrence of any seen field in order to
- -- avoid circumventing detection of duplicate fields (#9156)
ConDeclGADT { con_names = names, con_g_args = args }
- -> (toList (L loc . unLoc <$> names) ++ ns, flds ++ fs)
+ -> LConsWithFields (cons ++ ns) fs
where
- (remSeen', flds) = get_flds_gadt remSeen args
- (ns, fs) = go remSeen' rs
+ cons = map ( , con_flds ) $ toList (L loc . unLoc <$> names)
+ (con_flds, seen') = get_flds_gadt seen args
+ LConsWithFields ns fs = go seen' rs
ConDeclH98 { con_name = name, con_args = args }
- -> ([L loc (unLoc name)] ++ ns, flds ++ fs)
+ -> LConsWithFields ([(L loc (unLoc name), con_flds)] ++ ns) fs
where
- (remSeen', flds) = get_flds_h98 remSeen args
- (ns, fs) = go remSeen' rs
-
- get_flds_h98 :: Seen p -> HsConDeclH98Details (GhcPass p)
- -> (Seen p, [LFieldOcc (GhcPass p)])
- get_flds_h98 remSeen (RecCon flds) = get_flds remSeen flds
- get_flds_h98 remSeen _ = (remSeen, [])
-
- get_flds_gadt :: Seen p -> HsConDeclGADTDetails (GhcPass p)
- -> (Seen p, [LFieldOcc (GhcPass p)])
- get_flds_gadt remSeen (RecConGADT flds _) = get_flds remSeen flds
- get_flds_gadt remSeen _ = (remSeen, [])
-
- get_flds :: Seen p -> LocatedL [LConDeclField (GhcPass p)]
- -> (Seen p, [LFieldOcc (GhcPass p)])
- get_flds remSeen flds = (remSeen', fld_names)
- where
- fld_names = remSeen (concatMap (cd_fld_names . unLoc) (unLoc flds))
- remSeen' = foldr (.) remSeen
- [deleteBy ((==) `on` unLoc . foLabel . unLoc) v
- | v <- fld_names]
+ (con_flds, seen') = get_flds_h98 seen args
+ LConsWithFields ns fs = go seen' rs
+
+ get_flds_h98 :: FieldIndices p -> HsConDeclH98Details (GhcPass p)
+ -> (Maybe [Located Int], FieldIndices p)
+ get_flds_h98 seen (RecCon flds) = first Just $ get_flds seen flds
+ get_flds_h98 seen (PrefixCon _ []) = (Just [], seen)
+ get_flds_h98 seen _ = (Nothing, seen)
+
+ get_flds_gadt :: FieldIndices p -> HsConDeclGADTDetails (GhcPass p)
+ -> (Maybe [Located Int], FieldIndices p)
+ get_flds_gadt seen (RecConGADT flds _) = first Just $ get_flds seen flds
+ get_flds_gadt seen (PrefixConGADT []) = (Just [], seen)
+ get_flds_gadt seen _ = (Nothing, seen)
+
+ get_flds :: FieldIndices p -> LocatedL [LConDeclField (GhcPass p)]
+ -> ([Located Int], FieldIndices p)
+ get_flds seen flds =
+ foldr add_fld ([], seen) fld_names
+ where
+ add_fld fld (is, ixs) =
+ let (i, ixs') = insertField fld ixs
+ in (i:is, ixs')
+ fld_names = concatMap (cd_fld_names . unLoc) (unLoc flds)
+
+-- | A bijection between record fields of a datatype and integers,
+-- used to implement Note [Collecting record fields in data declarations].
+data FieldIndices p =
+ FieldIndices
+ { fields :: IntMap (LFieldOcc (GhcPass p))
+ -- ^ Look up a field from its index.
+ , fieldIndices :: Map RdrName Int
+ -- ^ Look up the index of a field label in the previous 'IntMap'.
+ , newInt :: !Int
+ -- ^ An integer @i@ such that no integer @i' >= i@ appears in the 'IntMap'.
+ }
+
+emptyFieldIndices :: FieldIndices p
+emptyFieldIndices =
+ FieldIndices { fields = IntMap.empty
+ , fieldIndices = Map.empty
+ , newInt = 0 }
+
+insertField :: LFieldOcc (GhcPass p) -> FieldIndices p -> (Located Int, FieldIndices p)
+insertField new_fld fi@(FieldIndices flds idxs new_idx)
+ | Just i <- Map.lookup rdr idxs
+ = (L loc i, fi)
+ | otherwise
+ = (L loc new_idx,
+ FieldIndices (IntMap.insert new_idx new_fld flds)
+ (Map.insert rdr new_idx idxs)
+ (new_idx + 1))
+ where
+ loc = getLocA new_fld
+ rdr = unLoc . foLabel . unLoc $ new_fld
{-
Note [SrcSpan for binders]
~~~~~~~~~~~~~~~~~~~~~~~~~~
-When extracting the (Located RdrNme) for a binder, at least for the
+When extracting the (Located RdrName) for a binder, at least for the
main name (the TyCon of a type declaration etc), we want to give it
the @SrcSpan@ of the whole /declaration/, not just the name itself
(which is how it appears in the syntax tree). This SrcSpan (for the