[Git][ghc/ghc][wip/fabu/T25014-mistakenly-accepted-parent] compiler: refactors renamer lookup for sub binder occurences
Fabricio Nascimento (@fabu)
gitlab at gitlab.haskell.org
Mon Jul 29 07:57:50 UTC 2024
Fabricio Nascimento pushed to branch wip/fabu/T25014-mistakenly-accepted-parent at Glasgow Haskell Compiler / GHC
Commits:
d69b4227 by Fabricio de Sousa Nascimento at 2024-07-29T16:57:29+09:00
compiler: refactors renamer lookup for sub binder occurences
Refactors lookupSubBndrOcc_helper into two functions that separately
deal with lookup for type classes and export lists. Removes the
Semigroup instance of DisambigInfo in favor of directly filtering
the GRE occurences, the refactored logic also fix and issue with
a program with multiple references being incorrectly accepted
Fix #25014
- - - - -
17 changed files:
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Types/Name/Reader.hs
- testsuite/tests/rename/T24452/all.T
- + testsuite/tests/rename/T25014/Ambig1.hs
- + testsuite/tests/rename/T25014/Ambig2.hs
- + testsuite/tests/rename/T25014/T25014a.hs
- + testsuite/tests/rename/T25014/T25014a.stderr
- + testsuite/tests/rename/T25014/T25014b.hs
- + testsuite/tests/rename/T25014/T25014b.stderr
- + testsuite/tests/rename/T25014/T25014c.hs
- + testsuite/tests/rename/T25014/T25014c.stderr
- + testsuite/tests/rename/T25014/T25014d.hs
- + testsuite/tests/rename/T25014/T25014d.stderr
- + testsuite/tests/rename/T25014/T25014e.hs
- + testsuite/tests/rename/T25014/T25014f.hs
- + testsuite/tests/rename/T25014/all.T
Changes:
=====================================
compiler/GHC/Rename/Env.hs
=====================================
@@ -32,7 +32,8 @@ module GHC.Rename.Env (
getUpdFieldLbls,
ChildLookupResult(..),
- lookupSubBndrOcc_helper,
+ lookupChildExportListSubBndr,
+ lookupInstanceDeclarationSubBndr,
HsSigCtxt(..), lookupLocalTcNames, lookupSigOccRn, lookupSigOccRnN,
lookupSigCtxtOccRn,
@@ -113,7 +114,6 @@ import Data.Either ( partitionEithers )
import Data.Function ( on )
import Data.List ( find, partition, groupBy, sortBy )
import qualified Data.List.NonEmpty as NE
-import qualified Data.Semigroup as Semi
import System.IO.Unsafe ( unsafePerformIO )
{-
@@ -403,7 +403,7 @@ lookupInstDeclBndr cls what rdr
-- In an instance decl you aren't allowed
-- to use a qualified name for the method
-- (Although it'd make perfect sense.)
- ; mb_name <- lookupSubBndrOcc
+ ; mb_name <- lookupInstanceDeclarationSubBndr
NoDeprecationWarnings
-- we don't give deprecated
-- warnings when a deprecated class
@@ -679,144 +679,285 @@ disambiguation anyway, because `x` is an original name, and
lookupGlobalOccRn will find it.
-}
--- | Used in export lists to lookup the children.
-lookupSubBndrOcc_helper :: Bool -> DeprecationWarnings
- -> Name
- -> RdrName -- ^ thing we are looking up
- -> LookupChild -- ^ how to look it up (e.g. which
- -- 'NameSpace's to look in)
- -> RnM ChildLookupResult
-lookupSubBndrOcc_helper must_have_parent warn_if_deprec parent rdr_name how_lkup
- | isUnboundName parent
- -- Avoid an error cascade
- = return (FoundChild (mkUnboundGRERdr rdr_name))
-
- | otherwise = do
- gre_env <- getGlobalRdrEnv
- let original_gres = lookupGRE gre_env (LookupChildren (rdrNameOcc rdr_name) how_lkup)
- picked_gres = pick_gres original_gres
- -- The remaining GREs are things that we *could* export here.
- -- Note that this includes things which have `NoParent`;
- -- those are sorted in `checkPatSynParent`.
- traceTc "parent" (ppr parent)
- traceTc "lookupExportChild must_have_parent:" (ppr must_have_parent)
- traceTc "lookupExportChild original_gres:" (ppr original_gres)
- traceTc "lookupExportChild picked_gres:" (ppr picked_gres)
+{-
+Note [Renaming the LHS on type class Instances]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Renaming the LHS of type class Instances is similar to [Renaming children on
+export lists] as they both rely on the [Configurable GRE lookup priority].
+
+One main difference is that we require parents to match on LHS names in type
+classes, while on export lists, we may sometimes export a name with a different
+parent than it was declared, for example PatternSynonyms (see [Typing Pattern
+Synonym Exports]).
+
+Another difference is in the presence of ambiguous names. Because we are strict
+on the parent, we won't report a name clash in the example bellow (see #24452),
+as it would lead to a confusing error.
+
+ module Ambiguous where
+
+ import Control.Applicative (Alternative)
+ import qualified Data.Map as Map (empty)
+ import qualified Data.Set as Set (empty)
+
+ instance Alternative Foo where
+ empty = undefined
+
+Note that the solution to this error would be to import the correct `empty` from
+Applicative.
+ import Control.Applicative (Alternative, empty)
+-}
+lookupInstanceDeclarationSubBndr :: DeprecationWarnings
+ -> Name -- ^ Parent
+ -> SDoc
+ -> RdrName -- ^ thing we are looking up
+ -> RnM (Either NotInScopeError Name)
+lookupInstanceDeclarationSubBndr warn_if_deprec parent doc rdr_name =
+ lookupExactOrOrig rdr_name (Right . greName) $
+ -- This happens for built-in classes, see mod52 for example
+ do
+ let lookup_method = LookupChild { wantedParent = parent
+ , lookupDataConFirst = False
+ , prioritiseParent = True -- See T23664.
+ }
+ (picked_gres, _) <- pick_matching_gres parent rdr_name lookup_method
+ traceRn "lookupInstanceDeclarationSubBndr" (ppr picked_gres)
+ -- See [Mismatched class methods and associated type families]
+ -- in TcInstDecls.
+ case picked_gres of
+ MatchingParentOccurrence g -> do
+ addUsedGRE warn_if_deprec g
+ return $ Right (greName g)
+ NoOccurrence ->
+ return $ Left (UnknownSubordinate doc)
+ NoParentOccurrence _ ->
+ return $ Left (UnknownSubordinate doc)
+ AmbiguousOccurrence _ ->
+ return $ Left (UnknownSubordinate doc)
+{-# INLINEABLE lookupInstanceDeclarationSubBndr #-}
+
+-- For details, see Note [Renaming children on export lists]
+lookupChildExportListSubBndr :: DeprecationWarnings
+ -> Name -- ^ Parent
+ -> RdrName -- ^ thing we are looking up
+ -> LookupChild -- ^ how to look it up (e.g. which
+ -- 'NameSpace's to look in)
+ -> RnM ChildLookupResult
+lookupChildExportListSubBndr warn_if_deprec parent rdr_name lookup_method = do
+ (picked_gres, original_gres) <-
+ pick_matching_gres parent rdr_name lookup_method
+ traceRn "lookupChildExportListSubBndr" (ppr picked_gres)
case picked_gres of
+ NoParentOccurrence g ->
+ success_found_child warn_if_deprec g
+ MatchingParentOccurrence g ->
+ success_found_child warn_if_deprec g
NoOccurrence ->
- noMatchingParentErr original_gres
- UniqueOccurrence g ->
- if must_have_parent
- then noMatchingParentErr original_gres
- else checkFld g
- DisambiguatedOccurrence g ->
- checkFld g
+ error_no_occurrence_after_disambiguation parent rdr_name original_gres
AmbiguousOccurrence gres ->
- if must_have_parent
- -- It is more helpful to tell the user that the ambiguous matches
- -- are for a wrong parent, then that there is a name clash,
- -- see (#24452). Also since `gres` is NonEmpty and is a sub-list
- -- of `original_gres` we are sure the original list is NonEmpty.
- then mkIncorrectParentErr (NE.fromList original_gres)
- else mkNameClashErr gres
- where
- checkFld :: GlobalRdrElt -> RnM ChildLookupResult
- checkFld g = do
- addUsedGRE warn_if_deprec g
- return $ FoundChild g
-
- -- Called when we find no matching GREs after disambiguation but
- -- there are three situations where this happens.
- -- 1. There were none to begin with.
- -- 2. None of the matching ones were the parent but
- -- a. They were from an overloaded record field so we can report
- -- a better error.
- -- b. The original lookup was actually ambiguous.
- -- For example, the case where overloading is off and two
- -- record fields are in scope from different record
- -- constructors, neither of which is the parent.
- noMatchingParentErr :: [GlobalRdrElt] -> RnM ChildLookupResult
- noMatchingParentErr original_gres = do
- traceRn "noMatchingParentErr" (ppr original_gres)
- dup_fields_ok <- xoptM LangExt.DuplicateRecordFields
- case original_gres of
- [] -> return NameNotFound
- [g] -> mkIncorrectParentErr (NE.fromList [g])
- gss@(g:gss'@(_:_)) ->
- if dup_fields_ok && all isRecFldGRE gss
- then mkIncorrectParentErr (NE.fromList gss)
- else mkNameClashErr $ g NE.:| gss'
-
- mkIncorrectParentErr :: NE.NonEmpty GlobalRdrElt -> RnM ChildLookupResult
- mkIncorrectParentErr gres = return $ IncorrectParent parent (NE.head gres)
- [p | x <- NE.toList gres, ParentIs p <- [greParent x]]
-
- mkNameClashErr :: NE.NonEmpty GlobalRdrElt -> RnM ChildLookupResult
- mkNameClashErr gres = do
- addNameClashErrRn rdr_name gres
- return (FoundChild (NE.head gres))
-
- pick_gres :: [GlobalRdrElt] -> DisambigInfo
- -- For Unqual, find GREs that are in scope qualified or unqualified
- -- For Qual, find GREs that are in scope with that qualification
- pick_gres gres
- | isUnqual rdr_name
- = mconcat (map right_parent gres)
- | otherwise
- = mconcat (map right_parent (pickGREs rdr_name gres))
-
- right_parent :: GlobalRdrElt -> DisambigInfo
- right_parent gre
- = case greParent gre of
- ParentIs cur_parent
- | parent == cur_parent -> DisambiguatedOccurrence gre
- | otherwise -> NoOccurrence
- NoParent -> UniqueOccurrence gre
-{-# INLINEABLE lookupSubBndrOcc_helper #-}
+ error_name_clash rdr_name gres
+ where
+ success_found_child warn_if_deprec g = do
+ addUsedGRE warn_if_deprec g
+ return $ FoundChild g
+{-# INLINEABLE lookupChildExportListSubBndr #-}
+
+{- Note [Picking and disambiguating children candidates]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The goal of `pick_matching_gres` is to pick one occurrence to rename, and fail
+at any ambiguity. Note that failing to reject a program with multiple
+parent matches can cause #25014.
+
+ module Ambig1 where
+ data family T a
+ data instance T Bool = MkT
+
+ module Ambig2 where
+ import Ambig1 (T)
+ data instance T Int = MkT
+
+ module Program (T(MkT)) where -- Ambig1 or Ambig2?
+ import Ambig1 (T(MkT))
+ import Ambig2 (T(MkT))
+
+Even when we don't find matching parents, we need to consider matching names
+without a parent, for example for pattern synonyms bellow.
+
+ module M (Maybe (Empty)) where
+ class Empty a where
+ isEmpty :: a -> Bool
+
+ instance Empty (Maybe a) where
+ isEmpty Nothing = True
+
+ pattern Empty :: Empty a => a
+ pattern Empty <- (isEmpty -> True)
+-}
+pick_matching_gres :: Name -- Parent
+ -> RdrName -- ^ thing we are looking up
+ -> LookupChild -- ^ how to look it up (e.g. which
+ -- 'NameSpace's to look in)
+ -> RnM (DisambigInfo, [GlobalRdrEltX GREInfo])
+pick_matching_gres parent rdr_name lookup_method = do
+ if isUnboundName parent
+ -- Avoid an error cascade, see Note [ Unbound vs Ambiguous Names ]
+ then return (MatchingParentOccurrence (mkUnboundGRERdr rdr_name), [])
+ else do
+ gre_env <- getGlobalRdrEnv
+ let lookup_chidren = LookupChildren (rdrNameOcc rdr_name) lookup_method
+ original_gres = lookupGRE gre_env lookup_chidren
+ picked_gres = pick_gres original_gres
+ -- The remaining GREs are things that we *could* export here.
+ -- Note that this includes things which have 'NoParent';
+ -- those are sorted in 'checkPatSynParent'.
+ traceTc "pick_matching_gres original_gres:" (ppr original_gres)
+ return (picked_gres, original_gres)
+ where
+ pick_gres :: [GlobalRdrElt] -> DisambigInfo
+ pick_gres gres
+ | length matching_parent_gres == 1 =
+ MatchingParentOccurrence (head matching_parent_gres)
+ | length no_parent_gres == 1 =
+ NoParentOccurrence (head no_parent_gres)
+ | null no_parent_gres && null matching_parent_gres =
+ NoOccurrence
+ | not $ null matching_parent_gres =
+ AmbiguousOccurrence (NE.fromList matching_parent_gres)
+ | otherwise =
+ -- no_parent_gres won't be empty due to the conditions above
+ AmbiguousOccurrence (NE.fromList no_parent_gres)
+ where
+ resolved_gres = resolve_gres rdr_name gres
+ (matching_parent_gres, no_parent_gres) = partition_gres resolved_gres
+
+ -- foldr preserves the order of the errors as they appear in the source
+ partition_gres :: [DisambigInfo] -> ([GlobalRdrElt], [GlobalRdrElt])
+ partition_gres = foldr separate_gres_matches ([], [])
+ where
+ separate_gres_matches :: DisambigInfo -> ([GlobalRdrElt], [GlobalRdrElt]) -> ([GlobalRdrElt], [GlobalRdrElt])
+ separate_gres_matches (MatchingParentOccurrence g) (matching_parent_gres, no_parent_gres) = (g:matching_parent_gres, no_parent_gres)
+ separate_gres_matches (NoParentOccurrence g) (matching_parent_gres, no_parent_gres) = (matching_parent_gres, g:no_parent_gres)
+ separate_gres_matches _ acc = acc
+
+ -- For Unqual, find GREs that are in scope qualified or unqualified
+ -- For Qual, find GREs that are in scope with that qualification
+ resolve_gres :: RdrName -> [GlobalRdrElt] -> [DisambigInfo]
+ resolve_gres rdr_name gres
+ | isUnqual rdr_name = map (match_parent parent) gres
+ | otherwise = map (match_parent parent) (pickGREs rdr_name gres)
+
+{- Note [Better errors for no matching GREs]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When GHC does not find a matching name on GREs after disambiguation
+(see [Picking and disambiguating children candidates]) it outputs an error like
+`Not in scope: ...` (NoOccurence). In some cases we can offer a better error
+by looking at the original GRE matches before disambiguation and attempt to
+surface problems that could have caused ghc to not being able to find the
+correct identifier.
+
+1. For example where the name exists for a different parent.
+
+ module IncorrectParent (A (b)) where
+ data A = A { a :: () }
+ data B = B { b :: () }
+
+In this case instead of `Not in scope: ‘b’` we prefer the error.
+ The type constructor ‘A’ is not the parent of the record selector ‘b’ (...)
+
+2. Another case is when there is an ambiguity and we have DuplicateRecordFields.
+
+ {-# LANGUAGE DuplicateRecordFields #-}
+ module IncorrectParent (A (other)) where
+ data A = A { one :: () }
+ data B = B { other :: () }
+ data C = C { other :: () }
+
+we also prefer
+ The type constructor ‘A’ is not the parent of the record selector ‘other’ (...)
+
+instead of:
+ Ambiguous occurrence ‘other’.
+ It could refer to
+ either the field ‘other’ of record ‘B’ ...
+ or the field ‘other’ of record ‘C’ ...
+-}
+error_no_occurrence_after_disambiguation :: Name
+ -> RdrName
+ -> [GlobalRdrEltX GREInfo]
+ -> RnM ChildLookupResult
+error_no_occurrence_after_disambiguation parent rdr_name original_gres = do
+ traceRn "error_no_matching_parent" (ppr original_gres)
+ dup_fields_ok <- xoptM LangExt.DuplicateRecordFields
+ case original_gres of
+ [] -> return NameNotFound
+ [g] -> error_incorrect_parent parent (NE.fromList [g])
+ gss@(g:gss'@(_:_)) ->
+ if dup_fields_ok && all isRecFldGRE gss
+ then error_incorrect_parent parent (NE.fromList gss)
+ else error_name_clash rdr_name $ g NE.:| gss'
+
+error_name_clash :: RdrName -> NE.NonEmpty GlobalRdrElt -> RnM ChildLookupResult
+error_name_clash rdr_name gres = do
+ addNameClashErrRn rdr_name gres
+ return (FoundChild (NE.head gres)) -- Avoid an error cascade, see Note [ Unbound vs Ambiguous Names ]
+
+error_incorrect_parent :: Name -> NE.NonEmpty GlobalRdrElt -> RnM ChildLookupResult
+error_incorrect_parent parent gres = return $ IncorrectParent parent (NE.head gres)
+ [p | x <- NE.toList gres, ParentIs p <- [greParent x]]
+
+
+{- Note [Disambiguating GREs by parent]
+
+Names can occur on GRE with or without Parent. When renaming an identifier
+for example the `foo` on export `A (foo)` on the export list of the program
+bellow, we have the following types of matches:
+
+ {-# LANGUAGE DuplicateRecordFields #-}
+ module Matches (A (foo, Pat, bar)) where
+ import Data.Map (empty)
+
+ data A = A { foo :: () }
+ data B = B { foo :: () }
+
+ pattern Pat = ...
+
+if we are looking for `foo` in the export list `A (foo)`
+1. `A.foo` is a MatchingParentOccurrence.
+2. `B.foo` is a NoOccurrence, as it is parent B, does not match the parent A
+we are looking for.
+
+if we are looking for `Pat` in the export list `A (Pat)`
+3. `pattern Pat` is a NoParentOccurrence.
+
+The AmbiguousOccurrence arise anytime multiple NoParentOccurrences or
+MatchingOccurrences are found, see [Picking and disambiguating children candidates]
+-}
+match_parent :: Name -> GlobalRdrElt -> DisambigInfo
+match_parent parent gre
+ = case greParent gre of
+ ParentIs cur_parent
+ | parent == cur_parent -> MatchingParentOccurrence gre
+ | otherwise -> NoOccurrence
+ NoParent -> NoParentOccurrence gre
-- | This domain specific datatype is used to record why we decided it was
-- possible that a GRE could be exported with a parent.
data DisambigInfo
= NoOccurrence
-- ^ The GRE could not be found, or it has the wrong parent.
- | UniqueOccurrence GlobalRdrElt
+ | NoParentOccurrence GlobalRdrElt
-- ^ The GRE has no parent. It could be a pattern synonym.
- | DisambiguatedOccurrence GlobalRdrElt
- -- ^ The parent of the GRE is the correct parent.
+ | MatchingParentOccurrence GlobalRdrElt
+ -- ^ The parent of the GRE is the correct parent. See match_parent.
| AmbiguousOccurrence (NE.NonEmpty GlobalRdrElt)
-- ^ The GRE is ambiguous.
- --
- -- For example, two normal identifiers with the same name are in
- -- scope. They will both be resolved to "UniqueOccurrence" and the
- -- monoid will combine them to this failing case.
-
instance Outputable DisambigInfo where
ppr NoOccurrence = text "NoOccurrence"
- ppr (UniqueOccurrence gre) = text "UniqueOccurrence:" <+> ppr gre
- ppr (DisambiguatedOccurrence gre) = text "DiambiguatedOccurrence:" <+> ppr gre
+ ppr (NoParentOccurrence gre) = text "UniqueOccurrence:" <+> ppr gre
+ ppr (MatchingParentOccurrence gre) = text "MatchingParentOccurrence:"
+ <+> ppr gre
ppr (AmbiguousOccurrence gres) = text "Ambiguous:" <+> ppr gres
-instance Semi.Semigroup DisambigInfo where
- -- These are the key lines: we prefer disambiguated occurrences to other
- -- names.
- _ <> DisambiguatedOccurrence g' = DisambiguatedOccurrence g'
- DisambiguatedOccurrence g' <> _ = DisambiguatedOccurrence g'
-
- NoOccurrence <> m = m
- m <> NoOccurrence = m
- UniqueOccurrence g <> UniqueOccurrence g'
- = AmbiguousOccurrence $ g NE.:| [g']
- UniqueOccurrence g <> AmbiguousOccurrence gs
- = AmbiguousOccurrence (g `NE.cons` gs)
- AmbiguousOccurrence gs <> UniqueOccurrence g'
- = AmbiguousOccurrence (g' `NE.cons` gs)
- AmbiguousOccurrence gs <> AmbiguousOccurrence gs'
- = AmbiguousOccurrence (gs Semi.<> gs')
-
-instance Monoid DisambigInfo where
- mempty = NoOccurrence
- mappend = (Semi.<>)
-
-- Lookup SubBndrOcc can never be ambiguous
--
-- Records the result of looking up a child.
@@ -829,7 +970,6 @@ data ChildLookupResult
[Name] -- ^ list of possible parents
-- | We resolved to a child
| FoundChild GlobalRdrElt
-
instance Outputable ChildLookupResult where
ppr NameNotFound = text "NameNotFound"
ppr (FoundChild n) = text "Found:" <+> ppr (greParent n) <+> ppr n
@@ -837,28 +977,6 @@ instance Outputable ChildLookupResult where
= text "IncorrectParent"
<+> hsep [ppr p, ppr $ greName g, ppr ns]
-lookupSubBndrOcc :: DeprecationWarnings
- -> Name -- Parent
- -> SDoc
- -> RdrName
- -> RnM (Either NotInScopeError Name)
--- ^ Find all the things the 'RdrName' maps to,
--- and pick the one with the right 'Parent' 'Name'.
-lookupSubBndrOcc warn_if_deprec the_parent doc rdr_name =
- lookupExactOrOrig rdr_name (Right . greName) $
- -- This happens for built-in classes, see mod052 for example
- do { child <- lookupSubBndrOcc_helper True warn_if_deprec the_parent rdr_name what_lkup
- ; return $ case child of
- FoundChild g -> Right (greName g)
- NameNotFound -> Left (UnknownSubordinate doc)
- IncorrectParent {} -> Left (UnknownSubordinate doc) }
- -- See [Mismatched class methods and associated type families]
- -- in TcInstDecls.
- where
- what_lkup = LookupChild { wantedParent = the_parent
- , lookupDataConFirst = False
- , prioritiseParent = True -- See T23664.
- }
{-
Note [Family instance binders]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2169,7 +2287,7 @@ lookupBindGroupOcc ctxt what rdr_name also_try_tycon_ns ns_spec
= NE.singleton (Left err)
lookup_cls_op cls
- = NE.singleton <$> lookupSubBndrOcc AllDeprecationWarnings cls doc rdr_name
+ = NE.singleton <$> lookupInstanceDeclarationSubBndr AllDeprecationWarnings cls doc rdr_name
where
doc = text "method of class" <+> quotes (ppr cls)
=====================================
compiler/GHC/Tc/Gen/Export.hs
=====================================
@@ -654,32 +654,65 @@ If the module has NO main function:
The IO action ‘main’ is not defined in module ‘Main’
-}
+{-
+Note [Renaming children on export lists]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Renaming export lists has many corner cases, and 5 different things can appear
+in a children export list under a parent.
--- Renaming exports lists is a minefield. Five different things can appear in
--- children export lists ( T(A, B, C) ).
--- 1. Record selectors
--- 2. Type constructors
--- 3. Data constructors
--- 4. Pattern Synonyms
--- 5. Pattern Synonym Selectors
---
--- However, things get put into weird name spaces.
--- 1. Some type constructors are parsed as variables (-.->) for example.
--- 2. All data constructors are parsed as type constructors
--- 3. When there is ambiguity, we default type constructors to data
--- constructors and require the explicit `type` keyword for type
--- constructors.
---
--- This function first establishes the possible namespaces that an
--- identifier might be in (`choosePossibleNameSpaces`).
---
--- Then for each namespace in turn, tries to find the correct identifier
--- there returning the first positive result or the first terminating
--- error.
---
+ module M (R (s), D (MkD), Maybe (Empty), Either (Empty), pattern Px) where
+
+ -- 1. Record Selector
+ data R = R { s :: Int }
+
+ -- 2. Data Constructor
+ data D a = MkD a
+
+ -- 3. Type Constructor
+ type S = MkD Int
+ -- 4. Pattern Synonyms
+ class Empty a where
+ isEmpty :: a -> Bool
+ instance Empty (Maybe a) where
+ isEmpty Nothing = True
+ instance Empty (Either a b) where
+ isEmpty (Left _) = True
+
+ pattern Empty :: Empty a => a
+ pattern Empty <- (isEmpty -> True)
+
+ -- 5. Record Pattern Synonym selectors
+ data Point = Point Int Int
+
+ pattern Px :: Int -> Point
+ pattern Px{x} <- Point x _
+
+
+To makes matter more complicate.
+1. Some type constructors are parsed as variables (-.->) for example.
+2. All data constructors are parsed as type constructors
+3. When there is ambiguity, we default type constructors to data
+constructors and require the explicit `type` keyword for type
+constructors.
+4. Pattern synonyms are very flexible in which parents they can be exported with
+(see [Typing Pattern Synonym Exports]).
+
+The strategy for renaming, is to first establish all the possible namespaces
+that an identifier might be in, then for each namespace in turn,
+tries to find the correct identifier there returning the
+first positive result or the first terminating error.
+
+For more details see
+[Renaming the LHS on type class Instances],
+[Configurable GRE lookup priority] and [Picking and disambiguating children
+candidates].
+
+Also notice that this logic is similar to
+[Renaming the LHS on type class Instances]
+-}
lookupChildrenExport :: Name -> [LIEWrappedName GhcPs]
-> RnM ([(LIEWrappedName GhcRn, GlobalRdrElt)])
lookupChildrenExport spec_parent rdr_items = mapAndReportM doOne rdr_items
@@ -698,8 +731,8 @@ lookupChildrenExport spec_parent rdr_items = mapAndReportM doOne rdr_items
, prioritiseParent = False -- See T11970.
}
- -- Do not report export list declaration deprecations
- name <- lookupSubBndrOcc_helper False ExportDeprecationWarnings
+ -- Do not report export list declaration deprecations
+ name <- lookupChildExportListSubBndr ExportDeprecationWarnings
spec_parent bareName what_lkup
traceRn "lookupChildrenExport" (ppr name)
-- Default to data constructors for slightly better error
=====================================
compiler/GHC/Types/Name/Reader.hs
=====================================
@@ -1215,7 +1215,7 @@ data LookupChild
-- - @True@: prioritise getting the right 'Parent'
-- - @False@: prioritise getting the right 'NameSpace'
--
- -- See Note [childGREPriority].
+ -- See Note [Configurable GRE lookup priority].
}
instance Outputable LookupChild where
@@ -1262,21 +1262,27 @@ greIsRelevant which_gres ns gre
where
other_ns = greNameSpace gre
-{- Note [childGREPriority]
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+{- Note [Configurable GRE lookup priority]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There are currently two places in the compiler where we look up GlobalRdrElts
-which have a given Parent. These are the two calls to lookupSubBndrOcc_helper:
+which have a given Parent.
- A. Looking up children in an export item, e.g.
+ A. lookupChildExportListSubBndr looks up children in an export item, e.g.
module M ( T(MkT, D) ) where { data T = MkT; data D = D }
- B. Looking up binders in a class or instance declaration, e.g.
+ -- see [Renaming children on export lists]
+
+ B. lookupInstanceDeclarationSubBndr looks up binders in a class or
+ instance declaration, e.g.
+
the operator +++ in the fixity declaration:
- class C a where { type (+++) :: a -> a ->; infixl 6 +++ }
+ class C a where { type (+++) :: a -> a -> a; infixl 6 +++ }
(+++) :: Int -> Int -> Int; (+++) = (+)
+ -- see [Renaming the LHS on type class Instances]
+
In these two situations, there are two competing metrics for finding the "best"
'GlobalRdrElt' that a particular 'OccName' resolves to:
@@ -1307,7 +1313,7 @@ Not doing so led to #23664.
--
-- We score by 'Parent' and 'NameSpace', with higher priorities having lower
-- numbers. Which lexicographic order we use ('Parent' or 'NameSpace' first)
--- is determined by the first argument; see Note [childGREPriority].
+-- is determined by the first argument; see Note [Configurable GRE lookup priority].
childGREPriority :: LookupChild -- ^ what kind of child do we want,
-- e.g. what should its parent be?
-> NameSpace -- ^ what 'NameSpace' are we originally looking in?
@@ -1327,7 +1333,7 @@ childGREPriority (LookupChild { wantedParent = wanted_parent
in Just $ if par_first
then (par_prio, ns_prio)
else (ns_prio, par_prio)
- -- See Note [childGREPriority].
+ -- See Note [Configurable GRE lookup priority].
where
-- Pick out the possible 'NameSpace's in order of priority.
=====================================
testsuite/tests/rename/T24452/all.T
=====================================
@@ -3,4 +3,4 @@ test('T24452b', normal, compile_fail, [''])
test('T24452c', normal, compile_fail, [''])
test('T24452d', normal, compile_fail, [''])
test('T24452e', normal, compile_fail, [''])
-test('T24452f', [extra_files(['AmbigPatSynA.hs', 'AmbigPatSynB.hs'])], multimod_compile_fail, ['T24452f','-v0'])
\ No newline at end of file
+test('T24452f', [extra_files(['AmbigPatSynA.hs', 'AmbigPatSynB.hs'])], multimod_compile_fail, ['T24452f','-v0'])
=====================================
testsuite/tests/rename/T25014/Ambig1.hs
=====================================
@@ -0,0 +1,5 @@
+-- A module that is ambiguous with Ambig2
+{-# LANGUAGE TypeFamilies #-}
+module Ambig1 where
+ data family T a
+ data instance T Bool = MkT
=====================================
testsuite/tests/rename/T25014/Ambig2.hs
=====================================
@@ -0,0 +1,5 @@
+-- A module that is ambiguous with Ambig1
+{-# LANGUAGE TypeFamilies #-}
+module Ambig2 where
+ import Ambig1 (T)
+ data instance T Int = MkT
=====================================
testsuite/tests/rename/T25014/T25014a.hs
=====================================
@@ -0,0 +1,5 @@
+-- Should not compile as it is unclear what gets exported
+module T25014a (T(MkT)) where
+ import Ambig1 (T(MkT))
+ import Ambig2 (T(MkT))
+ data S
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/T25014a.stderr
=====================================
@@ -0,0 +1,10 @@
+T25014a.hs:2:17: [GHC-87543]
+ Ambiguous occurrence ‘MkT’.
+ It could refer to
+ either ‘Ambig1.MkT’,
+ imported from ‘Ambig1’ at T25014a.hs:3:18-23
+ (and originally defined at Ambig1.hs:5:26-28),
+ or ‘Ambig2.MkT’,
+ imported from ‘Ambig2’ at T25014a.hs:4:18-23
+ (and originally defined at Ambig2.hs:5:25-27).
+ In the export: T(MkT)
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/T25014b.hs
=====================================
@@ -0,0 +1,4 @@
+-- Should not compile trying to export a missing name
+module T25014b (A (foo)) where
+
+data A
=====================================
testsuite/tests/rename/T25014/T25014b.stderr
=====================================
@@ -0,0 +1,3 @@
+T25014b.hs:2:17: [GHC-76037]
+ Not in scope: ‘foo’
+ In the export: A(foo)
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/T25014c.hs
=====================================
@@ -0,0 +1,8 @@
+-- Should not compile trying to export a missing name
+module T25014c (A (foo)) where
+
+data A
+
+data B = B {
+ foo :: Int
+}
=====================================
testsuite/tests/rename/T25014/T25014c.stderr
=====================================
@@ -0,0 +1,5 @@
+T25014c.hs:2:17: [GHC-88993]
+ The type constructor ‘A’ is not the parent of the record selector ‘foo’.
+ Record selectors can only be exported with their parent type constructor.
+ Parent: B
+ In the export: A(foo)
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/T25014d.hs
=====================================
@@ -0,0 +1,6 @@
+-- Should not compile trying to export a name with the wrong parent
+module T25014b (A (foo)) where
+
+data A
+
+foo = 1
=====================================
testsuite/tests/rename/T25014/T25014d.stderr
=====================================
@@ -0,0 +1,4 @@
+T25014d.hs:2:17: [GHC-88993]
+ The type constructor ‘A’ is not the parent of the identifier ‘foo’.
+ Identifiers can only be exported with their parent type constructor.
+ In the export: A(foo)
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/T25014e.hs
=====================================
@@ -0,0 +1,6 @@
+-- Should compile as A.foo matches parent
+module T25014b (A (foo)) where
+
+data A = A {
+ foo :: Int
+}
=====================================
testsuite/tests/rename/T25014/T25014f.hs
=====================================
@@ -0,0 +1,4 @@
+-- Should not compile as it is unclear what gets exported
+module T25014a (T(MkT)) where
+ import Ambig1 (T(MkT))
+ data S
\ No newline at end of file
=====================================
testsuite/tests/rename/T25014/all.T
=====================================
@@ -0,0 +1,6 @@
+test('T25014a', [extra_files(['Ambig1.hs', 'Ambig2.hs'])], multimod_compile_fail, ['T25014a','-v0'])
+test('T25014b', [], compile_fail, [''])
+test('T25014c', [], compile_fail, [''])
+test('T25014d', [], compile_fail, [''])
+test('T25014e', [], compile, [''])
+test('T25014f', [extra_files(['Ambig1.hs', 'Ambig2.hs'])], multimod_compile, ['T25014f','-v0'])
\ No newline at end of file
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d69b4227064944970ad776939c058a64be00f409
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d69b4227064944970ad776939c058a64be00f409
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240729/cd4d82db/attachment-0001.html>
More information about the ghc-commits
mailing list