[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 3 commits: ttg: HsModuleName in the AST, ModuleName in GHC
by Marge Bot (@marge-bot) 05 Oct '26
by Marge Bot (@marge-bot) 05 Oct '26
05 Oct '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
575c4bc6 by Rodrigo Mesquita at 2026-10-05T12:32:02-04:00
ttg: HsModuleName in the AST, ModuleName in GHC
This commit makes the AST fully independent from GHC by parsing a
"module name" as an `HsModuleName (= HText)` rather than constructing an
`ModuleName (= FastString)` straight away.
In the GHC side, everything remains as a `ModuleName`. We turn AST
`HsModuleName`s into fast strings with the cheap `mkFastStringShortText`
when we process the AST for manipulation within the compiler.
There are no performance regressions introduced by this approach.
This is the final piece of #21592
Alternative plans were discussed in
gitlab.haskell.org/ghc/ghc/-/merge_requests/16546#note_694647,
but the Simon suggested one won in simplicity and performance when
compared against my attempt at implementing my original plan of having
a family `ModuleNameP`.
- - - - -
811dc21b by mangoiv at 2026-10-05T13:04:47-04:00
configure: fix sed script and subst tooldir script
Fixes #27672
Adds sanity checking for SUBST_TOOLDIR
Metric Decrease:
LinkableUsage01
MultiLayerModulesTH_OneShot
T10547
T12234
T12425
T13035
T13820
T18140
T18730
T18923
T9198
TcPlugin_RewritePerf
mhu-perf
- - - - -
c5dc8554 by Hannes Siebenhandl at 2026-10-05T13:04:48-04:00
Prepend in-memory unit database for backpack
The packageDbFlags are stored in reverse order. Thus, prepending will
ensure that the in-memory unit database is the last item on the unit
database stack.
Further, the in-memory unit database needs to be able to shadow units
from earlier unit databases in the unit database stack.
Moreover, a `-clear-package-db` would remove the in-memory database if
it weren't the last item on the stack.
Closes #27835
- - - - -
96 changed files:
- changelog.d/unit-index
- compiler/GHC.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/Opt/CallerCC/Types.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Pipeline.hs-boot
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/Phases.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Lit.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Errors/Types.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Debug.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/Lexer.x
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/String.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Unbound.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/ErrCtxt.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Unit/Home.hs
- compiler/GHC/Unit/Module.hs
- compiler/GHC/Unit/Module/Env.hs
- + compiler/GHC/Unit/Module/Name.hs
- compiler/GHC/Unit/Parser.hs
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Unit/Types.hs-boot
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Lit.hs
- compiler/Language/Haskell/Syntax/Module/Name.hs
- compiler/Language/Haskell/Syntax/Text.hs
- compiler/ghc.cabal.in
- distrib/configure.ac.in
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Info.hs
- linters/lint-codes/LintCodes/Static.hs
- m4/subst_tooldir.m4
- + testsuite/tests/backpack/should_compile/T27835.bkp
- + testsuite/tests/backpack/should_compile/T27835.stderr
- testsuite/tests/backpack/should_compile/all.T
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/hiefile/should_run/T24544.hs
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/profiling/should_run/caller-cc/CallerCcParser.hs
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker/Renderer.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d37fd8fd3d956e04280ef589b973e5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d37fd8fd3d956e04280ef589b973e5…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/jeltsch/smaller-pit] Statically enforce module interface reduction for the PIT
by Wolfgang Jeltsch (@jeltsch) 05 Oct '26
by Wolfgang Jeltsch (@jeltsch) 05 Oct '26
05 Oct '26
Wolfgang Jeltsch pushed to branch wip/jeltsch/smaller-pit at Glasgow Haskell Compiler / GHC
Commits:
714ac687 by Wolfgang Jeltsch at 2026-10-05T19:54:35+03:00
Statically enforce module interface reduction for the PIT
`GHC.Iface.Load.loadInterface` used to replace certain fields of each
fetched module interface by `panic` applications before storing it in
the PIT. With the present change, the absence of those fields in the PIT
is guaranteed statically.
See #27044 for the bigger picture.
- - - - -
22 changed files:
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Driver/Session/Inspect.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Load.hs-boot
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Instance/Family.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Unit/External.hs
- compiler/GHC/Unit/Module/ModIface.hs
- ghc/Main.hs
- utils/count-deps/Main.hs
Changes:
=====================================
compiler/GHC/Driver/Downsweep.hs
=====================================
@@ -688,7 +688,8 @@ expandFixedModuleNode key loc = do
-- part of the compiler.
lookupIfaceByModuleHsc hsc_env (mnkToModule key) >>= \case
Just iface -> return (M.Succeeded iface)
- Nothing -> readIface (hsc_hooks hsc_env) (hsc_logger hsc_env) (hsc_dflags hsc_env) (hsc_NC hsc_env) (mnkToModule key) (ml_hi_file loc)
+ Nothing -> fmap shrinkModIface <$>
+ readIface (hsc_hooks hsc_env) (hsc_logger hsc_env) (hsc_dflags hsc_env) (hsc_NC hsc_env) (mnkToModule key) (ml_hi_file loc)
case read_result of
M.Succeeded iface -> do
-- Computer information about this node
=====================================
compiler/GHC/Driver/Env.hs
=====================================
@@ -372,10 +372,10 @@ lookupIfaceByModule
:: HomeUnitGraph
-> PackageIfaceTable
-> Module
- -> IO (Maybe ModIface)
+ -> IO (Maybe RetainedModIface)
lookupIfaceByModule hug pit mod
= HUG.lookupHugByModule mod hug >>= pure . \case
- Just hm -> Just (hm_iface hm)
+ Just hm -> Just (shrinkModIface (hm_iface hm))
Nothing -> lookupModuleEnv pit mod
-- If the module does come from the home package, why do we look in the PIT as well?
-- (a) In OneShot mode, even home-package modules accumulate in the PIT
@@ -384,7 +384,7 @@ lookupIfaceByModule hug pit mod
-- We could eliminate (b) if we wanted, by making GHC.Prim belong to a package
-- of its own, but it doesn't seem worth the bother.
-lookupIfaceByModuleHsc :: HscEnv -> Module -> IO (Maybe ModIface)
+lookupIfaceByModuleHsc :: HscEnv -> Module -> IO (Maybe RetainedModIface)
lookupIfaceByModuleHsc hsc_env mod = do
eps <- hscEPS hsc_env
lookupIfaceByModule (hsc_HUG hsc_env) (eps_PIT eps) mod
=====================================
compiler/GHC/Driver/Main/Interactive.hs
=====================================
@@ -148,7 +148,7 @@ hscIsGHCiMonad :: HscEnv -> String -> IO Name
hscIsGHCiMonad hsc_env name
= runHsc hsc_env $ ioMsgMaybe $ hoistTcRnMessage $ isGHCiMonad hsc_env name
-hscGetModuleInterface :: HscEnv -> Module -> IO ModIface
+hscGetModuleInterface :: HscEnv -> Module -> IO RetainedModIface
hscGetModuleInterface hsc_env0 mod = runInteractiveHsc hsc_env0 $ do
hsc_env <- getHscEnv
ioMsgMaybe $ hoistTcRnMessage $ getModuleInterface hsc_env mod
@@ -270,7 +270,7 @@ hscCheckSafe' m l = do
_ | isHomeModule home_unit mod -> True
_ -> isUnitTrusted unit_state (moduleUnit m)
- lookup' :: Module -> Hsc (Maybe ModIface)
+ lookup' :: Module -> Hsc (Maybe RetainedModIface)
lookup' m = do
hsc_env <- getHscEnv
iface <- liftIO $ lookupIfaceByModuleHsc hsc_env m
=====================================
compiler/GHC/Driver/Main/Passes.hs
=====================================
@@ -1335,7 +1335,7 @@ hscCheckSafe' m l = do
_ | isHomeModule home_unit mod -> True
_ -> isUnitTrusted unit_state (moduleUnit m)
- lookup' :: Module -> Hsc (Maybe ModIface)
+ lookup' :: Module -> Hsc (Maybe RetainedModIface)
lookup' m = do
hsc_env <- getHscEnv
iface <- liftIO $ lookupIfaceByModuleHsc hsc_env m
=====================================
compiler/GHC/Driver/Plugins.hs
=====================================
@@ -224,7 +224,7 @@ data PluginWithArgs = PluginWithArgs
data LoadedPlugin = LoadedPlugin
{ lpPlugin :: PluginWithArgs
-- ^ the actual plugin together with its commandline arguments
- , lpModule :: ModIface
+ , lpModule :: RetainedModIface
-- ^ the module containing the plugin
}
=====================================
compiler/GHC/Driver/Session/Inspect.hs
=====================================
@@ -87,7 +87,7 @@ data ModuleInfo = ModuleInfo {
minf_type_env :: TypeEnv,
minf_exports :: [AvailInfo],
minf_instances :: [ClsInst],
- minf_iface :: Maybe ModIface,
+ minf_iface :: Maybe RetainedModIface,
minf_safe :: SafeHaskellMode,
minf_modBreaks :: Maybe InternalModBreaks
}
@@ -140,7 +140,7 @@ getHomeModuleInfo hsc_env mdl =
Nothing -> return Nothing
Just hmi -> do
let details = hm_details hmi
- iface = hm_iface hmi
+ iface = shrinkModIface (hm_iface hmi)
return (Just (ModuleInfo {
minf_type_env = md_types details,
minf_exports = md_exports details,
@@ -187,7 +187,7 @@ modInfoLookupName minf name = withSession $ \hsc_env -> do
Just tyThing -> return (Just tyThing)
Nothing -> liftIO (lookupType hsc_env name)
-modInfoIface :: ModuleInfo -> Maybe ModIface
+modInfoIface :: ModuleInfo -> Maybe RetainedModIface
modInfoIface = minf_iface
-- | Retrieve module safe haskell mode
=====================================
compiler/GHC/HsToCore/Usage.hs
=====================================
@@ -276,7 +276,7 @@ mk_mod_usage_info uc home_unit home_unit_ids this_mod direct_imports imp_decls u
-- a) we used something from it; has something in used_names
-- b) we imported it, even if we used nothing from it
-- (need to recompile if its export list changes: export_fprint)
- mkUsage :: Module -> ModIface -> Maybe Usage
+ mkUsage :: Module -> RetainedModIface -> Maybe Usage
mkUsage mod iface
| toUnitId (moduleUnit mod) `Set.notMember` home_unit_ids
= Just $ UsagePackageModule{ usg_mod = mod,
@@ -302,9 +302,9 @@ mk_mod_usage_info uc home_unit home_unit_ids this_mod direct_imports imp_decls u
usg_entities = Map.toList ent_hashs,
usg_safe = imp_safe }
where
- finsts_mod = mi_finsts iface
+ finsts_mod = mi_abi_finsts (mi_abi_hashes iface)
hash_env = mi_hash_fn iface
- mod_hash = mi_mod_hash iface
+ mod_hash = mi_abi_mod_hash (mi_abi_hashes iface)
imported_exports
= if not depend_on_exports
then Nothing
@@ -312,9 +312,9 @@ mk_mod_usage_info uc home_unit home_unit_ids this_mod direct_imports imp_decls u
Just $
HomeModImport
{ hmiu_orphanLikeHash
- = mi_orphan_like_hash iface
+ = mi_abi_orphan_like_hash (mi_abi_hashes iface)
, hmiu_importedAvails
- = moduleImportedAvails mod (mi_export_avails_hash iface) imp_decls
+ = moduleImportedAvails mod (mi_abi_export_avails_hash (mi_abi_hashes iface)) imp_decls
}
by_is_safe (ImportedByUser imv) = imv_is_safe imv
=====================================
compiler/GHC/Iface/Load.hs
=====================================
@@ -308,7 +308,7 @@ loadKnownKeyOccMaps
known_key_maps :: Module -> IfM lcl KnownKeyNameMaps
known_key_maps mod
= do { iface <- loadInterfaceWithException doc mod ImportBySystem
- ; let !abi_hash = mi_mod_hash iface
+ ; let !abi_hash = mi_abi_mod_hash (mi_abi_hashes iface)
; eps <- getEps
; case lookupModuleEnv (eps_known_keys eps) mod of
Just (cached_hash, kk_maps)
@@ -321,7 +321,7 @@ loadKnownKeyOccMaps
(abi_hash, kk_maps) }
; return kk_maps } }
- build_maps :: ModIface -> KnownKeyNameMaps
+ build_maps :: RetainedModIface -> KnownKeyNameMaps
build_maps iface = (kk_map, occ_map)
where
kk_map :: UniqFM KnownKey Name
@@ -628,7 +628,7 @@ loadSrcInterface :: SDoc
-> ModuleName
-> IsBootInterface -- {-# SOURCE #-} ?
-> PkgQual -- "package", if any
- -> RnM ModIface
+ -> RnM RetainedModIface
loadSrcInterface doc scope mod want_boot maybe_pkg
= do { res <- loadSrcInterface_maybe doc scope mod want_boot maybe_pkg
@@ -648,7 +648,7 @@ loadSrcInterface_maybe :: SDoc
-> ModuleName
-> IsBootInterface -- {-# SOURCE #-} ?
-> PkgQual -- "package", if any
- -> RnM (MaybeErr MissingInterfaceError ModIface)
+ -> RnM (MaybeErr MissingInterfaceError RetainedModIface)
loadSrcInterface_maybe doc scope mod want_boot maybe_pkg
-- We must first find which Module this import refers to. This involves
@@ -667,7 +667,7 @@ loadSrcInterface_maybe doc scope mod want_boot maybe_pkg
-- rare operation, but in particular it is used to load orphan modules
-- in order to pull their instances into the global package table and to
-- handle some operations in GHCi).
-loadModuleInterface :: SDoc -> Module -> TcM ModIface
+loadModuleInterface :: SDoc -> Module -> TcM RetainedModIface
loadModuleInterface doc mod = initIfaceTcRn (loadSysInterface doc mod)
-- | Load interfaces for a collection of modules.
@@ -681,7 +681,7 @@ loadModuleInterfaces doc mods
-- | Loads the interface for a given Name.
-- Should only be called for an imported name;
-- otherwise loadSysInterface may not find the interface
-loadInterfaceForName :: SDoc -> Name -> TcRn ModIface
+loadInterfaceForName :: SDoc -> Name -> TcRn RetainedModIface
loadInterfaceForName doc name
= do { when debugIsOn $ -- Check pre-condition
do { this_mod <- getModule
@@ -690,7 +690,7 @@ loadInterfaceForName doc name
initIfaceTcRn $ loadSysInterface doc (nameModule name) }
-- | Loads the interface for a given Module.
-loadInterfaceForModule :: SDoc -> Module -> TcRn ModIface
+loadInterfaceForModule :: SDoc -> Module -> TcRn RetainedModIface
loadInterfaceForModule doc m
= do
-- Should not be called with this module
@@ -724,23 +724,23 @@ loadWiredInHomeIface name
------------------
-- | Loads a system interface and throws an exception if it fails
-loadSysInterface :: SDoc -> Module -> IfM lcl ModIface
+loadSysInterface :: SDoc -> Module -> IfM lcl RetainedModIface
loadSysInterface doc mod_name = loadInterfaceWithException doc mod_name ImportBySystem
------------------
-- | Loads a user interface and throws an exception if it fails. The first parameter indicates
-- whether we should import the boot variant of the module
-loadUserInterface :: IsBootInterface -> SDoc -> Module -> IfM lcl ModIface
+loadUserInterface :: IsBootInterface -> SDoc -> Module -> IfM lcl RetainedModIface
loadUserInterface is_boot doc mod_name
= loadInterfaceWithException doc mod_name (ImportByUser is_boot)
-loadPluginInterface :: SDoc -> Module -> IfM lcl ModIface
+loadPluginInterface :: SDoc -> Module -> IfM lcl RetainedModIface
loadPluginInterface doc mod_name
= loadInterfaceWithException doc mod_name ImportByPlugin
------------------
-- | A wrapper for 'loadInterface' that throws an exception if it fails
-loadInterfaceWithException :: SDoc -> Module -> WhereFrom -> IfM lcl ModIface
+loadInterfaceWithException :: SDoc -> Module -> WhereFrom -> IfM lcl RetainedModIface
loadInterfaceWithException doc mod_name where_from
= do
dflags <- getDynFlags
@@ -749,7 +749,7 @@ loadInterfaceWithException doc mod_name where_from
------------------
loadInterface :: SDoc -> Module -> WhereFrom
- -> IfM lcl (MaybeErr MissingInterfaceError ModIface)
+ -> IfM lcl (MaybeErr MissingInterfaceError RetainedModIface)
-- loadInterface looks in both the HPT and PIT for the required interface
-- If not found, it loads it, and puts it in the PIT (always).
@@ -796,7 +796,7 @@ loadInterface doc_str mod from
liftIO $ computeInterface hsc_env doc_str hi_boot_file mod
; case read_result of {
Failed err -> do
- { let fake_iface = emptyFullModIface mod
+ { let fake_iface = shrinkModIface (emptyFullModIface mod)
; updateEps_ $ \eps ->
eps { eps_PIT = extendModuleEnv (eps_PIT eps) (mi_module fake_iface) fake_iface }
@@ -856,16 +856,7 @@ loadInterface doc_str mod from
; new_eps_complete_matches <- tcIfaceCompleteMatches (mi_complete_matches iface)
; purged_hsc_env <- getTopEnv
- ; let final_iface = iface
- & set_mi_decls (panic "No mi_decls in PIT")
- & set_mi_insts (panic "No mi_insts in PIT")
- & set_mi_defaults (panic "No mi_defaults in PIT")
- & set_mi_fam_insts (panic "No mi_fam_insts in PIT")
- & set_mi_rules (panic "No mi_rules in PIT")
- & set_mi_anns (panic "No mi_anns in PIT")
- & set_mi_simplified_core (panic "No mi_simplified_core in PIT")
-
- bad_boot = mi_boot iface == IsBoot
+ ; let bad_boot = mi_boot iface == IsBoot
&& isJust (lookupKnotVars (if_rec_types gbl_env) mod)
-- Warn against an EPS-updating import
-- of one's own boot file! (one-shot only)
@@ -892,7 +883,7 @@ loadInterface doc_str mod from
then eps { eps_PTE = addDeclsToPTE (eps_PTE eps) new_eps_decls }
else
eps {
- eps_PIT = extendModuleEnv (eps_PIT eps) mod final_iface,
+ eps_PIT = extendModuleEnv (eps_PIT eps) mod (shrinkModIface iface),
eps_PTE = addDeclsToPTE (eps_PTE eps) new_eps_decls,
eps_iface_bytecode = add_bytecode (eps_iface_bytecode eps),
eps_rule_base = extendRuleBaseList (eps_rule_base eps)
@@ -921,10 +912,8 @@ loadInterface doc_str mod from
eps_defaults = extendModuleEnv (eps_defaults eps) mod new_eps_defaults
}
- ; -- invoke plugins with *full* interface, not final_iface, to ensure
- -- that plugins have access to declarations, etc.
- res <- withPlugins (hsc_plugins hsc_env) (\p -> interfaceLoadAction p) iface
- ; return (Succeeded res)
+ ; res <- withPlugins (hsc_plugins hsc_env) (\p -> interfaceLoadAction p) iface
+ ; return (Succeeded (shrinkModIface res))
}}}}
{- Note [Loading your own hi-boot file]
=====================================
compiler/GHC/Iface/Load.hs-boot
=====================================
@@ -2,7 +2,7 @@ module GHC.Iface.Load where
import GHC.Unit.Module (Module)
import GHC.Tc.Utils.Monad (IfM)
-import GHC.Unit.Module.ModIface (ModIface)
+import GHC.Unit.Module.ModIface (RetainedModIface)
import GHC.Utils.Outputable (SDoc)
-loadSysInterface :: SDoc -> Module -> IfM lcl ModIface
+loadSysInterface :: SDoc -> Module -> IfM lcl RetainedModIface
=====================================
compiler/GHC/Iface/Recomp.hs
=====================================
@@ -707,7 +707,7 @@ checkDependencies hsc_env summary iface
return $ needsRecompileBecause $ ModulePackageChanged new_name
-needInterface :: Module -> (ModIface -> IO RecompileRequired)
+needInterface :: Module -> (RetainedModIface -> IO RecompileRequired)
-> IfG RecompileRequired
needInterface mod continue
= do
@@ -727,7 +727,7 @@ needBytecode mod continue
Nothing -> return $ NeedsRecompile MustCompile
Just mbc -> liftIO $ continue mbc
-tryGetModIface :: String -> Module -> IfG (Maybe ModIface)
+tryGetModIface :: String -> Module -> IfG (Maybe RetainedModIface)
tryGetModIface doc_msg mod
= do -- Load the imported interface if possible
logger <- getLogger
@@ -779,7 +779,7 @@ checkModUsage _ UsagePackageModule{
logger <- getLogger
needInterface mod $ \iface -> do
let reason = ModuleChanged (moduleName mod)
- checkModuleFingerprint logger reason old_mod_hash (mi_mod_hash iface)
+ checkModuleFingerprint logger reason old_mod_hash (mi_abi_mod_hash (mi_abi_hashes iface))
-- We only track the ABI hash of package modules, rather than
-- individual entity usages, so if the ABI hash changes we must
-- recompile. This is safe but may entail more recompilation when
@@ -789,7 +789,7 @@ checkModUsage _ UsageMergedRequirement{ usg_mod = mod, usg_mod_hash = old_mod_ha
logger <- getLogger
needInterface mod $ \iface -> do
let reason = ModuleChangedRaw (moduleName mod)
- checkModuleFingerprint logger reason old_mod_hash (mi_mod_hash iface)
+ checkModuleFingerprint logger reason old_mod_hash (mi_abi_mod_hash (mi_abi_hashes iface))
checkModUsage _ UsageHomeModuleBytecode{ usg_mod_name = mod_name
, usg_unit_id = uid
, usg_bytecode_hash = old_bytecode_hash } = do
@@ -810,7 +810,7 @@ checkModUsage _ UsageHomeModule{
logger <- getLogger
needInterface mod $ \iface -> do
let
- new_mod_hash = mi_mod_hash iface
+ new_mod_hash = mi_abi_mod_hash (mi_abi_hashes iface)
new_decl_hash = mi_hash_fn iface
reason = ModuleChanged (moduleName mod)
@@ -864,7 +864,7 @@ checkModUsage fc UsageDirectory{ usg_dir_path = dir,
-- Does this require recompilation?
--
-- See Note [When to recompile when export lists change?]
-checkHomeModImport :: Logger -> RecompReason -> Maybe HomeModImport -> ModIface -> IO RecompileRequired
+checkHomeModImport :: Logger -> RecompReason -> Maybe HomeModImport -> RetainedModIface -> IO RecompileRequired
checkHomeModImport _ _ Nothing _ = return UpToDate
checkHomeModImport logger reason
(Just (HomeModImport old_orphan_like_hash old_avails))
@@ -901,8 +901,8 @@ checkHomeModImport logger reason
2 (ppr changes)
return $ needsRecompileBecause reason
where
- new_orphan_like_hash = mi_orphan_like_hash iface
- new_avails_hash = mi_export_avails_hash iface
+ new_orphan_like_hash = mi_abi_orphan_like_hash (mi_abi_hashes iface)
+ new_avails_hash = mi_abi_export_avails_hash (mi_abi_hashes iface)
new_exports = mi_exports iface
-- | The exported avails of a module have changed. Should this cause recompilation
@@ -1617,7 +1617,7 @@ getOrphanHashes hsc_env mods = do
get_orph_hash mod = do
iface <- initIfaceLoad hsc_env . withIfaceErr ctx
$ loadInterface (text "getOrphanHashes") mod ImportBySystem
- return (mi_orphan_hash iface)
+ return (mi_abi_orphan_hash (mi_abi_hashes iface))
mapM get_orph_hash mods
=====================================
compiler/GHC/Rename/Names.hs
=====================================
@@ -517,7 +517,7 @@ renamePkgQual unit_env mn mb_pkg = case mb_pkg of
-- interface, but without 'imp_mods'.
calculateAvails :: HomeUnit
-> S.Set UnitId
- -> ModIface
+ -> RetainedModIface
-> IsSafeImport
-> IsBootInterface
-> ImportedBy
@@ -525,8 +525,8 @@ calculateAvails :: HomeUnit
calculateAvails home_unit other_home_units iface mod_safe' want_boot imported_by =
let imp_mod = mi_module iface
imp_sem_mod= mi_semantic_module iface
- orph_iface = mi_orphan iface
- has_finsts = mi_finsts iface
+ orph_iface = mi_abi_orphan (mi_abi_hashes iface)
+ has_finsts = mi_abi_finsts (mi_abi_hashes iface)
deps = mi_deps iface
trust = getSafeMode $ mi_trust iface
trust_pkg = mi_trust_pkg iface
@@ -1209,7 +1209,7 @@ gresFromAvail hsc_env prov avail =
gresFromAvails :: HscEnv -> Maybe ImportSpec -> [AvailInfo] -> [GlobalRdrElt]
gresFromAvails hsc_env prov = concatMap (gresFromAvail hsc_env prov)
-importsFromIface :: HscEnv -> ModIface -> ImpDeclSpec -> Maybe NameSet -> GlobalRdrEnv
+importsFromIface :: HscEnv -> RetainedModIface -> ImpDeclSpec -> Maybe NameSet -> GlobalRdrEnv
importsFromIface hsc_env iface decl_spec hidden = mkGlobalRdrEnv $ case hidden of
Nothing -> all_gres
Just hidden_names -> filter (not . (`elemNameSet` hidden_names) . greName) all_gres
@@ -1225,7 +1225,7 @@ rnNamespaceSpecifier (DataNamespaceSpecifier x) = DataNamespaceSpecifier x
filterImports
:: HasDebugCallStack
=> HscEnv
- -> ModIface
+ -> RetainedModIface
-> ImpDeclSpec
-- ^ Import spec
-> Maybe (ImportListInterpretation, [LIE GhcPs])
@@ -2327,7 +2327,7 @@ getMinimalImports ie_decls
where
doc = text "Compute minimal imports for" <+> ppr decl
- to_ie :: GlobalRdrEnv -> ModIface -> AvailInfo -> RnM [IE GhcRn]
+ to_ie :: GlobalRdrEnv -> RetainedModIface -> AvailInfo -> RnM [IE GhcRn]
-- The main trick here is that if we're importing all the constructors
-- we want to say "T(..)", but if we're importing only a subset we want
-- to say "T(A,B,C)". So we have to find out what the module exports.
@@ -2529,7 +2529,7 @@ DRFPatSynExport for a test of this.
-}
badImportItemErr
- :: ModIface -> ImpDeclSpec -> IE GhcPs
+ :: RetainedModIface -> ImpDeclSpec -> IE GhcPs
-> IsSubordinateError
-> [AvailInfo]
-> TcRn (NonEmpty ImportLookupReason)
=====================================
compiler/GHC/Rename/Utils.hs
=====================================
@@ -534,7 +534,7 @@ warnIfDeclDeprecated gre@(GRE { gre_imp = iss })
definedMod = moduleName $ assertPpr (isExternalName name) (ppr name) (nameModule name)
doc = text "The name" <+> quotes (ppr occ) <+> text "is mentioned explicitly"
-lookupImpDeclDeprec :: ModIface -> GlobalRdrElt -> Maybe (WarningTxt GhcRn)
+lookupImpDeclDeprec :: RetainedModIface -> GlobalRdrElt -> Maybe (WarningTxt GhcRn)
lookupImpDeclDeprec iface gre
-- Bleat if the thing, or its parent, is warn'd
= mi_decl_warn_fn iface (greOccName gre) `mplus`
=====================================
compiler/GHC/Runtime/Loader.hs
=====================================
@@ -190,7 +190,7 @@ checkExternalInterpreter hsc_env = case interpInstance <$> hsc_interp hsc_env of
-> throwIO (InstallationError "Plugins require -fno-external-interpreter")
_ -> pure ()
-loadPlugin' :: OccName -> Name -> HscEnv -> ModuleName -> IO (a, ModIface, [LinkableUsage], PkgsLoaded)
+loadPlugin' :: OccName -> Name -> HscEnv -> ModuleName -> IO (a, RetainedModIface, [LinkableUsage], PkgsLoaded)
loadPlugin' occ_name plugin_name hsc_env mod_name
= do { let plugin_rdr_name = mkRdrQual mod_name occ_name
dflags = hsc_dflags hsc_env
@@ -341,7 +341,7 @@ lessUnsafeCoerce logger context what = do
-- Need the module as well to record information in the interface file
lookupRdrNameInModuleForPlugins :: HasDebugCallStack
=> HscEnv -> ModuleName -> RdrName
- -> IO (Maybe (Name, ModIface))
+ -> IO (Maybe (Name, RetainedModIface))
lookupRdrNameInModuleForPlugins hsc_env mod_name rdr_name = do
let dflags = hsc_dflags hsc_env
-- First find the unit the module resides in by searching exposed units and home modules
=====================================
compiler/GHC/Tc/Errors/Ppr.hs
=====================================
@@ -6386,7 +6386,7 @@ pprImportLookup :: ImportLookupReason -> SDoc
pprImportLookup = \case
ImportLookupBad k iface decl_spec ie _exts ->
let
- pprImpDeclSpec :: ModIface -> ImpDeclSpec -> SDoc
+ pprImpDeclSpec :: RetainedModIface -> ImpDeclSpec -> SDoc
pprImpDeclSpec iface decl_spec =
quotes (ppr (moduleName $ is_mod decl_spec)) <+> case mi_boot iface of
IsBoot -> text "(hi-boot interface)"
=====================================
compiler/GHC/Tc/Errors/Types.hs
=====================================
@@ -215,7 +215,7 @@ import GHC.Types.DefaultEnv (ClassDefaults)
import GHC.Unit.Types (Module)
import GHC.Unit.State (UnitState)
-import GHC.Unit.Module.ModIface (ModIface)
+import GHC.Unit.Module.ModIface (RetainedModIface)
import GHC.Utils.Outputable
@@ -6635,7 +6635,7 @@ data ImportLookupReason where
T9006, T11071, T9905fail2, T5385, T10668
-}
ImportLookupBad :: BadImportKind
- -> ModIface
+ -> RetainedModIface
-> ImpDeclSpec
-> IE GhcPs
-> ImportLookupExtensions
=====================================
compiler/GHC/Tc/Gen/Splice.hs
=====================================
@@ -1693,7 +1693,7 @@ lookupArgDoc i nm = do
_ -> pure Nothing
-- | Returns the module a Name belongs to, if it is isn't local.
-getExternalModIface :: Name -> TcM (Maybe ModIface)
+getExternalModIface :: Name -> TcM (Maybe RetainedModIface)
getExternalModIface nm = do
isLocal <- nameIsLocalOrFrom <$> getModule <*> pure nm
if isLocal
=====================================
compiler/GHC/Tc/Instance/Family.hs
=====================================
@@ -308,7 +308,7 @@ checkFamInstConsistency directlyImpMods
ifc <- modIface mod
deps <- dep_finsts . mi_deps <$> modIface mod
pure $
- if mi_finsts ifc
+ if mi_abi_finsts (mi_abi_hashes ifc)
then mod:deps
else deps
=====================================
compiler/GHC/Tc/Module.hs
=====================================
@@ -2964,7 +2964,7 @@ externaliseAndTidyId this_mod id
-- a package module with an interface on disk. If neither of these is
-- true, then the result will be an error indicating the interface
-- could not be found.
-getModuleInterface :: HscEnv -> Module -> IO (Messages TcRnMessage, Maybe ModIface)
+getModuleInterface :: HscEnv -> Module -> IO (Messages TcRnMessage, Maybe RetainedModIface)
getModuleInterface hsc_env mod
= runTcInteractive NoTcMPlugins hsc_env $
loadModuleInterface (text "getModuleInterface") mod
=====================================
compiler/GHC/Unit/External.hs
=====================================
@@ -49,7 +49,7 @@ type PackageAnnEnv = AnnEnv
type PackageCompleteMatches = CompleteMatches
-- | Helps us find information about modules in the imported packages
-type PackageIfaceTable = ModuleEnv ModIface
+type PackageIfaceTable = ModuleEnv RetainedModIface
-- Domain = modules in the imported packages
-- | Constructs an empty PackageIfaceTable
=====================================
compiler/GHC/Unit/Module/ModIface.hs
=====================================
@@ -2,6 +2,7 @@
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE RecordWildCards #-}
module GHC.Unit.Module.ModIface
( ModIface
@@ -68,19 +69,31 @@ module GHC.Unit.Module.ModIface
, set_mi_fix_fn
, set_mi_hash_fn
, completePartialModIface
+ , shrinkModIface
, IfaceBinHandle(..)
, PartialModIface
+ , RetainedModIface
, IfaceAbiHashes (..)
, IfaceSelfRecomp (..)
, IfaceCache (..)
, IfaceSimplifiedCore (..)
, withSelfRecomp
- , IfaceDeclExts
- , IfaceAbiHashesExts
+ , IfaceSelfRecompData
+ , IfaceSimplifiedCoreData
+ , ExtensibleFieldsData
+ , IfaceAnnotationsData
+ , IfaceDeclsData
+ , IfaceDefaultsData
+ , IfaceClsInstsData
+ , IfaceFamInstsData
+ , IfaceRulesData
+ , IfaceCompleteMatchesData
+ , IfaceAbiHashesData
, IfaceExport
, IfacePublic_(..)
, IfacePublic
, PartialIfacePublic
+ , RetainedIfacePublic
, IfaceModInfo(..)
, WhetherHasOrphans
, WhetherHasFamInst
@@ -167,9 +180,11 @@ We can build a full interface file two ways:
type PartialModIface = ModIface_ 'ModIfaceCore
type ModIface = ModIface_ 'ModIfaceFinal
+type RetainedModIface = ModIface_ 'ModIfaceRetained
type PartialIfacePublic = IfacePublic_ 'ModIfaceCore
type IfacePublic = IfacePublic_ 'ModIfaceFinal
+type RetainedIfacePublic = IfacePublic_ 'ModIfaceRetained
-- | Extends a PartialModIface with hashes of the ABI.
--
@@ -212,19 +227,11 @@ data IfaceCache = IfaceCache
data ModIfacePhase
= ModIfaceCore
- -- ^ Partial interface built based on output of core pipeline.
+ -- ^ Partial interface built from the output of the core pipeline
| ModIfaceFinal
-
--- | Selects a IfaceDecl representation.
--- For fully instantiated interfaces we also maintain
--- a fingerprint, which is used for recompilation checks.
-type family IfaceDeclExts (phase :: ModIfacePhase) = decl | decl -> phase where
- IfaceDeclExts 'ModIfaceCore = IfaceDecl
- IfaceDeclExts 'ModIfaceFinal = (Fingerprint, IfaceDecl)
-
-type family IfaceAbiHashesExts (phase :: ModIfacePhase) = bk | bk -> phase where
- IfaceAbiHashesExts 'ModIfaceCore = ()
- IfaceAbiHashesExts 'ModIfaceFinal = IfaceAbiHashes
+ -- ^ Complete interface
+ | ModIfaceRetained
+ -- ^ Interface shrunk to the parts that may be retained
-- | In-memory byte array representation of a 'ModIface'.
--
@@ -237,9 +244,66 @@ data IfaceBinHandle (phase :: ModIfacePhase) where
-- See Note [Private fields in ModIface] for when this fields needs to be cleared
-- (e.g., set to 'Nothing').
FullIfaceBinHandle :: !(Strict.Maybe FullBinData) -> IfaceBinHandle 'ModIfaceFinal
-
-
-withSelfRecomp :: ModIface_ phase -> r -> (IfaceSelfRecomp -> r) -> r
+ RetainedIfaceBinHandle :: IfaceBinHandle 'ModIfaceRetained
+
+type family IfaceSelfRecompData (phase :: ModIfacePhase) where
+ IfaceSelfRecompData 'ModIfaceCore = Maybe IfaceSelfRecomp
+ IfaceSelfRecompData 'ModIfaceFinal = Maybe IfaceSelfRecomp
+ IfaceSelfRecompData 'ModIfaceRetained = ()
+
+type family IfaceSimplifiedCoreData (phase :: ModIfacePhase) where
+ IfaceSimplifiedCoreData 'ModIfaceCore = Maybe IfaceSimplifiedCore
+ IfaceSimplifiedCoreData 'ModIfaceFinal = Maybe IfaceSimplifiedCore
+ IfaceSimplifiedCoreData 'ModIfaceRetained = ()
+
+type family ExtensibleFieldsData (phase :: ModIfacePhase) where
+ ExtensibleFieldsData 'ModIfaceCore = ExtensibleFields
+ ExtensibleFieldsData 'ModIfaceFinal = ExtensibleFields
+ ExtensibleFieldsData 'ModIfaceRetained = ()
+
+type family IfaceAnnotationsData (phase :: ModIfacePhase) where
+ IfaceAnnotationsData 'ModIfaceCore = [IfaceAnnotation]
+ IfaceAnnotationsData 'ModIfaceFinal = [IfaceAnnotation]
+ IfaceAnnotationsData 'ModIfaceRetained = ()
+
+-- | The fingerprint in the 'ModIfaceFinal' case is used for recompilation
+-- checks.
+type family IfaceDeclsData (phase :: ModIfacePhase) where
+ IfaceDeclsData 'ModIfaceCore = [IfaceDecl]
+ IfaceDeclsData 'ModIfaceFinal = [(Fingerprint, IfaceDecl)]
+ IfaceDeclsData 'ModIfaceRetained = ()
+
+type family IfaceDefaultsData (phase :: ModIfacePhase) where
+ IfaceDefaultsData 'ModIfaceCore = [IfaceDefault]
+ IfaceDefaultsData 'ModIfaceFinal = [IfaceDefault]
+ IfaceDefaultsData 'ModIfaceRetained = ()
+
+type family IfaceClsInstsData (phase :: ModIfacePhase) where
+ IfaceClsInstsData 'ModIfaceCore = [IfaceClsInst]
+ IfaceClsInstsData 'ModIfaceFinal = [IfaceClsInst]
+ IfaceClsInstsData 'ModIfaceRetained = ()
+
+type family IfaceFamInstsData (phase :: ModIfacePhase) where
+ IfaceFamInstsData 'ModIfaceCore = [IfaceFamInst]
+ IfaceFamInstsData 'ModIfaceFinal = [IfaceFamInst]
+ IfaceFamInstsData 'ModIfaceRetained = ()
+
+type family IfaceRulesData (phase :: ModIfacePhase) where
+ IfaceRulesData 'ModIfaceCore = [IfaceRule]
+ IfaceRulesData 'ModIfaceFinal = [IfaceRule]
+ IfaceRulesData 'ModIfaceRetained = ()
+
+type family IfaceCompleteMatchesData (phase :: ModIfacePhase) where
+ IfaceCompleteMatchesData 'ModIfaceCore = [IfaceCompleteMatch]
+ IfaceCompleteMatchesData 'ModIfaceFinal = [IfaceCompleteMatch]
+ IfaceCompleteMatchesData 'ModIfaceRetained = ()
+
+type family IfaceAbiHashesData (phase :: ModIfacePhase) where
+ IfaceAbiHashesData 'ModIfaceCore = ()
+ IfaceAbiHashesData 'ModIfaceFinal = IfaceAbiHashes
+ IfaceAbiHashesData 'ModIfaceRetained = IfaceAbiHashes
+
+withSelfRecomp :: ModIface -> r -> (IfaceSelfRecomp -> r) -> r
withSelfRecomp iface nk jk =
case mi_self_recomp_info iface of
Nothing -> nk
@@ -279,11 +343,11 @@ data ModIface_ (phase :: ModIfacePhase)
-- importing this module. The main, original part of an interface.
- mi_self_recomp_ :: Maybe IfaceSelfRecomp,
+ mi_self_recomp_ :: IfaceSelfRecompData phase,
-- ^ Information needed for checking self-recompilation.
-- See Note [Self recompilation information in interface files]
- mi_simplified_core_ :: Maybe IfaceSimplifiedCore,
+ mi_simplified_core_ :: IfaceSimplifiedCoreData phase,
-- ^ The part of the interface written when `-fwrite-if-simplified-core` is enabled.
-- These parts are used to restart bytecode generation.
@@ -301,7 +365,7 @@ data ModIface_ (phase :: ModIfacePhase)
-- defined by the user). Used for GHCi and for inspecting
-- the contents of modules via the GHC API only.
- mi_ext_fields_ :: ExtensibleFields
+ mi_ext_fields_ :: ExtensibleFieldsData phase
-- ^ Additional optional fields, where the Map key represents
-- the field name, resulting in a (size, serialized data) pair.
-- Because the data is intended to be serialized through the
@@ -334,12 +398,12 @@ data IfacePublic_ phase = IfacePublic {
-- ^ Warnings
-- NOT STRICT! we read this field lazily from the interface file
- mi_anns_ :: [IfaceAnnotation],
+ mi_anns_ :: IfaceAnnotationsData phase,
-- ^ Annotations
-- NOT STRICT! we read this field lazily from the interface file
- mi_decls_ :: [IfaceDeclExts phase],
+ mi_decls_ :: IfaceDeclsData phase,
-- ^ Type, class and variable declarations
-- The hash of an Id changes if its fixity or deprecations change
-- (as well as its type of course)
@@ -347,14 +411,14 @@ data IfacePublic_ phase = IfacePublic {
-- the hash of the parent class/tycon changes
- mi_defaults_ :: [IfaceDefault],
+ mi_defaults_ :: IfaceDefaultsData phase,
-- ^ default declarations exported by the module
-- Instance declarations and rules
- mi_insts_ :: [IfaceClsInst], -- ^ Sorted class instance
- mi_fam_insts_ :: [IfaceFamInst], -- ^ Sorted family instances
- mi_rules_ :: [IfaceRule], -- ^ Sorted rules
+ mi_insts_ :: IfaceClsInstsData phase, -- ^ Sorted class instance
+ mi_fam_insts_ :: IfaceFamInstsData phase, -- ^ Sorted family instances
+ mi_rules_ :: IfaceRulesData phase, -- ^ Sorted rules
mi_trust_ :: IfaceTrustInfo,
@@ -367,20 +431,20 @@ data IfacePublic_ phase = IfacePublic {
-- itself) but imports some trustworthy modules from its own
-- package (which does require its own package be trusted).
-- See Note [Trust Own Package] in GHC.Rename.Names
- mi_complete_matches_ :: [IfaceCompleteMatch],
+ mi_complete_matches_ :: IfaceCompleteMatchesData phase,
-- ^ {-# COMPLETE #-} declarations
mi_caches_ :: IfaceCache,
-- ^ Cached lookups of some parts of mi_public
- mi_abi_hashes_ :: (IfaceAbiHashesExts phase)
+ mi_abi_hashes_ :: IfaceAbiHashesData phase
-- ^ Either `()` or `IfaceAbiHashes` for
-- a fully instantiated interface.
-- These fields are hashes of different parts of the public interface.
}
mkIfacePublic :: [IfaceExport]
- -> [IfaceDeclExts 'ModIfaceFinal]
+ -> [(Fingerprint, IfaceDecl)]
-> [(OccName, Fixity)]
-> IfaceWarnings
-> [IfaceAnnotation]
@@ -501,22 +565,22 @@ That's why in GHC.Driver.Main.hscMaybeWriteIface there is the call to
forceModIface.
-}
-mi_flag_hash :: ModIface_ phase -> Maybe (FingerprintWithValue IfaceDynFlags)
+mi_flag_hash :: ModIface -> Maybe (FingerprintWithValue IfaceDynFlags)
mi_flag_hash = fmap mi_sr_flag_hash . mi_self_recomp_
-mi_opt_hash :: ModIface_ phase -> Maybe Fingerprint
+mi_opt_hash :: ModIface -> Maybe Fingerprint
mi_opt_hash = fmap mi_sr_opt_hash . mi_self_recomp_
-mi_hpc_hash :: ModIface_ phase -> Maybe Fingerprint
+mi_hpc_hash :: ModIface -> Maybe Fingerprint
mi_hpc_hash = fmap mi_sr_hpc_hash . mi_self_recomp_
-mi_src_hash :: ModIface_ phase -> Maybe Fingerprint
+mi_src_hash :: ModIface -> Maybe Fingerprint
mi_src_hash = fmap mi_sr_src_hash . mi_self_recomp_
-mi_usages :: ModIface_ phase -> Maybe [Usage]
+mi_usages :: ModIface -> Maybe [Usage]
mi_usages = fmap mi_sr_usages . mi_self_recomp_
-mi_plugin_hash :: ModIface_ phase -> Maybe Fingerprint
+mi_plugin_hash :: ModIface -> Maybe Fingerprint
mi_plugin_hash = fmap mi_sr_plugin_hash . mi_self_recomp_
-- | Accessor for the module hash of the ABI from a ModIface.
@@ -547,7 +611,7 @@ mi_orphan_hash iface = mi_abi_orphan_hash (mi_abi_hashes iface)
-- | Old-style accessor for whether or not the ModIface came from an hs-boot
-- file.
-mi_boot :: ModIface -> IsBootInterface
+mi_boot :: ModIface_ phase -> IsBootInterface
mi_boot iface = if mi_hsc_src iface == HsBootFile
then IsBoot
else NotBoot
@@ -557,7 +621,7 @@ mi_mnwib iface = GWIB (moduleName $ mi_module iface) (mi_boot iface)
-- | Lookups up a (possibly cached) fixity from a 'ModIface'. If one cannot be
-- found, 'defaultFixity' is returned instead.
-mi_fix :: ModIface -> OccName -> Fixity
+mi_fix :: ModIface_ phase -> OccName -> Fixity
mi_fix iface name = mi_fix_fn iface name `orElse` defaultFixity
-- | The semantic module for this interface; e.g., if it's a interface
@@ -573,7 +637,7 @@ mi_semantic_module iface = mi_mod_info_semantic_module (mi_mod_info iface)
-- | The "precise" free holes, e.g., the signatures that this
-- 'ModIface' depends on.
-mi_free_holes :: ModIface -> UniqDSet ModuleName
+mi_free_holes :: ModIface_ phase -> UniqDSet ModuleName
mi_free_holes iface =
case getModuleInstantiation (mi_module iface) of
(_, Just indef)
@@ -771,7 +835,7 @@ emptyPartialModIface mod
mi_iface_hash_ = fingerprint0,
mi_hi_bytes_ = PartialIfaceBinHandle,
mi_deps_ = noDependencies,
- mi_public_ = emptyPublicModIface (),
+ mi_public_ = emptyPublicModIface [] [] [] [] [] [] [] (),
mi_simplified_core_ = Nothing,
mi_top_env_ = IfaceTopEnv emptyDetOrdAvails [] ,
mi_docs_ = Nothing,
@@ -787,24 +851,39 @@ emptyIfaceModInfo mod = IfaceModInfo
, mi_mod_info_hsc_src = HsSrcFile
}
-
-emptyPublicModIface :: IfaceAbiHashesExts phase -> IfacePublic_ phase
-emptyPublicModIface abi_hashes = IfacePublic
- { mi_exports_ = []
- , mi_decls_ = []
- , mi_fixities_ = []
- , mi_warns_ = IfWarnSome [] []
- , mi_anns_ = []
- , mi_defaults_ = []
- , mi_insts_ = []
- , mi_fam_insts_ = []
- , mi_rules_ = []
- , mi_abi_hashes_ = abi_hashes
- , mi_trust_ = noIfaceTrustInfo
- , mi_trust_pkg_ = False
- , mi_caches_ = emptyModIfaceCache
- , mi_complete_matches_ = []
- }
+emptyPublicModIface :: IfaceAnnotationsData phase
+ -> IfaceDeclsData phase
+ -> IfaceDefaultsData phase
+ -> IfaceClsInstsData phase
+ -> IfaceFamInstsData phase
+ -> IfaceRulesData phase
+ -> IfaceCompleteMatchesData phase
+ -> IfaceAbiHashesData phase
+ -> IfacePublic_ phase
+emptyPublicModIface anns
+ decls
+ defaults
+ insts
+ fam_insts
+ rules
+ complete_matches
+ abi_hashes
+ = IfacePublic
+ { mi_exports_ = []
+ , mi_fixities_ = []
+ , mi_warns_ = IfWarnSome [] []
+ , mi_anns_ = anns
+ , mi_decls_ = decls
+ , mi_defaults_ = defaults
+ , mi_insts_ = insts
+ , mi_fam_insts_ = fam_insts
+ , mi_rules_ = rules
+ , mi_trust_ = noIfaceTrustInfo
+ , mi_trust_pkg_ = False
+ , mi_complete_matches_ = complete_matches
+ , mi_caches_ = emptyModIfaceCache
+ , mi_abi_hashes_ = abi_hashes
+ }
emptyModIfaceCache :: IfaceCache
emptyModIfaceCache = IfaceCache {
@@ -827,7 +906,7 @@ emptyIfaceBackend = IfaceAbiHashes
emptyFullModIface :: Module -> ModIface
emptyFullModIface mod =
(emptyPartialModIface mod)
- { mi_public_ = emptyPublicModIface emptyIfaceBackend
+ { mi_public_ = emptyPublicModIface [] [] [] [] [] [] [] emptyIfaceBackend
, mi_hi_bytes_ = FullIfaceBinHandle Strict.Nothing
}
@@ -848,8 +927,17 @@ emptyIfaceHashCache _occ = Nothing
-- ModIface is completely forced since it will live in memory for a long time.
-- If forcing it uses a lot of memory, then store less things in ModIface.
-instance ( NFData (IfaceAbiHashesExts (phase :: ModIfacePhase))
- , NFData (IfaceDeclExts (phase :: ModIfacePhase))
+instance ( NFData (IfaceSelfRecompData phase)
+ , NFData (IfaceSimplifiedCoreData phase)
+ , NFData (ExtensibleFieldsData phase)
+ , NFData (IfaceAnnotationsData phase)
+ , NFData (IfaceDeclsData phase)
+ , NFData (IfaceDefaultsData phase)
+ , NFData (IfaceClsInstsData phase)
+ , NFData (IfaceFamInstsData phase)
+ , NFData (IfaceRulesData phase)
+ , NFData (IfaceCompleteMatchesData phase)
+ , NFData (IfaceAbiHashesData phase)
) => NFData (ModIface_ phase) where
rnf (PrivateModIface a1 a2 a3 a4 a5 a6 a7 a8 a9 a10)
= (a1 :: IfaceBinHandle phase)
@@ -882,7 +970,15 @@ instance NFData IfaceAbiHashes where
`seq` rnf a5
`seq` rnf a6
-instance (NFData (IfaceAbiHashesExts phase), NFData (IfaceDeclExts phase)) => NFData (IfacePublic_ phase) where
+instance ( NFData (IfaceAnnotationsData phase)
+ , NFData (IfaceDeclsData phase)
+ , NFData (IfaceDefaultsData phase)
+ , NFData (IfaceClsInstsData phase)
+ , NFData (IfaceFamInstsData phase)
+ , NFData (IfaceRulesData phase)
+ , NFData (IfaceCompleteMatchesData phase)
+ , NFData (IfaceAbiHashesData phase)
+ ) => NFData (IfacePublic_ phase) where
rnf (IfacePublic a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14)
= rnf a1
`seq` rnf a2
@@ -958,7 +1054,6 @@ completePartialModIface partial iface_hash decls extra_decls final_exts cache =
, mi_hi_bytes_ = FullIfaceBinHandle Strict.Nothing
, mi_iface_hash_ = iface_hash
}
- where
-- | Given a 'PartialIfacePublic', turn it into an 'IfacePublic' by completing
-- missing fields.
@@ -973,10 +1068,36 @@ completePublicModIface decls abi_hashes cache partial = partial
, mi_caches_ = cache
}
+shrinkModIface :: ModIface -> RetainedModIface
+shrinkModIface PrivateModIface {..}
+ = PrivateModIface
+ {
+ mi_hi_bytes_ = RetainedIfaceBinHandle,
+ mi_public_ = shrinkIfacePublic mi_public_,
+ mi_self_recomp_ = (),
+ mi_simplified_core_ = (),
+ mi_ext_fields_ = (),
+ ..
+ }
+
+shrinkIfacePublic :: IfacePublic -> RetainedIfacePublic
+shrinkIfacePublic IfacePublic {..}
+ = IfacePublic
+ {
+ mi_anns_ = (),
+ mi_decls_ = (),
+ mi_defaults_ = (),
+ mi_insts_ = (),
+ mi_fam_insts_ = (),
+ mi_rules_ = (),
+ mi_complete_matches_ = (),
+ ..
+ }
+
set_mi_mod_info :: IfaceModInfo -> ModIface_ phase -> ModIface_ phase
set_mi_mod_info val iface = clear_mi_hi_bytes $ iface { mi_mod_info_ = val }
-set_mi_self_recomp :: Maybe IfaceSelfRecomp-> ModIface_ phase -> ModIface_ phase
+set_mi_self_recomp :: IfaceSelfRecompData phase -> ModIface_ phase -> ModIface_ phase
set_mi_self_recomp val iface = clear_mi_hi_bytes $ iface { mi_self_recomp_ = val }
set_mi_hi_bytes :: IfaceBinHandle phase -> ModIface_ phase -> ModIface_ phase
@@ -988,7 +1109,7 @@ set_mi_deps val iface = clear_mi_hi_bytes $ iface { mi_deps_ = val }
set_mi_public :: (IfacePublic_ phase -> IfacePublic_ phase) -> ModIface_ phase -> ModIface_ phase
set_mi_public f iface = clear_mi_hi_bytes $ iface { mi_public_ = f (mi_public_ iface) }
-set_mi_simplified_core :: Maybe IfaceSimplifiedCore -> ModIface_ phase -> ModIface_ phase
+set_mi_simplified_core :: IfaceSimplifiedCoreData phase -> ModIface_ phase -> ModIface_ phase
set_mi_simplified_core val iface = clear_mi_hi_bytes $ iface { mi_simplified_core_ = val }
set_mi_top_env :: IfaceTopEnv -> ModIface_ phase -> ModIface_ phase
@@ -997,7 +1118,7 @@ set_mi_top_env val iface = clear_mi_hi_bytes $ iface { mi_top_env_ = val }
set_mi_docs :: Maybe Docs -> ModIface_ phase -> ModIface_ phase
set_mi_docs val iface = clear_mi_hi_bytes $ iface { mi_docs_ = val }
-set_mi_ext_fields :: ExtensibleFields -> ModIface_ phase -> ModIface_ phase
+set_mi_ext_fields :: ExtensibleFieldsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_ext_fields val iface = clear_mi_hi_bytes $ iface { mi_ext_fields_ = val }
{- Settings for mi_public interface fields -}
@@ -1011,22 +1132,22 @@ set_mi_fixities val = set_mi_public (\iface -> iface { mi_fixities_ = val })
set_mi_warns :: IfaceWarnings -> ModIface_ phase -> ModIface_ phase
set_mi_warns val = set_mi_public (\iface -> iface { mi_warns_ = val })
-set_mi_anns :: [IfaceAnnotation] -> ModIface_ phase -> ModIface_ phase
+set_mi_anns :: IfaceAnnotationsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_anns val = set_mi_public (\iface -> iface { mi_anns_ = val })
-set_mi_insts :: [IfaceClsInst] -> ModIface_ phase -> ModIface_ phase
+set_mi_insts :: IfaceClsInstsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_insts val = set_mi_public (\iface -> iface { mi_insts_ = val })
-set_mi_fam_insts :: [IfaceFamInst] -> ModIface_ phase -> ModIface_ phase
+set_mi_fam_insts :: IfaceFamInstsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_fam_insts val = set_mi_public (\iface -> iface { mi_fam_insts_ = val })
-set_mi_rules :: [IfaceRule] -> ModIface_ phase -> ModIface_ phase
+set_mi_rules :: IfaceRulesData phase -> ModIface_ phase -> ModIface_ phase
set_mi_rules val = set_mi_public (\iface -> iface { mi_rules_ = val })
-set_mi_decls :: [IfaceDeclExts phase] -> ModIface_ phase -> ModIface_ phase
+set_mi_decls :: IfaceDeclsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_decls val = set_mi_public (\iface -> iface { mi_decls_ = val })
-set_mi_defaults :: [IfaceDefault] -> ModIface_ phase -> ModIface_ phase
+set_mi_defaults :: IfaceDefaultsData phase -> ModIface_ phase -> ModIface_ phase
set_mi_defaults val = set_mi_public (\iface -> iface { mi_defaults_ = val })
set_mi_trust :: IfaceTrustInfo -> ModIface_ phase -> ModIface_ phase
@@ -1035,10 +1156,10 @@ set_mi_trust val = set_mi_public (\iface -> iface { mi_trust_ = val })
set_mi_trust_pkg :: Bool -> ModIface_ phase -> ModIface_ phase
set_mi_trust_pkg val = set_mi_public (\iface -> iface { mi_trust_pkg_ = val })
-set_mi_complete_matches :: [IfaceCompleteMatch] -> ModIface_ phase -> ModIface_ phase
+set_mi_complete_matches :: IfaceCompleteMatchesData phase -> ModIface_ phase -> ModIface_ phase
set_mi_complete_matches val = set_mi_public (\iface -> iface { mi_complete_matches_ = val })
-set_mi_abi_hashes :: IfaceAbiHashesExts phase -> ModIface_ phase -> ModIface_ phase
+set_mi_abi_hashes :: IfaceAbiHashesData phase -> ModIface_ phase -> ModIface_ phase
set_mi_abi_hashes val = set_mi_public (\iface -> iface { mi_abi_hashes_ = val })
{- Setters for mi_caches interface fields -}
@@ -1084,8 +1205,9 @@ set_mi_mod_info_field f iface = clear_mi_hi_bytes $ iface { mi_mod_info_ = f (mi
clear_mi_hi_bytes :: ModIface_ phase -> ModIface_ phase
clear_mi_hi_bytes iface = iface
{ mi_hi_bytes_ = case mi_hi_bytes iface of
- PartialIfaceBinHandle -> PartialIfaceBinHandle
- FullIfaceBinHandle _ -> FullIfaceBinHandle Strict.Nothing
+ PartialIfaceBinHandle -> PartialIfaceBinHandle
+ FullIfaceBinHandle _ -> FullIfaceBinHandle Strict.Nothing
+ RetainedIfaceBinHandle -> RetainedIfaceBinHandle
}
-- ----------------------------------------------------------------------------
@@ -1183,22 +1305,22 @@ pattern ModIface ::
-> [IfaceExport]
-> [(OccName, Fixity)]
-> IfaceWarnings
- -> [IfaceAnnotation]
- -> [IfaceDeclExts phase]
- -> Maybe IfaceSimplifiedCore
- -> [IfaceDefault]
+ -> IfaceAnnotationsData phase
+ -> IfaceDeclsData phase
+ -> IfaceSimplifiedCoreData phase
+ -> IfaceDefaultsData phase
-> IfaceTopEnv
- -> [IfaceClsInst]
- -> [IfaceFamInst]
- -> [IfaceRule]
+ -> IfaceClsInstsData phase
+ -> IfaceFamInstsData phase
+ -> IfaceRulesData phase
-> IfaceTrustInfo
-> Bool
- -> [IfaceCompleteMatch]
+ -> IfaceCompleteMatchesData phase
-> Maybe Docs
- -> IfaceAbiHashesExts phase
- -> ExtensibleFields
+ -> IfaceAbiHashesData phase
+ -> ExtensibleFieldsData phase
-> IfaceBinHandle phase
- -> Maybe IfaceSelfRecomp
+ -> IfaceSelfRecompData phase
-> (OccName -> Maybe Fixity)
-> (OccName -> Maybe (OccName, Fingerprint))
-> (OccName -> Maybe (WarningTxt GhcRn))
=====================================
ghc/Main.hs
=====================================
@@ -515,7 +515,7 @@ abiHash strs = do
put_ bh hiVersion
-- package hashes change when the compiler version changes (for now)
-- see #5328
- mapM_ (put_ bh . mi_mod_hash) ifaces
+ mapM_ (put_ bh . mi_abi_mod_hash . mi_abi_hashes) ifaces
f <- fingerprintBinMem bh
putStrLn (showPpr dflags f)
=====================================
utils/count-deps/Main.hs
=====================================
@@ -79,5 +79,5 @@ calcDeps modName libdir =
mkModule :: String -> ModuleName -> Module
mkModule ghcUnitId = Module (stringToUnit ghcUnitId)
- modDeps :: ModIface -> [ModuleName]
+ modDeps :: RetainedModIface -> [ModuleName]
modDeps mi = map (gwib_mod . (\(_, _, mn) -> mn)) $ Set.toList $ dep_direct_mods (mi_deps mi)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/714ac6876bd0a527fe0397c2391a2f5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/714ac6876bd0a527fe0397c2391a2f5…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/multi-threaded-debugger] 8 commits: Look up a thread's ResumeContext without taking a lock
by Rodrigo Mesquita (@alt-romes) 05 Oct '26
by Rodrigo Mesquita (@alt-romes) 05 Oct '26
05 Oct '26
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC
Commits:
c3176c10 by Rodrigo Mesquita at 2026-09-23T17:27:47+01:00
Look up a thread's ResumeContext without taking a lock
modifyMVar is unnecessarily expensive and blocking when most often we
just care about reading without modifying. optimize for default case
(thread context already exists). It is called every single time a
breakpoint is hit (regardless of whether we stop there, e.g. in
interpreter_steplocal we do not stop there often)
-3.3% improvement over original regression
- - - - -
5d3cb7fa by Rodrigo Mesquita at 2026-09-23T17:28:04+01:00
Every thread resume-context keeps a var for its EvalStatus
Kill the global mapping from thread id to EvalStatus
Instead, keep the EvalStatus in the ResumeContext associated to a thread
in the threadContextVar (where entries are created on breakpoints being
hit and when writing an EvalComplete result)
Before, every breakpoint hit meant reading the global map TVar and
deleting a map entry which did a lot of allocations. Now, we have a
Nothing variable which we overwrite to "take" rather than deleting from
a Map and allocating the GlobalDbgMap too. Note that for every
breakpoint hit we'd have to insert the ThreadId entry in the map again
(because we deleted it last time we resumed), again allocating
unnecessarily. Now it's only a look up on the map and an update on the
variable directly.
And a LOT of breakpoints were hit on interpreter_steplocal, stress
testing the cost of hitting a breakpoint
The isolated threads move to their own top-level TVar Set.
-4.4% improvement over the regression
- - - - -
3ffdef29 by Rodrigo Mesquita at 2026-09-23T17:28:15+01:00
Move whether a thread is isolated to the same global map
Stops us from reconstructing the set every time this changed instead to
being a per thread variable just toggled on/off, by default non-isolated
like before (before it was Set.empty, Set.insert for True, Set.delete
for FALSE). This is more straightforward -- there's always an isolation
mode per thread and we can toggle it directly
reduces slightly allocations, but the main advantage is killing a second
global map and making it more uniform with just one thread map and no
membership check for toggledness.
- - - - -
4a826f19 by Rodrigo Mesquita at 2026-10-01T11:22:37+01:00
Prefer foldl' in readAnyThreadEvalBreak txn
retry 'orElse' next is just next, so we might as well use a strict fold
- - - - -
a3e3a66f by Rodrigo Mesquita at 2026-10-01T16:36:23+01:00
Add inline TODO about improving efficiency of readyAnyThreadBreak
It is currently O(N) in number of threads that have hit breakpoints
It should be number of *live* threads that have hit breaks, bc we still need to find a way to evacuate threads from the map
- - - - -
ee339734 by Rodrigo Mesquita at 2026-10-01T17:33:52+01:00
Set global breakpoint action in sandboxIO rather than withBreakAction
- - - - -
aa7ab6ca by Rodrigo Mesquita at 2026-10-01T17:35:10+01:00
TODO comment on breakOnException being global rather than per-thread
- - - - -
a7150ad3 by Rodrigo Mesquita at 2026-10-05T16:21:30+01:00
WIP: Asynchronous eval/resume
- - - - -
6 changed files:
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Interpreter.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/GHCi/Run/Breakpoints.hs
- libraries/ghci/GHCi/Server.hs
Changes:
=====================================
compiler/GHC/Runtime/Eval.hs
=====================================
@@ -188,12 +188,13 @@ execOptions = ExecOptions
-- by default we just care about breakpoints hit in this eval thread
}
--- | Run a statement in the current interactive context.
+-- | Run a statement in the current interactive context, as a new thread in the interpreter.
+-- Returns an action which blocks waiting for the execution to finish and yield a result.
execStmt
:: GhcMonad m
=> String -- ^ a statement (bind or expression)
-> ExecOptions
- -> m ExecResult
+ -> m (m ExecResult)
execStmt input exec_opts@ExecOptions{..} = do
hsc_env <- getSession
@@ -204,13 +205,15 @@ execStmt input exec_opts@ExecOptions{..} = do
case mb_stmt of
-- empty statement / comment
- Nothing -> return (ExecComplete (Right []) 0)
+ Nothing -> pure (pure (ExecComplete (Right []) 0))
Just stmt -> execStmt' stmt input exec_opts
-- | Like `execStmt`, but takes a parsed statement as argument. Useful when
-- doing preprocessing on the AST before execution, e.g. in GHCi (see
-- GHCi.UI.runStmt).
-execStmt' :: GhcMonad m => GhciLStmt GhcPs -> String -> ExecOptions -> m ExecResult
+--
+-- Returns an action which blocks waiting for the execution to finish and yield a result.
+execStmt' :: GhcMonad m => GhciLStmt GhcPs -> String -> ExecOptions -> m (m ExecResult)
execStmt' stmt stmt_text ExecOptions{..} = do
hsc_env <- getSession
let interp = hscInterp hsc_env
@@ -226,21 +229,22 @@ execStmt' stmt stmt_text ExecOptions{..} = do
case r of
Nothing ->
-- empty statement / comment
- return (ExecComplete (Right []) 0)
+ pure (pure (ExecComplete (Right []) 0))
Just (ids, hval, fix_env) -> do
updateFixityEnv fix_env
let eval_opts = initEvalOpts idflags' (enableGhcStepMode execSingleStep)
(enableIsolateThreadBreaks execIsolateMode)
- status <- liftIO $ evalStmt interp eval_opts (execWrap hval)
+ rtid <- liftIO $ evalStmt interp eval_opts (execWrap hval)
let ic = hsc_IC hsc_env
bindings = (ic_tythings ic, ic_gre_cache ic)
size = ghciHistSize idflags'
-
- handleRunStatus execSingleStep eval_opts stmt_text bindings ids
- status (emptyHistory size)
+ pure $ do
+ status <- liftIO (waitThreadResult interp rtid)
+ handleRunStatus execSingleStep eval_opts stmt_text bindings ids
+ status (emptyHistory size)
runDecls :: GhcMonad m => String -> m [TyThing]
runDecls = runDeclsWithLocation "<interactive>" 1
@@ -326,16 +330,16 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
return (ExecComplete (Left (fromSerializableException e)) alloc)
-- Nothing case: we stopped when an exception was raised, not at a breakpoint.
- EvalBreak apStack_ref Nothing resume_ctxt ccs -> do
- resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
- apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
+ EvalBreak apStack_ref Nothing resume_tid ccs -> do
+ resume_tid_fhv <- liftIO $ mkFinalizedHValue interp resume_tid
+ apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
let span = mkGeneralSrcSpan (fsLit "<unknown>")
(hsc_env1, break_ids) <- liftIO $
bindLocalsAtBreakpoint hsc_env apStack_fhv span Nothing
let
resume = Resume
{ resumeStmt = expr
- , resumeContext = resume_ctxt_fhv
+ , resumeContext = resume_tid_fhv
, resumeBindings = bindings
, resumeFinalIds = final_ids
, resumeApStack = apStack_fhv
@@ -356,7 +360,7 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
-- The interpreter yields on a breakpoint if:
-- - the breakpoint was explicitly enabled (in @BreakArray@)
-- - or one of the stepping options in @EvalOpts@ caused us to stop at one
- EvalBreak apStack_ref (Just eval_break) resume_ctxt ccs -> do
+ EvalBreak apStack_ref (Just eval_break) resume_tid ccs -> do
let ibi = evalBreakpointToId eval_break
let hug = hsc_HUG hsc_env
info_brks <- liftIO $ readIModBreaks hug ibi
@@ -368,8 +372,8 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
breakArray <- getBreakArray interp ibi info_brks
breakpointStatus interp breakArray (ibi_info_index ibi)
- apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
- resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
+ apStack_fhv <- liftIO $ mkFinalizedHValue interp apStack_ref
+ resume_tid_fhv <- liftIO $ mkFinalizedHValue interp resume_tid
-- This breakpoint is enabled or we mean to break here;
-- we want to stop instead of just logging it.
@@ -382,7 +386,7 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
let
resume = Resume
{ resumeStmt = expr
- , resumeContext = resume_ctxt_fhv
+ , resumeContext = resume_tid_fhv
, resumeBindings = bindings
, resumeFinalIds = final_ids
, resumeApStack = apStack_fhv
@@ -398,7 +402,9 @@ handleRunStatus step eval_opts expr bindings final_ids status history0 = do
return (ExecBreak break_ids (Just ibi) resume)
else do
-- resume with the same step type
- status <- liftIO $ GHCi.resumeStmt interp eval_opts resume_ctxt_fhv
+ status <- liftIO $ do
+ GHCi.resumeStmt interp eval_opts resume_tid_fhv
+ GHCi.waitThreadResult interp resume_tid_fhv
history <- if not tracing then pure history0 else do
history1 <- liftIO $ mkHistory hug apStack_fhv ibi
let !history' = history1 `consBL` history0
@@ -414,7 +420,9 @@ resumeExec :: GhcMonad m
-> ThreadBreaksIsolationMode
-> Maybe Int
-> Resume
- -> m ExecResult
+ -> m (m ExecResult)
+ -- ^ Returns an action which blocks waiting for the resumed thread
+ -- to break again or yield a result
resumeExec step isolateMode mbCnt r = do
hsc_env <- getSession
let ic = hsc_IC hsc_env
@@ -438,7 +446,7 @@ resumeExec step isolateMode mbCnt r = do
case r of
Resume { resumeStmt = expr
- , resumeContext = fhv
+ , resumeContext = rtid
, resumeBindings = bindings
, resumeFinalIds = final_ids
, resumeApStack = apStack
@@ -454,17 +462,19 @@ resumeExec step isolateMode mbCnt r = do
let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
(enableIsolateThreadBreaks isolateMode)
- status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
- let prevHistoryLst = fromListBL 50 hist
- hug = hsc_HUG hsc_env
- hist' = case mb_brkpt of
- Nothing -> pure prevHistoryLst
- Just bi
- | breakHere False step span -> do
- hist1 <- liftIO (mkHistory hug apStack bi)
- return $ hist1 `consBL` fromListBL 50 hist
- | otherwise -> pure prevHistoryLst
- handleRunStatus step eval_opts expr bindings final_ids status =<< hist'
+ liftIO $ GHCi.resumeStmt interp eval_opts rtid
+ pure $ do
+ status <- liftIO $ GHCi.waitThreadResult interp rtid
+ let prevHistoryLst = fromListBL 50 hist
+ hug = hsc_HUG hsc_env
+ hist' = case mb_brkpt of
+ Nothing -> pure prevHistoryLst
+ Just bi
+ | breakHere False step span -> do
+ hist1 <- liftIO (mkHistory hug apStack bi)
+ return $ hist1 `consBL` fromListBL 50 hist
+ | otherwise -> pure prevHistoryLst
+ handleRunStatus step eval_opts expr bindings final_ids status =<< hist'
setupBreakpoint :: GhcMonad m => Interp -> InternalBreakpointId -> Int -> m () -- #19157
setupBreakpoint interp ibi cnt = do
@@ -1275,7 +1285,9 @@ compileParsedExprRemote expr@(L loc _) = withSession $ \hsc_env -> do
updateFixityEnv fix_env
let eval_opts = initEvalOpts dflags EvalStepNone True
- status <- liftIO $ evalStmt interp eval_opts (EvalThis hvals_io)
+ status <- liftIO $ do
+ rtid <- evalStmt interp eval_opts (EvalThis hvals_io)
+ waitThreadResult interp rtid
case status of
EvalComplete _ (EvalSuccess [hval]) -> return hval
EvalComplete _ (EvalException e) ->
=====================================
compiler/GHC/Runtime/Interpreter.hs
=====================================
@@ -11,6 +11,7 @@ module GHC.Runtime.Interpreter
, evalStmt, EvalStatus_(..), EvalStatus, EvalResult(..), EvalExpr(..)
, resumeStmt
, abandonStmt
+ , waitThreadResult
, evalIO
, evalString
, evalStringToIOString
@@ -286,11 +287,11 @@ evalStmt
:: Interp
-> EvalOpts
-> EvalExpr ForeignHValue
- -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
+ -> IO (ForeignRef ThreadId)
evalStmt interp opts foreign_expr = do
- status <- withExpr foreign_expr $ \expr ->
+ r <- withExpr foreign_expr $ \expr ->
interpCmd interp (EvalStmt opts expr)
- handleEvalStatus interp status
+ mkFinalizedHValue interp r
where
withExpr :: EvalExpr ForeignHValue -> (EvalExpr HValueRef -> IO a) -> IO a
withExpr (EvalThis fhv) cont =
@@ -304,17 +305,25 @@ resumeStmt
:: Interp
-> EvalOpts
-> ForeignRef ThreadId
- -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
+ -> IO ()
resumeStmt interp opts resume_tid = do
- status <- withForeignRef resume_tid $ \rhv ->
+ withForeignRef resume_tid $ \rhv ->
interpCmd interp (ResumeStmt opts rhv)
- handleEvalStatus interp status
abandonStmt :: Interp -> ForeignRef ThreadId -> IO ()
abandonStmt interp resume_tid =
withForeignRef resume_tid $ \rhv ->
interpCmd interp (AbandonStmt rhv)
+waitThreadResult
+ :: Interp
+ -> ForeignRef ThreadId
+ -> IO (EvalStatus_ [ForeignHValue] [HValueRef])
+waitThreadResult interp tid = do
+ status <- withForeignRef tid $ \rhv -> do
+ interpCmd interp (WaitThreadResult rhv)
+ handleEvalStatus interp status
+
handleEvalStatus
:: Interp
-> EvalStatus [HValueRef]
@@ -417,8 +426,9 @@ whereFrom interp ref =
seqHValue :: Interp -> UnitEnv -> Logger -> ForeignHValue -> IO (EvalResult ())
seqHValue interp unit_env logger ref =
withForeignRef ref $ \hval -> do
- status <- interpCmd interp (Seq hval)
- handleSeqHValueStatus interp unit_env logger status
+ rtid <- interpCmd interp (Seq hval)
+ status <- interpCmd interp (WaitThreadResult rtid)
+ handleSeqHValueStatus interp unit_env logger (clearEvalStatus status)
evalBreakpointToId :: EvalBreakpoint -> InternalBreakpointId
evalBreakpointToId eval_break =
@@ -460,9 +470,10 @@ handleSeqHValueStatus interp unit_env logger eval_status =
Just modbreaks -> put =<< getBreakLoc (readIModModBreaks hug) ibi modbreaks
-- resume the seq (:force) processing in the iserv process
- withForeignRef resume_ctxt_fhv $ \hval -> do
- status <- interpCmd interp (ResumeSeq hval)
- handleSeqHValueStatus interp unit_env logger status
+ withForeignRef resume_ctxt_fhv $ \rtid -> do
+ interpCmd interp (ResumeSeq rtid)
+ status <- interpCmd interp (WaitThreadResult rtid)
+ handleSeqHValueStatus interp unit_env logger (clearEvalStatus status)
(EvalComplete _ r) -> return r
@@ -771,3 +782,9 @@ readIModModBreaks hug mod = imodBreaks_modBreaks . expectJust <$> readIModBreaks
fromEvalResult :: EvalResult a -> IO a
fromEvalResult (EvalException e) = throwIO (fromSerializableException e)
fromEvalResult (EvalSuccess a) = return a
+
+clearEvalStatus :: EvalStatus a -> EvalStatus ()
+clearEvalStatus = \case
+ EvalComplete w (EvalException se) -> EvalComplete w (EvalException se)
+ EvalComplete w (EvalSuccess _) -> EvalComplete w (EvalSuccess ())
+ EvalBreak ap mb rt rccs -> EvalBreak ap mb rt rccs
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -62,6 +62,8 @@ import Data.Dynamic
import Data.Typeable (TypeRep)
import Data.IORef
import Data.Map (Map)
+import Data.IntMap (IntMap)
+import qualified Data.IntMap as IM
import Foreign
import GHC.Generics
import GHC.Stack.CCS
@@ -132,13 +134,13 @@ data Message a where
EvalStmt
:: EvalOpts
-> EvalExpr HValueRef {- IO [a] -}
- -> Message (EvalStatus [HValueRef]) {- [a] -}
+ -> Message (RemoteRef ThreadId) {- [a] -}
-- | Resume evaluation of a statement after a breakpoint
ResumeStmt
:: EvalOpts
-> RemoteRef ThreadId
- -> Message (EvalStatus [HValueRef])
+ -> Message ()
-- | Abandon evaluation of a statement after a breakpoint
AbandonStmt
@@ -240,12 +242,23 @@ data Message a where
-- | Evaluate something. This is used to support :force in GHCi.
Seq
:: HValueRef
- -> Message (EvalStatus ())
+ -> Message (RemoteRef ThreadId)
-- | Resume forcing a free variable in a breakpoint (#2950)
ResumeSeq
:: RemoteRef ThreadId
- -> Message (EvalStatus ())
+ -> Message ()
+
+ WaitThreadResult
+ :: RemoteRef ThreadId
+ -> Message (EvalStatus [HValueRef])
+
+ WaitAnyThreadBreak
+ :: Message EvalBreak
+
+ WaitThreadResultOrAnyThreadBreak
+ :: RemoteRef ThreadId
+ -> Message (EvalStatus [HValueRef])
-- | User-defined request encoded as a tag/payload pair. This is left
-- uninterpreted by GHC and is meant for GHC API applications to be able to supply
@@ -724,6 +737,7 @@ data Pipe = Pipe
{ getSome :: !(IO ByteString)
, putAll :: !(B.Builder -> IO ())
, pipeLeftovers :: !(IORef (Maybe ByteString))
+ -- , waitingStatus :: !(IORef (Map (RemoteRef ThreadId) (MVar (EvalStatus ()))))
}
-- | Make a 'Pipe' from a 'Handle' to read and a 'Handle' to write.
@@ -744,8 +758,19 @@ mkPipeFromContinuations getSome putAll = do
remoteCall :: Binary a => Pipe -> Message a -> IO a
remoteCall pipe msg = do
- writePipe pipe (putMessage msg)
- readPipe pipe get
+ -- synchronous, write, read
+ writePipe pipe (putMessage msg) -- write goes in the handle
+ readPipe pipe get -- instead, this takes from the channel
+ -- the channel is fed from the handle by a different thread that creates a
+ -- stream of notifications and a stream of replies.
+ --
+ -- Since a reply will only come in response to a write request that happens
+ -- while the entire is thing is locked, we'll only get 1 reply per write, and
+ -- in fact the channel of replies is just 1 cell (MVar)
+ --
+ -- ATOMIC:
+ -- write pipe
+ -- read reply -- will never have more than 1 reply, because the Lock guarantees only 1 write.
writePipe :: Pipe -> Put -> IO ()
writePipe Pipe{..} put = putAll $ execPut put
@@ -755,6 +780,11 @@ remoteTHCall pipe msg = do
writePipe pipe (putTHMessage msg)
readPipe pipe get
+-- todo: this will have to drain the pipe and write to a queue?
+-- Normal replies pushed to reply channel
+-- Remote call instead of ReadPipe, reads from the channel.
+--
+-- We need to drain channel for notifications...
readPipe :: Pipe -> Get a -> IO a
readPipe Pipe{..} get = do
leftovers <- readIORef pipeLeftovers
=====================================
libraries/ghci/GHCi/Run.hs
=====================================
@@ -128,6 +128,15 @@ run m = case m of
Seq ref -> doSeq ref
ResumeSeq ref -> resumeSeq ref
+ WaitThreadResult tid -> localRef tid >>= atomically . readThreadEvalStatus
+ WaitAnyThreadBreak -> atomically $ readAnyThreadEvalBreak
+ WaitThreadResultOrAnyThreadBreak t -> do
+ tid <- localRef t
+ atomically $
+ readThreadEvalStatus tid
+ `orElse`
+ (EvalPaused <$> readAnyThreadEvalBreak)
+
Shutdown -> unexpectedMessage m
RunTH {} -> unexpectedMessage m
RunModFinalizers {} -> unexpectedMessage m
@@ -206,7 +215,7 @@ tryEval io = do
--------------------------------------------------------------------------------
-evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (EvalStatus [HValueRef])
+evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (RemoteRef ThreadId)
evalStmt opts expr = do
io <- mkIO expr
sandboxIO opts $ do
@@ -248,16 +257,16 @@ evalStringToString r str = do
-- The UI process has more and therefore also can show more
-- information about the breakpoint than the current iserv
-- process.
-doSeq :: RemoteRef a -> IO (EvalStatus ())
-doSeq ref = clearEvalStatus <$> do
+doSeq :: RemoteRef a -> IO (RemoteRef ThreadId)
+doSeq ref = do
sandboxIO evalOptsSeq $ do
_ <- (void $ evaluate =<< localRef ref)
return []
-- | Process a ResumeSeq message. Continue the :force processing #2950
-- after a breakpoint.
-resumeSeq :: RemoteRef ThreadId -> IO (EvalStatus ())
-resumeSeq hvref = clearEvalStatus <$> resumeStmt evalOptsSeq hvref
+resumeSeq :: RemoteRef ThreadId -> IO ()
+resumeSeq hvref = resumeStmt evalOptsSeq hvref
evalOptsSeq :: EvalOpts
evalOptsSeq = EvalOpts
@@ -296,15 +305,16 @@ abandonStmt hvref = do
--------------------------------------------------------------------------------
-resumeStmt :: EvalOpts -> RemoteRef ThreadId -> IO (EvalStatus [HValueRef])
+resumeStmt :: EvalOpts -> RemoteRef ThreadId -> IO ()
resumeStmt opts rtid = do
resumeThreadId <- localRef rtid
ResumeContext{..} <- getThreadResumeContext resumeThreadId
- withBreakAction opts resumeThreadId $ \waitForResult ->
- mask_ $ do
- putMVar resumeBreakMVar () -- this awakens the stopped thread...
- redirectInterrupts resumeThreadId $
- waitForResult
+ setBreakAction opts resumeThreadId -- See Note [TODO: all break options must be set every time]
+ -- withBreakAction opts resumeThreadId $ \waitForResult ->
+ -- mask_ $ do
+ putMVar resumeBreakMVar () -- this awakens the stopped thread...
+ -- redirectInterrupts resumeThreadId $
+ -- waitForResult
-- When running a computation, we redirect ^C exceptions to the running
-- thread. ToDo: we might want a way to continue even if the target
@@ -316,26 +326,51 @@ resumeStmt opts rtid = do
-- only while we execute the user's code. We can't afford to lose the final
-- putMVar, otherwise deadlock ensues. (#1583, #1922, #1946)
-sandboxIO :: EvalOpts -> IO [HValueRef] -> IO (EvalStatus [HValueRef])
+sandboxIO :: EvalOpts -> IO [HValueRef] -> IO (RemoteRef ThreadId)
sandboxIO opts io
| useSandboxThread opts
= do
wait <- newEmptyMVar
+ -- If we are evaluating expressions (sandboxIO), then make sure the
+ -- global breakpoint action is set.
+ --
+ -- TODO: Is this thread unsafe? Already running threads may access this
+ -- pointer as we write it when evaluating a new expression.
+ --
+ -- A solution would be to have an "initialize interpreter" command
+ -- which sets this at the start. A different solution is to have a
+ -- global lock and only write this once here, then lock.
+ poke breakPointIOAction globalBreakStablePtr
+
tid <- forkIO $ do
takeMVar wait -- don't run anything in this thread before
- -- `withBreakAction` has a chance to set up.
+ -- `setBreakAction` has a chance to set up.
tid <- myThreadId
labelThread tid "GHCi sandbox"
+
+ -- WHEN THE THREAD IS FINISHED, write the value itself.
+ --
+ -- we only write the TID status map in two places:
+ --
+ -- When the "main" thread finishes (here)
+ -- When any thread hits a breakpoint (global break action)
unsafeUnmask runIt >>= writeThreadEvalStatus tid
+ -- TODO: This unsafe-unmask is very dubious. Consult with Andrea. I
+ -- don't think we should be uninterruptable masking interp commands
+ -- somewhere else. This should just be runIt?
- -- We are running in uninterruptibleMask
- withBreakAction opts tid $ \waitForResult -> do
- putMVar wait ()
- redirectInterrupts tid $ unsafeUnmask $
- waitForResult
+ -- We are running in uninterruptibleMask -- TODO WHY??
+ setBreakAction opts tid
+ putMVar wait ()
+
+ mkRemoteRef tid
+ -- withBreakAction opts tid $ \waitForResult -> do
+ -- putMVar wait ()
+ -- redirectInterrupts tid $ unsafeUnmask $
+ -- waitForResult
| otherwise
= do
@@ -349,7 +384,17 @@ sandboxIO opts io
-- tracing, etc.) need the expression to be running in a
-- separate thread, so debugging is only enabled when
-- using the sandbox.
- runIt
+ tid <- forkIO (pure ())
+ runIt >>= writeThreadEvalStatus tid
+ mkRemoteRef tid
+ -- We `runIt` on the main thread, but fork a new thread to store to the
+ -- result with `writeThreadEvalStatus` to fit the asynchronous API. We
+ -- can't use myThreadId because if the user happens to runIt twice
+ -- before awaiting, the second result will clobber the first.
+
+ -- TODO We have to be careful evacuating this map bc there may be
+ -- uncollected results from finished threads. So the thread map can't
+ -- be weak refs ? Or maybe because of the RemoteRef it's fine. Ah, prob.
where
runIt = measureAlloc $ tryEval $ rethrow opts $ clearCCS io
@@ -404,17 +449,17 @@ sandboxIO opts io
-- whenever "main" or one of the threads spawned by "main" hit a breakpoint,
-- rather than just "main" being observed. This should be used for the main
-- debuggee execution of a debugger.
-withBreakAction :: EvalOpts -> ThreadId -> (IO (EvalStatus [HValueRef]) -> IO a) -> IO a
-withBreakAction opts tid@(ThreadId tid#) act
- = bracket setBreakAction resetBreakAction (\_ -> act waitForResult)
+-- withBreakAction :: EvalOpts -> ThreadId -> (IO (EvalStatus [HValueRef]) -> IO a) -> IO a
+-- withBreakAction opts tid@(ThreadId tid#) act = do
+
+setBreakAction :: EvalOpts -> ThreadId -> IO ()
+setBreakAction opts tid@(ThreadId tid#) = do
+ ctx <- getThreadResumeContext tid
+ setBreakAction' ctx
+ -- bracket _ (resetBreakAction ctx) (\_ -> act (waitForResult ctx))
where
- setBreakAction = do
- poke breakPointIOAction globalBreakStablePtr
- -- TODO: This is poke thread unsafe, as one thread might be accessing this
- -- global variable while this thread tries to overwrite it. We should
- -- rather do it when the interpreter process/internal is initialized
- -- somehow.
-
+ setBreakAction' ctx = do
+ -- TODO: TERRIBLY NOT MULTI-THREAD FRIENDLY. Each thread needs its own RTS flag exceptionFlag, like we have for step-in or step-out.
runIf breakOnException $ \_ -> poke exceptionFlag 1
-- Breaking on exceptions is not enabled by default, since it
-- might be a bit surprising. The exception flag is turned off
@@ -422,32 +467,34 @@ withBreakAction opts tid@(ThreadId tid#) act
runIf# singleStep rts_enableStopNextBreakpoint
runIf# stepOut rts_enableStopAfterReturn
- runIf isolateThreadBreaks setIsolatedThread
- resetBreakAction () = do
- poke exceptionFlag 0
+ runIfCtx ctx isolateThreadBreaks setIsolatedCtx
+
+ resetBreakAction ctx () = do
+ poke exceptionFlag 0 -- just wrong, expection flag should be per-thread
rts_disableStopAfterReturn tid#
rts_disableStopNextBreakpoint tid#
- runIf isolateThreadBreaks unsetIsolatedThread
+ runIfCtx ctx isolateThreadBreaks unsetIsolatedCtx
-- freeStablePtr stablePtr TODO: we never free the global stablePtr for
-- global action. Maybe we should have a de-init for clean up.
-- TODO: don't set it in `setBreakAction` either. Should be on
-- interpreter initialization.
- runIf pred what = when (pred opts) (what tid)
- runIf# pred what = when (pred opts) (what tid#)
+ runIf pred what = when (pred opts) (what tid)
+ runIf# pred what = when (pred opts) (what tid#)
+ runIfCtx ctx pred what = when (pred opts) (what ctx)
- waitForResult
+ waitForResult ctx
| isolateThreadBreaks opts
= atomically $
- readThreadEvalStatus tid
+ readCtxEvalStatus ctx
| otherwise
= atomically $
(EvalPaused <$> readAnyThreadEvalBreak)
`orElse`
- readThreadEvalStatus tid
+ readCtxEvalStatus ctx
-- While we're waiting for the sandbox thread to return a result, if
-- the current thread receives an asynchronous exception we re-throw
@@ -476,9 +523,3 @@ redirectInterrupts target wait = do
Nothing -> wait
Just target -> do throwTo target (e :: SomeException); wait
-clearEvalStatus :: EvalStatus a -> EvalStatus ()
-clearEvalStatus = \case
- EvalComplete w (EvalException se) -> EvalComplete w (EvalException se)
- EvalComplete w (EvalSuccess _) -> EvalComplete w (EvalSuccess ())
- EvalBreak ap mb rt rccs -> EvalBreak ap mb rt rccs
-
=====================================
libraries/ghci/GHCi/Run/Breakpoints.hs
=====================================
@@ -8,10 +8,12 @@ module GHCi.Run.Breakpoints
-- * Per-thread breakpoints
, readThreadEvalStatus, readAnyThreadEvalBreak
, writeThreadEvalStatus
+ , readCtxEvalStatus, writeCtxEvalStatus
-- ** Isolating thread breakpoints
, withIsolatedThread
, setIsolatedThread, unsetIsolatedThread
+ , setIsolatedCtx, unsetIsolatedCtx
-- ** Querying the global thread breakpoint map
, listOtherPausedThreads
@@ -29,18 +31,15 @@ import GHCi.Message
import GHCi.RemoteTypes
import qualified Data.Map as Map
-import qualified Data.Set as Set
import Control.Concurrent
import Control.Exception
import qualified Data.ByteString.Short.Internal as BS
-#if defined(PROFILING)
-import GHC.Data.ShortByteString
-#endif
import GHC.Exts
import GHC.Stack
import Foreign hiding (void)
import GHC.Conc.Sync
import GHC.IO hiding ( bracket )
+import Control.Monad
--------------------------------------------------------------------------------
-- * Global break action
@@ -72,7 +71,7 @@ globalBreakStablePtr = unsafePerformIO $ newStablePtr globalBreakAction
globalBreakAction :: BreakpointCallback
globalBreakAction info_mod# info_mod_uid# infox# is_exception apStack = do
tid <- myThreadId
- ResumeContext{..} <- getThreadResumeContext tid
+ ctx@ResumeContext{..} <- getThreadResumeContext tid
resume_r <- mkRemoteRef tid
apStack_r <- mkRemoteRef apStack
ccs <- toRemotePtr <$> getCCSOf apStack
@@ -83,7 +82,7 @@ globalBreakAction info_mod# info_mod_uid# infox# is_exception apStack = do
info_mod <- BS.packCString (Ptr info_mod#)
info_mod_uid <- BS.packCString (Ptr info_mod_uid#)
pure (Just (EvalBreakpoint info_mod info_mod_uid (I# infox#)))
- writeThreadEvalStatus tid $ EvalBreak apStack_r mb_breakpoint resume_r ccs
+ writeCtxEvalStatus ctx $ EvalBreak apStack_r mb_breakpoint resume_r ccs
-- Block until this thread is resumed (by the thread which took the
-- `ResumeContext` from the `statusMVar`).
@@ -109,14 +108,19 @@ globalBreakAction info_mod# info_mod_uid# infox# is_exception apStack = do
-- Succeeds when the given thread writes to the global debugger map an eval result.
readThreadEvalStatus :: ThreadId -> STM (EvalStatus [HValueRef])
readThreadEvalStatus tid = do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- case Map.lookup tid threadEvalStatuses of
+ ctxs <- readTVar threadContextsVar
+ case Map.lookup tid ctxs of
+ Just ctx -> readCtxEvalStatus ctx
+ Nothing -> retry -- to write a thread break or result, the
+ -- 'threadContextsVar' entry will be created
+
+-- | Like 'readThreadEvalStatus', but when the 'ResumeContext' is already
+-- available (to avoid looking it up unnecessarily).
+readCtxEvalStatus :: ResumeContext -> STM (EvalStatus [HValueRef])
+readCtxEvalStatus ResumeContext{resumeEvalStatus} =
+ readTVar resumeEvalStatus >>= \case
Just r -> do
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses = Map.delete tid threadEvalStatuses -- clear it
- , isolatedThreads
- }
+ writeTVar resumeEvalStatus Nothing -- clear it
pure r
Nothing -> retry -- until someone writes the result for this thread
@@ -126,38 +130,46 @@ readThreadEvalStatus tid = do
-- The debugger will be waiting for threads to break using `readAnyThreadEvalBreak`.
--
-- Succeeds when any thread hits a breakpoint.
+--
+-- TODO: Instead of reading all elems of the map, we could have the global
+-- break action write breakpoints hit into a global `TChan ThreadId`, and then
+-- `readAnyThreadEvalBreak` would read off of this channel breakpoints that
+-- were hit, and do the lookup on the map by ThreadId, retrying if it has
+-- already been handled (i.e. set to Nothing)
readAnyThreadEvalBreak :: STM EvalBreak
readAnyThreadEvalBreak = do
- dbgs <- readTVar globalDbgStatusVar
+ ctxs <- readTVar threadContextsVar
-- try reading any thread's 'EvalBreak' and retry if none have yielded one yet
- foldr (\x next -> readStatus dbgs x `orElse` next) retry (Map.toList (threadEvalStatuses dbgs))
+ foldl' (\next ctx -> next `orElse` readBreak ctx) retry (Map.elems ctxs)
where
- readStatus GlobalDbgStatus{..} (tid, _) -- don't look at isolated threads
- | Set.member tid isolatedThreads
- = retry
-
- readStatus GlobalDbgStatus{..} (tid, status)
- = do
- case status of
- EvalComplete{} -> retry -- successes can be read with `readThreadEvalStatus`
- EvalPaused b -> do
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses = Map.delete tid threadEvalStatuses
- , isolatedThreads
- }
- pure b
+ readBreak ResumeContext{resumeEvalStatus, resumeIsolated} = do
+ isolated <- readTVar resumeIsolated
+ if isolated
+ then retry -- don't look at isolated threads
+ else readTVar resumeEvalStatus >>= \case
+ Just (EvalPaused b) -> do
+ writeTVar resumeEvalStatus Nothing
+ pure b
+ Just EvalComplete{} ->
+ retry -- successes can be read with `readThreadEvalStatus`
+ Nothing ->
+ retry -- no status for this thread yet
writeThreadEvalStatus :: ThreadId -> EvalStatus [HValueRef] -> IO ()
-writeThreadEvalStatus tid status =
+writeThreadEvalStatus tid status = do
+ -- get or create a 'ResumeContext' for the thread (e.g. we may write the
+ -- EvalComplete without breakpoints occurring, so the map won't have an entry):
+ ctx <- getThreadResumeContext tid
+ writeCtxEvalStatus ctx status
+
+-- | Like 'writeThreadEvalStatus', but for when the 'ResumeContext' is already
+-- available
+writeCtxEvalStatus :: ResumeContext -> EvalStatus [HValueRef] -> IO ()
+writeCtxEvalStatus ResumeContext{resumeEvalStatus} status =
atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar $
- GlobalDbgStatus
- { threadEvalStatuses =
- Map.insertWith (\_ _ -> error "writeThreadEvalStatus: should be impossible")
- tid status threadEvalStatuses
- , isolatedThreads }
+ readTVar resumeEvalStatus >>= \case
+ Just{} -> error "writeCtxEvalStatus: should be impossible"
+ Nothing -> writeTVar resumeEvalStatus (Just status)
-- ** Isolating thread breakpoints ---------------------------------------------
@@ -174,23 +186,15 @@ withIsolatedThread tid = bracket_ (setIsolatedThread tid) (unsetIsolatedThread t
-- expression to run should not be seen at all by the debugger (e.g. when
-- evaluating an expr to :force a variable)
setIsolatedThread :: ThreadId -> IO ()
-setIsolatedThread tid = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , isolatedThreads = Set.insert tid isolatedThreads
- }
+setIsolatedThread = getThreadResumeContext >=> setIsolatedCtx
-- | Undoes 'setIsolatedThread'
unsetIsolatedThread :: ThreadId -> IO ()
-unsetIsolatedThread tid = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , isolatedThreads = Set.delete tid isolatedThreads
- }
+unsetIsolatedThread = getThreadResumeContext >=> unsetIsolatedCtx
+
+setIsolatedCtx, unsetIsolatedCtx :: ResumeContext -> IO ()
+setIsolatedCtx ctx = atomically $ writeTVar (resumeIsolated ctx) True
+unsetIsolatedCtx ctx = atomically $ writeTVar (resumeIsolated ctx) False
-- ** Querying the global thread breakpoint map --------------------------------
@@ -201,64 +205,74 @@ unsetIsolatedThread tid = atomically $ do
-- the global thread mapping) and are asking about which other threads are
-- paused to list them.
listOtherPausedThreads :: IO [ThreadId]
-listOtherPausedThreads = do
- m <- atomically $ readTVar globalDbgStatusVar
- pure $ Map.keys $ Map.filter isBreak (threadEvalStatuses m)
+listOtherPausedThreads = atomically $ do
+ ctxs <- readTVar threadContextsVar
+ paused <- mapM isBreak (Map.toList ctxs)
+ pure [tid | (tid, True) <- paused]
where
- isBreak EvalBreak{} = True
- isBreak EvalComplete{} = False
-
--- ** Global thread exec status map --------------------------------------------
-
--- | When we launch a new thread using the debugger, we map ... TODO ... all
--- threads launched by that main stmt may further spawn threads, and any of
--- them might hit a breakpoint (break array toggled, or per-thread step in
--- might be used, or pause button, etc...).
---
--- When we launch that main thread we also write its result to this map. So
--- this map will only contain the results of threads we explicitly care to
--- observe the result of. Not all threads will write their results here,
--- certainly not ones spawned unknowningly by the main stmt.
--- ...
--- todo words
-globalDbgStatusVar :: TVar GlobalDbgStatus
-globalDbgStatusVar = unsafePerformIO $ newTVarIO $ GlobalDbgStatus Map.empty Set.empty
-{-# NOINLINE globalDbgStatusVar #-}
-
-data GlobalDbgStatus = GlobalDbgStatus
- { threadEvalStatuses :: !(Map.Map ThreadId (EvalStatus [HValueRef]))
- -- ^ Whenever a thread hits a breakpoint it inserts in this map its 'EvalBreak'.
- -- We can additionally observe a thread's result by calling
- -- `writeThreadEvalStatus tid (EvalComplete ...)` when the main expression
- -- finishes evaluating, see e.g. 'sandboxIO'.
- , isolatedThreads :: !(Set.Set ThreadId)
- -- ^ Threads in this map are meant to be ignored by 'readAnyThreadEvalBreak'.
- -- That is, even if one of the threads in this set hits a breakpoint, we
- -- can only read its 'EvalBreak' with @'readThreadEvalStatus' tid@ directly.
- }
+ isBreak (tid, ResumeContext{resumeEvalStatus}) = do
+ status <- readTVar resumeEvalStatus
+ pure (tid, case status of
+ Just EvalBreak{} -> True
+ Just EvalComplete{} -> False
+ Nothing -> False)
--------------------------------------------------------------------------------
-- * Per-thread resume-ing
--------------------------------------------------------------------------------
getThreadResumeContext :: ThreadId -> IO ResumeContext
-getThreadResumeContext tid = modifyMVar globalBreakActionMap $ \gbm0 -> do
- case Map.lookup tid gbm0 of
+getThreadResumeContext tid = do
+ ctxs0 <- readTVarIO threadContextsVar
+ case Map.lookup tid ctxs0 of
+ Just ctx -> pure ctx -- common case: hence read without transaction
Nothing -> do
- r <- ResumeContext <$> newEmptyMVar
- let !gbm1 = Map.insert tid r gbm0
- pure (gbm1, r)
- Just ctx -> do
- pure (gbm0, ctx)
+ new_ctx <- ResumeContext <$> newEmptyMVar
+ <*> newTVarIO Nothing
+ <*> newTVarIO False
+ atomically $ do -- write new thread context atomically
+ ctxs1 <- readTVar threadContextsVar
+ case Map.lookup tid ctxs1 of
+ Just ctx -> pure ctx -- already exists
+ Nothing -> do
+ let !ctxs2 = Map.insert tid new_ctx ctxs1
+ writeTVar threadContextsVar ctxs2
+ pure new_ctx
--- | A global mapping from thread ids to the MVars on which they block when stopped.
-globalBreakActionMap :: MVar (Map.Map ThreadId ResumeContext)
-globalBreakActionMap = unsafePerformIO $ newMVar Map.empty
-{-# NOINLINE globalBreakActionMap #-}
+-- | A global mapping from threads to their per-thread state 'ResumeContext'
+-- (that e.g. contain the MVars on which they block when stopped)
+threadContextsVar :: TVar (Map.Map ThreadId ResumeContext)
+threadContextsVar = unsafePerformIO $ newTVarIO Map.empty
+{-# NOINLINE threadContextsVar #-}
-newtype ResumeContext = ResumeContext
- { resumeBreakMVar :: MVar ()
+-- | Global context for a thread, which is created and inserted in the global
+-- 'threadContextsVar' when a thread hits a breakpoint (and, thus, the
+-- 'globalBreakAction') is run, or when 'withBreakAction' sets the thread
+-- isolation (e.g. in 'sandboxIO'), or in other calls to
+-- 'getThreadResumeContext', the function which gets or creates a thread
+-- 'ResumeContext'.
+--
+-- TODO: Rename ResumeContext to something like ThreadContext or
+-- ThreadDbgContext or (Thread)BreakContext, and fields to something like
+-- dbgEvalStatus, threadIsolated, ...
+data ResumeContext = ResumeContext
+ { resumeBreakMVar :: !(MVar ())
-- ^ A thread that hits a breakpoint blocks reading its corresponding MVar
- -- (gotten from the 'globalBreakActionMap').
+ -- (gotten from the 'threadContextsVar').
-- The debugger can unblock that thread by signaling its MVar.
+ , resumeEvalStatus :: !(TVar (Maybe (EvalStatus [HValueRef])))
+ -- ^ When a thread hits a breakpoint, the 'globalBreakAction' is run and
+ -- writes an 'EvalBreak' in this @TVar@ (which was found in 'threadContextsVar').
+ --
+ -- Additionally, the 'EvalComplete' result of a thread may be written here
+ -- using `writeThreadEvalStatus tid (EvalComplete ...)` after the main
+ -- expression finishes evaluating, see e.g. 'sandboxIO'.
+ --
+ -- The variable should be emptied (i.e. set to 'Nothing') by whoever
+ -- reads the status.
+ , resumeIsolated :: !(TVar Bool)
+ -- ^ When set, this thread is meant to be ignored by 'readAnyThreadEvalBreak'.
+ -- That is, if a thread with resumeIsolated=True hits a breakpoint, its
+ -- 'EvalBreak' can only be read with @'readThreadEvalStatus' tid@ for this
+ -- thread's @tid@ directly.
}
=====================================
libraries/ghci/GHCi/Server.hs
=====================================
@@ -70,6 +70,14 @@ servWithCustom verbose hook pipe restore customHandler = loop
when verbose $ trace ("msg: " ++ (show msg))
case msg of
+ -- The expressions which block waiting for a result musn't block the
+ -- server thread, so send them off and notify over the wire when done.
+ --
+ -- The other end (see 'remoteCall') will be processing notifications, and
+ -- it will use the returned pointer as the index for the matching notification.
+ WaitThreadResult {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
+ WaitAnyThreadBreak {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
+ WaitThreadResultOrAnyThreadBreak {} -> forkIO (run msg >>= (notification =<< myThreadId)) >>= reply . mkRemoteRef
CustomMessage tag payload -> handleCustom tag payload
Shutdown -> return ()
RunTH st q ty loc -> wrapRunTH $ runTH pipe st q ty loc
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/64836f8c2dc5ec9cde23253cf1407d…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/64836f8c2dc5ec9cde23253cf1407d…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] [CLC 341] Strict variants of mutable types
by Marge Bot (@marge-bot) 05 Oct '26
by Marge Bot (@marge-bot) 05 Oct '26
05 Oct '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
f8cfb1a5 by Hécate Kleidukos at 2026-10-04T19:36:39+02:00
[CLC 341] Strict variants of mutable types
This commit is the implementation of CLC Proposal 341
https://github.com/haskell/core-libraries-committee/issues/341
- - - - -
12 changed files:
- + changelog.d/clc-341-strict-mutable-data-types
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- libraries/base/src/Control/Concurrent/Chan.hs
- + libraries/base/src/Control/Concurrent/Chan/Strict.hs
- libraries/base/src/Control/Concurrent/MVar.hs
- + libraries/base/src/Control/Concurrent/MVar/Strict.hs
- libraries/base/src/Data/IORef.hs
- + libraries/base/src/Data/IORef/Strict.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
Changes:
=====================================
changelog.d/clc-341-strict-mutable-data-types
=====================================
@@ -0,0 +1,9 @@
+section: base
+synopsis: Add strict variants to mutable types (IORef, Chan, MVar)
+issues: #27343
+mrs: !15450
+
+description: {
+ These new types prevent users from (easily) mis-using existing and dangerous APIs (writeIORef, modifyIORef, modifyMVar, etc) which can lead to space leaks when stored in mutable containers.
+ They also prevent invisible coercions to and from their lazy equivalents by not exposing their constructors.
+}
=====================================
libraries/base/base.cabal.in
=====================================
@@ -41,6 +41,7 @@ Library
Control.Applicative
, Control.Concurrent
, Control.Concurrent.Chan
+ , Control.Concurrent.Chan.Strict
, Control.Concurrent.QSem
, Control.Concurrent.QSemN
, Control.Monad.IO.Class
@@ -80,6 +81,7 @@ Library
, Control.Arrow
, Control.Category
, Control.Concurrent.MVar
+ , Control.Concurrent.MVar.Strict
, Control.Exception
, Control.Exception.Annotation
, Control.Exception.Backtrace
@@ -109,6 +111,7 @@ Library
, Data.Functor.Const
, Data.Functor.Identity
, Data.IORef
+ , Data.IORef.Strict
, Data.Int
, Data.Ix
, Data.Kind
=====================================
libraries/base/changelog.md
=====================================
@@ -12,6 +12,7 @@
* Add `withEmptyCallStack` to `GHC.Stack`. ([CLC proposal #428](https://github.com/haskell/core-libraries-committee/issues/428))
* Add new `Data.RealFloat` module re-exporting `RealFloat` from `GHC.Float` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394))
* Add `Infinity`, `NegInfinity`, and `NaN` pattern synonyms to `Data.RealFloat` ([CLC proposal #394](https://github.com/haskell/core-libraries-committee/issues/394))
+ * Add strict variants to mutable types (IORef, Chan, MVar). ([CLC Proposal #341](https://github.com/haskell/core-libraries-committee/issues/341))
## 4.23.0.0 *TBA*
* Add `System.IO.hGetNewlineMode`. ([CLC proposal #370](https://github.com/haskell/core-libraries-committee/issues/370))
=====================================
libraries/base/src/Control/Concurrent/Chan.hs
=====================================
@@ -18,6 +18,8 @@
-- @stm@ (software transactional memory) library has a more robust implementation
-- of channels called @TChan@s.
--
+-- A variant that forces its contents to weak head normal form is available in
+-- "Control.Concurrent.Chan.Strict".
-----------------------------------------------------------------------------
module Control.Concurrent.Chan
=====================================
libraries/base/src/Control/Concurrent/Chan/Strict.hs
=====================================
@@ -0,0 +1,81 @@
+-- | Unbounded channels whose contents are always in weak head normal form
+-- (WHNF).
+--
+-- This module is a thin wrapper around "Control.Concurrent.Chan": each
+-- operation has the same semantics as its counterpart there, except that the
+-- value written to the channel is forced to WHNF first.
+-- "Control.Concurrent.Chan" is the reference documentation for these
+-- operations; only the differences are repeated here.
+--
+-- The caveats of the lazy module apply unchanged. In particular a 'Chan' is
+-- /unbounded/: a producer that outruns its consumer makes the channel grow
+-- without limit. The @stm@ package offers channels with a different
+-- concurrency model, and @TBQueue@ in particular is bounded.
+--
+-- Note that WHNF is not deep: writing @Just <thunk>@ to a
+-- @'Chan' (Maybe a)@ still writes a thunk inside the @Just@.
+module Control.Concurrent.Chan.Strict
+ ( Chan
+
+ -- * Operations
+ , newChan
+ , writeChan
+ , readChan
+ , dupChan
+ , getChanContents
+ , writeList2Chan
+ ) where
+
+import Data.Foldable (mapM_)
+import Data.Functor
+import GHC.Base
+import qualified Control.Concurrent.Chan as Lazy
+
+-- | An unbounded FIFO channel, wrapping @Chan@ from "Control.Concurrent.Chan".
+--
+-- The values it holds are always in WHNF: every operation of this module forces
+-- a value before it is written.
+newtype Chan a = Chan (Lazy.Chan a)
+ deriving Eq
+
+-- | Build and return a new instance of 'Chan'.
+--
+-- See @newChan@ in "Control.Concurrent.Chan" for the full semantics.
+newChan :: IO (Chan a)
+newChan = Chan <$> Lazy.newChan
+
+-- | Write a value to a 'Chan'.
+--
+-- See @writeChan@ in "Control.Concurrent.Chan" for the full semantics.
+--
+-- The value is forced to WHNF before it is written.
+writeChan :: Chan a -> a -> IO ()
+writeChan (Chan chan) a = Lazy.writeChan chan =<< (pure $! a)
+
+-- | Read the next value from the 'Chan'. Blocks when the channel is empty.
+--
+-- See @readChan@ in "Control.Concurrent.Chan" for the full semantics, in
+-- particular for the fairness guarantees and for the
+-- 'Control.Exception.BlockedIndefinitelyOnMVar' exception.
+readChan :: Chan a -> IO a
+readChan (Chan chan) = Lazy.readChan chan
+
+-- | Duplicate a 'Chan'.
+--
+-- See @dupChan@ in "Control.Concurrent.Chan" for the full semantics.
+dupChan :: Chan a -> IO (Chan a)
+dupChan (Chan chan) = Chan <$> Lazy.dupChan chan
+
+-- | Return a lazy list representing the contents of the supplied 'Chan'.
+--
+-- See @getChanContents@ in "Control.Concurrent.Chan" for the full semantics.
+getChanContents :: Chan a -> IO [a]
+getChanContents (Chan chan) = Lazy.getChanContents chan
+
+-- | Write an entire list of items to a 'Chan'.
+--
+-- See @writeList2Chan@ in "Control.Concurrent.Chan" for the full semantics.
+--
+-- Evaluates the list elements to WHNF.
+writeList2Chan :: Chan a -> [a] -> IO ()
+writeList2Chan = mapM_ . writeChan
=====================================
libraries/base/src/Control/Concurrent/MVar.hs
=====================================
@@ -65,6 +65,11 @@
-- in an 'MVar' to the appropriate normal form, or utilize a strict
-- @MVar@ provided by the [strict-concurrency](https://hackage.haskell.org/package/strict-concurrency) package.
--
+-- A variant that forces its contents to weak head normal form is available in
+-- "Control.Concurrent.MVar.Strict". Note that weak head normal form is often
+-- not the appropriate normal form: it does not force the fields of the
+-- constructor it exposes.
+--
-- === Ordering
--
-- 'MVar' operations are always observed to take place in the order
=====================================
libraries/base/src/Control/Concurrent/MVar/Strict.hs
=====================================
@@ -0,0 +1,202 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE TupleSections #-}
+
+-- | Synchronising variables whose contents are always in weak head normal form
+-- (WHNF).
+--
+-- This module is a thin wrapper around "Control.Concurrent.MVar": each
+-- operation has the same semantics as its counterpart there, except that the
+-- value put in the variable is forced to WHNF first.
+-- "Control.Concurrent.MVar" is the reference documentation for these
+-- operations — for the fairness and ordering guarantees, for the atomicity
+-- caveats of the \"bigger\" functions, and for worked examples; only the
+-- differences are repeated here.
+--
+-- Note that WHNF is not deep: putting @Just <thunk>@ in an
+-- @'MVar' (Maybe a)@ still puts a thunk inside the @Just@.
+
+module Control.Concurrent.MVar.Strict
+ ( MVar
+
+ -- * Operations
+ , newEmptyMVar
+ , newMVar
+ , takeMVar
+ , putMVar
+ , readMVar
+ , swapMVar
+ , tryTakeMVar
+ , tryPutMVar
+ , tryReadMVar
+ , isEmptyMVar
+ , withMVar
+ , withMVarMasked
+ , modifyMVar_
+ , modifyMVar
+ , modifyMVarMasked_
+ , modifyMVarMasked
+ , mkWeakMVar
+ ) where
+
+import Data.Functor
+import GHC.Base
+import GHC.Weak (Weak)
+import qualified Control.Concurrent.MVar as Lazy
+
+-- | A synchronising variable, wrapping @MVar@ from "Control.Concurrent.MVar".
+-- It can be thought of as a box, which may be empty or full.
+--
+-- The value it holds is always in WHNF: every operation of this module forces
+-- the value before it is put in the box.
+newtype MVar a = MVar (Lazy.MVar a)
+ deriving stock (Eq)
+
+-- | Create an 'MVar' which is initially empty.
+--
+-- See @newEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics.
+newEmptyMVar :: IO (MVar a)
+newEmptyMVar = MVar <$> Lazy.newEmptyMVar
+
+-- | Create an 'MVar' which contains the supplied value.
+--
+-- See @newMVar@ in "Control.Concurrent.MVar" for the full semantics.
+--
+-- The initial value is forced to WHNF before it is stored.
+newMVar :: a -> IO (MVar a)
+newMVar a = fmap MVar . Lazy.newMVar =<< (pure $! a)
+
+-- | Return the contents of the 'MVar', leaving it empty. Blocks while the
+-- 'MVar' is empty.
+--
+-- See @takeMVar@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for the single-wakeup and FIFO guarantees.
+takeMVar :: MVar a -> IO a
+takeMVar (MVar var) = Lazy.takeMVar var
+
+-- | Atomically read the contents of an 'MVar'. Blocks while the 'MVar' is
+-- empty.
+--
+-- See @readMVar@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for the multiple-wakeup guarantee.
+readMVar :: MVar a -> IO a
+readMVar (MVar var) = Lazy.readMVar var
+
+-- | Put a value into an 'MVar'. Blocks while the 'MVar' is full.
+--
+-- See @putMVar@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for the single-wakeup and FIFO guarantees.
+--
+-- The value is forced to WHNF before it is put.
+putMVar :: MVar a -> a -> IO ()
+putMVar (MVar var) a = Lazy.putMVar var =<< (pure $! a)
+
+-- | A non-blocking version of 'takeMVar'.
+--
+-- See @tryTakeMVar@ in "Control.Concurrent.MVar" for the full semantics.
+tryTakeMVar :: MVar a -> IO (Maybe a)
+tryTakeMVar (MVar var) = Lazy.tryTakeMVar var
+
+-- | A non-blocking version of 'putMVar'.
+--
+-- See @tryPutMVar@ in "Control.Concurrent.MVar" for the full semantics.
+--
+-- The value is forced to WHNF before it is put. Note that it is forced whether
+-- or not the 'MVar' turns out to be empty.
+tryPutMVar :: MVar a -> a -> IO Bool
+tryPutMVar (MVar var) a = Lazy.tryPutMVar var =<< (pure $! a)
+
+-- | Take a value from an 'MVar', put a new value into the 'MVar' and return the
+-- value taken.
+--
+-- See @swapMVar@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for its atomicity caveat.
+--
+-- The new value is forced to WHNF before it is put.
+swapMVar :: MVar a -> a -> IO a
+swapMVar (MVar var) a = Lazy.swapMVar var =<< (pure $! a)
+
+-- | A non-blocking version of 'readMVar'.
+--
+-- See @tryReadMVar@ in "Control.Concurrent.MVar" for the full semantics.
+tryReadMVar :: MVar a -> IO (Maybe a)
+tryReadMVar (MVar var) = Lazy.tryReadMVar var
+
+-- | Check whether a given 'MVar' is empty.
+--
+-- See @isEmptyMVar@ in "Control.Concurrent.MVar" for the full semantics. The
+-- result is only a snapshot, so prefer 'tryTakeMVar' where possible.
+isEmptyMVar :: MVar a -> IO Bool
+isEmptyMVar (MVar var) = Lazy.isEmptyMVar var
+
+-- | An exception-safe wrapper for operating on the contents of an 'MVar'.
+--
+-- See @withMVar@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for its atomicity caveat.
+withMVar :: MVar a -> (a -> IO b) -> IO b
+withMVar (MVar var) action = Lazy.withMVar var action
+{-# INLINE withMVar #-}
+
+-- | Like 'withMVar', but the @IO@ action in the second argument is executed
+-- with asynchronous exceptions masked.
+--
+-- See @withMVarMasked@ in "Control.Concurrent.MVar" for the full semantics.
+withMVarMasked :: MVar a -> (a -> IO b) -> IO b
+withMVarMasked (MVar var) action = Lazy.withMVarMasked var action
+{-# INLINE withMVarMasked #-}
+
+-- | An exception-safe wrapper for modifying the contents of an 'MVar'.
+--
+-- See @modifyMVar_@ in "Control.Concurrent.MVar" for the full semantics, in
+-- particular for its atomicity caveat.
+--
+-- The new value is forced to WHNF before it is put back.
+modifyMVar_ :: MVar a -> (a -> IO a) -> IO ()
+modifyMVar_ (MVar var) action = Lazy.modifyMVar_ var $ \a0 -> do
+ a <- action a0
+ pure $! a
+{-# INLINE modifyMVar_ #-}
+
+-- | A slight variation on 'modifyMVar_' that allows a value to be returned
+-- (@b@) in addition to the modified value of the 'MVar'.
+--
+-- See @modifyMVar@ in "Control.Concurrent.MVar" for the full semantics.
+--
+-- The new value is forced to WHNF before it is put back. The returned value is
+-- not forced.
+modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b
+modifyMVar (MVar var) action = Lazy.modifyMVar var $ \a0 -> do
+ (a, b) <- action a0
+ (, b) <$> (pure $! a)
+{-# INLINE modifyMVar #-}
+
+-- | Like 'modifyMVar_', but the @IO@ action in the second argument is executed
+-- with asynchronous exceptions masked.
+--
+-- See @modifyMVarMasked_@ in "Control.Concurrent.MVar" for the full semantics.
+--
+-- The new value is forced to WHNF before it is put back.
+modifyMVarMasked_ :: MVar a -> (a -> IO a) -> IO ()
+modifyMVarMasked_ (MVar var) action = Lazy.modifyMVarMasked_ var $ \a0 -> do
+ a <- action a0
+ pure $! a
+{-# INLINE modifyMVarMasked_ #-}
+
+-- | Like 'modifyMVar', but the @IO@ action in the second argument is executed
+-- with asynchronous exceptions masked.
+--
+-- See @modifyMVarMasked@ in "Control.Concurrent.MVar" for the full semantics.
+--
+-- The new value is forced to WHNF before it is put back. The returned value is
+-- not forced.
+modifyMVarMasked :: MVar a -> (a -> IO (a, b)) -> IO b
+modifyMVarMasked (MVar var) action = Lazy.modifyMVarMasked var $ \a0 -> do
+ (a, b) <- action a0
+ (, b) <$> (pure $! a)
+{-# INLINE modifyMVarMasked #-}
+
+-- | Make a 'Weak' pointer to an 'MVar', using the second argument as a
+-- finalizer to run when the 'MVar' is garbage-collected.
+--
+-- See @mkWeakMVar@ in "Control.Concurrent.MVar" for the full semantics.
+mkWeakMVar :: MVar a -> IO () -> IO (Weak (MVar a))
+mkWeakMVar (MVar var) finalizer = coerce <$> Lazy.mkWeakMVar var finalizer
=====================================
libraries/base/src/Data/IORef.hs
=====================================
@@ -12,6 +12,8 @@
--
-- Mutable references in the IO monad.
--
+-- A variant that forces its contents to weak head normal form is available in
+-- "Data.IORef.Strict".
module Data.IORef
(-- * IORefs
=====================================
libraries/base/src/Data/IORef/Strict.hs
=====================================
@@ -0,0 +1,93 @@
+{-# LANGUAGE DerivingStrategies #-}
+
+-- | Mutable references in the IO monad whose contents are always in weak head
+-- normal form (WHNF).
+--
+-- This module is a thin wrapper around "Data.IORef": each operation has the
+-- same semantics as its counterpart there, except that the value put in the
+-- reference is forced to WHNF first. "Data.IORef" is the reference
+-- documentation for these operations; only the differences are repeated here.
+--
+-- Note that WHNF is not deep: storing @Just <thunk>@ in an
+-- @'IORef' (Maybe a)@ still stores a thunk inside the @Just@.
+
+module Data.IORef.Strict
+ ( IORef
+
+ -- * Operations
+ , newIORef
+ , readIORef
+ , writeIORef
+ , modifyIORef
+ , atomicModifyIORef
+ , atomicWriteIORef
+ , mkWeakIORef
+ ) where
+
+import GHC.Base
+import GHC.Weak (Weak)
+import qualified Data.IORef as Lazy
+
+-- | A mutable reference in the IO monad, wrapping @IORef@ from "Data.IORef".
+--
+-- The value it holds is always in WHNF: every operation of this module forces
+-- the value before it is written.
+newtype IORef a = IORef (Lazy.IORef a)
+ deriving stock (Eq)
+
+-- | Build a new 'IORef'.
+--
+-- See @newIORef@ in "Data.IORef" for the full semantics.
+--
+-- The initial value is forced to WHNF before it is stored.
+newIORef :: a -> IO (IORef a)
+newIORef a = fmap IORef . Lazy.newIORef =<< (pure $! a)
+
+-- | Read the value of an 'IORef'.
+--
+-- See @readIORef@ in "Data.IORef" for the full semantics, in particular for the
+-- memory model that applies to reads and writes.
+readIORef :: IORef a -> IO a
+readIORef (IORef var) = Lazy.readIORef var
+
+-- | Write a new value into an 'IORef'.
+--
+-- See @writeIORef@ in "Data.IORef" for the full semantics. This function does
+-- not create a memory barrier; use 'atomicWriteIORef' when that matters.
+--
+-- The new value is forced to WHNF before it is stored.
+writeIORef :: IORef a -> a -> IO ()
+writeIORef (IORef var) a = Lazy.writeIORef var =<< (pure $! a)
+
+-- | Mutate the contents of an 'IORef', combining 'readIORef' and 'writeIORef'.
+--
+-- See @modifyIORef@ in "Data.IORef" for the full semantics. This is not an
+-- atomic update; use 'atomicModifyIORef' in a multithreaded environment.
+--
+-- The new value is forced to WHNF before it is stored.
+modifyIORef :: IORef a -> (a -> a) -> IO ()
+modifyIORef (IORef var) f = Lazy.modifyIORef' var f
+
+-- | Atomically modify the contents of an 'IORef'.
+--
+-- See @atomicModifyIORef@ in "Data.IORef" for the full semantics, in particular
+-- for the atomicity and memory-barrier guarantees.
+--
+-- Both the new value and the returned value are forced to WHNF.
+atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b
+atomicModifyIORef (IORef var) f = Lazy.atomicModifyIORef' var f
+
+-- | Variant of 'writeIORef' that imposes a reordering barrier.
+--
+-- See @atomicWriteIORef@ in "Data.IORef" for the full semantics.
+--
+-- The new value is forced to WHNF before it is stored.
+atomicWriteIORef :: IORef a -> a -> IO ()
+atomicWriteIORef (IORef var) a = Lazy.atomicWriteIORef var =<< (pure $! a)
+
+-- | Make a 'Weak' pointer to an 'IORef', using the second argument as a
+-- finalizer to run when the 'IORef' is garbage-collected.
+--
+-- See @mkWeakIORef@ in "Data.IORef" for the full semantics.
+mkWeakIORef :: IORef a -> IO () -> IO (Weak (IORef a))
+mkWeakIORef (IORef var) finalizer = fmap coerce (Lazy.mkWeakIORef var finalizer)
=====================================
testsuite/tests/interface-stability/base-exports.stdout
=====================================
@@ -173,6 +173,17 @@ module Control.Concurrent.Chan where
writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+module Control.Concurrent.Chan.Strict where
+ -- Safety: None
+ type Chan :: * -> *
+ newtype Chan a = ...
+ dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a)
+ getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a]
+ newChan :: forall a. GHC.Internal.Types.IO (Chan a)
+ readChan :: forall a. Chan a -> GHC.Internal.Types.IO a
+ writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
+ writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+
module Control.Concurrent.MVar where
-- Safety: Safe
type MVar :: * -> *
@@ -196,6 +207,28 @@ module Control.Concurrent.MVar where
withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+module Control.Concurrent.MVar.Strict where
+ -- Safety: None
+ type MVar :: * -> *
+ newtype MVar a = ...
+ isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a))
+ modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a)
+ newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a)
+ putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO ()
+ readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a
+ takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+ withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+
module Control.Concurrent.QSem where
-- Safety: Safe
type QSem :: *
@@ -1286,6 +1319,18 @@ module Data.IORef where
readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+module Data.IORef.Strict where
+ -- Safety: None
+ type IORef :: * -> *
+ newtype IORef a = ...
+ atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b
+ atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+ mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a))
+ modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO ()
+ newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a)
+ readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
+ writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+
module Data.Int where
-- Safety: Safe
type Int :: *
@@ -11232,6 +11277,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’
@@ -11291,6 +11338,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’
instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’
+instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’
=====================================
testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
=====================================
@@ -173,6 +173,17 @@ module Control.Concurrent.Chan where
writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+module Control.Concurrent.Chan.Strict where
+ -- Safety: None
+ type Chan :: * -> *
+ newtype Chan a = ...
+ dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a)
+ getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a]
+ newChan :: forall a. GHC.Internal.Types.IO (Chan a)
+ readChan :: forall a. Chan a -> GHC.Internal.Types.IO a
+ writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
+ writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+
module Control.Concurrent.MVar where
-- Safety: Safe
type MVar :: * -> *
@@ -196,6 +207,28 @@ module Control.Concurrent.MVar where
withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+module Control.Concurrent.MVar.Strict where
+ -- Safety: None
+ type MVar :: * -> *
+ newtype MVar a = ...
+ isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a))
+ modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a)
+ newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a)
+ putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO ()
+ readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a
+ takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+ withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+
module Control.Concurrent.QSem where
-- Safety: Safe
type QSem :: *
@@ -1286,6 +1319,18 @@ module Data.IORef where
readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+module Data.IORef.Strict where
+ -- Safety: None
+ type IORef :: * -> *
+ newtype IORef a = ...
+ atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b
+ atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+ mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a))
+ modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO ()
+ newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a)
+ readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
+ writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+
module Data.Int where
-- Safety: Safe
type Int :: *
@@ -11264,6 +11309,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’
@@ -11323,6 +11370,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’
instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’
+instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’
=====================================
testsuite/tests/interface-stability/base-exports.stdout-mingw32
=====================================
@@ -173,6 +173,17 @@ module Control.Concurrent.Chan where
writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+module Control.Concurrent.Chan.Strict where
+ -- Safety: None
+ type Chan :: * -> *
+ newtype Chan a = ...
+ dupChan :: forall a. Chan a -> GHC.Internal.Types.IO (Chan a)
+ getChanContents :: forall a. Chan a -> GHC.Internal.Types.IO [a]
+ newChan :: forall a. GHC.Internal.Types.IO (Chan a)
+ readChan :: forall a. Chan a -> GHC.Internal.Types.IO a
+ writeChan :: forall a. Chan a -> a -> GHC.Internal.Types.IO ()
+ writeList2Chan :: forall a. Chan a -> [a] -> GHC.Internal.Types.IO ()
+
module Control.Concurrent.MVar where
-- Safety: Safe
type MVar :: * -> *
@@ -196,6 +207,28 @@ module Control.Concurrent.MVar where
withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+module Control.Concurrent.MVar.Strict where
+ -- Safety: None
+ type MVar :: * -> *
+ newtype MVar a = ...
+ isEmptyMVar :: forall a. MVar a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ mkWeakMVar :: forall a. MVar a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (MVar a))
+ modifyMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO (a, b)) -> GHC.Internal.Types.IO b
+ modifyMVarMasked_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ modifyMVar_ :: forall a. MVar a -> (a -> GHC.Internal.Types.IO a) -> GHC.Internal.Types.IO ()
+ newEmptyMVar :: forall a. GHC.Internal.Types.IO (MVar a)
+ newMVar :: forall a. a -> GHC.Internal.Types.IO (MVar a)
+ putMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO ()
+ readMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ swapMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO a
+ takeMVar :: forall a. MVar a -> GHC.Internal.Types.IO a
+ tryPutMVar :: forall a. MVar a -> a -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
+ tryReadMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ tryTakeMVar :: forall a. MVar a -> GHC.Internal.Types.IO (GHC.Internal.Maybe.Maybe a)
+ withMVar :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+ withMVarMasked :: forall a b. MVar a -> (a -> GHC.Internal.Types.IO b) -> GHC.Internal.Types.IO b
+
module Control.Concurrent.QSem where
-- Safety: Safe
type QSem :: *
@@ -1286,6 +1319,18 @@ module Data.IORef where
readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+module Data.IORef.Strict where
+ -- Safety: None
+ type IORef :: * -> *
+ newtype IORef a = ...
+ atomicModifyIORef :: forall a b. IORef a -> (a -> (a, b)) -> GHC.Internal.Types.IO b
+ atomicWriteIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+ mkWeakIORef :: forall a. IORef a -> GHC.Internal.Types.IO () -> GHC.Internal.Types.IO (GHC.Internal.Weak.Weak (IORef a))
+ modifyIORef :: forall a. IORef a -> (a -> a) -> GHC.Internal.Types.IO ()
+ newIORef :: forall a. a -> GHC.Internal.Types.IO (IORef a)
+ readIORef :: forall a. IORef a -> GHC.Internal.Types.IO a
+ writeIORef :: forall a. IORef a -> a -> GHC.Internal.Types.IO ()
+
module Data.Int where
-- Safety: Safe
type Int :: *
@@ -11490,6 +11535,8 @@ instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.MVar.MVar a) -- Defined
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.BlockReason -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadId -- Defined in ‘GHC.Internal.Conc.Sync’
instance GHC.Internal.Classes.Eq GHC.Internal.Conc.Sync.ThreadStatus -- Defined in ‘GHC.Internal.Conc.Sync’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.Chan.Strict.Chan a) -- Defined in ‘Control.Concurrent.Chan.Strict’
+instance forall a. GHC.Internal.Classes.Eq (Control.Concurrent.MVar.Strict.MVar a) -- Defined in ‘Control.Concurrent.MVar.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ArrayException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.AsyncException -- Defined in ‘GHC.Internal.IO.Exception’
instance GHC.Internal.Classes.Eq GHC.Internal.IO.Exception.ExitCode -- Defined in ‘GHC.Internal.IO.Exception’
@@ -11549,6 +11596,7 @@ instance forall a. GHC.Internal.Classes.Eq a => GHC.Internal.Classes.Eq (GHC.Int
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Product.Product f g a) -- Defined in ‘Data.Functor.Product’
instance [safe] forall k (f :: k -> *) (a :: k) (g :: k -> *). (GHC.Internal.Classes.Eq (f a), GHC.Internal.Classes.Eq (g a)) => GHC.Internal.Classes.Eq (Data.Functor.Sum.Sum f g a) -- Defined in ‘Data.Functor.Sum’
instance forall a. GHC.Internal.Classes.Eq (GHC.Internal.IORef.IORef a) -- Defined in ‘GHC.Internal.IORef’
+instance forall a. GHC.Internal.Classes.Eq (Data.IORef.Strict.IORef a) -- Defined in ‘Data.IORef.Strict’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int16 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int32 -- Defined in ‘GHC.Internal.Int’
instance GHC.Internal.Classes.Eq GHC.Internal.Int.Int64 -- Defined in ‘GHC.Internal.Int’
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8cfb1a5eed7fce615b42bd783631c0…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f8cfb1a5eed7fce615b42bd783631c0…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] ttg: HsModuleName in the AST, ModuleName in GHC
by Marge Bot (@marge-bot) 05 Oct '26
by Marge Bot (@marge-bot) 05 Oct '26
05 Oct '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
575c4bc6 by Rodrigo Mesquita at 2026-10-05T12:32:02-04:00
ttg: HsModuleName in the AST, ModuleName in GHC
This commit makes the AST fully independent from GHC by parsing a
"module name" as an `HsModuleName (= HText)` rather than constructing an
`ModuleName (= FastString)` straight away.
In the GHC side, everything remains as a `ModuleName`. We turn AST
`HsModuleName`s into fast strings with the cheap `mkFastStringShortText`
when we process the AST for manipulation within the compiler.
There are no performance regressions introduced by this approach.
This is the final piece of #21592
Alternative plans were discussed in
gitlab.haskell.org/ghc/ghc/-/merge_requests/16546#note_694647,
but the Simon suggested one won in simplicity and performance when
compared against my attempt at implementing my original plan of having
a family `ModuleNameP`.
- - - - -
90 changed files:
- compiler/GHC.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/Opt/CallerCC/Types.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Pipeline.hs-boot
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/Phases.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Lit.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Errors/Types.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Debug.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/Lexer.x
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/String.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Unbound.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/ErrCtxt.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Unit/Home.hs
- compiler/GHC/Unit/Module.hs
- compiler/GHC/Unit/Module/Env.hs
- + compiler/GHC/Unit/Module/Name.hs
- compiler/GHC/Unit/Parser.hs
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Unit/Types.hs-boot
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Lit.hs
- compiler/Language/Haskell/Syntax/Module/Name.hs
- compiler/Language/Haskell/Syntax/Text.hs
- compiler/ghc.cabal.in
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Info.hs
- linters/lint-codes/LintCodes/Static.hs
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/hiefile/should_run/T24544.hs
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/profiling/should_run/caller-cc/CallerCcParser.hs
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hyperlinker/Renderer.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/575c4bc6cb701a3b26982bf36998aad…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/575c4bc6cb701a3b26982bf36998aad…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/27514] 3 commits: Driver: structured concurrent worker abstraction
by sheaf (@sheaf) 05 Oct '26
by sheaf (@sheaf) 05 Oct '26
05 Oct '26
sheaf pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC
Commits:
9aacb029 by sheaf at 2026-10-05T18:24:05+02:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown.
- - - - -
3645b1fc by sheaf at 2026-10-05T18:24:05+02:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
f2731bcf by sheaf at 2026-10-05T18:24:06+02:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
- - - - -
30 changed files:
- + changelog.d/T27763
- + changelog.d/parallel-downsweep
- + compiler/GHC/Driver/Concurrency.hs
- + compiler/GHC/Driver/Config/Concurrency.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeAction.hs
- compiler/GHC/Driver/MakeSem.hs
- compiler/GHC/Driver/Pipeline/LogQueue.hs
- compiler/GHC/Types/Error/Codes.hs
- + compiler/GHC/Utils/Concurrent/Scope.hs
- compiler/GHC/Utils/TmpFs.hs
- compiler/ghc.cabal.in
- testsuite/tests/diagnostic-codes/codes.stdout
- testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461c.stderr
- testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Wrong.hs
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/downsweep/OldModLocation.hs
- testsuite/tests/ghc-api/downsweep/PartialDownsweep.hs
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/splice-imports/SI35.hs
- utils/check-ppr/Main.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/020fd5d5a985d0bba56b32675a294f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/020fd5d5a985d0bba56b32675a294f…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/27514] 120 commits: Add Data.RealFloat and Infinity/NegInfinity/NaN pattern synonyms (#26961)
by sheaf (@sheaf) 05 Oct '26
by sheaf (@sheaf) 05 Oct '26
05 Oct '26
sheaf pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC
Commits:
b5d29ab8 by Brandon Chinn at 2026-08-25T18:42:08-04:00
Add Data.RealFloat and Infinity/NegInfinity/NaN pattern synonyms (#26961)
- - - - -
e60eb3bc by Andreas Klebinger at 2026-08-25T18:42:59-04:00
rts linker: Fix pointer arithmetic issue in flushInstructionCacheRISCV64
We accidentally operated over `uint64_t*` when we should use `uint8_t`.
Fixes #27569
- - - - -
e9bbe8f9 by Andreas Klebinger at 2026-08-26T15:09:23-04:00
cmm dumps: Add machop width info with -dppr-debug for infix ops.
- - - - -
86e3a9d8 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
CmmLint: Check for unsupported MachOp widths
machOpArgReps now maps MachOp + Width to a list of supported
argument widths or Nothing if the given operation is not supported
at the given width.
This allows us to check for nonsensical combinations like FloatToInt
at Word16.
Similarly we now check that every address is actually wordwidth.
- - - - -
13781cca by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: The big subword truncation fix.
A set of slightly related fixes to arm subword handling:
Bitmask immediates:
Don't produce overflowing assembly literals.
There is still another bug here that causes us to miss some valid
literals but we will fix that later.
Improve subword truncation handling:
We now use a small set of helpers to truncate `Register` values rather
than truncating immediate `Reg` values which greatly simplifies the code
structure. This fixes a great many bugs to do with sign/zero extending subwords
or the lack thereof.
We now establish the invariant that subword values are zero-extended at
every site at which they come into "scope" of the ncg, and rely on the
invariant throughout rather than pessimistically inserting redundant
extensions in a hodgepodge manner at the use sites of these values.
This fixes at least the bugs described in issues #27533, #27430
#27537, #27538, #27539, and #27550. But likely more bugs yet not
found.
Subword ffi results:
Apply truncations when calling functions returning
subword values.
genCondJump:
Don't sign extend signed values in the input register as
it might map to a local variable, corrupting the value stored within.
Fix subword store/load instructions.:
We used to read those at 32bit width even for smaller values possibly
resulting in invalid memory access. Now we construct the suffix for
subword variants based on the instruction format for these.
- - - - -
d8fa5d7c by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: Fix MO_V_Broadcast for non-literals.
We now use OpReg instead of OpScalarAsVec as required since we broadcast a gp register.
Also adds a test. Fixes #27565.
- - - - -
94822c95 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
Add some test cases covering bugs in the arm ncg.
* Test for #27430 (subword ffi results)
* #27537 - subword conversions
* #27538 - subwords used in conditional
* #27533 - single byte read
- - - - -
dd1ba88a by Andreas Klebinger at 2026-08-26T15:09:24-04:00
cmmLint: Lint against MO_FS_Truncate subword use.
- - - - -
fd22f71e by Zubin Duggal at 2026-08-26T15:10:20-04:00
ghc-internal: annotateSTM should use catchSTM# rather than catch#
A catch# frame inside a transaction breaks retry and async exception
delivery.
Fixes #27657
- - - - -
bb324171 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: refactor to reduce THREADED_RTS in MSG_UPD_TSO_FLAGS
- No behavior change in this commit (well, a small optimization here
makes us do less work if the target TSO owned by the curr. capability)
- Move all THREADED_RTS CPP needed into `updThreadFlag`
- Merge MSG_SET_TSO_FLAGS and MSG_UNSET_TSO_FLAGS into MSG_UPD_TSO_FLAGS
plus a `set` bool field in the MessageUpdTSOFlag struct
Towards #27729
- - - - -
ed99b7b7 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: Fix race condition in MSG_UPD_TSO_FLAGS execution
The code for processing the MSG_UPD_TSO_FLAGS message was not taking
into consideration that the TSO's owner might have moved in between that
capability receiving the message (since it was its previous owner) and
starting to process its inbox (a point at which it was no longer the
owner)
Added Note [TSO owner may change in between Msg being sent and received]
to explain this race and the pattern used to fix this, where we just
forward the message to the new owner.
Fixes #27729
- - - - -
cd653714 by Alan Zimmerman at 2026-08-26T15:11:49-04:00
EPA: Uses Parsers.parseModule for exactprint tests
Parsers.parseModule is the advertised way to parse for use for exact
printing in the ghc-exactprint library. This commit updates the GHC
exact print testing to use it.
This requires moving the comment balancing that was occurring
only in the test path into the advertising parser path, so it moves
from Transforms.hs to Utils.hs.
Also update the comment adding to honour trailing annotations
- - - - -
d1d01fa5 by Wolfgang Jeltsch at 2026-08-27T13:17:59+03:00
Add `rethrowSTM` and improve STM-related documentation
Adding `rethrowSTM` resolves #26758.
The implementation of `rethrowSTM` is completely analogous to the one of
`rethrowIO`.
The following is established for the documentation of `throwSTM` and
`catchSTM`:
* Both operations are directly described as analogs of their `IO`
counterparts.
* There is no reference to `throw` in the documentation of `throwSTM`,
because, although such a reference is great in the documentation of
`throwIO`, it is somewhat out of place in the documentation of
`throwSTM`.
* Instead of repeating part of `throwIO`’s documentation, the
documentation of `throwSTM` just recommends using `throwSTM` instead
of `throw` and references the corresponding arguments in the
documentation of `throwIO`.
- - - - -
06fde293 by fendor at 2026-08-28T06:06:44-04:00
GHCi: Fix order of `PackageDBFlag`s for interactive home unit
`PackageDBFlag`s are stored in reverse order of cli specification.
When sorting the `PackageDBFlag`s by longest common prefix, we need thus
to reverse the package db stacks before calculating the prefix.
We make sure to reverse the package db stack for the interactive home
unit to uphold that later specified package dbs overwrite earlier ones.
Resolved and adds regression test for #27640
- - - - -
024c4d04 by fendor at 2026-08-28T06:07:23-04:00
Reuse the UnitIndexCache after initialising multiple home units
- - - - -
55326fa0 by Alan Zimmerman at 2026-08-28T06:08:03-04:00
EPA: Some Haddock processing tweaks
These changes to the Haddock postprocessing should not change
behaviour, but just bring it more closely in line with the
original, changed at 44309cd377f
And add some haddock exactprint tests to show they work.
- - - - -
b3ddee95 by Andreas Klebinger at 2026-08-28T13:57:46-04:00
hadrian: Deprecate quickest flavour.
It was more of a trap for new users than actually beneficial so we
deprecate it and suggest quick+no_dynamic_libs to users instead.
- - - - -
a1d81390 by Andreas Klebinger at 2026-08-28T13:58:37-04:00
cmm: Always favour entry block during block deduplication.
We now always keep the first block in the CmmGraph. This way we avoid
the need to update the entry info table.
Failing to do so caused #27722.
Fixes #27722.
- - - - -
5bd65f00 by Andreas Klebinger at 2026-08-28T13:59:16-04:00
test: FamAppCachePerf - Only collect bytes allocated. Fixes 27747
- - - - -
ced53ce6 by mangoiv at 2026-08-29T07:15:24-04:00
nightlies: output yaml to file only
Previously we would just output the metadata to stdout
which risks that it's clobbered by incidental debugt output.
We now output to file only.
Fixes #27511
- - - - -
578bd185 by Andreas Klebinger at 2026-08-29T07:16:05-04:00
Specialise: Stop looping on recursive dictionaries in interestingDict
interestingDict now doesn't look through loopbreaker unfoldings.
Doing so would cause infinite loops on certain dictionaries.
Fixes #27705.
- - - - -
7bf546fc by Simon Peyton Jones at 2026-08-31T23:48:53-04:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
type family F a :: Constraint
type instance F W = TC W
a :: F W => Int -> Int -- (F W) argument is absent
Oops! Entered absent arg Arg: irred
Type: F W
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
- - - - -
5f474953 by Zubin Duggal at 2026-08-31T23:48:53-04:00
Add tests for absent fillers at dictionary types
T27627 a unary class whose superclass is a non-unary class
T27627a ...whose superclass is a Constraint-kinded type family
T27627b ...whose superclass is a quantified constraint
T27627c a unary class applied to itself, (UC (UC (TC a)))
T27627e a (forall b. P b) dictionary that loops
- - - - -
cd5c6bcc by Zubin Duggal at 2026-08-31T23:48:53-04:00
An abstract TyCon may hide a unary class
A class declared in an hs-boot file is an AbstractTyCon inside the
module loop, and compiling the real declaration may reveal it to be a
UnaryClassTyCon.
- isTerminatingType returned True for such AbstractTyCons
- IfaceToCore set the unary flag to False in the DFunId
So we could end up speculating bottom dictionaries because inside a module
loop we see an UnaryClassTyCon as an AbstractTyCon
Use isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
- - - - -
abfc224a by Zubin Duggal at 2026-08-31T23:48:53-04:00
Specialise: don't replace dead args with absent fillers
specHeader decides an argument is dead by calling isDeadBinder on a binder of
the /optimised RHS/, then applies the filler to the /stable unfolding/
template instead. The two may differ, so the argument can be dead in
the RHS and not in the template.
The specialised function's unfolding then has an absent filler, and any call
site that inlines it evaluates the error thunk.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
- - - - -
1557fd1c by Zubin Duggal at 2026-08-31T23:48:53-04:00
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
- - - - -
4117e5ae by Wolfgang Jeltsch at 2026-08-31T23:49:35-04:00
Incorporate the `rethrowSTM` reexport into the `stm` submodule
- - - - -
4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T3586
unsafeRead and unsafeWrite use 0-based index.
Looking at #3586, the expected output seems to be 2.8e8.
Addresses #27596
- - - - -
3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T21305
writeInt64Array# takes an index measured in units of Int64 elements.
Fixes #27596
- - - - -
44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00
Fix buglet in INLINE-arity calculation for pattern synonyms
This fixes #27744.
The buglet was accidentally introduced by
commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sat Aug 1 00:13:02 2026 +0100
Fix three bugs related to required type args and INLINE pragmas
I failed to find all the calls to `addInlinePragArity`!
- - - - -
df058f1d by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, although their
noise profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates the remaining users of 'all' (and of the 'all'
default) to explicit per-metric collection, ahead of removing 'all'
from the driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved
by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425), residency remains gated via max_bytes_used,
now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all. One is a simplifier-ticks blowup and
the other is stated entirely in allocation numbers, so the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric. They are already gated at 1% via
collect_compiler_runtime. The ghc/max gate had previously broken CI
spuriously (9fd11585eb widened it from 1% to 10% for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
72dd2432 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated
at a single tolerance, although their noise profiles are incompatible,
making such tests either flaky or toothless (#27653).
Closes #27653.
Assisted-by: Claude Fable 5
- - - - -
2228cb30 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Make the deviation argument of collect_stats mandatory
Almost every caller passes an explicit tolerance matched to the
metric's noise profile, and the silent 20% default is far slacker than
'bytes allocated' merits. Only two tests relied on it. They now state
their tolerance explicitly:
large-project gets 10%, in line with other large compile-time tests.
T9848 gets 2%: its metric is byte-for-byte deterministic across CI
jobs and platforms of a given test_env, has drifted only about 2.5%
since 2015, and the fusion failure it guards against would show up as
a roughly +30000% jump.
Assisted-by: Claude Fable 5
- - - - -
56291fc5 by Cheng Shao at 2026-09-02T07:18:45-04:00
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
- - - - -
20eb3f41 by Cheng Shao at 2026-09-02T07:18:45-04:00
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
- - - - -
9e7739ab by Simon Jakobi at 2026-09-03T13:45:43-04:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (`!` or `~`). It complements the
LazyFieldAnnotations extension (4762a8bf30f) from GHC proposal 752,
which makes `~` annotations available for this purpose.
Deciding which fields to report requires their levity, so the check
runs after typechecking. To keep the noise down, the diagnostic is
emitted once per data declaration, grouped by constructor.
Closes #16836.
Assisted-by: Claude Fable 5
- - - - -
6a44d591 by mangoiv at 2026-09-03T13:46:22-04:00
simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
Prior to this commit
commit 08bc245be70d95801bc1138804ed1de9474fbdc0
Author: sheaf <sam.derbyshire(a)gmail.com>
Date: Sat Feb 28 16:30:43 2026 +0100
Clean up join points, casts & ticks
This commit shores up the logic dealing with casts and ticks occurring
in between a join point binding and a jump
any `PlaceRuntime` ticks we observed were profiling ticks, even though that
isn't necessary.
The more liberal rules in the above commit allow e.g. breakpoint ticks
(which are valid PlaceRuntime ticks) to legitimately appear in an argument
position.
Fixes #27556
- - - - -
eaa95a6f by Andrei Borzenkov at 2026-09-04T08:08:40-04:00
Parentheses in prefix GADT constructors (#27423)
Updated `splitLHsGadtTy` to allow looking
through the parentheses for inner binders. General example
of a code pattern that's allowed now:
data S a where
MkS :: (forall a. S a)
That should work now with any combination of nested
foralls and parentheses.
We don't perform parenthesis unwrapping for record
GADT constructors in accordance with GHC Proposal #402.
To this end `con_inner_bndrs` no longer stores plain forall
telescopes: `[HsForAllTelescope pass]` is replaced with
`[LHsGadtTelescope pass]`, a new `HsArg`-style type whose
`HsGadtForAll` holds an inner telescope and whose `HsGadtPar`
holds a pair of parentheses. The parentheses carry no meaning
for renaming or type checking; the only reason to record them
is exact-printing.
Updated `pprConDecl` to improve the `parse == parse . ppr . parse`
property of GADT pretty-printing.
The pretty printer can now output code that's similar to this:
data T a where
MkT1 :: (forall a. T a)
MkT2 :: forall . forall a. T a
These are special cases of inner forall binders
for prefix GADT constructors, when we have either
implicit or zero explicit outer binders.
- - - - -
639456a8 by Evgeny Malyshev at 2026-09-04T08:09:25-04:00
HsPat: Add spacing to unary tuple patterns
Use hsep when printing unary boxed TuplePat applications so that the
MkSolo constructor is separated from its argument. The old hcat rendered
wildcard and parenthesized arguments as MkSolo_ and MkSolo().
Add T27034 to exercise the HsPat splice-dump path with wildcard and
variable arguments, and update existing affected golden output.
Fixes #27034
Assisted-By: OpenAI Codex
- - - - -
2908ab36 by Simon Jakobi at 2026-09-05T07:22:17-04:00
X86 NCG: use btr/bts/btc for single-bit operations
Previously the Cmm patterns
x & ~(1 << i)
x | (1 << i)
x ^ (1 << i)
compiled to mov/shl/not/and-style sequences of 3-4 instructions. Now
they compile to a single btr, bts or btc, matching what C compilers
produce.
When the bit index is a literal, constant folding has already collapsed
these patterns into ones with a literal mask, such as
x & 0xfffffeffffffffff for x & ~(1 << 40). Such masks are now also
compiled to a bit-test instruction when they don't fit in an imm32 and
would otherwise have to be loaded into a register first.
For a variable bit index, this applies only when the shift is unchecked
(uncheckedShiftL#, Data.Bits.unsafeShiftL): the bounds-checked shiftL
used by e.g. the default clearBit/setBit/complementBit implementations
wraps the shift in a bounds mask that this optimisation does not see
through. With a literal index, the bounds mask is constant-folded away,
so the checked operations benefit too.
See Note [Bit-test instructions] in GHC.CmmToAsm.X86.CodeGen.
Fixes #25233.
Assisted-by: Claude Fable 5
- - - - -
c673ecf0 by Simon Peyton Jones at 2026-09-05T07:22:59-04:00
Move HsStatic free-var test to typechecker
A `static` form should have no free *term* variables, but it
can have free *type* variables. Alas, the renamer does not really know what
is a term variable and what is a type variable, because of required type
arguments. This patch moves the test to the typechecker, which does know.
Addresses #27664
- - - - -
5939ceaf by sheaf at 2026-09-05T22:06:18-04:00
Windows: enforce path convention in ./configure
As detailed in Note [MSYS paths] in Hadrian.Utilities, the standing
convention (using Windows-style paths with forward slashes) is now
enforced in ./configure instead of within Hadrian, removing the need for
'cygpath' calls within Hadrian.
Fixes #26683
- - - - -
a6061455 by sheaf at 2026-09-05T22:06:18-04:00
Hadrian: introduce ExeSpawnPath
Specific details about the filepath used to specify the executable to
spawn with CreateProcess matters on Windows: whether we use forward or
backward slashes, a leading ./, or an absolute path changes how the
executable is found.
This commit introduces 'ExeSpawnPath' which is a path that is guaranteed
to be found when spawning a process. All command invocations now go
through this type to ensure the path has been properly sanitised.
See Note [NeedCurrentDirectoryForExePath] in Hadrian.Utilities.
The same treatment is applied to hsc2hs. Updates hsc2hs submodule.
- - - - -
e28313e3 by sheaf at 2026-09-05T22:07:06-04:00
Preserve tick ordering in 'tickTickedExpr'
'GHC.Core.Utils.tickTickedExpr' tries to combine a tick 't1' into an
existing stack of ticks 't2s'. There are two situations:
1. 't1' is subsumed by a tick in 't2s': drop it.
2. A tick in 't2s' is subsumed by 't1', say 't2'.
This commit ensures that in case (2) we keep 't1' on the outside instead
of replacing 't2' at its position in the stack. This avoids re-ordering
source notes (which was the cause of #27749).
This fixes a regression introduced in 2dadf3b0d05.
Fixes #27749
- - - - -
3172f557 by sheaf at 2026-09-05T22:07:06-04:00
Consistently prefer local source note ticks
GHC.Cmm.DebugBlock.cmmDebugGen (DWARF annotations) and
GHC.Stg.Debug.quickSourcePos (-finfo-table-map) both contained logic to
prioritise source note ticks from the current module.
This commit commons up this logic and propagates it to a third consumer:
IPE stack frames, in GHC.Driver.GenerateCgIPEStub.
See the new function GHC.Types.Tickish.bestSourceNote.
- - - - -
192be0b6 by Luite Stegeman at 2026-09-07T19:43:36-04:00
rts: fix ctoi_tuple_spill_words getting out of sync
Fix a few places that were not updating ctoi_tuple_spill_words
correctly, leading to corruption/crashes when dealing with large
unboxed tuples in bytecode:
- captureContinuationAndAbort
- findRetryFrameHelper/findAtomicallyFrameHelper
- interpretBCO bci_BRK_FUN
fixes #27633
- - - - -
604eb43c by Simon Jakobi at 2026-09-07T19:44:15-04:00
Hadrian: don't capture the testsuite driver's output (#27780)
a6061455d54 switched the Testsuite RunTest case from Shake's cmd to the
cmd' wrapper. cmd' always captures stdout and stderr, and since the
caller asks for Exit, it returns without dumping what it captured. As a
result the testsuite output no longer appears in CI job logs: failures,
performance metrics and the summary were lost with the job.
Use cmdExe, the uncaptured cmd, as the other plain-cmd sites in that
commit do.
Fixes #27780.
Assisted-by: Claude Fable 5.1
- - - - -
06eee015 by Luite Stegeman at 2026-09-08T06:03:06+02:00
rts: Fix missing memory barrier in eval_thunk_selector (#27477)
unchain_thunk_selectors() was missing an ACQUIRE_LOAD for the
indirectee, leading to segfaults and corruption during GC on
weakly-ordered architectures.
Fixes #27477
- - - - -
f8b2bd8f by Alan Zimmerman at 2026-09-08T09:39:12-04:00
EPA Fix HsCmdDo exact print with comments
Exact printing of HsCmdDo was ignoring the location for the do
statements, and this is an annotation that can have comments in it.
Update it so we print the statements as a unit, including any
comments.
Also add the result of auditing that we capture comments in all needed
places, noting that the remaining Anno SrcSpan instances are benign.
- - - - -
430967ab by Duncan Coutts at 2026-09-09T11:07:01+01:00
Reorder cmm decls in HeapStackCheck for a better logical grouping
And put more section headers in to deliniate the groups.
We're about to add more here, so better to organise it first.
- - - - -
344080fd by Duncan Coutts at 2026-09-09T11:07:01+01:00
Add raisePrimIOException and add it to RTS<->ghc-internal API
The raisePrimIOException is a new helper function that I/O primops will
use to help them report I/O errors. This is implemented in Haskell
(since that's a lot easier), but has a calling convention that is
easy(ish) to use from Cmm in the I/O primops.
So we add it to the RTS API struct, and since we'll use it from Cmm we
also need a field accessor macro for cmm (in deriveConstants).
See the Note about how we cannot have nice things due to async
exceptions and thunks preventing us from using catch.
- - - - -
cfdb3399 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Add new blocking functions for I/O primops
See the Note [Thread blocking for new I/O primops],
and the Note [Calling convention for raisePrimIOException].
The point is, it will allow us to report synchronous exceptions from I/O
primops, and do so much more flexibly.
Previously the I/O managers could only report async exceptions and only
nullary exceptions. This was OK historically, but no good as we add
more I/O managers and expand the range of I/O operations we support.
- - - - -
f09c09d8 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Change the encoding of results from the I/O manager to I/O primops
Previously we just had async continue or heap overflow.
We now extend what we can report with synchronous success, and
synchronous failure with an errno.
See Note [Encoding of result of I/O manager operations]
We don't use these two new cases yet, but we will. In particular an
epoll I/O manager needs to be able to report synchronous success or
failure for waitRead#/waitWrite#.
- - - - -
30f074d9 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Switch waitRead/Write# to use new blocking return frames
and update the I/O managers to set the result before resuming the
blocked threads.
This makes it possible for I/O managers to report synchronous exceptions
from the I/O primops, but that will be done in a subsequent commit.
- - - - -
a8786ced by Duncan Coutts at 2026-09-09T11:07:01+01:00
Switch Poll and Select I/O managers to report sync exceptions
rather than using raiseAsync with blockedOnBadFD_closure.
This uses the new mechanism in the blocking frame return code to report
synchronous exceptions.
- - - - -
7a9df79a by Duncan Coutts at 2026-09-09T11:07:01+01:00
Remove now-unused blockedOnBadFD
It was previously thrown by the select and poll I/O managers, but now
they use raisePrimIOException (with an EBADF errno).
- - - - -
6b0424e3 by Duncan Coutts at 2026-09-09T11:07:01+01:00
Improve the docs for delay# waitRead# and waitWrite#
Document that the waitRead/Write# can throw exceptions (this was true
before too), and that all of them are async exception cancellation
points.
- - - - -
afeb2c30 by Duncan Coutts at 2026-09-09T20:08:58-04:00
Enable printf warnings for trace functions and fix resulting warnings
Most of the existing printf-style functions are annotated with
attributes to enable gcc/clang warnings for the printf format string,
but several trace functions in Trace.h were missing this annotation.
Enable them, and fix the resulting warnings.
- - - - -
9a442c93 by Alan Zimmerman at 2026-09-09T20:09:36-04:00
EPA: Exact print ConDeclGADT without custom enterAnn
!16321 brought in explicit capture of parens in a ConDeclGADT.
The ExactPrint update introduced a modification of the fundamental
function in exact printing, `enterAnn`, by splitting it into a version
allowing injection of functionality normally handled by the
ExactPrint class methods.
This commit refactors that code, to restore the prior `enterAnn`
version, by following the convention in ExactPrint of introducing a
helper data structure with its own `ExactPrint` instance to achieve
the same effect.
- - - - -
4e9297ea by Simon Jakobi at 2026-09-11T08:32:45-04:00
ci: Actually pass --ignore-perf-failures to the testsuite driver
684c0018d9 changed the IGNORE_PERF_FAILURES handling to append to
RUNTEST_ARGS with bash array syntax, but the three use sites still
expand RUNTEST_ARGS as a scalar, which yields only the first array
element. The appended flag was therefore dropped in every job, so
perf improvements kept failing Marge Bot batches and master pipelines,
contrary to #19562.
Append as a string instead, matching the existing --force-colors
append in test_hadrian.
Fixes #27563.
Assisted-by: Claude Fable 5.1
- - - - -
4f78dfdf by Simon Jakobi at 2026-09-11T08:33:24-04:00
testsuite: Give T26537 a 1.5x compile timeout
Its -fregs-graph compile step tends to time out in unoptimized builds
(#27188, #27776).
Assisted-by: Claude Fable 5.1
- - - - -
130286e3 by Alan Zimmerman at 2026-09-11T08:34:08-04:00
EPA: Remove ListBanana / ListParens from AnnListBrackets
This is a step towards making AnnList only represent
lists of items which can have either braces or layout.
- - - - -
a9827d13 by Simon Jakobi at 2026-09-11T18:53:32-04:00
Reject ~, UNPACK and multiplicity annotations on type data fields
Only `!` was rejected so far. `~`, UNPACK and multiplicity annotations
were silently accepted. None of them make sense at the type level, so
restriction (R3) of Note [Type data declarations] now covers all of
them.
Fixes #27732.
Assisted-by: Claude Fable 5.1
- - - - -
c71d5e23 by Alan Zimmerman at 2026-09-11T18:54:11-04:00
EPA: Add a Note for the exact print main loop
- - - - -
7a108e43 by Simon Jakobi at 2026-09-12T18:35:59-04:00
FastString: Drop mkFastStringWith's constructor callback
All three callers passed the same callback, a partial application of
mkNewFastStringShortByteString to the string being interned. That
partial application is allocated as a closure before the table lookup,
on the common hit path too, although the callback is needed only after
a miss.
Drop the parameter and call mkNewFastStringShortByteString directly
after a miss. Since nothing is passed "with" anymore, rename the
function to internSB.
Suggested by Simon PJ in #27528:
https://gitlab.haskell.org/ghc/ghc/-/work_items/27528#note_687031
Assisted-by: Claude Fable 5
- - - - -
82c73b22 by Alan Zimmerman at 2026-09-12T18:36:38-04:00
EPA: More targeted HsDo exact print annotation
HsDo is multi-purpose, as encoded in its HsDoFlavour field. Some of
these are in a layout context (DoExpr, MDoExpr), others are not
(ListComp, MonadComp).
We are moving towards using AnnList only in layout contexts, so we
switch the HsDo TTG annotation from holding an AnnList for this, to
holding
Either (EpToken "[", EpToken "]") AnnList
This also allows us to trim down AnnListBrackets to only have braces
or None, thereby opening the door for unification with the existing
layout context data type EpLayout.
- - - - -
749d2448 by Luite Stegeman at 2026-09-15T14:07:01-04:00
rts: handle large CONSTR closures in compacting GC
The function update_fwd_large in the compacting GC could run into
an unexpected object with the following error:
internal error: update_fwd_large: unknown/strange object 1
Closure type 1 is CONSTR. This patch adds cases for CONSTR and
CONSTR_NOCAF.
This is the same problem as #27434 (large AP closures, fixed by
cca0d58963), which missed this case.
Fixes #27649
- - - - -
177d6155 by Luite Stegeman at 2026-09-15T14:07:02-04:00
testsuite: fix T27434 test with compacting GC
Ensure that the interpreter is actually run with the compacting GC
- - - - -
5796aa63 by Luite Stegeman at 2026-09-15T14:07:49-04:00
rts: initialise the stack frame header for mask_frame and apply_mask_frame
We must leave the stack in consistent state before jumping to
mask_frame or apply_mask_frame because they may result. Failing
to do so could lead to a crash if there were waiting exceptions.
Fixes #27651
- - - - -
2486cc92 by Simon Peyton Jones at 2026-09-15T14:08:36-04:00
Re-introduce the "weird SpecDict" case
In this commit
commit f80375dd4945350a1d784e436975b48b9c736eaf
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sun Jun 29 15:26:58 2025 +0100
Refactor of Specialise.hs
I removed a test for a dictionary with unbound type variables. This
turned out to be wrong; see #27629.
So this MR re-introduces it.
- - - - -
8e09a46f by Duncan Coutts at 2026-09-15T20:11:35-04:00
Refactor (and rename) removeFromQueues, to simplify I/O managers
Rename it to unblockAndAppendToRunQueue which better reflects what it is
intended to do.
The post-condition for unblockAndAppendToRunQueue is that the TSO is on
the run queue or it is in the process of migrating to another cap.
Previously it achieved that by always directly adding the TSO to the run
queue itself.
But this actually made things more complicated for the I/O managers,
because it meant they needed a separate code path for notifying for
cancellation compared to notifying for completion. The general
notification code would always add the TSO to the run queue itself.
So the improvement is to allow different cases in
unblockAndAppendToRunQueue to achieve the same outcome in different
ways: either directly adding to the run queue or calling helper
functions that do so themselves.
This then allows the new I/O managers to share code between the sync and
async cancellation, and to reuse their notifyIOCompletion helpers for
cancellation. This avoids a source of bugs where the completion path may
be updated but the cancellation path may be forgotten, or similarly in
future for sync/async operations.
Update all the existing in-RTS I/O managers, and the posix timeout code.
- - - - -
d668782c by Duncan Coutts at 2026-09-15T20:11:35-04:00
Document that awaitCompletedTimeoutsOrIO expects an empty run queue
This was true before but implicit and not relied on much. It's better to
be explicit, and allow things to depend on it.
- - - - -
11d938bb by Duncan Coutts at 2026-09-15T20:11:35-04:00
Store the I/O opcode and fd in the StgAsyncIOOp
This will be useful in several I/O managers and it is handy for logging
and debugging.
It also doesn't increase the size of the StgAsyncIOOp structure. There
was enough spare padding space already.
Update the poll I/O manager to set the new fields.
Add a helper function to convert the enum IOReadOrWrite into the enum
IOOpCode. Also change IOReadOrWrite to be an enum without a typedef, for
consistency with other enumerations in IOManager.h
- - - - -
bfa87882 by Duncan Coutts at 2026-09-15T20:11:36-04:00
Add a new I/O manager based on select()
Yes, this is the second such I/O manager, but it is a modern
re-implementation based on the new in-RTS I/O manager infrastructure. So
it is cleaner and faster than the old select I/O manager.
Why do we need another I/O manager based on select? Why isn't the poll()
one good enough as a baseline portable unix I/O manager? Because macOS.
Apple Inc. is why we cannot have nice things.
The man page for poll on macOS documents the fact that it does not work.
At least, it does not work for all files. Specifically, it does not work
for device files. Whereas macOS select() does work for device files.
Aaaaarg!
We _do_ want to deprecate and remove the old select I/O manager, but due to
macOS we cannot do that until we have a replacement. This is that
replacement. Until of course a nice new k-queue I/O manager arrives,
which could become the new default for macOS and FreeBSD.
Interestingly, this select I/O manager is actually faster than the poll
one, on Linix, in some circumstances: specifically when many Haskell
threads are waiting on the same fd. The poll I/O manager does O(n) work
for n threads waiting on I/O, whereas the select one does O(fds) work
for the number of fds that threads are waiting on. Usually this is 1:1,
so it's not noticable, but one can concoct extreme benchmarks to show
the difference.
- - - - -
8e9de15a by Duncan Coutts at 2026-09-15T20:11:36-04:00
Minor updates in the poll I/O manager to keep in sync with select
This keeps it in sync with select one. The changes are based on code review
while implementing the new select I/O manager. The two I/O managers are so
similar in structure that it makes sense to try to minimise the diff between
them. This should aid understanding, and fixes to both in future.
- - - - -
222668bb by Duncan Coutts at 2026-09-15T20:11:36-04:00
Document the new select I/O manager in the user guide
in the RTS section about I/O managers.
And add a changelog entry.
- - - - -
0f48735b by Cheng Shao at 2026-09-15T20:12:23-04:00
Revert "wasm: workaround WebKit bug in dyld"
This reverts commit c9b8465c2c338176fcab9d197e9d31f4aee11f68. The
underlying webkit bug has been fixed in
https://commits.webkit.org/314219@main. Closes #27564.
- - - - -
809294f1 by Luite Stegeman at 2026-09-15T20:13:08-04:00
rts: make stg_threadLabelzh return a valid pointer for unlabeled threads.
This fixes a segfault in the GC caused by stg_threadLabelzh returning
a 0 pointer in a GC pointer field.
stg_threadLabelzh returns a tuple of type (# Int#, ByteArray# #). If a
thread has no label, the second field is unused. We must still return
a valid heap object pointer.
Instead of returning 0, we now return stg_DEAD_SLOT_closure.
fixes #27618
- - - - -
e2d57026 by Luite Stegeman at 2026-09-15T20:13:54-04:00
JS: fix selector thunk handling
fix a bug where entering a selector thunk could lead to a crash
if the selected field was still a thunk.
fixes #27788
- - - - -
1b4a622f by sheaf at 2026-09-17T02:39:13-04:00
Improvements to type/data family handling
This commit makes several improvements to the treatment of type and data
families in GHC:
- We consistently use "type family" instead of "type synonym family"
to avoid a confusion with type synonyms. For example:
- OpenSynFamilyTyCon ==> OpenTypeFamilyTyCon
- ClosedSynFamilyTyCon => ClosedTypeFamilyTyCon
- Various functions that were specific to data families have been
renamed to make this clear. For example:
- tyConFamInst_maybe ==> tyConDataFamInst_maybe
- isFamInstTyCon ==> isDataFamInstTyCon
- 'FamTyConFlav' has been refactored so that all closed type families
share a single 'ClosedTypeFamilyTyCon' constructor.
The corresponding 'IfaceFamTyConFlav' type was updated similarly.
This is then used to tidy up the implementation of
GHC.Core.FamInstEnv.reduceTyFamApp_maybe.
- - - - -
c5aa9098 by sheaf at 2026-09-17T02:39:13-04:00
Small refactor of lookupFamInstEnv
This commit reworks the code for looking up family instances in
GHC.Core.FamInstEnv, reducing duplication a little bit.
No change in behaviour.
- - - - -
a3ad4588 by sheaf at 2026-09-17T02:39:14-04:00
Use a type family for boxing unboxed types
This commit overhauls the way in which unboxed types are boxed, as per
the updated Note [Boxing constructors] in GHC.Builtin.WiredIn.Types.Box.
The main change is to allow boxing to be parametrised by representation,
e.g. making 'Box ty' a valid type. We achieve this by implementing 'Box'
using a closed type family:
newtype Box (a :: TYPE r) = Box (BoxTF r)
type BoxTF :: RuntimeRep -> Type
type family BoxTF r where
BoxTF IntRep = BoxInt
BoxTF Int8Rep = BoxInt8
BoxTF WordRep = BoxWord
BoxTF LiftedRep = Any @Type
BoxTF (TupleRep [r1, r2]) = (BoxTF r1, BoxTF r2)
...
BoxTF (SumRep rs) = BoxSum rs
This new design solves the following problems:
- It allows boxing a type whose 'RuntimeRep' has not yet been
determined (e.g. because we only learn the 'RuntimeRep' after
constraint solving). That is, 'Box @r a' is a perfectly valid type
even if 'r' is an unfilled metavariable. This is a requirement
to fix #25065 (in which we need to box a type whose 'RuntimeRep'
is only filled in during constraint solving).
- It handles boxing of unboxed tuples and unboxed sums, via recursive
boxing. See Note [Boxing TupleRep and SumRep] in
GHC.Builtin.WiredIn.Types.Box.
'Box', 'BoxTF' and all of the boxing data constructors are defined in
a new 'ghc-internal' module, 'GHC.Internal.Box'. This module contains
nothing but wired-in declarations, and GHC never loads its interface on
account of a wired-in thing (GHC.Builtin.Modules.isWiredInOnlyModule);
see Note [Loading instances for wired-in things] in GHC.Iface.Load.
The individual boxing data constructors are deliberately left as an
internal implementation detail, and not exported. The intended API is
via the two new magic 'Id's:
box :: forall {r} (a :: TYPE r). a -> Box a
unbox :: forall {r} (a :: TYPE r). Box a -> a
exported from 'GHC.PrimOps' (in 'ghc-experimental'). The old boxing
data types (IntBox, WordBox, FloatBox, DoubleBox, DictBox) are no longer
exported from base (CLC proposal #421).
We use this new design to consistently desugar:
- parallel comprehensions (GHC.HsToCore.ListComp.{deListComp,dsMcStmt})
- then/group/using statements (GHC.HsToCore.ListComp.{dsTransStmt,dsMcStmt})
- recursive do blocks (mdo/rec) (GHC.HsToCore.Expr.dsDo)
- arrows (GHC.HsToCore.Arrows)
by consistently using 'box'/'unbox' to deal with unboxed types being put
into big tuples. We also adapt 'GHC.Tc.Gen.Match.{tcMcStmt,tcDoStmt}'
to create fresh /representation-polymorphic/ type variables for the
binders that we are going to put into a big tuple (previously, GHC only
allowed lifted types in that position).
These desugaring of box and unbox is explained in
Note [Desugaring box & unbox] in GHC.Core.Make.Box. Roughly speaking,
the desugaring looks like:
box @r @ty e ==> K e
unbox @r @ty e ==> case e of K x -> x
where 'K' is the boxing data constructor at RuntimeRep 'r'. It's
slightly more complicated than that in practice: we first need to
insert a cast (see Note [The canonical type of a RuntimeRep]) using the
new 'CanonicalProv' 'UnivCoProvenance', and unboxed tuples/sums need
recursive logic explained in Note [box/unbox for unboxed tuples and sums],
including a specific 'BoxSum' datatypes for unboxed sum (which also
needed a wired-in type-level list indexing type family (!!)).
The desugaring happens in two ways:
- Occurrences of 'box'/'unbox' emitted by the typechecker are
eliminated in the 'ds_app_var' case of GHC.HsToCore.Expr.
- Other code that needs to box/unbox emits the appropriate code inline.
This includes the big tuple machinery of GHC.Core.Make used by the
desugarer (GHC.HsToCore.{Utils,Binds,Arrows,ListComp}) as well as
the float-out pass (GHC.Core.Opt.SetLevels).
Core Lint checks that no occurrence of 'box'/'unbox' survives desugaring.
On the way, we overhaul the litany of big tuple functions by introducing
data BigTupleLayout = BoxedElements | BareElements
to distinguish the two kinds of "big tuples" that GHC routinely deals
with; for example 'mkBigCoreVarTupSolo' becomes 'mkBigCoreVarTup BareElements'.
All of this code is moved to the new module GHC.Core.Make.BigTuple.
Fixes #20864 #22336 #22473 #25065 #27331
-------------------------
Metric Decrease:
T9872a
-------------------------
- - - - -
4cf7628a by Evgeny Malyshev at 2026-09-17T02:39:56-04:00
Fix the CompactionFailed documentation link to compact
The old Haddock reference links into base instead of ghc-compact.
Use an explicit URL because ghc-compact depends on base, not vice versa.
Fixes #27558
Assisted-By: OpenAI Codex
- - - - -
098bba70 by Facundo Domínguez at 2026-09-17T14:57:41-04:00
Remove the call to hoistFlag from processCmdLineP
This call was undesirable because it was causing a reconstruction
of the list of flag processors.
Even relying on list fusion would not be sufficient because we want
to replace the list with a data structure with faster lookups to
address #25763.
The removal is done in two moves:
* We move CmdLineP to CmdLine.hs
* Then we move the effect of hoisting to processArgs via a new function
hoistEwM
- - - - -
e01799c7 by Facundo Domínguez at 2026-09-17T14:57:42-04:00
Do not create duplicate flag processors for the -W flag
This makes simpler implementing the command line parser with a trie map
holding flag processors. If we were to allow duplicate flag processors
for any flag, the trie map would need to be a multimap.
- - - - -
3875b257 by Facundo Domínguez at 2026-09-17T14:57:42-04:00
Replace the list of flag processors with a trie map in `processArgs`
Previously `GHC.Driver.CmdLine.findArg` was called for each
command-line argument, doing a linear search on the list of flag
processors each time. This was somewhat costly as the list of flag
processor has near 2000 elements.
A lookup with a trie map is faster in general, but the speed up is
only noticeable when calling `GHC.Driver.CmdLine.processArgs` sufficient
times, so the amount of lookups piles up. This could happen in large
projects with many units each having many dependencies. If only a
module needs to be recompiled, the user still needs to wait for all of
the flag parsing to complete.
The benchmark at testsuite/tests/perf/compiler/cmdline-parser shows a
10x speed up when using the trie.
The trie map is implemented in `GHC.Data.StringTrie`.
The design is explained in Note [Processing command line argument fast]
in GHC.Driver.Session.
The commit also:
* adds two property-based tests for trie map operations,
* adds a benchmark of the command line parser, and
* renames the predicate `arg_ok` to `valid_flag_suffix` to decide what
flag processor should be used when there are multiple matching
prefixes on a flag.
Fixes #25763.
- - - - -
5236634a by Andreas Klebinger at 2026-09-17T14:58:24-04:00
Compile Word64{Map,Set} with -O2 always.
This is the hottest code and even if we build GHC with -O0 otherwise -O2
pays off for these modules.
Fixes #27814.
- - - - -
545fd4fb by ARATA Mizuki at 2026-09-22T15:03:24-04:00
Change the C types of hs_{bitrev,bswap}{16,32} to match the Haskell types
bitReverse{16,32}# and byteSwap{16,32}# have the type
Word# -> Word#, but their C correspondents took and
returned StgWord{16,32}.
The mismatch on bitReverse16# led to the problem reported
in #27299. Although no similar issue is reported for
bitReverse32# and byteSwap{16,32}#, it seemed wise to
change them proactively.
This commit fixes the mismatch by changing the C type.
Fixes #27299
- - - - -
bf17f289 by fendor at 2026-09-22T15:04:05-04:00
Don't lose instances from shadowed imported modules with the same name
When we look for instances below the current module, we need to check
the unit id as well when we check for whether the module is the current
module, otherwise we ignore modules we import that have the same name as
the import module.
Adds regression test.
- - - - -
53eead15 by Wolfgang Jeltsch at 2026-09-24T20:12:03-04:00
Correct the documentation of `explicitUnits`
Resolves #27787.
- - - - -
35bf6f4b by Simon Jakobi at 2026-09-24T20:12:49-04:00
Remove stale specHeader bullet from Note [Don't make fillers for constraint types]
The Note said that Specialise.specHeader also calls mkAbsentFiller.
abfc224a27c removed that call: the specialiser no longer drops dead
arguments at all, see Note [Do not drop dead args from specialisations]
in GHC.Core.Opt.Specialise.
Assisted-by: Claude Fable 5.1
- - - - -
1abef1ef by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Improve the `mhu-closure` makefile
- - - - -
a79a68ea by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Make `ghci-multi` hide all unneeded packages
- - - - -
d6e72450 by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Improve the definition of the home unit closure property
- - - - -
c9a2766d by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Re-implement the home unit closure check
Compared to the previous implementation, the new one has the following
advantages:
* It is correct.
- It distinguishes between units that have the same unit ID but
different ABI hashes.
- When `-hide-all-packages` is not used, it considers as home unit
dependencies also units that are made implicitly available
because they are in the package database.
* It seems to be faster in usual settings.
- In particular, it does not have a preparation phase in which it
merges the dependency information from all home units, so that
its running time does not grow linearly with the size of the
package database in normal Cabal scenarios.
* It is (hopefully) clearer.
* It is better documented.
- - - - -
fa96acf8 by Wolfgang Jeltsch at 2026-09-25T17:02:07-04:00
Remove erroneous GHC flag in `hadrian-multi`
The flag was `-Werror=-Wno-error=incomplete-record-selectors`. Its
purpose was to permit incomplete record selectors, but because of the
illegal combination of `-Werror=` and `-Wno-error` it was ignored,
without any negative consequences.
- - - - -
9f48a5b9 by Alan Zimmerman at 2026-09-25T17:02:50-04:00
EPA: Add specific layout field to AnnList
Up to now AnnList has effectively served to represent lists of things
having layout, regardless of whether they do or not.
In Haskell, layout is optional in many places, as there is also an
alternative syntax using braces and semi-colons, which is free-form.
This commit introduces AnnListLayout, a data structure capturing this
distinction.
It is deliberately set up similarly to EpLayout, so that these two can
be merged in time.
The transition is done mechanically, where AnnListBraces is not used
yet, the original Nothing case maps to AnnListNoLayout, and the Just
case to AnnListLayout.
Later commits will fine tune these against the actual parser layout.
- - - - -
234bab08 by Vladislav Zavialov at 2026-09-29T17:22:54-04:00
Add regression test for #7803
Commit 2effe18ab5, first shipped with GHC 8.2.1, fixed #7803 "Superclass
methods are left unspecialized".
This change turns the example from the ticket description into a regression
test, checking that foo1 is fully specialised at Double, with no calls to the
overloaded exp5Tail.
Closes #7803
Assisted-by: Claude Opus 5
- - - - -
c406135c by Andreas Klebinger at 2026-10-01T06:57:28-04:00
RTS: Update flag parsing/handling:
Using 0 as argument to any of these flags now resets the flags setting to default:
- ``-n0`` disables nursery chunking.
- ``-K0`` (or a bare ``-K``) removes the stack size limit.
- ``-M0`` removes the heap size limit.
- ``-H0`` resets the suggested heap size, including the nursery scaling set by ``-H``.
- ``-AL0`` makes the large object allocation limit follow ``-A`` again.
GC Generations are now limited to 64.
-n larger than -A now emits a warning and disables chunking.
Fixes #27830 #27829
- - - - -
984ee82b by Andreas Klebinger at 2026-10-01T06:58:10-04:00
Fix MultiLayerModules timeout multiplier.
We accidentally used the compile multiplier for ghci commands which
use the run timeout setting to set the timeout.
Fixes #26263 and #27796.
- - - - -
50a9cf26 by Andreas Klebinger at 2026-10-01T06:58:10-04:00
testsuite: Include runtime in JUnit report.
- - - - -
e358619f by Alan Zimmerman at 2026-10-01T06:59:01-04:00
EPA: Use EpaLocation in EpVirtualBraces
Haddock processing uses EpLayout.EpVirtualBraces to record the current
layout column, for allocating haddock comments correctly.
For exact printing, we need the line as well as the col, so we know
how many lines to skip when the indented code block starts.
This commit updates EpLayout.EpVirtualBraces so that it carries an
EpaLocation instead of just the Int column, to allow it to be used for
both purposes.
It is a simple mechanical change, with no other impact, but it allows
us to use EpLayout instead of AnnListLayout in AnnList, in the next
commit in this series.
- - - - -
1d2a26ee by Andrew Lelechenko at 2026-10-01T20:22:04-04:00
Remove bitSize from MINIMAL pragma of class Bits
Cf. https://github.com/haskell/core-libraries-committee/issues/441
The function is deprecated for 12 years and has a default implementation,
yet it was never removed from the MINIMAL pragma, putting library authors
in a very awkward position. Let's finally remove bitSize from MINIMAL.
- - - - -
3cc21e4c by Hannes Siebenhandl at 2026-10-01T20:22:59-04:00
Add regression test for #27641
- - - - -
c35096f5 by Hannes Siebenhandl at 2026-10-01T20:23:00-04:00
Don't `:add` modules the loader will reject
Only home modules can be actually added to the loader context, thus we
only accept the module if it belongs to a home unit.
- - - - -
a563cb27 by Simon Jakobi at 2026-10-02T17:37:36-04:00
ci: accept modified changelog.d entries in lint-changelog
The changelog guide says to extend an existing entry rather than add a
new one when the feature is already documented. lint_changelog only
counted added files, so such MRs failed before the validator ran.
Fixes #27779.
Assisted-by: Claude Fable 5.1
- - - - -
03082cd4 by Simon Jakobi at 2026-10-02T17:38:17-04:00
changelog.d: Fix warning flag name in the semaphore-v2 entry
The entry refers to -Wsemaphore-version-mismatch, which does not exist.
The warning GHC emits when it cannot use a -jsem semaphore, including
on a protocol version mismatch, is -Wsemaphore-open-failure.
Context: !15729, #25087
Assisted-by: Claude Fable 5.1
- - - - -
a93705ee by Simon Jakobi at 2026-10-02T17:38:17-04:00
changelog.d: Fix the CLC proposal link in the T27455 entry
The entry used the Sphinx :ref: role with a URL as its target. :ref:
resolves only internal labels, so the generated release notes would
show the proposal name without a link. Use the plain hyperlink form
like the other entries.
Context: !16274, #27455
Assisted-by: Claude Fable 5.1
- - - - -
54381079 by Hannes Siebenhandl at 2026-10-02T17:39:01-04:00
Extend the `TrustOverlay` for wired-in units
The TrustOverlay only contained the `UnitId` of all on-disk units.
However, wired-in units are referred to by their wired-in unit id, which
is not the same as the on-disk unit id.
Thus, trust checks for wired-in units failed because the lookup was
using the wired-in unit, which wasn't present in the `TrustOverlay`.
We fix #27834, by copying the trust info for wired-in unit ids.
- - - - -
bb8ae7de by Rodrigo Mesquita at 2026-10-05T18:14:12+02:00
Refactor GHC.Driver.MakeAction
Pull out of 'runParPipelines' the logic for creating a 'MakeEnv' ready
to be used by multiple threads, as that will be useful for downsweep as
well (which doesn't fit the 'runPipelines' flow), rather than being just
for upsweep.
The code is moved and re-structured to match the export list, simplify
the sequentiality checks previously both in 'runPipelines' and
'runAllPipelines', which were weirdly similar and confusing; into the
two part step where we need these checks: (1) to construct the MakeEnv,
(2) to run the MakeActions in parallel. These two steps are separate and
used to be too mixed up.
Some additional little simplifications or clean ups here and there.
- - - - -
427b45c6 by Rodrigo Mesquita at 2026-10-05T18:14:12+02:00
Refactor GHC.Driver.Downsweep in preparation for parallel downsweep
Pure refactor to improve the code to facilitate implementing parallel
downsweep in the next commit.
This commit puts a MakeEnv into the DownsweepEnv, gives the fields
proper names and uses RecordWildcards to simplify, rather than passing
around all diagnostic wrappers, driver-message-things and using 10s of
positional fields.
No behavior changes here!
- - - - -
1c55df67 by sheaf at 2026-10-05T18:14:12+02:00
Driver: enableCodeGenForTH in load', not downsweep
enableCodeGenForTH modifies the module graph for planning, creating
fresh temporary paths. So it should only be run when we are about to
execute a build plan (e.g. in load'), so that consumers that want to
know facts about the module graph don't go looking for files that don't
exist yet (in particular 'haddock --no-compilation').
- - - - -
6c62b5cf by sheaf at 2026-10-05T18:14:12+02:00
2-phase Cache/Search Finder monad
This commit restructures the finder abstraction by introducing the
'FinderM' monad, which splits module lookup operations into two phases:
- a cache-only phase, performing no filesystem access,
- from the first cache miss onwards, a search action which may access
the filesystem.
This allows consumers to distinguish between quick cached results versus
more expensive filesystem search operations.
- - - - -
8d2c89c2 by sheaf at 2026-10-05T18:14:13+02:00
Separate Home/External finder caches
The caches for home modules and modules from external units have
different lifetimes, needing to be invalidated at different points.
This commit moves the cache for modules from external units into the
'UnitState' type, which keeps the cache correct whenever package flags
or package databases change (setSessionDynFlags, :set -package ...,
Backpack's addUnit).
This also makes it easier to clear the cache of home modules, as they
are now stored separately (obviating the 'isUnitEnvInstalledModule'
check we used to perform).
- - - - -
057b0495 by sheaf at 2026-10-05T18:14:13+02:00
Add known home modules to the finder cache
The finder looks in the file system for the on-disk source that
corresponds to a Haskell module name. There are two kinds of modules
where we need to bypass this search:
- a file passed directly in the command line, which may not be in any
search path and whose module name may differ from the file name;
- Backpack signatures and modules, which do not exist as files at all.
This commit handles these two situations by adding an immutable
'KnownHomeModules' in the finder cache, which is always consulted first
before doing any search.
This removes the ad-hoc imperative logic which relied on mutating the
finder cache at careful points.
'setModuleGraph' now also takes the known home modules, so that the
module graph and the known home modules are always set consistently.
See Note [Known home modules] in GHC.Unit.Finder.Types.
- - - - -
61949af3 by sheaf at 2026-10-05T18:14:37+02:00
Make 'UnresolvedImport' into a suitable cache key
The goal of this commit is to make 'UnresolvedImport' suitable for use
as a cache key. Specifically, during downsweep we want to be able to
cache module resolution queries. We want to do this using some kind of
map, e.g. (roughly speaking):
Map UnresolvedImport ResolvedImport
For this to work, 'UnresolvedImport' must contain **precisely** the
input data to each resolution computation. In other words:
1. If something changes that materially affects resolution, it must
be stored in the cache key.
2. If changing some input does not materially affect the outcome,
then it must not be stored in the cache key.
To satisfy (1), we change 'UnitNode' to also keeps track of the home
unit in whose unit state the unit's dependencies were looked up.
For (2), 'ImportResolution' now stores a 'ModuleLookupScope' instead of an
'UnresolvedImportOrigin'. Plugin imports are expressed with a new
'LookupPlugin' scope. The source location of an import, which is
immaterial to the resolution, is stored out of band outside
'UnresolvedImport', in 'ModSummary'.
- - - - -
2c23e070 by sheaf at 2026-10-05T18:14:39+02:00
Add GHC.Data.Dependent
This commit adds a minimal implementation of dependent sums
('Some', 'DSum') and dependent maps ('DMap').
The implementation is not entirely performance optimal, as there are a
few extraneous allocations compared to the full-blown implementation in
the 'dependent-map' package. For our use case (caching downsweep
computations), this does not matter much.
See GHC.Data.Dependent.
- - - - -
9caaeb45 by sheaf at 2026-10-05T18:14:39+02:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown.
- - - - -
045b0365 by sheaf at 2026-10-05T18:15:54+02:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
020fd5d5 by sheaf at 2026-10-05T18:15:55+02:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
- - - - -
619 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- .gitlab/rel_eng/mk-ghcup-metadata/mk_ghcup_metadata.py
- + changelog.d/25763-cmdline-opt
- + changelog.d/26610
- + changelog.d/27556
- + changelog.d/27627
- + changelog.d/27641
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- + changelog.d/27732
- changelog.d/T27202
- changelog.d/T27455
- + changelog.d/T27629
- + changelog.d/T27657
- + changelog.d/T27705
- + changelog.d/T27722-cbe-entry-block.md
- + changelog.d/T27744
- + changelog.d/T27749
- + changelog.d/T27763
- + changelog.d/T27764
- + changelog.d/T27788-js-selector
- + changelog.d/T27830-rts-flag-bounds
- + changelog.d/allow-gadt-prefix-con-parens
- + changelog.d/arm_ncg_fixes_T27430
- + changelog.d/boxing-family
- + changelog.d/clc-421
- + changelog.d/fix-compacting-gc-constr-27649
- + changelog.d/fix-control0-mask-trampoline
- + changelog.d/fix-parallel-gc-selector-race-27477
- + changelog.d/fix-threadlabel-segfault-27618
- + changelog.d/implicit-field-strictness-warning
- changelog.d/lazy-field-annotations
- + changelog.d/llvm-23
- + changelog.d/ncg-x86-bit-test-instructions
- + changelog.d/parallel-downsweep
- + changelog.d/rethrow-stm
- + changelog.d/select-io-manager
- changelog.d/semaphore-v2
- changelog.d/unit-index
- compiler/GHC.hs
- compiler/GHC/Builtin.hs
- compiler/GHC/Builtin/KnownKeys.hs
- compiler/GHC/Builtin/Modules.hs
- compiler/GHC/Builtin/Uniques.hs
- compiler/GHC/Builtin/WiredIn/Ids.hs
- compiler/GHC/Builtin/WiredIn/Prim.hs
- compiler/GHC/Builtin/WiredIn/TypeLits.hs
- compiler/GHC/Builtin/WiredIn/Types.hs
- + compiler/GHC/Builtin/WiredIn/Types/Box.hs
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/Cmm/CommonBlockElim.hs
- compiler/GHC/Cmm/DebugBlock.hs
- compiler/GHC/Cmm/Expr.hs
- compiler/GHC/Cmm/Lint.hs
- compiler/GHC/Cmm/MachOp.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Core/Coercion/Axiom.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/FamInstEnv.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Make.hs
- + compiler/GHC/Core/Make/BigTuple.hs
- + compiler/GHC/Core/Make/Box.hs
- compiler/GHC/Core/Opt/SetLevels.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCo/Ppr.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.hs
- compiler/GHC/CoreToStg/Prep.hs
- + compiler/GHC/Data/Dependent.hs
- compiler/GHC/Data/FastString.hs
- + compiler/GHC/Data/StringTrie.hs
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Data/Word64Set/Internal.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/CmdLine.hs
- + compiler/GHC/Driver/Concurrency.hs
- + compiler/GHC/Driver/Config/Concurrency.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeAction.hs
- compiler/GHC/Driver/MakeFile.hs
- compiler/GHC/Driver/MakeSem.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/LogQueue.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Binds.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/ListComp.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Rename.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Runtime/Interpreter/JS.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Stg/Debug.hs
- compiler/GHC/Stg/Lint.hs
- compiler/GHC/StgToJS/Apply.hs
- compiler/GHC/StgToJS/CodeGen.hs
- compiler/GHC/StgToJS/Linker/Linker.hs
- compiler/GHC/StgToJS/Symbols.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Instance/Family.hs
- compiler/GHC/Tc/Instance/Typeable.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Plugin.hs
- compiler/GHC/Tc/Solver/Default.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Types/CompleteMatch.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Types/Id.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Types/TyThing.hs
- compiler/GHC/Types/TyThing/Ppr.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/External/Wired.hs
- compiler/GHC/Unit/Finder.hs
- + compiler/GHC/Unit/Finder/Cache.hs
- compiler/GHC/Unit/Finder/Types.hs
- compiler/GHC/Unit/Module/Graph.hs
- compiler/GHC/Unit/Module/ModSummary.hs
- compiler/GHC/Unit/Module/WholeCoreBindings.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/Types.hs
- + compiler/GHC/Utils/Concurrent/Scope.hs
- compiler/GHC/Utils/TmpFs.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Type.hs
- compiler/ghc.cabal.in
- configure.ac
- docs/users_guide/exts/gadt_syntax.rst
- docs/users_guide/exts/strict.rst
- docs/users_guide/exts/type_data.rst
- docs/users_guide/runtime_control.rst
- docs/users_guide/using-warnings.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/GHCi/UI.hs
- ghc/Main.hs
- hadrian/README.md
- hadrian/doc/cross-compile.md
- hadrian/doc/flavours.md
- hadrian/doc/make.md
- hadrian/doc/windows.md
- hadrian/ghci-multi-cabal.in
- hadrian/hadrian.cabal
- hadrian/src/Base.hs
- hadrian/src/Builder.hs
- hadrian/src/CommandLine.hs
- hadrian/src/Flavour.hs
- hadrian/src/Hadrian/Builder.hs
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Oracles/Path.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Oracles/Setting.hs
- hadrian/src/Oracles/TestSettings.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Changelog.hs
- hadrian/src/Rules/Codes.hs
- hadrian/src/Rules/Lint.hs
- hadrian/src/Rules/Nofib.hs
- hadrian/src/Rules/Test.hs
- hadrian/src/Settings.hs
- − hadrian/src/Settings/Flavours/Quickest.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- + libraries/base/src/Data/RealFloat.hs
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/Conc.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/base/tests/all.T
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- libraries/ghc-internal/src/GHC/Internal/Bits.hs
- + libraries/ghc-internal/src/GHC/Internal/Box.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/IO.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Thread.hs
- libraries/ghc-internal/src/GHC/Internal/Exts.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/ghc-internal/src/GHC/Internal/TypeNats.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
- libraries/stm
- linters/lint-codes/LintCodes/Static.hs
- m4/find_python.m4
- + m4/fp_canonicalise_win_path.m4
- m4/fp_find_nm.m4
- m4/fp_find_root.m4
- m4/fp_prog_ar.m4
- m4/fp_prog_ar_args.m4
- m4/fp_prog_sh.m4
- m4/fptools_alex.m4
- m4/fptools_happy.m4
- rts/Capability.c
- rts/CloneStack.c
- rts/Continuation.c
- rts/ContinuationOps.cmm
- rts/HeapStackCheck.cmm
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/Interpreter.c
- rts/Messages.c
- rts/Prelude.h
- rts/PrimOps.cmm
- rts/Printer.c
- rts/ProfHeap.c
- rts/RaiseAsync.c
- rts/RtsFlags.c
- rts/STM.c
- rts/Schedule.c
- rts/Sparks.c
- rts/StableName.c
- rts/StgMiscClosures.cmm
- rts/ThreadPaused.c
- rts/Threads.c
- rts/Threads.h
- rts/Trace.h
- rts/configure.ac
- rts/eventlog/EventLog.c
- rts/include/rts/Flags.h
- rts/include/rts/RtsToHsIface.h
- rts/include/rts/storage/Closures.h
- rts/include/rts/storage/GC.h
- rts/include/stg/MiscClosures.h
- rts/include/stg/Prim.h
- rts/linker/elf_reloc_riscv64.c
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Select.c
- + rts/posix/SelectBis.c
- + rts/posix/SelectBis.h
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/prim/atomic.c
- rts/prim/bitrev.c
- rts/prim/bswap.c
- rts/rts.cabal
- rts/sm/Compact.c
- rts/sm/Evac.c
- rts/sm/GC.c
- rts/sm/GCUtils.c
- rts/sm/MBlock.c
- rts/sm/NonMoving.c
- rts/sm/NonMovingMark.c
- rts/sm/Storage.c
- rts/sm/Sweep.c
- testsuite/driver/README.md
- testsuite/driver/junit.py
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- testsuite/tests/MiniQuickCheck.hs
- testsuite/tests/arrows/should_run/all.T
- + testsuite/tests/arrows/should_run/arrowrun005.hs
- + testsuite/tests/arrows/should_run/arrowrun005.stdout
- + testsuite/tests/bytecode/T27633/Obj.hs
- + testsuite/tests/bytecode/T27633/T27633a.hs
- + testsuite/tests/bytecode/T27633/T27633a.stdout
- + testsuite/tests/bytecode/T27633/T27633b.hs
- + testsuite/tests/bytecode/T27633/T27633b.stdout
- + testsuite/tests/bytecode/T27633/all.T
- testsuite/tests/cmm/should_compile/Makefile
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.stderr
- − testsuite/tests/cmm/should_compile/T27368-ppr-debug.stdout
- testsuite/tests/cmm/should_compile/all.T
- + testsuite/tests/codeGen/should_gen_asm/T25233.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233.hs
- + testsuite/tests/codeGen/should_gen_asm/T25233b.asm
- + testsuite/tests/codeGen/should_gen_asm/T25233b.cmm
- testsuite/tests/codeGen/should_gen_asm/all.T
- + testsuite/tests/codeGen/should_run/T27430.hs
- + testsuite/tests/codeGen/should_run/T27430.stdout
- + testsuite/tests/codeGen/should_run/T27430_c.c
- + testsuite/tests/codeGen/should_run/T27533.hs
- + testsuite/tests/codeGen/should_run/T27533.stdout
- + testsuite/tests/codeGen/should_run/T27533_cmm.cmm
- + testsuite/tests/codeGen/should_run/T27537.hs
- + testsuite/tests/codeGen/should_run/T27537.stdout
- + testsuite/tests/codeGen/should_run/T27538.hs
- + testsuite/tests/codeGen/should_run/T27538.stdout
- testsuite/tests/codeGen/should_run/all.T
- + testsuite/tests/concurrent/should_run/T27657a.hs
- + testsuite/tests/concurrent/should_run/T27657a.stdout
- + testsuite/tests/concurrent/should_run/T27657b.hs
- + testsuite/tests/concurrent/should_run/T27657b.stdout
- testsuite/tests/concurrent/should_run/all.T
- + testsuite/tests/core-to-stg/T27627/Callee.hs
- + testsuite/tests/core-to-stg/T27627/Caller.hs
- + testsuite/tests/core-to-stg/T27627/Main.hs
- + testsuite/tests/core-to-stg/T27627/T27627.stdout
- + testsuite/tests/core-to-stg/T27627/all.T
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
- + testsuite/tests/core-to-stg/T27627c/Callee.hs
- + testsuite/tests/core-to-stg/T27627c/Caller.hs
- + testsuite/tests/core-to-stg/T27627c/Main.hs
- + testsuite/tests/core-to-stg/T27627c/T27627c.stdout
- + testsuite/tests/core-to-stg/T27627c/all.T
- + testsuite/tests/core-to-stg/T27627e.hs
- + testsuite/tests/core-to-stg/T27627e.stdout
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- testsuite/tests/core-to-stg/all.T
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/deSugar/should_compile/T25065a.hs
- testsuite/tests/deSugar/should_compile/all.T
- + testsuite/tests/deSugar/should_run/T20864.hs
- + testsuite/tests/deSugar/should_run/T20864.stdout
- + testsuite/tests/deSugar/should_run/T25065b.hs
- + testsuite/tests/deSugar/should_run/T25065b.stdout
- testsuite/tests/deSugar/should_run/all.T
- testsuite/tests/diagnostic-codes/codes.stdout
- testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461c.stderr
- testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Wrong.hs
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/Makefile
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
- + testsuite/tests/driver/stringtrie.hs
- + testsuite/tests/driver/stringtrie.stdout
- testsuite/tests/ffi/should_run/T21305.hs
- − testsuite/tests/gadt/T14320.stderr
- testsuite/tests/gadt/T18191.hs
- testsuite/tests/gadt/T18191.stderr
- + testsuite/tests/gadt/T27423a.hs
- + testsuite/tests/gadt/T27423b.hs
- + testsuite/tests/gadt/T27423b.stderr
- testsuite/tests/gadt/all.T
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/downsweep/OldModLocation.hs
- testsuite/tests/ghc-api/downsweep/PartialDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- + testsuite/tests/ghci.debugger/scripts/T27633c.hs
- + testsuite/tests/ghci.debugger/scripts/T27633c.script
- + testsuite/tests/ghci.debugger/scripts/T27633c.stdout
- + testsuite/tests/ghci.debugger/scripts/T27633c_obj.hs
- testsuite/tests/ghci.debugger/scripts/all.T
- + testsuite/tests/ghci/prog-mhu007/Makefile
- + testsuite/tests/ghci/prog-mhu007/a/A.hs
- + testsuite/tests/ghci/prog-mhu007/all.T
- + testsuite/tests/ghci/prog-mhu007/b/B.hs
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.script
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.stdout
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/Bar.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/testpkg-bar.pkg
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/Foo.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/testpkg-foo.pkg
- + testsuite/tests/ghci/prog-mhu007/unitA
- + testsuite/tests/ghci/prog-mhu007/unitB
- + testsuite/tests/ghci/prog-mhu008/Makefile
- + testsuite/tests/ghci/prog-mhu008/a/M.hs
- + testsuite/tests/ghci/prog-mhu008/all.T
- + testsuite/tests/ghci/prog-mhu008/b/M.hs
- + testsuite/tests/ghci/prog-mhu008/prog-mhu008.script
- + testsuite/tests/ghci/prog-mhu008/prog-mhu008.stdout
- + testsuite/tests/ghci/prog-mhu008/unitA
- + testsuite/tests/ghci/prog-mhu008/unitB
- + testsuite/tests/ghci/scripts/T27641.script
- + testsuite/tests/ghci/scripts/T27641.stderr
- + testsuite/tests/ghci/scripts/T27641.stdout
- + testsuite/tests/ghci/scripts/T27834.hs
- + testsuite/tests/ghci/scripts/T27834.script
- + testsuite/tests/ghci/scripts/T27834.stdout
- testsuite/tests/ghci/scripts/all.T
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- + testsuite/tests/indexed-types/should_run/T27331.hs
- + testsuite/tests/indexed-types/should_run/T27331.stdout
- + testsuite/tests/indexed-types/should_run/T27331b.hs
- + testsuite/tests/indexed-types/should_run/T27331b.stdout
- + testsuite/tests/indexed-types/should_run/T27331b_Plugin.hs
- testsuite/tests/indexed-types/should_run/all.T
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-prim-exports.stdout
- testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32
- + testsuite/tests/javascript/T27788.hs
- + testsuite/tests/javascript/T27788.stdout
- testsuite/tests/javascript/all.T
- testsuite/tests/linters/notes.stdout
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- + testsuite/tests/patsyn/should_compile/T27744.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/perf/compiler/T25763.stderr
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/compiler/large-project/all.T
- testsuite/tests/perf/should_run/T3586.hs
- testsuite/tests/perf/should_run/T3586.stdout
- testsuite/tests/perf/space_leaks/all.T
- testsuite/tests/plugins/defaulting-plugin/DefaultLifted.hs
- + testsuite/tests/primops/should_run/T27299.hs
- + testsuite/tests/primops/should_run/T27299.stdout
- testsuite/tests/primops/should_run/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- + testsuite/tests/printer/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- + testsuite/tests/printer/T27423c.hs
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- testsuite/tests/rename/should_fail/RnStaticPointersFail01.stderr
- testsuite/tests/rename/should_fail/RnStaticPointersFail03.stderr
- testsuite/tests/rename/should_fail/T26545.stderr
- + testsuite/tests/rts/T27477.hs
- + testsuite/tests/rts/T27477.stdout
- + testsuite/tests/rts/T27618.hs
- + testsuite/tests/rts/T27618.stdout
- + testsuite/tests/rts/T27649.hs
- + testsuite/tests/rts/T27649.stdout
- testsuite/tests/rts/all.T
- + testsuite/tests/rts/continuations/T27651.hs
- + testsuite/tests/rts/continuations/T27651.stdout
- testsuite/tests/rts/continuations/all.T
- testsuite/tests/rts/rtsflags001.stderr-ws-32
- testsuite/tests/rts/rtsflags001.stderr-ws-64
- + testsuite/tests/simd/should_run/T27565.hs
- + testsuite/tests/simd/should_run/T27565.stdout
- testsuite/tests/simd/should_run/all.T
- testsuite/tests/simplCore/should_compile/Makefile
- testsuite/tests/simplCore/should_compile/T17966.stderr
- + testsuite/tests/simplCore/should_compile/T27556.hs
- + testsuite/tests/simplCore/should_compile/T27556.script
- + testsuite/tests/simplCore/should_compile/T27629.hs
- + testsuite/tests/simplCore/should_compile/T27629.stderr
- + testsuite/tests/simplCore/should_compile/T27629Plugin.hs
- + testsuite/tests/simplCore/should_compile/T27749.hs
- + testsuite/tests/simplCore/should_compile/T27749.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- + testsuite/tests/simplCore/should_compile/T7803.hs
- + testsuite/tests/simplCore/should_compile/T7803.stderr
- + testsuite/tests/simplCore/should_compile/T7803a.hs
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
- + testsuite/tests/simplCore/should_run/T27705.hs
- + testsuite/tests/simplCore/should_run/T27705.stdout
- + testsuite/tests/simplCore/should_run/T27705_Inst.hs
- testsuite/tests/simplCore/should_run/all.T
- testsuite/tests/splice-imports/SI35.hs
- testsuite/tests/th/T17380.stderr
- testsuite/tests/th/T18612.stderr
- + testsuite/tests/th/T27034.hs
- + testsuite/tests/th/T27034.stderr
- testsuite/tests/th/T8761.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/type-data/should_fail/T27732a.hs
- + testsuite/tests/type-data/should_fail/T27732a.stderr
- + testsuite/tests/type-data/should_fail/T27732b.hs
- + testsuite/tests/type-data/should_fail/T27732b.stderr
- + testsuite/tests/type-data/should_fail/T27732c.hs
- + testsuite/tests/type-data/should_fail/T27732c.stderr
- + testsuite/tests/type-data/should_fail/T27732d.hs
- + testsuite/tests/type-data/should_fail/T27732d.stderr
- + testsuite/tests/type-data/should_fail/T27732e.hs
- + testsuite/tests/type-data/should_fail/T27732e.stderr
- + testsuite/tests/type-data/should_fail/T27732f.hs
- + testsuite/tests/type-data/should_fail/T27732f.stderr
- testsuite/tests/type-data/should_fail/TDStrictnessGADT.stderr
- testsuite/tests/type-data/should_fail/TDStrictnessH98.stderr
- testsuite/tests/type-data/should_fail/all.T
- + testsuite/tests/typecheck/should_compile/T27664.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/warnings/should_compile/T16836a.hs
- + testsuite/tests/warnings/should_compile/T16836a.stderr
- + testsuite/tests/warnings/should_compile/T16836b.hs
- + testsuite/tests/warnings/should_compile/T16836c.hs
- + testsuite/tests/warnings/should_compile/T16836c.stderr
- + testsuite/tests/warnings/should_compile/T16836d.hs
- + testsuite/tests/warnings/should_compile/T16836d.stderr
- testsuite/tests/warnings/should_compile/all.T
- + testsuite/tests/warnings/should_fail/WExpectedNoArgError.stderr
- testsuite/tests/warnings/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/check-ppr/Main.hs
- utils/deriveConstants/Main.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Target.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
- utils/hsc2hs
- utils/jsffi/dyld.mjs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bda147c2e259e1451fc1c14e0ddf88…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bda147c2e259e1451fc1c14e0ddf88…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/27514] 7 commits: Separate Home/External finder caches
by sheaf (@sheaf) 05 Oct '26
by sheaf (@sheaf) 05 Oct '26
05 Oct '26
sheaf pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC
Commits:
448beaf1 by sheaf at 2026-10-05T18:11:35+02:00
Separate Home/External finder caches
The caches for home modules and modules from external units have
different lifetimes, needing to be invalidated at different points.
This commit moves the cache for modules from external units into the
'UnitState' type, which keeps the cache correct whenever package flags
or package databases change (setSessionDynFlags, :set -package ...,
Backpack's addUnit).
This also makes it easier to clear the cache of home modules, as they
are now stored separately (obviating the 'isUnitEnvInstalledModule'
check we used to perform).
- - - - -
47af3406 by sheaf at 2026-10-05T18:11:45+02:00
Add known home modules to the finder cache
The finder looks in the file system for the on-disk source that
corresponds to a Haskell module name. There are two kinds of modules
where we need to bypass this search:
- a file passed directly in the command line, which may not be in any
search path and whose module name may differ from the file name;
- Backpack signatures and modules, which do not exist as files at all.
This commit handles these two situations by adding an immutable
'KnownHomeModules' in the finder cache, which is always consulted first
before doing any search.
This removes the ad-hoc imperative logic which relied on mutating the
finder cache at careful points.
'setModuleGraph' now also takes the known home modules, so that the
module graph and the known home modules are always set consistently.
See Note [Known home modules] in GHC.Unit.Finder.Types.
- - - - -
918b2c73 by sheaf at 2026-10-05T18:12:32+02:00
Make 'UnresolvedImport' into a suitable cache key
The goal of this commit is to make 'UnresolvedImport' suitable for use
as a cache key. Specifically, during downsweep we want to be able to
cache module resolution queries. We want to do this using some kind of
map, e.g. (roughly speaking):
Map UnresolvedImport ResolvedImport
For this to work, 'UnresolvedImport' must contain **precisely** the
input data to each resolution computation. In other words:
1. If something changes that materially affects resolution, it must
be stored in the cache key.
2. If changing some input does not materially affect the outcome,
then it must not be stored in the cache key.
To satisfy (1), we change 'UnitNode' to also keeps track of the home
unit in whose unit state the unit's dependencies were looked up.
For (2), 'ImportResolution' now stores a 'ModuleLookupScope' instead of an
'UnresolvedImportOrigin'. Plugin imports are expressed with a new
'LookupPlugin' scope. The source location of an import, which is
immaterial to the resolution, is stored out of band outside
'UnresolvedImport', in 'ModSummary'.
- - - - -
73e4a93e by sheaf at 2026-10-05T18:12:33+02:00
Add GHC.Data.Dependent
This commit adds a minimal implementation of dependent sums
('Some', 'DSum') and dependent maps ('DMap').
The implementation is not entirely performance optimal, as there are a
few extraneous allocations compared to the full-blown implementation in
the 'dependent-map' package. For our use case (caching downsweep
computations), this does not matter much.
See GHC.Data.Dependent.
- - - - -
df511386 by sheaf at 2026-10-05T18:12:33+02:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown.
- - - - -
d87a5d8f by sheaf at 2026-10-05T18:12:34+02:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
bda147c2 by sheaf at 2026-10-05T18:12:43+02:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
- - - - -
51 changed files:
- + changelog.d/T27763
- + changelog.d/parallel-downsweep
- compiler/GHC.hs
- compiler/GHC/Builtin.hs
- + compiler/GHC/Data/Dependent.hs
- compiler/GHC/Driver/Backpack.hs
- + compiler/GHC/Driver/Concurrency.hs
- + compiler/GHC/Driver/Config/Concurrency.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeAction.hs
- compiler/GHC/Driver/MakeFile.hs
- compiler/GHC/Driver/MakeSem.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/LogQueue.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Runtime/Interpreter/JS.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Unit/Finder.hs
- + compiler/GHC/Unit/Finder/Cache.hs
- compiler/GHC/Unit/Finder/Types.hs
- compiler/GHC/Unit/Module/Graph.hs
- compiler/GHC/Unit/Module/ModSummary.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/Types.hs
- + compiler/GHC/Utils/Concurrent/Scope.hs
- compiler/GHC/Utils/TmpFs.hs
- compiler/ghc.cabal.in
- ghc/GHCi/UI.hs
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/diagnostic-codes/codes.stdout
- testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461c.stderr
- testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Wrong.hs
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/downsweep/OldModLocation.hs
- testsuite/tests/ghc-api/downsweep/PartialDownsweep.hs
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/splice-imports/SI35.hs
- utils/check-ppr/Main.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/8fce7042931f6b6cbc1c8f47689d31…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/8fce7042931f6b6cbc1c8f47689d31…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/romes/27514] 3 commits: Driver: structured concurrent worker abstraction
by sheaf (@sheaf) 05 Oct '26
by sheaf (@sheaf) 05 Oct '26
05 Oct '26
sheaf pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC
Commits:
79f6f831 by sheaf at 2026-10-05T17:53:46+02:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown.
- - - - -
60686799 by sheaf at 2026-10-05T17:53:46+02:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
8fce7042 by sheaf at 2026-10-05T17:53:46+02:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
- - - - -
29 changed files:
- + changelog.d/T27763
- + changelog.d/parallel-downsweep
- + compiler/GHC/Driver/Concurrency.hs
- + compiler/GHC/Driver/Config/Concurrency.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeAction.hs
- compiler/GHC/Driver/MakeSem.hs
- compiler/GHC/Driver/Pipeline/LogQueue.hs
- compiler/GHC/Types/Error/Codes.hs
- + compiler/GHC/Utils/Concurrent/Scope.hs
- compiler/GHC/Utils/TmpFs.hs
- compiler/ghc.cabal.in
- testsuite/tests/diagnostic-codes/codes.stdout
- testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461c.stderr
- testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Wrong.hs
- testsuite/tests/ghc-api/downsweep/IncrementalDownsweep.hs
- testsuite/tests/ghc-api/downsweep/OldModLocation.hs
- testsuite/tests/ghc-api/downsweep/PartialDownsweep.hs
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/splice-imports/SI35.hs
- utils/check-ppr/Main.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/aafa95c3d768c39ab8d1711c04fdee…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/aafa95c3d768c39ab8d1711c04fdee…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/fendor/dep-json] 5 commits: Refactor Makefile to use phases appropriately
by Hannes Siebenhandl (@fendor) 05 Oct '26
by Hannes Siebenhandl (@fendor) 05 Oct '26
05 Oct '26
Hannes Siebenhandl pushed to branch wip/fendor/dep-json at Glasgow Haskell Compiler / GHC
Commits:
c637378c by fendor at 2026-10-05T17:06:41+02:00
Refactor Makefile to use phases appropriately
Prepare refactoring to allow json and Makefile implementation
- - - - -
c22252e7 by fendor at 2026-10-05T17:06:51+02:00
Add json output format
- - - - -
8a596866 by fendor at 2026-10-05T17:08:53+02:00
Add support for multiple home units
- - - - -
3307459e by fendor at 2026-10-05T17:09:15+02:00
Record OPTIONS_GHC in the `PreprocessingNode`
This allows user to treat modules if they use -XTemplateHaskell, for
example.
- - - - -
1fa7d882 by fendor at 2026-10-05T17:09:15+02:00
Testsuite changes
- - - - -
45 changed files:
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/MakeFile.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Pipeline/Phases.hs
- compiler/GHC/Driver/Session.hs
- docs/users_guide/separate_compilation.rst
- ghc/Main.hs
- testsuite/driver/testglobals.py
- testsuite/driver/testlib.py
- testsuite/tests/driver/mkdepend/Makefile
- testsuite/tests/driver/mkdepend/A.hs → testsuite/tests/driver/mkdepend/T24384/A.hs
- testsuite/tests/driver/mkdepend/C.hs → testsuite/tests/driver/mkdepend/T24384/C.hs
- testsuite/tests/driver/mkdepend/C.hs-boot → testsuite/tests/driver/mkdepend/T24384/C.hs-boot
- testsuite/tests/driver/mkdepend/D.hs → testsuite/tests/driver/mkdepend/T24384/D.hs
- testsuite/tests/driver/mkdepend/E.hs → testsuite/tests/driver/mkdepend/T24384/E.hs
- testsuite/tests/driver/mkdepend/dep-internal/DepInternal.hs → testsuite/tests/driver/mkdepend/T24384/dep-internal/DepInternal.hs
- testsuite/tests/driver/mkdepend/dep-internal/dep-internal.pkg → testsuite/tests/driver/mkdepend/T24384/dep-internal/dep-internal.pkg
- testsuite/tests/driver/mkdepend/dep-public/DepPublic.hs → testsuite/tests/driver/mkdepend/T24384/dep-public/DepPublic.hs
- testsuite/tests/driver/mkdepend/dep-public/dep-public.pkg → testsuite/tests/driver/mkdepend/T24384/dep-public/dep-public.pkg
- testsuite/tests/driver/mkdepend/dep/Dep.hs → testsuite/tests/driver/mkdepend/T24384/dep/Dep.hs
- testsuite/tests/driver/mkdepend/dep/dep.pkg → testsuite/tests/driver/mkdepend/T24384/dep/dep.pkg
- testsuite/tests/driver/mkdepend/foo.h → testsuite/tests/driver/mkdepend/T24384/foo.h
- testsuite/tests/driver/mkdepend/lib/B.hs → testsuite/tests/driver/mkdepend/T24384/lib/B.hs
- + testsuite/tests/driver/mkdepend/T24384/lib/B.hs-boot
- testsuite/tests/driver/mkdepend/preproc.sh → testsuite/tests/driver/mkdepend/T24384/preproc.sh
- testsuite/tests/driver/mkdepend/T24384a.stdout
- + testsuite/tests/driver/mkdepend/T24384b.stdout
- + testsuite/tests/driver/mkdepend/T24384c.stdout
- testsuite/tests/driver/mkdepend/all.T
- + testsuite/tests/driver/mkdepend/format_json.sh
- + testsuite/tests/driver/mkdepend/format_makefile.sh
- + testsuite/tests/driver/mkdepend/mhu01/a/A/Base.hs
- + testsuite/tests/driver/mkdepend/mhu01/a/A/Base.hs-boot
- + testsuite/tests/driver/mkdepend/mhu01/a/A/TH.hs
- + testsuite/tests/driver/mkdepend/mhu01/a/Util.hs
- + testsuite/tests/driver/mkdepend/mhu01/b/B/Normal.hs
- + testsuite/tests/driver/mkdepend/mhu01/b/B/Splice.hs
- + testsuite/tests/driver/mkdepend/mhu01/b/Util.hs
- + testsuite/tests/driver/mkdepend/mhu01/c/C.hs
- + testsuite/tests/driver/mkdepend/mhu01/unitA
- + testsuite/tests/driver/mkdepend/mhu01/unitB
- + testsuite/tests/driver/mkdepend/mhu01/unitC
- + testsuite/tests/driver/mkdepend/mkdepend_mhu01.dep-json
- + testsuite/tests/driver/mkdepend/mkdepend_mhu01.dep-makefile
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/ecb9a91b0fce49b5171863d33c8628…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/ecb9a91b0fce49b5171863d33c8628…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0