Brandon Chinn pushed to branch wip/T21730-import at Glasgow Haskell Compiler / GHC
Commits:
-
e19c0006
by Brandon Chinn at 2025-04-29T21:34:20-07:00
-
7c3b4022
by Brandon Chinn at 2025-04-29T21:34:20-07:00
-
66ee95b4
by Brandon Chinn at 2025-04-29T21:34:20-07:00
-
eec0bf4f
by Brandon Chinn at 2025-04-29T21:34:20-07:00
-
f125a173
by Brandon Chinn at 2025-04-29T21:34:20-07:00
14 changed files:
- compiler/GHC.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Types/Name/Reader.hs
- testsuite/tests/plugins/Makefile
- + testsuite/tests/plugins/T21730-plugin/Makefile
- + testsuite/tests/plugins/T21730-plugin/Setup.hs
- + testsuite/tests/plugins/T21730-plugin/T21730-plugin.cabal
- + testsuite/tests/plugins/T21730-plugin/T21730_Plugin.hs
- + testsuite/tests/plugins/T21730.hs
- testsuite/tests/plugins/all.T
Changes:
... | ... | @@ -1503,7 +1503,8 @@ availsToGlobalRdrEnv hsc_env mod avails |
1503 | 1503 | -- all the specified modules into the global interactive module
|
1504 | 1504 | imp_spec = ImpSpec { is_decl = decl, is_item = ImpAll}
|
1505 | 1505 | decl = ImpDeclSpec { is_mod = mod, is_as = moduleName mod,
|
1506 | - is_qual = False, is_isboot = NotBoot, is_pkg_qual = NoPkgQual,
|
|
1506 | + is_qual = False, is_implicit = False,
|
|
1507 | + is_isboot = NotBoot, is_pkg_qual = NoPkgQual,
|
|
1507 | 1508 | is_dloc = srcLocSpan interactiveSrcLoc,
|
1508 | 1509 | is_level = NormalLevel }
|
1509 | 1510 |
... | ... | @@ -80,14 +80,19 @@ type instance XCImportDecl GhcTc = DataConCantHappen |
80 | 80 | data XImportDeclPass = XImportDeclPass
|
81 | 81 | { ideclAnn :: EpAnn EpAnnImportDecl
|
82 | 82 | , ideclSourceText :: SourceText -- Note [Pragma source text] in "GHC.Types.SourceText"
|
83 | - , ideclImplicit :: Bool
|
|
84 | - -- ^ GHC generates an `ImportDecl` to represent the invisible `import Prelude`
|
|
85 | - -- that appears in any file that omits `import Prelude`, setting
|
|
86 | - -- this field to indicate that the import doesn't appear in the
|
|
87 | - -- original source. True => implicit import (of Prelude)
|
|
83 | + , ideclImplicit :: Bool -- ^ See Note [Implicit imports]
|
|
88 | 84 | }
|
89 | 85 | deriving (Data)
|
90 | 86 | |
87 | +{- Note [Implicit imports]
|
|
88 | +~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
89 | + |
|
90 | +GHC generates an `ImportDecl` to represent the invisible `import Prelude`
|
|
91 | +that appears in any file that omits `import Prelude`, setting
|
|
92 | +this field to indicate that the import doesn't appear in the
|
|
93 | +original source. True => implicit import (of Prelude)
|
|
94 | +-}
|
|
95 | + |
|
91 | 96 | type instance XXImportDecl (GhcPass _) = DataConCantHappen
|
92 | 97 | |
93 | 98 | type instance Anno ModuleName = SrcSpanAnnA
|
... | ... | @@ -1982,6 +1982,7 @@ lookupQualifiedNameGHCi fos rdr_name |
1982 | 1982 | , is_as = moduleName mod
|
1983 | 1983 | , is_pkg_qual = NoPkgQual
|
1984 | 1984 | , is_qual = True
|
1985 | + , is_implicit = False
|
|
1985 | 1986 | , is_isboot = NotBoot
|
1986 | 1987 | , is_dloc = noSrcSpan
|
1987 | 1988 | , is_level = NormalLevel }
|
... | ... | @@ -391,7 +391,7 @@ rnImportDecl this_mod |
391 | 391 | |
392 | 392 | let imp_mod = mi_module iface
|
393 | 393 | qual_mod_name = fmap unLoc as_mod `orElse` imp_mod_name
|
394 | - imp_spec = ImpDeclSpec { is_mod = imp_mod, is_qual = qual_only,
|
|
394 | + imp_spec = ImpDeclSpec { is_mod = imp_mod, is_qual = qual_only, is_implicit = implicit,
|
|
395 | 395 | is_dloc = locA loc, is_as = qual_mod_name,
|
396 | 396 | is_pkg_qual = pkg_qual, is_isboot = want_boot,
|
397 | 397 | is_level = convImportLevel import_level }
|
... | ... | @@ -2014,40 +2014,38 @@ The ImportMap is a short-lived intermediate data structure records, for |
2014 | 2014 | each import declaration, what stuff brought into scope by that
|
2015 | 2015 | declaration is actually used in the module.
|
2016 | 2016 | |
2017 | -The SrcLoc is the location of the END of a particular 'import'
|
|
2018 | -declaration. Why *END*? Because we don't want to get confused
|
|
2019 | -by the implicit Prelude import. Consider (#7476) the module
|
|
2020 | - import Foo( foo )
|
|
2021 | - main = print foo
|
|
2022 | -There is an implicit 'import Prelude(print)', and it gets a SrcSpan
|
|
2023 | -of line 1:1 (just the point, not a span). If we use the *START* of
|
|
2024 | -the SrcSpan to identify the import decl, we'll confuse the implicit
|
|
2025 | -import Prelude with the explicit 'import Foo'. So we use the END.
|
|
2026 | -It's just a cheap hack; we could equally well use the Span too.
|
|
2027 | - |
|
2028 | 2017 | The [GlobalRdrElt] are the things imported from that decl.
|
2029 | 2018 | -}
|
2030 | 2019 | |
2031 | -type ImportMap = Map RealSrcLoc [GlobalRdrElt] -- See [The ImportMap]
|
|
2032 | - -- If loc :-> gres, then
|
|
2033 | - -- 'loc' = the end loc of the bestImport of each GRE in 'gres'
|
|
2020 | +data ImportMap = ImportMap
|
|
2021 | + { im_imports :: Map RealSrcSpan [GlobalRdrElt]
|
|
2022 | + -- ^ See [The ImportMap]
|
|
2023 | + -- If loc :-> gres, then
|
|
2024 | + -- 'loc' = the end loc of the bestImport of each GRE in 'gres'
|
|
2025 | + , im_implicitImports :: Map ModuleName [GlobalRdrElt]
|
|
2026 | + , im_generatedImports :: Map ModuleName [GlobalRdrElt]
|
|
2027 | + }
|
|
2034 | 2028 | |
2035 | 2029 | mkImportMap :: [GlobalRdrElt] -> ImportMap
|
2036 | 2030 | -- For each of a list of used GREs, find all the import decls that brought
|
2037 | 2031 | -- it into scope; choose one of them (bestImport), and record
|
2038 | 2032 | -- the RdrName in that import decl's entry in the ImportMap
|
2039 | -mkImportMap = foldr insertImportMap Map.empty
|
|
2033 | +mkImportMap = foldr insertImportMap $ ImportMap Map.empty Map.empty Map.empty
|
|
2040 | 2034 | |
2041 | 2035 | insertImportMap :: GlobalRdrElt -> ImportMap -> ImportMap
|
2042 | -insertImportMap gre@(GRE { gre_imp = imp_specs }) importMap =
|
|
2043 | - case toImportMapSrcLoc (is_dloc (is_decl best_imp_spec)) of
|
|
2044 | - Just decl_loc -> insertElem decl_loc gre importMap
|
|
2045 | - Nothing -> importMap
|
|
2036 | +insertImportMap gre@(GRE { gre_imp = imp_specs }) importMap
|
|
2037 | + | is_implicit best_imp_spec =
|
|
2038 | + importMap{im_implicitImports = insertElem (moduleName $ is_mod best_imp_spec) gre $ im_implicitImports importMap}
|
|
2039 | + | RealSrcSpan importSpan _ <- is_dloc best_imp_spec =
|
|
2040 | + importMap{im_imports = insertElem importSpan gre $ im_imports importMap}
|
|
2041 | + | UnhelpfulSpan UnhelpfulGenerated <- is_dloc best_imp_spec =
|
|
2042 | + importMap{im_generatedImports = insertElem (moduleName $ is_mod best_imp_spec) gre $ im_generatedImports importMap}
|
|
2043 | + | otherwise = importMap
|
|
2046 | 2044 | where
|
2047 | 2045 | best_imp_spec =
|
2048 | 2046 | case bagToList imp_specs of
|
2049 | 2047 | [] -> pprPanic "mkImportMap: GRE with no ImportSpecs" (ppr gre)
|
2050 | - is:iss -> bestImport (is NE.:| iss)
|
|
2048 | + is:iss -> is_decl $ bestImport (is NE.:| iss)
|
|
2051 | 2049 | |
2052 | 2050 | -- https://github.com/haskell/containers/issues/784
|
2053 | 2051 | insertElem :: Ord k => k -> v -> Map k [v] -> Map k [v]
|
... | ... | @@ -2056,16 +2054,14 @@ insertImportMap gre@(GRE { gre_imp = imp_specs }) importMap = |
2056 | 2054 | Nothing -> Just [v]
|
2057 | 2055 | |
2058 | 2056 | lookupImportMap :: LImportDecl GhcRn -> ImportMap -> [GlobalRdrElt]
|
2059 | -lookupImportMap (L srcSpan _) importMap =
|
|
2057 | +lookupImportMap (L srcSpan ImportDecl{ideclName = L _ modName}) importMap =
|
|
2060 | 2058 | fromMaybe [] $
|
2061 | - toImportMapSrcLoc (locA srcSpan) >>= (`Map.lookup` importMap)
|
|
2062 | - |
|
2063 | -toImportMapSrcLoc :: SrcSpan -> Maybe RealSrcLoc
|
|
2064 | -toImportMapSrcLoc srcSpan =
|
|
2065 | - -- see Note [The ImportMap] for why we're using srcSpanEnd
|
|
2066 | - case srcSpanEnd srcSpan of
|
|
2067 | - RealSrcLoc loc _ -> Just loc
|
|
2068 | - UnhelpfulLoc _ -> Nothing
|
|
2059 | + -- should match logic in insertImportMap
|
|
2060 | + case locA srcSpan of
|
|
2061 | + _ | Just gres <- modName `Map.lookup` im_implicitImports importMap -> Just gres
|
|
2062 | + RealSrcSpan realSrcSpan _ -> realSrcSpan `Map.lookup` im_imports importMap
|
|
2063 | + UnhelpfulSpan UnhelpfulGenerated -> modName `Map.lookup` im_generatedImports importMap
|
|
2064 | + _ -> Nothing
|
|
2069 | 2065 | |
2070 | 2066 | warnUnusedImport :: GlobalRdrEnv -> ImportDeclUsage -> RnM ()
|
2071 | 2067 | warnUnusedImport rdr_env (L loc decl, used, unused)
|
... | ... | @@ -354,7 +354,8 @@ lookupRdrNameInModuleForPlugins hsc_env mod_name rdr_name = do |
354 | 354 | Just iface -> do
|
355 | 355 | -- Try and find the required name in the exports
|
356 | 356 | let decl_spec = ImpDeclSpec { is_mod = mod, is_as = mod_name, is_pkg_qual = NoPkgQual
|
357 | - , is_qual = False, is_dloc = noSrcSpan, is_isboot = NotBoot, is_level = SpliceLevel }
|
|
357 | + , is_qual = False, is_implicit = False
|
|
358 | + , is_dloc = noSrcSpan, is_isboot = NotBoot, is_level = SpliceLevel }
|
|
358 | 359 | imp_spec = ImpSpec decl_spec ImpAll
|
359 | 360 | env = mkGlobalRdrEnv
|
360 | 361 | $ gresFromAvails hsc_env (Just imp_spec) (mi_exports iface)
|
... | ... | @@ -633,6 +633,7 @@ mergeSignatures |
633 | 633 | is_as = mod_name,
|
634 | 634 | is_pkg_qual = NoPkgQual,
|
635 | 635 | is_qual = False,
|
636 | + is_implicit = False,
|
|
636 | 637 | is_isboot = NotBoot,
|
637 | 638 | is_dloc = locA loc,
|
638 | 639 | is_level = NormalLevel
|
... | ... | @@ -1821,6 +1821,7 @@ shadowNames drop_only_qualified env new_gres = minusOccEnv_C_Ns do_shadowing env |
1821 | 1821 | , is_as = old_mod_name
|
1822 | 1822 | , is_pkg_qual = NoPkgQual
|
1823 | 1823 | , is_qual = True
|
1824 | + , is_implicit = False
|
|
1824 | 1825 | , is_level = NormalLevel -- MP: Not 100% sure this is correct
|
1825 | 1826 | , is_isboot = NotBoot
|
1826 | 1827 | , is_dloc = greDefinitionSrcSpan old_gre }
|
... | ... | @@ -1983,6 +1984,7 @@ data ImpDeclSpec |
1983 | 1984 | is_as :: !ModuleName, -- ^ Import alias, e.g. from @as M@ (or @Muggle@ if there is no @as@ clause)
|
1984 | 1985 | is_pkg_qual :: !PkgQual, -- ^ Was this a package import?
|
1985 | 1986 | is_qual :: !Bool, -- ^ Was this import qualified?
|
1987 | + is_implicit :: !Bool, -- ^ Was this import implicit? See Note [Implicit imports]
|
|
1986 | 1988 | is_dloc :: !SrcSpan, -- ^ The location of the entire import declaration
|
1987 | 1989 | is_isboot :: !IsBootInterface, -- ^ Was this a SOURCE import?
|
1988 | 1990 | is_level :: !ImportLevel -- ^ Was this import level modified? splice/quote +-1
|
... | ... | @@ -1995,11 +1997,12 @@ instance NFData ImpDeclSpec where |
1995 | 1997 | |
1996 | 1998 | |
1997 | 1999 | instance Binary ImpDeclSpec where
|
1998 | - put_ bh (ImpDeclSpec mod as pkg_qual qual _dloc isboot isstage) = do
|
|
2000 | + put_ bh (ImpDeclSpec mod as pkg_qual qual implicit _dloc isboot isstage) = do
|
|
1999 | 2001 | put_ bh mod
|
2000 | 2002 | put_ bh as
|
2001 | 2003 | put_ bh pkg_qual
|
2002 | 2004 | put_ bh qual
|
2005 | + put_ bh implicit
|
|
2003 | 2006 | put_ bh isboot
|
2004 | 2007 | put_ bh (fromEnum isstage)
|
2005 | 2008 | |
... | ... | @@ -2008,9 +2011,10 @@ instance Binary ImpDeclSpec where |
2008 | 2011 | as <- get bh
|
2009 | 2012 | pkg_qual <- get bh
|
2010 | 2013 | qual <- get bh
|
2014 | + implicit <- get bh
|
|
2011 | 2015 | isboot <- get bh
|
2012 | 2016 | isstage <- toEnum <$> get bh
|
2013 | - return (ImpDeclSpec mod as pkg_qual qual noSrcSpan isboot isstage)
|
|
2017 | + return (ImpDeclSpec mod as pkg_qual qual implicit noSrcSpan isboot isstage)
|
|
2014 | 2018 | |
2015 | 2019 | -- | Import Item Specification
|
2016 | 2020 | --
|
... | ... | @@ -234,3 +234,7 @@ test-late-plugin: |
234 | 234 | ContainsLateBinding=$$(echo $$SHOW_IFACE | grep -o 222222) ; \
|
235 | 235 | echo "$$ContainsLateBinding" ; \
|
236 | 236 | [ "$$ContainsEarlyBinding" = "111111" ] && [ "$$ContainLateBinding" = "" ]
|
237 | + |
|
238 | +.PHONY: T21730
|
|
239 | +T21730:
|
|
240 | + "$(TEST_HC)" $(TEST_HC_OPTS) $(ghcPluginWayFlags) -v0 T21730.hs -package-db T21730-plugin/pkg.T21730-plugin/local.package.conf |
1 | +TOP=../../..
|
|
2 | +include $(TOP)/mk/boilerplate.mk
|
|
3 | +include $(TOP)/mk/test.mk
|
|
4 | + |
|
5 | +clean.%:
|
|
6 | + rm -rf pkg.$*
|
|
7 | + |
|
8 | +HERE := $(abspath .)
|
|
9 | +$(eval $(call canonicalise,HERE))
|
|
10 | + |
|
11 | +package.%:
|
|
12 | + $(MAKE) -s --no-print-directory clean.$*
|
|
13 | + mkdir pkg.$*
|
|
14 | + "$(TEST_HC)" -outputdir pkg.$* --make -v0 -o pkg.$*/setup Setup.hs
|
|
15 | + "$(GHC_PKG)" init pkg.$*/local.package.conf
|
|
16 | + pkg.$*/setup configure --distdir pkg.$*/dist -v0 $(CABAL_PLUGIN_BUILD) --prefix="$(HERE)/pkg.$*/install" --with-compiler="$(TEST_HC)" --with-hc-pkg="$(GHC_PKG)" --package-db=pkg.$*/local.package.conf $(if $(findstring YES,$(HAVE_PROFILING)), --enable-library-profiling)
|
|
17 | + pkg.$*/setup build --distdir pkg.$*/dist -v0
|
|
18 | + pkg.$*/setup install --distdir pkg.$*/dist -v0 |
1 | +import Distribution.Simple
|
|
2 | +main = defaultMain |
1 | +Name: T21730-plugin
|
|
2 | +Version: 0.1
|
|
3 | +Synopsis: For testing
|
|
4 | +Cabal-Version: >= 1.2
|
|
5 | +Build-Type: Simple
|
|
6 | + |
|
7 | +Library
|
|
8 | + Build-Depends: base, ghc
|
|
9 | + Exposed-Modules: T21730_Plugin |
1 | +module T21730_Plugin (plugin) where
|
|
2 | + |
|
3 | +import GHC.Hs
|
|
4 | +import GHC.Plugins
|
|
5 | +import GHC.Types.Fixity
|
|
6 | +import qualified GHC.Types.Name.Occurrence as NameSpace
|
|
7 | + |
|
8 | +plugin :: Plugin
|
|
9 | +plugin = defaultPlugin
|
|
10 | + { parsedResultAction = \_ _ parsedResult ->
|
|
11 | + pure $ parsedResult
|
|
12 | + { parsedResultModule =
|
|
13 | + let modl = parsedResultModule parsedResult
|
|
14 | + in modl { hpm_module = update <$> hpm_module modl }
|
|
15 | + }
|
|
16 | + }
|
|
17 | + where
|
|
18 | + update modl =
|
|
19 | + modl
|
|
20 | + { hsmodImports = newImport : hsmodImports modl
|
|
21 | + , hsmodDecls = newDecl : hsmodDecls modl
|
|
22 | + }
|
|
23 | + |
|
24 | + newImport = genLoc $ simpleImportDecl $ mkModuleName "Data.Char"
|
|
25 | + newFuncName = genLoc $ mkRdrName "toLower2"
|
|
26 | + newDecl =
|
|
27 | + genLoc . ValD NoExtField $
|
|
28 | + mkFunBind (Generated OtherExpansion SkipPmc) newFuncName $
|
|
29 | + [ mkSimpleMatch
|
|
30 | + (mkPrefixFunRhs newFuncName noAnn)
|
|
31 | + (L noAnn [])
|
|
32 | + (nlHsVar (mkRdrName "toLower"))
|
|
33 | + ]
|
|
34 | + |
|
35 | +genLoc :: (NoAnn ann) => e -> GenLocated (EpAnn ann) e
|
|
36 | +genLoc = L (noAnnSrcSpan generatedSrcSpan)
|
|
37 | + |
|
38 | +mkRdrName :: String -> RdrName
|
|
39 | +mkRdrName = mkRdrUnqual . mkOccName NameSpace.varName |
1 | +{-# OPTIONS_GHC -fplugin T21730_Plugin #-}
|
|
2 | +{-# OPTIONS_GHC -Werror=unused-imports #-}
|
|
3 | + |
|
4 | +main :: IO ()
|
|
5 | +main = return () |
... | ... | @@ -376,3 +376,9 @@ test('test-late-plugin', |
376 | 376 | [extra_files(['late-plugin/LatePlugin.hs']), ignore_stdout],
|
377 | 377 | makefile_test,
|
378 | 378 | [])
|
379 | + |
|
380 | +test('T21730',
|
|
381 | + [extra_files(['T21730-plugin/']),
|
|
382 | + pre_cmd('$MAKE -s --no-print-directory -C T21730-plugin package.T21730-plugin TOP={top}')
|
|
383 | + ],
|
|
384 | + makefile_test, []) |