[Git][ghc/ghc][wip/mpickering/get-link-deps] 5 commits: stable sort
Matthew Pickering (@mpickering)
gitlab at gitlab.haskell.org
Fri Jan 3 17:52:33 UTC 2025
Matthew Pickering pushed to branch wip/mpickering/get-link-deps at Glasgow Haskell Compiler / GHC
Commits:
4c9a552e by Matthew Pickering at 2025-01-03T12:52:52+00:00
stable sort
- - - - -
60579bfd by Matthew Pickering at 2025-01-03T12:52:57+00:00
Revert "stable sort"
This reverts commit 4c9a552e74a0f71457ade17f1074d6c87d5b6d7c.
- - - - -
74376306 by Matthew Pickering at 2025-01-03T14:34:34+00:00
Revert "WIP: Use a stable scc algorithm"
This reverts commit c1f36074deb6f3b1508aa9ac92d464dd20624c82.
- - - - -
a2d7b093 by Matthew Pickering at 2025-01-03T17:22:39+00:00
fixes
- - - - -
d29af41a by Matthew Pickering at 2025-01-03T17:51:32+00:00
Rework module structure
- - - - -
10 changed files:
- compiler/GHC/Data/Graph/Directed/Internal.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Unit/Module/Graph.hs
- compiler/GHC/Unit/Module/ModIface.hs
- + compiler/GHC/Unit/Module/ModNodeKey.hs
- compiler/ghc.cabal.in
- testsuite/tests/count-deps/CountDepsAst.stdout
Changes:
=====================================
compiler/GHC/Data/Graph/Directed/Internal.hs
=====================================
@@ -7,10 +7,6 @@ import Data.Array
import qualified Data.Graph as G
import Data.Graph ( Vertex, SCC(..) ) -- Used in the underlying representation
import Data.Tree
-import Data.Array.ST.Safe (STUArray)
-import Control.Monad.ST
-import Data.Array.ST.Safe (newArray, readArray, writeArray)
-import Data.List (sort)
data Graph node = Graph {
gr_int_graph :: IntGraph,
@@ -73,7 +69,7 @@ reachable g vs = preorderF (G.dfs g vs)
scc :: IntGraph -> [SCC Vertex]
scc graph = map decode forest
where
- forest = {-# SCC "Digraph.scc" #-} scc2 graph
+ forest = {-# SCC "Digraph.scc" #-} G.scc graph
decode (Node v []) | mentions_itself v = CyclicSCC [v]
| otherwise = AcyclicSCC v
@@ -81,68 +77,3 @@ scc graph = map decode forest
where dec (Node v ts) vs = v : foldr dec vs ts
mentions_itself v = v `elem` (graph ! v)
-
-
-newtype SetM s a = SetM { runSetM :: STUArray s Vertex Bool -> ST s a }
-
-instance Monad (SetM s) where
- return = pure
- {-# INLINE return #-}
- SetM v >>= f = SetM $ \s -> do { x <- v s; runSetM (f x) s }
- {-# INLINE (>>=) #-}
-
-instance Functor (SetM s) where
- f `fmap` SetM v = SetM $ \s -> f `fmap` v s
- {-# INLINE fmap #-}
-
-instance Applicative (SetM s) where
- pure x = SetM $ const (return x)
- {-# INLINE pure #-}
- SetM f <*> SetM v = SetM $ \s -> f s >>= (`fmap` v s)
- -- We could also use the following definition
- -- SetM f <*> SetM v = SetM $ \s -> f s <*> v s
- -- but Applicative (ST s) instance is present only in GHC 7.2+
- {-# INLINE (<*>) #-}
-
-run :: G.Bounds -> (forall s. SetM s a) -> a
-run bnds act = runST (newArray bnds False >>= runSetM act)
-
-contains :: Vertex -> SetM s Bool
-contains v = SetM $ \ m -> readArray m v
-
-include :: Vertex -> SetM s ()
-include v = SetM $ \ m -> writeArray m v True
-
-scc2 :: G.Graph -> [Tree Vertex]
-scc2 g = dfs g (reverse (postOrd (G.transposeG g)))
-
-postorder :: Tree a -> [a] -> [a]
-postorder (Node a ts) = postorderF ts . (a :)
-
-postorderF :: [Tree a] -> [a] -> [a]
-postorderF ts = foldr (.) id $ map postorder ts
-
-postOrd :: G.Graph -> [Vertex]
-postOrd g = postorderF (dff g) []
-
-dff :: G.Graph -> [Tree Vertex]
-dff g = dfs g (G.vertices g)
-
-
--- This dfs provides stability under transposition.
-dfs :: G.Graph -> [Vertex] -> [Tree Vertex]
-dfs g vs0 = run (bounds g) $ go vs0
- where
- go :: [Vertex] -> SetM s [Tree Vertex]
- go [] = pure []
- go (k: is') = do
- visited <- contains k
- if visited
- then go is'
- else do
- include k
- as <- go (sort (g!k))
- bs <- go is'
- pure $ Node k as : bs
-
-
=====================================
compiler/GHC/Driver/Make.hs
=====================================
@@ -125,7 +125,8 @@ import qualified Control.Monad.Catch as MC
import Data.IORef
import Data.Maybe
import Data.Time
-import Data.List (sortOn, unfoldr)
+import Data.Function
+import Data.List (sortOn, unfoldr, sortBy)
import Data.Bifunctor (first)
import System.Directory
import System.FilePath
@@ -1491,7 +1492,24 @@ topSortModuleGraph
topSortModuleGraph drop_hs_boot_nodes module_graph mb_root_mod =
-- stronglyConnCompG flips the original order, so if we reverse
-- the summaries we get a stable topological sort.
- topSortModules drop_hs_boot_nodes (reverse $ mgModSummaries' module_graph) mb_root_mod
+ topSortModules drop_hs_boot_nodes (sortBy (cmpModuleGraphNodes `on` mkNodeKey) $ mgModSummaries' module_graph) mb_root_mod
+
+
+ where
+ -- In order to get the "right" ordering
+ -- Module nodes must be in reverse lexigraphic order.
+ -- All modules nodes must appear before package nodes or link nodes.
+ --
+ -- Given the current implementation of scc, the result is in
+ -- The order is sensitive to the internal implementation in Data.Graph,
+ -- if it changes in future then this ordering will need to be modified.
+ cmpModuleGraphNodes (NodeKey_Module k1) (NodeKey_Module k2) = compare k2 k1
+ cmpModuleGraphNodes (NodeKey_Link uid1) (NodeKey_Link uid2) = compare uid2 uid1
+ cmpModuleGraphNodes (NodeKey_Link {}) _ = LT
+ cmpModuleGraphNodes _ (NodeKey_Link {}) = GT
+ cmpModuleGraphNodes (NodeKey_Module {}) _ = LT
+ cmpModuleGraphNodes _ (NodeKey_Module {}) = GT
+ cmpModuleGraphNodes n1 n2 = compare n2 n1
topSortModules :: Bool -> [ModuleGraphNode] -> Maybe HomeUnitModule -> [SCC ModuleGraphNode]
topSortModules drop_hs_boot_nodes summaries mb_root_mod
@@ -1723,7 +1741,10 @@ downsweep_imports hsc_env old_summaries excl_mods allow_dup_roots (root_errs, ro
case mb_s of
NotThere -> loopImports ss done summarised
External uid -> do
- let done' = loopUnit done [uid]
+ -- Pass an updated hsc_env to loopUnit, as each unit might
+ -- have a different visible package database.
+ let hsc_env' = hscSetActiveHomeUnit home_unit hsc_env
+ let done' = loopUnit hsc_env' done [uid]
(other_deps, done'', summarised') <- loopImports ss done' summarised
return (NodeKey_ExternalUnit uid : other_deps, done'', summarised')
FoundInstantiation iud -> do
@@ -1743,14 +1764,14 @@ downsweep_imports hsc_env old_summaries excl_mods allow_dup_roots (root_errs, ro
GWIB { gwib_mod = L loc mod, gwib_isBoot = is_boot } = gwib
wanted_mod = L loc mod
- loopUnit :: Map.Map NodeKey ModuleGraphNode -> [UnitId] -> Map.Map NodeKey ModuleGraphNode
- loopUnit cache [] = cache
- loopUnit cache (u:uxs) = do
+ loopUnit :: HscEnv -> Map.Map NodeKey ModuleGraphNode -> [UnitId] -> Map.Map NodeKey ModuleGraphNode
+ loopUnit _ cache [] = cache
+ loopUnit lcl_hsc_env cache (u:uxs) = do
let nk = (NodeKey_ExternalUnit u)
case Map.lookup nk cache of
- Just {} -> loopUnit cache uxs
- Nothing -> case unitDepends <$> lookupUnitId (hsc_units hsc_env) u of
- Just us -> loopUnit (loopUnit (Map.insert nk (PackageNode us u) cache) us) uxs
+ Just {} -> loopUnit lcl_hsc_env cache uxs
+ Nothing -> case unitDepends <$> lookupUnitId (hsc_units lcl_hsc_env) u of
+ Just us -> loopUnit lcl_hsc_env (loopUnit lcl_hsc_env (Map.insert nk (PackageNode us u) cache) us) uxs
Nothing -> pprPanic "loopUnit" (text "Malformed package database, missing " <+> ppr u)
getRootSummary ::
=====================================
compiler/GHC/Iface/Load.hs
=====================================
@@ -25,7 +25,7 @@ module GHC.Iface.Load (
-- IfM functions
loadInterface,
loadSysInterface, loadUserInterface, loadPluginInterface,
- loadHomePackageInterfacesBelow,
+ loadExternalGraphBelow,
findAndReadIface, readIface, writeIface,
flagsToIfCompression,
moduleFreeHolesPrecise,
@@ -423,44 +423,96 @@ loadInterfaceWithException doc mod_name where_from
-- For convenience, reads the module graph out of the EPS after having loaded
-- all the modules and returns it. It would be harder to get the updated module
-- graph in 'getLinkDeps' another way.
-loadHomePackageInterfacesBelow :: (Module -> SDoc) -> Maybe HomeUnit {-^ The current home unit -}
- -> [Module] -> IfM lcl ()
-loadHomePackageInterfacesBelow _ Nothing _ = panic "loadHomePackageInterfacesBelow: No home unit"
-loadHomePackageInterfacesBelow msg (Just home_unit) mods = do
+loadExternalGraphBelow :: (Module -> SDoc) -> Maybe HomeUnit {-^ The current home unit -}
+ -> Set.Set ExternalKey -> [Module] -> IfM lcl (Set.Set ExternalKey)
+loadExternalGraphBelow _ Nothing _ _ = panic "loadHomePackageInterfacesBelow: No home unit"
+loadExternalGraphBelow msg (Just home_unit) init_loaded mods =
+ foldM (loadExternalGraphModule msg home_unit) init_loaded mods
+
+loadExternalGraphModule :: (Module -> SDoc) -> HomeUnit
+ -> Set.Set ExternalKey -> Module -> IfM lcl (Set.Set ExternalKey)
+loadExternalGraphModule msg home_unit init_loaded mod
+ | homeUnitId home_unit /= moduleUnitId mod = do
+ loadExternalPackageBelow init_loaded (moduleUnitId mod)
+ | otherwise = do
+
+ let key = ExternalModuleKey $ ModNodeKeyWithUid (GWIB (moduleName mod) NotBoot) (moduleUnitId mod)
+ let new_cache = Set.insert key init_loaded
+ graph <- eps_module_graph <$> getEps
+
+ if (not (isFullyLoadedModule key graph || Set.member key init_loaded))
+ then actuallyLoadExternalGraphModule msg home_unit new_cache key mod
+ else
+ return init_loaded
+
+actuallyLoadExternalGraphModule
+ :: (Module -> SDoc)
+ -> HomeUnit
+ -> Set.Set ExternalKey
+ -> ExternalKey
+ -> Module
+ -> IOEnv (Env IfGblEnv lcl) (Set.Set ExternalKey)
+actuallyLoadExternalGraphModule msg home_unit new_cache key mod = do
dflags <- getDynFlags
let ctx = initSDocContext dflags defaultUserStyle
+ iface <- withIfaceErr ctx $
+ loadInterface (msg mod) mod (ImportByUser NotBoot)
+
+ -- RM:TODO: THINGS WE ARE NOT DOING
+ --
+ -- The ModIface contains the transitive closure of the module dependencies
+ -- within the current package, *except* for boot modules: if we encounter
+ -- a boot module, we have to find its real interface and discover the
+ -- dependencies of that. Hence we need to traverse the dependency
+ -- tree recursively. See bug #936, testcase ghci/prog007.
+
+ let deps = mi_deps iface
+ mod_deps = dep_direct_mods deps
+ pkg_deps = dep_direct_pkgs deps
+
+ -- Load all direct dependencies that are in the home package
+ cache_mods <- loadExternalGraphBelow msg (Just home_unit) new_cache
+ $ map (\(uid, GWIB mn _) -> mkModule (RealUnit (Definite uid)) mn)
+ $ Set.toList mod_deps
+
+ cache_pkgs <- foldM loadExternalPackageBelow cache_mods (Set.toList pkg_deps)
+
+ registerFullyLoaded key
+ return cache_pkgs
+
+registerFullyLoaded :: ExternalKey -> IfM lcl ()
+registerFullyLoaded key = do
+ -- Update the external graph with this module being fully loaded.
+ logger <- getLogger
+ liftIO $ trace_if logger (text "Fully loaded:" <+> ppr key)
+ updateEps_ $ \eps ->
+ eps{eps_module_graph = setFullyLoadedModule key (eps_module_graph eps)}
- forM_ mods $ \mod -> do
-
+loadExternalPackageBelow :: Set.Set ExternalKey -> UnitId -> IfM lcl (Set.Set ExternalKey)
+loadExternalPackageBelow cache uid = do
graph <- eps_module_graph <$> getEps
- let key = ExternalModuleKey $ ModNodeKeyWithUid (GWIB (moduleName mod) NotBoot) (moduleUnitId mod)
-
- if isFullyLoadedModule key graph
- then return ()
- else do
- iface <- withIfaceErr ctx $
- loadInterface (msg mod) mod (ImportByUser NotBoot)
+ us <- hsc_units <$> getTopEnv
+ let key = ExternalPackageKey uid
+ let cache' = Set.insert key cache
+ if not (isFullyLoadedModule key graph || Set.member key cache)
+ then do
+ case unitDepends <$> lookupUnitId us uid of
+ Just dep_uids -> do
+ loadPackageIntoEPSGraph uid dep_uids
+ final_cache <- foldM loadExternalPackageBelow cache' dep_uids
+ registerFullyLoaded key
+ return final_cache
+ Nothing -> pprPanic "loadExternalPackagesBelow: missing" (ppr uid)
+ else
+ return cache
- -- RM:TODO: THINGS WE ARE NOT DOING
- --
- -- The ModIface contains the transitive closure of the module dependencies
- -- within the current package, *except* for boot modules: if we encounter
- -- a boot module, we have to find its real interface and discover the
- -- dependencies of that. Hence we need to traverse the dependency
- -- tree recursively. See bug #936, testcase ghci/prog007.
- let deps = mi_deps iface
- mod_deps = dep_direct_mods deps
- -- Load all direct dependencies that are in the home package
- loadHomePackageInterfacesBelow msg (Just home_unit)
- $ map (\(uid, GWIB mn _) -> mkModule (RealUnit (Definite uid)) mn)
- $ filter ((==) (homeUnitId home_unit) . fst)
- $ Set.toList mod_deps
+loadPackageIntoEPSGraph :: UnitId -> [UnitId] -> IfM lcl ()
+loadPackageIntoEPSGraph uid dep_uids =
+ updateEps_ $ \eps ->
+ eps { eps_module_graph = extendExternalModuleGraph (NodeExternalPackage uid (Set.fromList dep_uids)) (eps_module_graph eps) }
- -- Update the external graph with this module being fully loaded.
- updateEps_ $ \eps ->
- eps{eps_module_graph = setFullyLoadedModule key (eps_module_graph eps)}
------------------
loadInterface :: SDoc -> Module -> WhereFrom
@@ -571,24 +623,13 @@ loadInterface doc_str mod from
; purged_hsc_env <- getTopEnv
; let direct_deps = map (uncurry (flip ModNodeKeyWithUid)) $ (Set.toList (dep_direct_mods $ mi_deps iface))
- ; let direct_pkg_deps = dep_direct_pkgs $ mi_deps iface
+ ; let direct_pkg_deps = Set.toList $ dep_direct_pkgs $ mi_deps iface
; let !module_graph_key = -- pprTrace "module_graph_on_load" (ppr (eps_module_graph eps)) $
if moduleUnitId mod `elem` hsc_all_home_unit_ids hsc_env
--- ^ home unit mods in eps can only happen in oneshot mode
- then Just $ NodeHomePackage (miKey iface) (map ExternalModuleKey direct_deps)
+ then Just $ NodeHomePackage (miKey iface) (map ExternalModuleKey direct_deps
+ ++ map ExternalPackageKey direct_pkg_deps)
else Nothing
- ; let !module_graph_pkg_key =
- -- ROMES:TODO: This doesn't work as expected. Insertions on the
- -- graph don't override previous nodes, they just create new
- -- ones. We get multiple duplicate nodes with incomplete dependencies each.
- let pkg_key = toUnitId $ moduleUnit (mi_module iface)
- in case emgLookupKey (ExternalPackageKey pkg_key) (eps_module_graph eps) of
- Nothing -> NodeExternalPackage pkg_key direct_pkg_deps
- Just pkg_node -> case pkg_node of
- NodeHomePackage{} -> panic "ExternalPackageKey lookup should never return a NodeHomePackage node"
- NodeExternalPackage _ deps_uids ->
- NodeExternalPackage pkg_key (deps_uids `Set.union` direct_pkg_deps)
-
; let final_iface = iface
& set_mi_decls (panic "No mi_decls in PIT")
@@ -634,8 +675,7 @@ loadInterface doc_str mod from
let eps_graph' = case module_graph_key of
Just k -> extendExternalModuleGraph k (eps_module_graph eps)
Nothing -> eps_module_graph eps
- eps_graph'' = extendExternalModuleGraph module_graph_pkg_key eps_graph'
- in eps_graph'',
+ in eps_graph',
eps_complete_matches
= eps_complete_matches eps ++ new_eps_complete_matches,
eps_inst_env = extendInstEnvList (eps_inst_env eps)
=====================================
compiler/GHC/Linker/Deps.hs
=====================================
@@ -233,10 +233,12 @@ get_reachable_nodes opts mods
| otherwise
= return Nothing
- go (ExternalModuleKey . mkModuleNk) emgNodeKey (emgReachableMany emg) (map emgProject) get_mod_info_eps
- --romes:todo:^ do we need to make sure we only get non-boot files out of
- --this. perhaps as easy as filtering them out by ModNodeKeyWithUid with is
- --boot information?
+ get_mod_key m
+ | moduleUnitId m == homeUnitId (ue_unsafeHomeUnit unit_env)
+ = ExternalModuleKey (mkModuleNk m)
+ | otherwise = ExternalPackageKey (moduleUnitId m)
+
+ go get_mod_key emgNodeKey (emgReachableMany emg) (map emgProject) get_mod_info_eps
-- Reachability on 'ModuleGraph' (for --make mode)
| otherwise
=====================================
compiler/GHC/Linker/Loader.hs
=====================================
@@ -610,7 +610,7 @@ initLinkDepsOpts hsc_env = opts
ldLoadHomeIfacesBelow msg hu mods
= do
initIfaceCheck (text "loader") hsc_env
- $ loadHomePackageInterfacesBelow msg hu mods
+ $ void $ loadExternalGraphBelow msg hu Set.empty mods
-- Read the EPS only after `loadHomePackageInterfacesBelow`
hscEPS hsc_env
=====================================
compiler/GHC/Unit/Module/Graph.hs
=====================================
@@ -87,6 +87,7 @@ module GHC.Unit.Module.Graph
, ModNodeKey
, ModNodeKeyWithUid(..)
, msKey
+ , miKey
-- ** Internal node representation
--
@@ -116,6 +117,7 @@ import GHC.Types.SourceFile ( hscSourceString )
import GHC.Unit.Module.ModSummary
import GHC.Unit.Types
import GHC.Utils.Outputable
+import GHC.Unit.Module.ModIface
import GHC.Utils.Misc ( partitionWith )
import System.FilePath
@@ -124,6 +126,7 @@ import GHC.Types.Unique.DSet
import qualified Data.Set as Set
import Data.Set (Set)
import GHC.Unit.Module
+import GHC.Unit.Module.ModNodeKey
import GHC.Linker.Static.Utils
import Data.Bifunctor
@@ -480,16 +483,14 @@ nodeKeyModName :: NodeKey -> Maybe ModuleName
nodeKeyModName (NodeKey_Module mk) = Just (gwib_mod $ mnkModuleName mk)
nodeKeyModName _ = Nothing
-type ModNodeKey = ModuleNameWithIsBoot
-data ModNodeKeyWithUid = ModNodeKeyWithUid { mnkModuleName :: !ModuleNameWithIsBoot
- , mnkUnitId :: !UnitId } deriving (Eq, Ord)
-
-instance Outputable ModNodeKeyWithUid where
- ppr (ModNodeKeyWithUid mnwib uid) = ppr uid <> colon <> ppr mnwib
-
msKey :: ModSummary -> ModNodeKeyWithUid
msKey ms = ModNodeKeyWithUid (ms_mnwib ms) (ms_unitid ms)
+miKey :: ModIface -> ModNodeKeyWithUid
+miKey hmi = ModNodeKeyWithUid (mi_mnwib hmi) ((toUnitId $ moduleUnit (mi_module hmi)))
+
+type ModNodeKey = ModuleNameWithIsBoot
+
--------------------------------------------------------------------------------
-- ** Internal node representation (exposed)
--------------------------------------------------------------------------------
=====================================
compiler/GHC/Unit/Module/ModIface.hs
=====================================
@@ -82,7 +82,6 @@ module GHC.Unit.Module.ModIface
, IfaceImport(..)
, mi_boot
, mi_fix
- , miKey
, mi_semantic_module
, mi_free_holes
, mi_mnwib
@@ -126,7 +125,6 @@ import GHC.Utils.Binary
import Control.DeepSeq
import Control.Exception
-import GHC.Unit.Module.Graph (ModNodeKeyWithUid(..))
{- Note [Interface file stages]
@@ -364,8 +362,6 @@ data ModIface_ (phase :: ModIfacePhase)
-- See Note [Sharing of ModIface].
}
-miKey :: ModIface -> ModNodeKeyWithUid
-miKey hmi = ModNodeKeyWithUid (mi_mnwib hmi) ((toUnitId $ moduleUnit (mi_module hmi)))
-- Enough information to reconstruct the top level environment for a module
data IfaceTopEnv
=====================================
compiler/GHC/Unit/Module/ModNodeKey.hs
=====================================
@@ -0,0 +1,12 @@
+module GHC.Unit.Module.ModNodeKey ( ModNodeKeyWithUid(..) ) where
+
+import GHC.Prelude
+import GHC.Utils.Outputable
+import GHC.Unit.Types
+
+data ModNodeKeyWithUid = ModNodeKeyWithUid { mnkModuleName :: !ModuleNameWithIsBoot
+ , mnkUnitId :: !UnitId } deriving (Eq, Ord)
+
+instance Outputable ModNodeKeyWithUid where
+ ppr (ModNodeKeyWithUid mnwib uid) = ppr uid <> colon <> ppr mnwib
+
=====================================
compiler/ghc.cabal.in
=====================================
@@ -941,6 +941,7 @@ Library
GHC.Unit.Module.Deps
GHC.Unit.Module.Env
GHC.Unit.Module.Graph
+ GHC.Unit.Module.ModNodeKey
GHC.Unit.Module.External.Graph
GHC.Unit.Module.Imported
GHC.Unit.Module.Location
=====================================
testsuite/tests/count-deps/CountDepsAst.stdout
=====================================
@@ -199,6 +199,7 @@ GHC.Unit.Module.Env
GHC.Unit.Module.Imported
GHC.Unit.Module.Location
GHC.Unit.Module.ModIface
+GHC.Unit.Module.ModNodeKey
GHC.Unit.Module.Warnings
GHC.Unit.Module.WholeCoreBindings
GHC.Unit.Parser
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/c220dfed4f7279e19aec4ae68624590f5e051083...d29af41ae2f249ee403f5307300c83c4a5615607
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/c220dfed4f7279e19aec4ae68624590f5e051083...d29af41ae2f249ee403f5307300c83c4a5615607
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/20250103/98a9f1af/attachment-0001.html>
More information about the ghc-commits
mailing list