[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 5 commits: Hadrian: fix ghc-internal .def file name
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC Commits: 1314f2fd by David Eichmann at 2026-06-16T05:46:52-04:00 Hadrian: fix ghc-internal .def file name - - - - - 7f72bcb3 by mangoiv at 2026-06-16T05:47:39-04:00 compiler: ignore camelCase and Eta reduce hlint hints These do not cohere with the style used in GHC. After disabling them, hlint lints are much less noisy again. - - - - - 842bef9f by Alan Zimmerman at 2026-06-16T05:48:25-04:00 EPA: Use standard type family declaration for Anno - - - - - 38b3e94d by Wolfgang Jeltsch at 2026-06-16T11:32:09-04:00 Fix two issues in the documentation of pipeline interruption One issue is a typo (“interreuptible”), the other one the lack of an end of a sentence, which has been reconstructed from the message of 633bbc1fd4762a2bb73ba1c5b9e0c279f1dd3c40, the commit that introduced said documentation. - - - - - 678ee355 by Christian Georgii at 2026-06-16T11:32:13-04:00 Find plugins in sibling home units in multiple-home-unit sessions In a multiple-home-unit session (e.g. `cabal repl --enable-multi-repl` or HLS), enabling a plugin with -fplugin that is defined in (or reexported by) another home unit failed with a "hidden package" error. The plugin module finder only searched the current home unit and the registered external packages, never the sibling home units. findPluginModuleNoHsc now searches the home units that the current home unit depends on, following module reexports and respecting hidden modules, exactly as ordinary import resolution does in findImportedModuleNoHsc. To avoid two divergent copies of this logic, the shared home-unit search (the current home unit first, then its dependencies in priority order, with the accompanying ordering invariant) is extracted into findHomeModuleAmongDeps, which both findImportedModuleNoHsc and findPluginModuleNoHsc now call. Add testsuite/tests/driver/multipleHomeUnits/plugin01, which loads a plugin as byte-code from a sibling home unit, and plugin02, in which the consumer enables a plugin reexported by a sibling home unit without depending on the plugin's own home unit directly. Fixes #27349 - - - - - 18 changed files: - .gitlab-ci.yml - + changelog.d/fix-plugin-finder-multi-home-unit.md - compiler/.hlint.yaml - compiler/GHC/Unit/Finder.hs - compiler/Language/Haskell/Syntax/Extension.hs - hadrian/src/Rules/Rts.hs - + testsuite/tests/driver/multipleHomeUnits/plugin01/all.T - + testsuite/tests/driver/multipleHomeUnits/plugin01/appunit - + testsuite/tests/driver/multipleHomeUnits/plugin01/p/MyPlugin.hs - + testsuite/tests/driver/multipleHomeUnits/plugin01/pluginunit - + testsuite/tests/driver/multipleHomeUnits/plugin01/q/App.hs - + testsuite/tests/driver/multipleHomeUnits/plugin02/all.T - + testsuite/tests/driver/multipleHomeUnits/plugin02/appunit - + testsuite/tests/driver/multipleHomeUnits/plugin02/p/MyPlugin.hs - + testsuite/tests/driver/multipleHomeUnits/plugin02/pluginunit - + testsuite/tests/driver/multipleHomeUnits/plugin02/q/App.hs - + testsuite/tests/driver/multipleHomeUnits/plugin02/r/RexLib.hs - + testsuite/tests/driver/multipleHomeUnits/plugin02/reexportunit Changes: ===================================== .gitlab-ci.yml ===================================== @@ -194,8 +194,9 @@ workflow: # https://gitlab.com/gitlab-org/gitlab/-/issues/32837). This leads to the # hack in this MR where by default all jobs are `interruptible: True`, but # for pipelines we definitely want to run, there is a dummy job which -# happens first, which is `interreuptible: False`. This has the effect of -# dirtying the whole pipeline and +# happens first, which is `interruptible: False`. This has the effect of +# dirtying the whole pipeline and preventing another push to master from +# cancelling it. # # For now, this patch solves the immediate problem of making sure nightly # jobs are not cancelled. ===================================== changelog.d/fix-plugin-finder-multi-home-unit.md ===================================== @@ -0,0 +1,7 @@ +section: compiler +synopsis: Find plugins defined in sibling home units in multiple-home-unit sessions +issues: #27349 +mrs: !16156 +description: { + In a multiple-home-unit session (e.g. ``cabal repl --enable-multi-repl`` or HLS), the plugin finder now also searches the home units that the current home unit depends on, following module reexports along the way, so a ``-fplugin`` defined in (or reexported by) a sibling home unit is found and loaded instead of failing as a hidden package. +} ===================================== compiler/.hlint.yaml ===================================== @@ -3,6 +3,8 @@ ########################## - ignore: {} +- ignore: {name: Use camelCase} +- ignore: {name: Eta reduce} - warn: {name: Unused LANGUAGE pragma} - warn: {name: Use fewer LANGUAGE pragmas} - warn: {name: Redundant return} ===================================== compiler/GHC/Unit/Finder.hs ===================================== @@ -231,36 +231,14 @@ findImportedModuleNoHsc fc fopts ue home_module_name_providers_map mb_home_unit NoPackage (panic "findImportedModule: no home-unit") home_pkg_import :: (UnitId, FinderOpts) -> IO FindResult - home_pkg_import (uid, opts) - -- If the module is reexported, then look for it as if it was from the - -- perspective of that package which reexports it. - | Just real_mod_name - <- lookupUniqMap (finder_reexportedModules opts) mod_name - = findImportedModuleNoHsc fc opts ue home_module_name_providers_map - (Just $ DefiniteHomeUnit uid Nothing) - real_mod_name - NoPkgQual - | elementOfUniqSet mod_name (finder_hiddenModules opts) - = return (mkHomeHidden uid) - | otherwise - = findHomePackageModule fc opts uid mod_name - - any_home_import :: IO FindResult - any_home_import = foldr1 orIfNotFound $ - home_import :| map home_pkg_import other_fopts - -- Do not try to be smart and change this to `foldr orIfNotFound home_import - -- (map home_pkg_import other_fopts)`, as that would not be the same. - -- `home_import` is first because we need to first look within the current - -- unit before looking at the other units in order. + home_pkg_import = findHomeUnitDepModule fc ue home_module_name_providers_map mod_name pkg_import :: IO FindResult pkg_import = findExposedPackageModule fc fopts unit_state mod_name mb_pkg unqual_import :: IO FindResult - unqual_import - = any_home_import - `orIfNotFound` - findExposedPackageModule fc fopts unit_state mod_name NoPkgQual + unqual_import = findHomeOrRegularPackageModule fc fopts ue + home_module_name_providers_map mb_home_unit mod_name unit_state :: UnitState unit_state = case mb_home_unit_id of @@ -268,22 +246,44 @@ findImportedModuleNoHsc fc fopts ue home_module_name_providers_map mb_home_unit Just home_unit_id -> HUG.homeUnitEnv_units $ ue_findHomeUnitEnv home_unit_id ue - home_unit_deps :: Set UnitId - home_unit_deps = homeUnitDepends unit_state + other_fopts :: [(UnitId, FinderOpts)] + other_fopts = homeUnitDepsFinderOpts ue home_module_name_providers_map + unit_state mod_name + +-- | Locate a plugin module requested by the user, for a compiler +-- plugin. This consults the same set of exposed packages as +-- 'findImportedModule', unless @-hide-all-plugin-packages@ or +-- @-plugin-package@ are specified. +findPluginModuleNoHsc + :: FinderCache + -> FinderOpts + -> UnitEnv + -> HomeModuleNameProvidersMap + -> Maybe HomeUnit + -> ModuleName + -> IO FindResult +findPluginModuleNoHsc fc fopts ue home_module_name_providers_map mb_home_unit@(Just home_unit) mod_name = + findHomeModuleAmongDeps fc fopts ue home_module_name_providers_map + mb_home_unit mod_name + `orIfNotFound` + findExposedPluginPackageModule fc fopts unit_state mod_name + where + unit_state = HUG.homeUnitEnv_units $ + ue_findHomeUnitEnv (homeUnitId home_unit) ue +findPluginModuleNoHsc fc fopts ue _ Nothing mod_name = + findExposedPluginPackageModule fc fopts (ue_homeUnitState ue) mod_name - ranked_home_unit_deps :: [UnitId] - ranked_home_unit_deps = rankedHomeUnitDeps home_module_name_providers_map - mod_name - home_unit_deps +findPluginModule :: HscEnv -> ModuleName -> IO FindResult +findPluginModule hsc_env mod_name = do + let fc = hsc_FC hsc_env + mb_home_unit = hsc_home_unit_maybe hsc_env + home_module_name_providers_map = + mgHomeModuleNameProvidersMap (hsc_mod_graph hsc_env) + findPluginModuleNoHsc fc (initFinderOpts (hsc_dflags hsc_env)) + (hsc_unit_env hsc_env) home_module_name_providers_map mb_home_unit mod_name - other_fopts :: [(UnitId, FinderOpts)] - other_fopts - = [ - (uid, opts) | - uid <- ranked_home_unit_deps, - let opts = initFinderOpts $ - homeUnitEnv_dflags (ue_findHomeUnitEnv uid ue) - ] +-- ----------------------------------------------------------------------------- +-- Home Module Finder Helpers -- | Yields the unit IDs from the given set as a list with those that refer to -- providers of the given home module name coming first. This is to prioritize @@ -323,24 +323,93 @@ rankedHomeUnitDeps home_module_name_providers_map mod_name home_unit_deps uncached_deps :: Set UnitId uncached_deps = Set.difference home_unit_deps cached_providers --- | Locate a plugin module requested by the user, for a compiler --- plugin. This consults the same set of exposed packages as --- 'findImportedModule', unless @-hide-all-plugin-packages@ or --- @-plugin-package@ are specified. -findPluginModuleNoHsc :: FinderCache -> FinderOpts -> UnitState -> Maybe HomeUnit -> ModuleName -> IO FindResult -findPluginModuleNoHsc fc fopts units (Just home_unit) mod_name = - findHomeModule fc fopts home_unit mod_name - `orIfNotFound` - findExposedPluginPackageModule fc fopts units mod_name -findPluginModuleNoHsc fc fopts units Nothing mod_name = - findExposedPluginPackageModule fc fopts units mod_name +-- | The 'FinderOpts' of the home units that should be searched, sorted by +-- priority order specified by 'rankedHomeUnitDeps'. +homeUnitDepsFinderOpts + :: UnitEnv + -> HomeModuleNameProvidersMap + -> UnitState -- ^ unit state of the requesting home unit + -> ModuleName + -> [(UnitId, FinderOpts)] +homeUnitDepsFinderOpts ue home_module_name_providers_map unit_state mod_name = + [ (uid, initFinderOpts (ue_unitFlags uid ue)) + | uid <- rankedHomeUnitDeps home_module_name_providers_map mod_name + (homeUnitDepends unit_state) + ] + +-- | Search for @mod_name@ in the given home unit. +findHomeUnitDepModule + :: FinderCache + -> UnitEnv + -> HomeModuleNameProvidersMap + -> ModuleName + -> (UnitId, FinderOpts) + -> IO FindResult +findHomeUnitDepModule fc ue home_module_name_providers_map mod_name (uid, opts) + -- If the module is reexported, then look for it as if it was from the + -- perspective of the package which reexports it. + | Just real_mod_name + <- lookupUniqMap (finder_reexportedModules opts) mod_name + = findHomeOrRegularPackageModule fc opts ue home_module_name_providers_map + (Just $ DefiniteHomeUnit uid Nothing) + real_mod_name + | elementOfUniqSet mod_name (finder_hiddenModules opts) + = return (mkHomeHidden uid) + | otherwise + = findHomePackageModule fc opts uid mod_name + +-- | Search for @mod_name@ among the home units: first the current home unit, +-- then the home units it depends on, in priority order, following module +-- reexports along the way (see 'findHomeUnitDepModule'). Yields the first +-- successful result. +findHomeModuleAmongDeps + :: FinderCache + -> FinderOpts + -> UnitEnv + -> HomeModuleNameProvidersMap + -> Maybe HomeUnit + -> ModuleName + -> IO FindResult +findHomeModuleAmongDeps fc fopts ue home_module_name_providers_map mb_home_unit mod_name = + foldr1 orIfNotFound (home_import :| map home_pkg_import other_fopts) + -- Do not try to be smart and change this to `foldr orIfNotFound home_import + -- (map home_pkg_import other_fopts)`, as that would not be the same. + -- `home_import` is first because we need to first look within the current + -- unit before looking at the other units in order. + where + home_import = case mb_home_unit of + Just home_unit -> findHomeModule fc fopts home_unit mod_name + Nothing -> pure $ + NoPackage (panic "findHomeModuleAmongDeps: no home-unit") + home_pkg_import = findHomeUnitDepModule fc ue home_module_name_providers_map mod_name -findPluginModule :: HscEnv -> ModuleName -> IO FindResult -findPluginModule hsc_env mod_name = do - let fc = hsc_FC hsc_env - let units = hsc_units hsc_env - let mb_home_unit = hsc_home_unit_maybe hsc_env - findPluginModuleNoHsc fc (initFinderOpts (hsc_dflags hsc_env)) units mb_home_unit mod_name + unit_state = case homeUnitId <$> mb_home_unit of + Nothing -> ue_homeUnitState ue + Just home_unit_id -> HUG.homeUnitEnv_units $ + ue_findHomeUnitEnv home_unit_id ue + other_fopts = homeUnitDepsFinderOpts ue home_module_name_providers_map + unit_state mod_name + +-- | Search the home-unit graph and otherwise the regular exposed package +-- database. +findHomeOrRegularPackageModule + :: FinderCache + -> FinderOpts + -> UnitEnv + -> HomeModuleNameProvidersMap + -> Maybe HomeUnit + -> ModuleName + -> IO FindResult +findHomeOrRegularPackageModule fc fopts ue home_module_name_providers_map mb_home_unit mod_name = + findHomeModuleAmongDeps fc fopts ue home_module_name_providers_map + mb_home_unit mod_name + `orIfNotFound` + findExposedPackageModule fc fopts unit_state mod_name NoPkgQual + where + unit_state = case homeUnitId <$> mb_home_unit of + Nothing -> ue_homeUnitState ue + Just home_unit_id -> HUG.homeUnitEnv_units $ + ue_findHomeUnitEnv home_unit_id ue -- | A version of findExactModule which takes the exact parts of the HscEnv it needs ===================================== compiler/Language/Haskell/Syntax/Extension.hs ===================================== @@ -108,7 +108,7 @@ dataConCantHappen x = case x of {} -- See Note [XRec and SrcSpans in the AST] type family XRec p a = r | r -> a -type family Anno a = b -- See Note [XRec and Anno in the AST] in GHC.Parser.Annotation +type family Anno a -- See Note [XRec and Anno in the AST] in GHC.Parser.Annotation {- Note [XRec and SrcSpans in the AST] ===================================== hadrian/src/Rules/Rts.hs ===================================== @@ -25,7 +25,7 @@ buildGhcInternalImportDef target = do buildGhcInternalImportLib :: FilePath -> Action () buildGhcInternalImportLib target = do - let input = dropExtensions target <.> "def" -- the .def file + let input = dropExtension (dropExtension target) <.> "def" -- the .def file output = target -- the .dll.a import lib need [input] runBuilder Dlltool ["-d", input, "-l", output] [input] [output] ===================================== testsuite/tests/driver/multipleHomeUnits/plugin01/all.T ===================================== @@ -0,0 +1,14 @@ +# A plugin (MyPlugin, in home unit plugin-0) is enabled by a module (App, in +# home unit app-0) belonging to a *different* home unit in the same session. +# Resolving -fplugin must search sibling home units; previously the plugin +# finder only looked at the current home unit and registered external packages, +# so this failed with "Could not load module 'MyPlugin' ... hidden package". +# +# -fbyte-code-and-object-code -fprefer-byte-code makes the plugin load as +# byte-code from its (interpreted) home unit, mirroring how a multiple-home-unit +# session is driven by 'cabal repl --enable-multi-repl' and HLS. +test('multipleHomeUnits_plugin01', + [req_plugins, extra_files(['p/', 'q/', 'pluginunit', 'appunit'])], + multiunit_compile, + [['pluginunit', 'appunit'], + '-v0 -fbyte-code-and-object-code -fprefer-byte-code']) ===================================== testsuite/tests/driver/multipleHomeUnits/plugin01/appunit ===================================== @@ -0,0 +1 @@ +-working-dir q App -this-unit-id app-0 -this-package-name app -package-id plugin-0 ===================================== testsuite/tests/driver/multipleHomeUnits/plugin01/p/MyPlugin.hs ===================================== @@ -0,0 +1,8 @@ +module MyPlugin (plugin) where + +import GHC.Plugins + +-- A no-op plugin: it makes no changes to the program but, to run at all, it +-- must be found and loaded by the consumer module's compilation. +plugin :: Plugin +plugin = defaultPlugin { pluginRecompile = purePlugin } ===================================== testsuite/tests/driver/multipleHomeUnits/plugin01/pluginunit ===================================== @@ -0,0 +1 @@ +-working-dir p MyPlugin -this-unit-id plugin-0 -this-package-name plugin -package ghc ===================================== testsuite/tests/driver/multipleHomeUnits/plugin01/q/App.hs ===================================== @@ -0,0 +1,8 @@ +{-# OPTIONS_GHC -fplugin=MyPlugin #-} +module App where + +-- The plugin 'MyPlugin' lives in a *sibling* home unit (plugin-0), not in a +-- registered package. Resolving -fplugin must therefore search sibling home +-- units, just like an ordinary import does. +app :: () +app = () ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/all.T ===================================== @@ -0,0 +1,16 @@ +# A plugin (MyPlugin) lives in home unit plugin-0 and is *reexported* by a +# second home unit, reexport-0 (via -reexported-module). The consumer module +# App, in home unit app-0, enables it with -fplugin=MyPlugin but depends only on +# reexport-0, not on plugin-0 directly. Resolving -fplugin must therefore follow +# the reexport through the sibling home unit, exactly as an ordinary import +# does. A home-unit search that ignores reexported-modules fails with +# "Could not load module 'MyPlugin' ... hidden package". +# +# -fbyte-code-and-object-code -fprefer-byte-code makes the plugin load as +# byte-code from its (interpreted) home unit, mirroring how a multiple-home-unit +# session is driven by 'cabal repl --enable-multi-repl' and HLS. +test('multipleHomeUnits_plugin02', + [req_plugins, extra_files(['p/', 'q/', 'r/', 'pluginunit', 'reexportunit', 'appunit'])], + multiunit_compile, + [['pluginunit', 'reexportunit', 'appunit'], + '-v0 -fbyte-code-and-object-code -fprefer-byte-code']) ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/appunit ===================================== @@ -0,0 +1 @@ +-working-dir q App -this-unit-id app-0 -this-package-name app -package-id reexport-0 ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/p/MyPlugin.hs ===================================== @@ -0,0 +1,8 @@ +module MyPlugin (plugin) where + +import GHC.Plugins + +-- A no-op plugin: it makes no changes to the program but, to run at all, it +-- must be found and loaded by the consumer module's compilation. +plugin :: Plugin +plugin = defaultPlugin { pluginRecompile = purePlugin } ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/pluginunit ===================================== @@ -0,0 +1 @@ +-working-dir p MyPlugin -this-unit-id plugin-0 -this-package-name plugin -package ghc ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/q/App.hs ===================================== @@ -0,0 +1,9 @@ +{-# OPTIONS_GHC -fplugin=MyPlugin #-} +module App where + +-- The plugin 'MyPlugin' lives in home unit plugin-0, but this unit (app-0) +-- depends only on reexport-0, which *reexports* MyPlugin (it does not contain +-- MyPlugin itself). Resolving -fplugin must therefore follow the reexport into +-- the sibling home unit, exactly as an ordinary import does. +app :: () +app = () ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/r/RexLib.hs ===================================== @@ -0,0 +1,8 @@ +module RexLib where + +-- Home unit reexport-0 re-exports MyPlugin (from plugin-0) via the +-- -reexported-module flag; see the 'reexportunit' arguments. This module exists +-- only to make reexport-0 a non-empty home unit, mirroring a real library +-- package that reexports a module from one of its dependencies. +rexLib :: () +rexLib = () ===================================== testsuite/tests/driver/multipleHomeUnits/plugin02/reexportunit ===================================== @@ -0,0 +1 @@ +-working-dir r RexLib -this-unit-id reexport-0 -this-package-name reexport -package-id plugin-0 -reexported-module MyPlugin View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/8c3af523b8cd0900ae37d4926488327... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/8c3af523b8cd0900ae37d4926488327... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Marge Bot (@marge-bot)