[Git][ghc/ghc][wip/fendor/homeunit-is-just] 33 commits: rts: handle large CONSTR closures in compacting GC
by Hannes Siebenhandl (@fendor) 29 Sep '26
by Hannes Siebenhandl (@fendor) 29 Sep '26
29 Sep '26
Hannes Siebenhandl pushed to branch wip/fendor/homeunit-is-just at Glasgow Haskell Compiler / GHC
Commits:
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.
- - - - -
e108bd77 by fendor at 2026-09-29T11:35:06+02:00
`HomeUnitEnv` should always have a concrete `HomeUnit`
The `HomeUnit` describes what kind of home unit a particular
`HomeUnitEnv` is and its `UnitId`.
To get a `HomeUnit`, we need to initialise the `UnitState` via `initUnits` otherwise,
we can't tell whether the `HomeUnit` is a definite one, or an indefinite
one, for example a backpack signature file.
That's why previously we maintained a `Maybe HomeUnit`.
However, a `HomeUnitEnv` always has at least a `UnitId` (accessible via
`DynFlags`).
We can fake a `HomeUnit` until we have actually initialised the
home unit and assume it is not a backpack unit.
This avoids many partial functions that should have never been partial
in the first case.
- - - - -
6251dc45 by fendor at 2026-09-29T11:40:20+02:00
Use `panic` instead of dummy value for initial HomeUnit
- - - - -
231 changed files:
- .gitlab-ci.yml
- + changelog.d/25763-cmdline-opt
- + changelog.d/26610
- + changelog.d/T27629
- + changelog.d/T27788-js-selector
- + changelog.d/boxing-family
- + changelog.d/clc-421
- + changelog.d/fix-compacting-gc-constr-27649
- + changelog.d/fix-control0-mask-trampoline
- + changelog.d/fix-threadlabel-segfault-27618
- + changelog.d/select-io-manager
- 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/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/CoreToStg.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.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/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Main/Hsc.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Driver/Session/Units.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/Usage.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Errors.hs
- compiler/GHC/Iface/Load.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/PostProcess.hs
- compiler/GHC/Rename/Names.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/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/Solver/Default.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Tc/Utils/Env.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Types/CompleteMatch.hs
- compiler/GHC/Types/Id.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/TyThing.hs
- compiler/GHC/Types/TyThing/Ppr.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/Home.hs
- compiler/GHC/Unit/Home/Graph.hs
- compiler/GHC/Unit/State.hs
- compiler/ghc.cabal.in
- docs/users_guide/runtime_control.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/GHCi/UI.hs
- hadrian/ghci-multi-cabal.in
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- + libraries/ghc-internal/src/GHC/Internal/Box.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/TypeNats.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
- linters/lint-codes/LintCodes/Static.hs
- rts/ContinuationOps.cmm
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/Prelude.h
- rts/PrimOps.cmm
- rts/RaiseAsync.c
- rts/StgMiscClosures.cmm
- rts/configure.ac
- rts/include/rts/Flags.h
- rts/include/rts/RtsToHsIface.h
- rts/include/rts/storage/Closures.h
- rts/include/stg/MiscClosures.h
- rts/include/stg/Prim.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- + rts/posix/SelectBis.c
- + rts/posix/SelectBis.h
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/prim/bitrev.c
- rts/prim/bswap.c
- rts/rts.cabal
- rts/sm/Compact.c
- rts/sm/Evac.c
- 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/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/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/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- + 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/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/T20718.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- + testsuite/tests/perf/compiler/T25763.stderr
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/primops/should_run/T27299.hs
- + testsuite/tests/primops/should_run/T27299.stdout
- testsuite/tests/primops/should_run/all.T
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.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/simplCore/should_compile/Makefile
- + 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/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/Transform.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- 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/b2dba136e0f750545c0908b99a9a39…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b2dba136e0f750545c0908b99a9a39…
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/T27844] 9 commits: Correct the documentation of `explicitUnits`
by Simon Peyton Jones (@simonpj) 29 Sep '26
by Simon Peyton Jones (@simonpj) 29 Sep '26
29 Sep '26
Simon Peyton Jones pushed to branch wip/T27844 at Glasgow Haskell Compiler / GHC
Commits:
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.
- - - - -
0437d21c by Simon Peyton Jones at 2026-09-29T09:06:37+01:00
Refactor and simplify solver's unification code
Ticket #27844 showed that GHC's constraint solver could miss an
occurs check, and thereby build an infinite type.
This patch significantly simplifes the code in
GHC.Tc.Solver.Equality.canEqCanLHSFinish_try_unification
GHC.Tc.Solver.Equality.canEqCanLHSFinish_no_unification
It is now significantly simpler, and requires much less subtle
reasoning to understand.
Main changes
* If `canEqCanLHSFinish_try_unification` fails to unify, it now /always/ calls
`canEqCanLHSFinish_no_unification` rather than trying to finish the job itself.
* `GHC.Tc.Solver.Monad.checkTypeEq` is moved from Monad into Equality, and renamed
to `GHC.Tc.Solver.Equality.checkTypeEqNoUnification`.
* `canEqCanLHSFinish_no_unification` calls `checkTypeEqNoUnification` to test
the invariants for an inert equality. Before this MR there was a special case for
`do_not_prevent_rewriting`, but that has gone; instead it's just a consequence of
the flags passed to `checkTyEqRhs`, documented in the `PuOK` case of
`canEqCanLHSFinish_no_unification`.
* There is only one bit of "swap back" code, in `finish_no_unify` in
`canEqCanLHSFinish_try_unification. Previously this code was duplicated.
* No need to gather coercion holes in the `PuOK` case of `canEqCanLHSFinish_no_unification`.
See documentation with that code.
- - - - -
41 changed files:
- .gitlab-ci.yml
- + changelog.d/T27844
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/State.hs
- hadrian/ghci-multi-cabal.in
- 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/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/indexed-types/should_fail/T9662.stderr
- 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/T20718.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- + testsuite/tests/typecheck/should_fail/T27844.hs
- + testsuite/tests/typecheck/should_fail/T27844.stderr
- testsuite/tests/typecheck/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Transform.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a4233abb501f48b8f359a2af0dc1fc…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a4233abb501f48b8f359a2af0dc1fc…
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
Simon Peyton Jones pushed to branch wip/T27844 at Glasgow Haskell Compiler / GHC
Commits:
a4233abb by Simon Peyton Jones at 2026-09-29T00:07:46+01:00
Final wibbles
.. still need a decent commit message
- - - - -
6 changed files:
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Tc/Solver/Equality.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Utils/Unify.hs
Changes:
=====================================
compiler/GHC/Runtime/Heap/Inspect.hs
=====================================
@@ -630,7 +630,7 @@ be sigmas and not taus.
~~~~~~~~~~~~~~~~~~~~~~
In the GHCi debugger we use unification variables whose MetaInfo is
RuntimeUnkTv. The special property of a RuntimeUnkTv is that it can
-unify with a polytype (see GHC.Tc.Utils.Unify.checkTypeEq).
+unify with a polytype (see GHC.Tc.Solver.Equality.checkTypeEqNoUnification).
If we don't do this `:print <term>` will fail if the type of <term>
has nested `forall`s or `=>`s.
=====================================
compiler/GHC/Tc/Solver/Equality.hs
=====================================
@@ -21,6 +21,7 @@ import GHC.Tc.Types.Evidence
import GHC.Tc.Types.Constraint
import GHC.Tc.Types.CtLoc
import GHC.Tc.Types.Origin
+import GHC.Tc.Types( TcM )
import GHC.Tc.Utils.Unify
import GHC.Tc.Utils.TcType
import GHC.Tc.Instance.Family ( tcUnwrapNewtype_maybe )
@@ -36,8 +37,8 @@ import GHC.Core.Coercion
import GHC.Core.Reduction
import GHC.Core.FamInstEnv ( FamInstEnvs )
import GHC.Core
-import GHC.Types.Var
+import GHC.Types.Var
import GHC.Types.Basic
import GHC.Types.Name.Reader
import GHC.Types.Var.Env
@@ -49,6 +50,8 @@ import GHC.Utils.Misc
import GHC.Utils.Monad
import GHC.Data.Pair
+import GHC.Data.Bag
+
import Control.Monad
import Data.Maybe ( isJust, isNothing )
import Data.List ( zip4 )
@@ -2257,7 +2260,8 @@ canEqCanLHSFinish_try_unification ev eq_rel swapped lhs rhs
else rewriteEqEvidence ev swapped
(mkReflRedn Nominal (mkTyVarTy tv)) rhs_redn
emptyCoHoleSet
- -- emptyCoHoleSet: rhs_redn has no CoercionHoles
+ -- emptyCoHoleSet: we are about to /solve/ this
+ -- constraint, so its CoHoleSet doesn't matter
; let tv_ty = mkTyVarTy tv
final_rhs = reductionReducedType rhs_redn
@@ -2285,21 +2289,12 @@ canEqCanLHSFinish_no_unification ev eq_rel swapped lhs rhs
-- We can't unify, but we can see if this equality can go in the inert set
-- and be used to rewrite other constraints.
-- See Note [Equalities where unification fails]
- = do { -- Do checkTypeEq to guarantee (TyEq:OC), (TyEq:F)
+ = do { -- Do checkTypeEqNoUnfication to guarantee (TyEq:OC), (TyEq:F)
-- Must do the occurs check even on tyvar/tyvar equalities,
-- in case have x ~ (y :: ..x...); this is #12593.
- ; check_result <- checkTypeEq ev eq_rel lhs rhs
+ ; check_result <- checkTypeEqNoUnification ev eq_rel lhs rhs
; case check_result of
PuFail reason
-{-
- | reason `cterHasOnlyProblems` do_not_prevent_rewriting
- -> do { new_ev <- rewriteEqEvidenceSwapOnly ev eq_rel swapped lhs rhs
- ; continueWith $ Right $
- EqCt { eq_ev = new_ev, eq_eq_rel = eq_rel
- , eq_lhs = lhs , eq_rhs = rhs } }
-
- | otherwise
--}
-> -- We can't unify, and this equality should not be used to rewrite
-- other constraints (e.g. because it has an occurs check).
-- So add it to the inert Irreds.
@@ -2307,19 +2302,27 @@ canEqCanLHSFinish_no_unification ev eq_rel swapped lhs rhs
; finishCanWithIrred (NonCanonicalReason reason) new_ev }
PuOK new_eqs rhs_redn
- -> -- Claim: there should be no new_eqs
- -- The non-unification check does non promotion nor type-family
- -- flattening for Wanteds
+ -> -- Put this constraint into the inert set, so that it will be used
+ -- to solve or rewrite other constraints.
+ --
+ -- NB: checkTypeEqNoUnification does not check for
+ -- (b) skolem-escape (tefTyVar_levelCheck = LC_None)
+ -- (a) concreteness (tefTyVar_concreteCheck = CC_None)
+ -- So an equality whose only problem is e.g. skolem-escape will
+ -- return PuOK, will be put in the inert set, will be used for
+ -- rewriting. That is fine.
+ --
+ -- assertPpr (isEmptyCts new_eqs):
+ -- checkTypeEqNoUnification does neither promotion nor type-family
+ -- flattening for Wanteds, and hence creates no equalities to solve
assertPpr (isEmptyCts new_eqs)
(vcat [ text "new_eqs" <+> ppr new_eqs
, text "rhs_redn" <+> ppr rhs_redn
, text "lhs" <+> ppr lhs
, text "rhs" <+> ppr rhs ]) $
- do { emitWork new_eqs
- ; let new_holes = rewriterSetFromCts new_eqs
- lhs_ty = canEqLHSType lhs
+ do { let lhs_ty = canEqLHSType lhs
lhs_redn = mkReflRedn (eqRelRole eq_rel) lhs_ty
- ; new_ev <- rewriteEqEvidence ev swapped lhs_redn rhs_redn new_holes
+ ; new_ev <- rewriteEqEvidence ev swapped lhs_redn rhs_redn emptyCoHoleSet
-- Important: even if the coercion is Refl,
-- * new_ev has reductionReducedType on the RHS
@@ -2330,19 +2333,54 @@ canEqCanLHSFinish_no_unification ev eq_rel swapped lhs rhs
, eq_lhs = lhs
, eq_rhs = reductionReducedType rhs_redn } } }
-{-
--- | Some problems prevent /unification/ but not /rewriting/:
---
--- Skolem-escape: if we have [W] alpha[2] ~ Maybe b[3]
--- we can't unify (skolem-escape); but it /is/ canonical,
--- and hence we /can/ use it for rewriting
---
--- Concrete-ness: alpha[conc] ~ b[sk]
--- We can use it to rewrite; we still have to solve the original
-do_not_prevent_rewriting :: CheckTyEqResult
-do_not_prevent_rewriting = cteProblem cteSkolemEscape S.<>
- cteProblem cteConcrete
--}
+checkTypeEqNoUnification :: CtEvidence -> EqRel -> CanEqLHS -> TcType
+ -> TcS (PuResult Ct Reduction)
+-- Used for general CanEqLHSs, when we have decided that we cannot unify
+checkTypeEqNoUnification ev eq_rel lhs rhs =
+ case ev of
+ CtGiven {} ->
+ do { traceTcS "checkTypeEqNoUnification (given) {" (vcat [ text "lhs:" <+> ppr lhs
+ , text "rhs:" <+> ppr rhs ])
+ ; check_result <- wrapTcS (checkTyEqRhs given_flags rhs)
+ ; traceTcS "checkTypeEqNoUnification }" (ppr check_result)
+ ; case check_result of
+ PuFail reason -> return (PuFail reason)
+ PuOK prs redn -> do { new_givens <- mapBagM mk_new_given prs
+ ; emitWork new_givens
+ ; updInertSet (addCycleBreakerBindings prs)
+ ; return (pure redn) } }
+ CtWanted {} -> do { traceTcS "checkTypeEqNoUnification (wanted) {" $
+ vcat [ text "lhs:" <+> ppr lhs
+ , text "rhs:" <+> ppr rhs ]
+ ; check_result <- wrapTcS (checkTyEqRhs wanted_flags rhs)
+ ; traceTcS "checkTypeEqNoUnification }" (ppr check_result)
+ ; return check_result }
+ where
+ wanted_flags :: TyEqFlags TcM Ct
+ wanted_flags = notUnifying_TEFTask occ_prob lhs
+ -- See Note [Don't cycle-break Wanteds when not unifying]
+
+ given_flags :: TyEqFlags TcM (TcTyVar, TcType)
+ given_flags = wanted_flags { tef_fam_app = mkTEFA_Break ev eq_rel BreakGiven }
+ -- TEFA_Break used for: [G] a ~ Maybe (F a)
+ -- or [G] F a ~ Maybe (F a)
+
+ -- occ_prob: see Note [Occurs check and representational equality]
+ occ_prob = case eq_rel of
+ NomEq -> cteInsolubleOccurs
+ ReprEq -> cteSolubleOccurs
+
+ ---------------------------
+ mk_new_given :: (TcTyVar, TcType) -> TcS Ct
+ mk_new_given (new_tv, fam_app)
+ = mkNonCanonical . CtGiven <$> newGivenEv cb_loc (given_pred, given_term)
+ where
+ new_ty = mkTyVarTy new_tv
+ given_pred = mkNomEqPred fam_app new_ty
+ given_term = evCoercion $ mkNomReflCo new_ty -- See Detail (4) of Note
+
+ -- See Detail (7) of the Note
+ cb_loc = updateCtLocOrigin (ctEvLoc ev) CycleBreakerOrigin
----------------------
finishCanWithIrred :: CtIrredReason -> CtEvidence
@@ -2371,16 +2409,53 @@ inert set so that it can be used to rewrite other Wanteds. (There are many
reasons by unification might not happen: the LHS is not a touchable unification
variable, it has rewriters, it's a Given etc.)
-* In this case we call `checkTypeEq`to see if it is canonical (which in turn
- calls `checkTypeEqRhs`).
+* In this case we call `checkTypeEqNoUnification`to see if it is canonical (which
+ in turn calls `checkTypeEqRhs` with suitable flags).
-* Even if `checkTypeEq` returns `PuFail`, we may /still/ be able to add it to the inert
- set, depending on the reason for failure; examples are given in `do_not_prevent_rewriting`.
+* NB: we do /not/ try to take advantage of the case where
+ `canEqCanLHSFinish_try_unification` has already called `checkTyEqRhs`; doing so
+ caused #27844. (The first call did fam-app breaking, which concealed a genuine
+ occurs check.) It's just too clever; better to do the simple thing.
-* NB: we do /not/ try to take advantage of the case where `canEqCanLHSFinish_try_unification`
- has already called `checkTyEqRhs`; doing so caused #27844. (The first call did fam-app
- breaking, which concealed a genuine occurs check.) It's just too clever; better to do
- the simple thing.
+Note [Don't cycle-break Wanteds when not unifying]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consdier
+ [W] a[2] ~ Maybe (F a[2])
+
+Should we cycle-break this Wanted, thus?
+
+ [W] a[2] ~ Maybe delta[2]
+ [W] delta[2] ~ F a[2]
+
+For a start, this is dodgy because we might just unify delta, thus undoing
+what we have done, and getting an infinite loop in the solver. Even if we
+somehow prevented ourselves from doing so, is there any merit in the split?
+Maybe: perhaps we can use that equality on `a` to unlock other constraints?
+Consider
+ type instance F (Maybe _) = Bool
+
+ [G] g1: a ~ Maybe Bool
+ [W] w1: a ~ Maybe (F a)
+
+If we loop-break w1 to get
+ [W] w1': a ~ Maybe gamma
+ [W] w3: gamma ~ F a
+Now rewrite w3 with w1'
+ [W] w3': gamma ~ F (Maybe gamma)
+Now use the type instance to get
+ gamma := Bool
+Now we are left with
+ [W] w1': a ~ Maybe Bool
+which we can solve from the Given.
+
+BUT in this situation we could have rewritten the
+/original/ Wanted from the Given, like this:
+ [W] w1': Maybe Bool ~ Maybe (F (Maybe Bool))
+and that is readily soluble.
+
+In short: loop-breaking Wanteds, when we aren't unifying,
+seems of no merit. Hence TEFA_Recurse, rather than TEFA_Break,
+in `wanted_flags` in `checkTypeEqNoUnification`.
Note [Equalities with heterogeneous kinds]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2565,8 +2640,7 @@ The details depend on whether we're working with a Given or a Wanted.
Given
-----
We emit a new Given, [G] F a ~ cbv, equating the type family application
-to our new cbv. This is actually done by `break_given` in
-`GHC.Tc.Solver.Monad.checkTypeEq`.
+to our new cbv. This is actually done by `break_given` in `checkTypeEqNoUnification`.
Note its orientation: The type family ends up on the left; see
Note [Orienting TyFamLHS/TyFamLHS]. No special treatment for
=====================================
compiler/GHC/Tc/Solver/InertSet.hs
=====================================
@@ -983,7 +983,7 @@ How do we establish these conditions?
with S(fw,_).
* (T3) is guaranteed by an occurs-check on the work item.
- This is done during canonicalisation, in checkTypeEq; invariant
+ This is done during canonicalisation, in checkTyEqRhs; invariant
(TyEq:OC) of CEqCan. See also Note [EqCt occurs check] in GHC.Tc.Types.Constraint.
* (T4) is established by GHC.Tc.Solver.Monad.kickOutRewritable. If the inert
=====================================
compiler/GHC/Tc/Solver/Monad.hs
=====================================
@@ -117,10 +117,7 @@ module GHC.Tc.Solver.Monad (
getDefaultInfo, getDynFlags, getGlobalRdrEnvTcS,
matchFam, matchFamTcM,
checkWellLevelledDFun,
- pprEq,
-
- -- Enforcing invariants for type equalities
- checkTypeEq
+ pprEq
) where
import GHC.Prelude
@@ -2302,57 +2299,6 @@ wrapUnifier rws loc role do_unifications
************************************************************************
-}
-checkTypeEq :: CtEvidence -> EqRel -> CanEqLHS -> TcType
- -> TcS (PuResult Ct Reduction)
--- Used for general CanEqLHSs, ones that do
--- not have a touchable type variable on the LHS (i.e. not unifying)
-checkTypeEq ev eq_rel lhs rhs =
- case ev of
- CtGiven {} ->
- do { traceTcS "checkTypeEq (given) {" (vcat [ text "lhs:" <+> ppr lhs
- , text "rhs:" <+> ppr rhs ])
- ; check_result <- wrapTcS (checkTyEqRhs given_flags rhs)
- ; traceTcS "checkTypeEq }" (ppr check_result)
- ; case check_result of
- PuFail reason -> return (PuFail reason)
- PuOK prs redn -> do { new_givens <- mapBagM mk_new_given prs
- ; emitWork new_givens
- ; updInertSet (addCycleBreakerBindings prs)
- ; return (pure redn) } }
- CtWanted {} -> do { traceTcS "checkTypeEq (wanted) {" $
- vcat [ text "lhs:" <+> ppr lhs
- , text "rhs:" <+> ppr rhs ]
- ; check_result <- wrapTcS (checkTyEqRhs wanted_flags rhs)
- ; traceTcS "checkTypeEq }" (ppr check_result)
- ; return check_result }
- where
- wanted_flags :: TyEqFlags TcM Ct
- wanted_flags = notUnifying_TEFTask occ_prob lhs
- -- checkTypeEq deals only with the non-unifying case
-
- given_flags :: TyEqFlags TcM (TcTyVar, TcType)
- given_flags = wanted_flags { tef_fam_app = mkTEFA_Break ev eq_rel BreakGiven }
- -- TEFA_Break used for: [G] a ~ Maybe (F a)
- -- or [G] F a ~ Maybe (F a)
-
- -- occ_prob: see Note [Occurs check and representational equality]
- occ_prob = case eq_rel of
- NomEq -> cteInsolubleOccurs
- ReprEq -> cteSolubleOccurs
-
- ---------------------------
- mk_new_given :: (TcTyVar, TcType) -> TcS Ct
- mk_new_given (new_tv, fam_app)
- = mkNonCanonical . CtGiven <$> newGivenEv cb_loc (given_pred, given_term)
- where
- new_ty = mkTyVarTy new_tv
- given_pred = mkNomEqPred fam_app new_ty
- given_term = evCoercion $ mkNomReflCo new_ty -- See Detail (4) of Note
-
- -- See Detail (7) of the Note
- cb_loc = updateCtLocOrigin (ctEvLoc ev) CycleBreakerOrigin
-
--------------------------
-- | Fill in CycleBreakerTvs with the variables they stand for.
-- See Note [Type equality cycles] in GHC.Tc.Solver.Equality
restoreTyVarCycles :: InertSet -> TcM ()
@@ -2368,43 +2314,4 @@ restoreTyVarCycles is
(a ~R# b a) is soluble if b later turns out to be Identity
So we treat this as a "soluble occurs check".
-Note [Don't cycle-break Wanteds when not unifying]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Consdier
- [W] a[2] ~ Maybe (F a[2])
-
-Should we cycle-break this Wanted, thus?
-
- [W] a[2] ~ Maybe delta[2]
- [W] delta[2] ~ F a[2]
-
-For a start, this is dodgy because we might just unify delta, thus undoing
-what we have done, and getting an infinite loop in the solver. Even if we
-somehow prevented ourselves from doing so, is there any merit in the split?
-Maybe: perhaps we can use that equality on `a` to unlock other constraints?
-Consider
- type instance F (Maybe _) = Bool
-
- [G] g1: a ~ Maybe Bool
- [W] w1: a ~ Maybe (F a)
-
-If we loop-break w1 to get
- [W] w1': a ~ Maybe gamma
- [W] w3: gamma ~ F a
-Now rewrite w3 with w1'
- [W] w3': gamma ~ F (Maybe gamma)
-Now use the type instance to get
- gamma := Bool
-Now we are left with
- [W] w1': a ~ Maybe Bool
-which we can solve from the Given.
-
-BUT in this situation we could have rewritten the
-/original/ Wanted from the Given, like this:
- [W] w1': Maybe Bool ~ Maybe (F (Maybe Bool))
-and that is readily soluble.
-
-In short: loop-breaking Wanteds, when we aren't unifying,
-seems of no merit. Hence TEFA_Recurse, rather than TEFA_Break,
-in `wanted_flags` in `checkTypeEq`.
-}
=====================================
compiler/GHC/Tc/Types/Constraint.hs
=====================================
@@ -524,7 +524,7 @@ isInsolubleReason (InsolubleFunDepReason {}) = True
------------------------------------------------------------------------------
-- | A /set/ of problems in checking the validity of a type equality.
--- See 'checkTypeEq'.
+-- See 'GHC.Tc.Utils.Unify.checkTyEqRhs'.
newtype CheckTyEqResult = CTER Word8
-- | No problems in checking the validity of a type equality.
=====================================
compiler/GHC/Tc/Utils/Unify.hs
=====================================
@@ -3617,7 +3617,7 @@ instance Outputable SimpleUnifyResult where
simpleUnifyCheck :: UnifyCheckCaller -> TcLevel -> TcTyVar -> TcType -> SimpleUnifyResult
-- ^ A fast check for unification. May return "not sure", in which case
-- unification might still be OK, but it'll take more work to do
--- (use the full 'checkTypeEq').
+-- (use the full 'checkTyEqRhs').
--
-- It returns SUC_CannotUnify for
-- * Touchability failure
@@ -4289,7 +4289,7 @@ NB: We never see a TyVarLHS here, such as
[G] a ~ F tys here
because we'd have swapped it to
[G] F tys ~ a
-in canEqCanLHS2, before getting to checkTypeEq.
+in canEqCanLHS2, before getting to checkTyEqRhs.
-}
check_ty_eq_rhs :: forall m a
@@ -4813,7 +4813,6 @@ promote_meta_tyvar info dest_lvl occ_tv
touchabilityTest :: TcLevel -> TcTyVar -> Bool
-- ^ This is the key test for untouchability:
-- See Note [Unification preconditions] in GHC.Tc.Utils.Unify
--- and Note [Solve by unification] in GHC.Tc.Solver.Equality
--
-- @True@ <=> the variable is touchable
touchabilityTest given_eq_lvl tv
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a4233abb501f48b8f359a2af0dc1fc6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a4233abb501f48b8f359a2af0dc1fc6…
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/torsten.schmits/worker-gbc] Split `getLinkDeps` and abstract over the interface
by Torsten Schmits (@torsten.schmits) 29 Sep '26
by Torsten Schmits (@torsten.schmits) 29 Sep '26
29 Sep '26
Torsten Schmits pushed to branch wip/torsten.schmits/worker-gbc at Glasgow Haskell Compiler / GHC
Commits:
373fc0f2 by Torsten Schmits at 2026-09-29T00:09:36+02:00
Split `getLinkDeps` and abstract over the interface
This allows API clients to customize bytecode loading to implement
on-demand compilation of interface core bindings.
- - - - -
5 changed files:
- compiler/GHC/Driver/Env/Types.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
Changes:
=====================================
compiler/GHC/Driver/Env/Types.hs
=====================================
@@ -29,6 +29,7 @@ import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
import Data.IORef
import GHC.Driver.Env.KnotVars
+import GHC.Linker.Types (LoaderState, Linkables)
-- | The Hsc monad: Passing an environment and diagnostic state
newtype Hsc a = Hsc (HscEnv -> Messages GhcMessage -> IO (a, Messages GhcMessage))
@@ -50,7 +51,6 @@ instance ContainsDynFlags HscEnv where
instance HasLogger Hsc where
getLogger = Hsc $ \e w -> return (hsc_logger e, w)
-
-- | HscEnv is like 'GHC.Driver.Monad.Session', except that some of the fields are immutable.
-- An HscEnv is used to compile a single module from plain Haskell source
-- code (after preprocessing) to either C, assembly or C--. It's also used
@@ -90,6 +90,8 @@ data HscEnv
-- ^ target code interpreter (if any) to use for TH and GHCi.
-- See Note [Target code interpreter]
+ , hsc_linkables :: HscEnv -> LoaderState -> IO Linkables
+
, hsc_plugins :: !Plugins
-- ^ Plugins
=====================================
compiler/GHC/Driver/Main.hs
=====================================
@@ -342,6 +342,7 @@ newHscEnvWithHUG top_dir top_dynflags cur_unit home_unit_graph = do
, hsc_FC = fc_var
, hsc_type_env_vars = emptyKnotVars
, hsc_interp = Nothing
+ , hsc_linkables = linkablesDefault
, hsc_unit_env = unit_env
, hsc_plugins = emptyPlugins
, hsc_hooks = emptyHooks
@@ -2936,10 +2937,10 @@ jsCodeGen hsc_env srcspan i this_mod stg_binds_with_deps binding_id = do
-- Take lock for the actual work.
(dep_linkables, dep_units) <- modifyLoaderState interp $ \pls -> do
- let link_opts = initLinkDepsOpts hsc_env
-- Find what packages and linkables are required
- deps <- getLinkDeps link_opts interp pls srcspan needed_mods
+ linkables <- hsc_linkables hsc_env hsc_env pls
+ deps <- linkablesGet linkables srcspan needed_mods
-- We update the LinkerState even if the JS interpreter maintains its linker
-- state independently to load new objects here.
=====================================
compiler/GHC/Linker/Deps.hs
=====================================
@@ -11,6 +11,9 @@ module GHC.Linker.Deps
( LinkDepsOpts (..)
, LinkDeps (..)
, getLinkDeps
+ , Linkables (..)
+ , resolveLinkDeps
+ , selectLinkDeps
)
where
@@ -62,13 +65,6 @@ data LinkDepsOpts = LinkDepsOpts
, ldGetDependencies :: !([Module] -> IO ([Module], UniqDSet UnitId))
}
-data LinkDeps = LinkDeps
- { ldNeededLinkables :: [Linkable]
- , ldAllLinkables :: [LinkableUsage]
- , ldUnits :: [UnitId]
- , ldNeededUnits :: UniqDSet UnitId
- }
-
-- | Find all the packages and linkables that a set of modules depends on
--
-- Return the module and package dependencies for the needed modules.
@@ -84,22 +80,20 @@ getLinkDeps
-> [Module] -- If you need these
-> IO LinkDeps -- ... then link these first
getLinkDeps opts interp pls span mods = do
- -- The interpreter and dynamic linker can only handle object code built
- -- the "normal" way, i.e. no non-std ways like profiling or ticky-ticky.
- -- So here we check the build tag: if we're building a non-standard way
- -- then we need to find & link object files built the "normal" way.
- maybe_normal_osuf <- checkNonStdWay opts interp span
-
- get_link_deps opts pls maybe_normal_osuf span mods
+ linkables <- resolveLinkDeps opts pls span mods
+ selectLinkDeps opts interp span linkables
-get_link_deps
+-- | Compute the transitive dependencies of the given modules and split them
+-- into those that are already loaded and those that need to be linked.
+--
+-- Returns @(loaded linkables, needed modules, needed units, all units)@.
+resolveLinkDeps
:: LinkDepsOpts
-> LoaderState
- -> Maybe FilePath -- replace object suffixes?
-> SrcSpan
-> [Module]
- -> IO LinkDeps
-get_link_deps opts pls maybe_normal_osuf span mods = do
+ -> IO ([LinkableUsage], [Module], [UnitId], UniqDSet UnitId)
+resolveLinkDeps opts pls _span mods = do
-- Three step process:
@@ -120,6 +114,33 @@ get_link_deps opts pls maybe_normal_osuf span mods = do
Just linkable -> Right linkable
Nothing -> Left mod
+ pure (links_got, mods_needed, pkgs_needed, pkgs_s)
+ where
+ relevant_mods = filterOut isInteractiveModule mods
+
+-- | Find the linkables for the modules that need to be loaded, as determined
+-- by 'resolveLinkDeps'.
+selectLinkDeps
+ :: LinkDepsOpts
+ -> Interp
+ -> SrcSpan -- for error messages
+ -> ([LinkableUsage], [Module], [UnitId], UniqDSet UnitId)
+ -> IO LinkDeps -- ... then link these first
+selectLinkDeps opts interp span linkables = do
+ -- The interpreter and dynamic linker can only handle object code built
+ -- the "normal" way, i.e. no non-std ways like profiling or ticky-ticky.
+ -- So here we check the build tag: if we're building a non-standard way
+ -- then we need to find & link object files built the "normal" way.
+ maybe_normal_osuf <- checkNonStdWay opts interp span
+ select_link_deps opts maybe_normal_osuf span linkables
+
+select_link_deps
+ :: LinkDepsOpts
+ -> Maybe FilePath -- replace object suffixes?
+ -> SrcSpan
+ -> ([LinkableUsage], [Module], [UnitId], UniqDSet UnitId)
+ -> IO LinkDeps
+select_link_deps opts maybe_normal_osuf span (links_got, mods_needed, pkgs_needed, pkgs_s) = do
-- 3. For each dependent module, find its linkable
-- This will either be in the HPT or (in the case of one-shot
-- compilation) we may need to use maybe_getFileLinkable
@@ -139,7 +160,6 @@ get_link_deps opts pls maybe_normal_osuf span mods = do
return link_deps
where
unit_env = ldUnitEnv opts
- relevant_mods = filterOut isInteractiveModule mods
no_obj :: Outputable a => a -> IO b
no_obj mod = dieWith opts span $
=====================================
compiler/GHC/Linker/Loader.hs
=====================================
@@ -36,6 +36,7 @@ module GHC.Linker.Loader
, initLinkDepsOpts
, getGccSearchDirectory
, mkDynLoadLib
+ , linkablesDefault
)
where
@@ -253,10 +254,10 @@ loadDependencies
-> IO (LoaderState, SuccessFlag, [LinkableUsage], PkgsLoaded) -- ^ returns the set of linkables required
-- When called, the loader state must have been initialized (see `initLoaderState`)
loadDependencies interp hsc_env pls span needed_mods = do
- let opts = initLinkDepsOpts hsc_env
+ linkables <- hsc_linkables hsc_env hsc_env pls
-- Find what packages and linkables are required
- deps <- getLinkDeps opts interp pls span needed_mods
+ deps <- linkablesGet linkables span needed_mods
let this_pkgs_needed = ldNeededUnits deps
@@ -651,6 +652,18 @@ findWholeCoreBindings hsc_env mod = do
sequence (lookupModuleEnv eps_iface_bytecode mod)
+linkablesDefault ::
+ HscEnv ->
+ LoaderState ->
+ IO Linkables
+linkablesDefault hsc_env pls =
+ pure Linkables {
+ linkablesResolve = resolveLinkDeps opts pls,
+ linkablesSelect = selectLinkDeps opts (hscInterp hsc_env)
+ }
+ where
+ opts = initLinkDepsOpts hsc_env
+
findBytecodeLinkableMaybe :: HscEnv -> ModLocation -> IO (Maybe Linkable)
findBytecodeLinkableMaybe hsc_env locn = do
let bytecode_fn = ml_bytecode_file locn
=====================================
compiler/GHC/Linker/Types.hs
=====================================
@@ -1,6 +1,8 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE GADTs #-}
-----------------------------------------------------------------------------
--
@@ -78,6 +80,9 @@ module GHC.Linker.Types
, mkLinkableUsage
, ModuleByteCode(..)
+ , LinkDeps (..)
+ , Linkables (..)
+ , linkablesGet
)
where
@@ -95,6 +100,7 @@ import GHC.Stack.CCS
import GHC.Types.Name.Env ( NameEnv, emptyNameEnv, extendNameEnvList, lookupNameEnv )
import GHC.Types.Name ( Name )
import GHC.Types.SptEntry
+import GHC.Types.SrcLoc (SrcSpan)
import GHC.Types.Unique.DSet
import GHC.Types.Unique.DFM
import GHC.Unit.Module.Deps (LinkablePartUsage (..), linkablePartUsageObjectPaths)
@@ -693,3 +699,23 @@ instance Outputable LibrarySpec where
ppr (DLLPath f) = text "DLLPath" <+> text f
ppr (Framework s) = text "Framework" <+> text s
ppr (BytecodeLibrary f) = text "BytecodeLibrary" <+> text f
+
+data LinkDeps = LinkDeps
+ { ldNeededLinkables :: [Linkable]
+ , ldAllLinkables :: [LinkableUsage]
+ , ldUnits :: [UnitId]
+ , ldNeededUnits :: UniqDSet UnitId
+ }
+
+-- | Customizable procedure for determining the linkables required by a set of
+-- modules, split into a resolution phase and a selection phase with an
+-- intermediate representation that is opaque to consumers.
+data Linkables where
+ Linkables :: {
+ linkablesResolve :: SrcSpan -> [Module] -> IO a,
+ linkablesSelect :: SrcSpan -> a -> IO LinkDeps
+ } -> Linkables
+
+linkablesGet :: Linkables -> SrcSpan -> [Module] -> IO LinkDeps
+linkablesGet Linkables {..} span mods =
+ linkablesSelect span =<< linkablesResolve span mods
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/373fc0f2bd7e13cf2edb012d46bf612…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/373fc0f2bd7e13cf2edb012d46bf612…
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/marge_bot_batch_merge_job] 8 commits: Improve the `mhu-closure` makefile
by Marge Bot (@marge-bot) 28 Sep '26
by Marge Bot (@marge-bot) 28 Sep '26
28 Sep '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
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.
- - - - -
079fdad3 by Vladislav Zavialov at 2026-09-28T16:14:18-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
- - - - -
a90c0c72 by Alan Zimmerman at 2026-09-28T16:14:18-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.
- - - - -
43 changed files:
- .gitlab-ci.yml
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Unit/Env.hs
- hadrian/ghci-multi-cabal.in
- 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/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/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/printer/AnnotationNoListTuplePuns.stdout
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- + 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
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Transform.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/ce0872041f8d203b3253ec23b966b3…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/ce0872041f8d203b3253ec23b966b3…
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/27836] Fix #27836, normalise unit database path in ExternalUnitDatabases
by Hannes Siebenhandl (@fendor) 28 Sep '26
by Hannes Siebenhandl (@fendor) 28 Sep '26
28 Sep '26
Hannes Siebenhandl pushed to branch wip/fendor/27836 at Glasgow Haskell Compiler / GHC
Commits:
bb48bf8e by Hannes Siebenhandl at 2026-09-28T18:18:11+02:00
Fix #27836, normalise unit database path in ExternalUnitDatabases
Add a regression test, proofing that all variations:
- local-unit-db/my-test.conf
- ./local-unit-db/my-test.conf
- ./local-unit-db/my-test.conf/
are parsed only once.
- - - - -
11 changed files:
- compiler/GHC/Data/OsPath.hs
- compiler/GHC/Unit/External/Database.hs
- + testsuite/tests/driver/T27836/A.hs
- + testsuite/tests/driver/T27836/B.hs
- + testsuite/tests/driver/T27836/C.hs
- + testsuite/tests/driver/T27836/Makefile
- + testsuite/tests/driver/T27836/T27836.stdout
- + testsuite/tests/driver/T27836/all.T
- + testsuite/tests/driver/T27836/unitA
- + testsuite/tests/driver/T27836/unitB
- + testsuite/tests/driver/T27836/unitC
Changes:
=====================================
compiler/GHC/Data/OsPath.hs
=====================================
@@ -13,6 +13,7 @@ module GHC.Data.OsPath
, (<.>)
, splitSearchPath
, isRelative
+ , normalise
, dropTrailingPathSeparator
, takeDirectory
, isSuffixOf
=====================================
compiler/GHC/Unit/External/Database.hs
=====================================
@@ -53,9 +53,15 @@ import System.FilePath as FilePath
-- ExternalUnitDatabases
-- ----------------------------------------------------------------------------
+-- | A normalised 'OsPath' that we use as the key to cache 'UnitDatabase's.
+--
+newtype NormalisedUnitDbLoc = NormalisedUnitDbLoc OsPath
+ deriving (Eq, Ord)
+
-- | Caches unit databases in-memory.
data ExternalUnitDatabases unit = ExternalUnitDatabases
- { eud_cachedDatabases :: !(Map OsPath (UnitDatabase unit))
+ { eud_cachedDatabases :: !(Map NormalisedUnitDbLoc (UnitDatabase unit))
+ -- ^ The 'Map' is keyed by a normalised 'OsPath', ensuring
}
emptyExternalUnitDatabases :: ExternalUnitDatabases unit
@@ -67,18 +73,20 @@ emptyExternalUnitDatabases =
insertExternalUnitDatabases :: UnitDatabase unit -> ExternalUnitDatabases unit -> ExternalUnitDatabases unit
insertExternalUnitDatabases unit_db eud =
ExternalUnitDatabases
- { eud_cachedDatabases = Map.insert (unitDatabasePath unit_db) unit_db (eud_cachedDatabases eud)
+ { eud_cachedDatabases =
+ Map.insert (normalisedUnitDatabaseKey unit_db) unit_db (eud_cachedDatabases eud)
}
deleteExternalUnitDatabases :: OsPath -> ExternalUnitDatabases unit -> ExternalUnitDatabases unit
deleteExternalUnitDatabases unit_db_path eud =
ExternalUnitDatabases
- { eud_cachedDatabases = Map.delete unit_db_path (eud_cachedDatabases eud)
+ { eud_cachedDatabases =
+ Map.delete (normalisedUnitDatabaseKeyOsPath unit_db_path) (eud_cachedDatabases eud)
}
lookupExternalUnitDatabases :: OsPath -> ExternalUnitDatabases unit -> Maybe (UnitDatabase unit)
lookupExternalUnitDatabases key eud =
- Map.lookup key (eud_cachedDatabases eud)
+ Map.lookup (normalisedUnitDatabaseKeyOsPath key) (eud_cachedDatabases eud)
-- ----------------------------------------------------------------------------
-- UnitDatabase
@@ -87,12 +95,38 @@ lookupExternalUnitDatabases key eud =
-- | Unit database entry.
data UnitDatabase unit = UnitDatabase
{ unitDatabasePath :: OsPath
+ -- ^ On-disk location of the unit database.
+ --
, unitDatabaseUnits :: [GenUnitInfo unit]
+ -- ^ Units read from the database located at 'unitDatabasePath'.
+ --
+ -- These units have the @$pkgroot@ variable already resolved.
}
instance (Outputable u) => Outputable (UnitDatabase u) where
ppr (UnitDatabase fp _u) = text "DB:" <+> ppr fp
+normalisedUnitDatabaseKey :: UnitDatabase unit -> NormalisedUnitDbLoc
+normalisedUnitDatabaseKey unit_db = normalisedUnitDatabaseKeyOsPath $ unitDatabasePath unit_db
+
+-- | Normalise the unit database location and drop trailing path separators.
+--
+-- This ensures that two databases that point to the same location can be cached.
+--
+-- We need to remove trailing slash from the 'OsPath' before calculating @$pkgroot@ (Fix #16360)
+normalisedUnitDatabaseKeyOsPath :: OsPath -> NormalisedUnitDbLoc
+normalisedUnitDatabaseKeyOsPath db_path =
+ NormalisedUnitDbLoc $ OsPath.normalise $ OsPath.dropTrailingPathSeparator db_path
+
+-- | Get the root location of a unit database.
+-- Used to replace @$pkgroot@ variables in the 'GenericUnitInfo'.
+unitDatabaseRoot :: OsPath -> OsPath
+unitDatabaseRoot p =
+ let
+ NormalisedUnitDbLoc np = normalisedUnitDatabaseKeyOsPath p
+ in
+ OsPath.takeDirectory np
+
-- ----------------------------------------------------------------------------
--
-- Merging databases
@@ -258,14 +292,12 @@ readUnitDatabase logger cfg conf_file = do
"can't find a package database at " ++ show conf_file
let
- -- Fix #16360: remove trailing slash from conf_file before calculating pkgroot
- conf_file' = OsPath.dropTrailingPathSeparator conf_file
top_dir = OsPath.unsafeEncodeUtf (unitDbConfigGHCDir cfg)
- pkgroot = OsPath.takeDirectory conf_file'
+ pkgroot = unitDatabaseRoot conf_file
pkg_configs1 = map (mungeUnitInfo top_dir pkgroot . mapUnitInfo (\(UnitKey x) -> UnitId x) . mkUnitKeyInfo)
proto_pkg_configs
--
- return $ UnitDatabase conf_file' pkg_configs1
+ return $ UnitDatabase conf_file pkg_configs1
where
readDirStyleUnitInfo :: OsPath -> IO [DbUnitInfo]
readDirStyleUnitInfo conf_dir = do
=====================================
testsuite/tests/driver/T27836/A.hs
=====================================
@@ -0,0 +1,4 @@
+module A where
+
+a :: Int
+a = 1
=====================================
testsuite/tests/driver/T27836/B.hs
=====================================
@@ -0,0 +1,4 @@
+module B where
+
+b :: Int
+b = 1
=====================================
testsuite/tests/driver/T27836/C.hs
=====================================
@@ -0,0 +1,4 @@
+module C where
+
+b :: Int
+b = 1
=====================================
testsuite/tests/driver/T27836/Makefile
=====================================
@@ -0,0 +1,21 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+T27836:
+ mkdir local-unit-db/
+ cd local-unit-db/ && '$(GHC_PKG)' init my-test.conf
+ # All these unit database paths should resolve to the same location
+ # thus after the first time parsing it, we shouldn't parse it again.
+ '$(TEST_HC)' $(TEST_HC_OPTS) --make \
+ -unit @unitA -unit @unitB -unit @unitC \
+ -v2 -fforce-recomp 2>&1 \
+ | grep 'Using binary package database:'
+
+ echo ""
+ echo ""
+ echo "All -package-db variations:"
+ echo "- local-unit-db/my-test.conf"
+ echo "- ./local-unit-db/my-test.conf"
+ echo "- ./local-unit-db/my-test.conf/"
+ echo "Must be parsed only once."
=====================================
testsuite/tests/driver/T27836/T27836.stdout
=====================================
@@ -0,0 +1,8 @@
+Using binary package database: local-unit-db/my-test.conf/package.cache
+
+
+All -package-db variations:
+- local-unit-db/my-test.conf
+- ./local-unit-db/my-test.conf
+- ./local-unit-db/my-test.conf/
+Must be parsed only once.
=====================================
testsuite/tests/driver/T27836/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27836',
+ [ extra_files(['A.hs', 'B.hs', 'C.hs', 'unitA', 'unitB', 'unitC']), normalise_slashes
+ ],
+ makefile_test, ['T27836'])
=====================================
testsuite/tests/driver/T27836/unitA
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db local-unit-db/my-test.conf -package base -this-unit-id a-0.0.0 A
=====================================
testsuite/tests/driver/T27836/unitB
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db ./local-unit-db/my-test.conf -package base -this-unit-id b-0.0.0 B
=====================================
testsuite/tests/driver/T27836/unitC
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db ./local-unit-db/my-test.conf/ -package base -this-unit-id c-0.0.0 C
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bb48bf8e1b0c8d77e7272d628bbb31c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bb48bf8e1b0c8d77e7272d628bbb31c…
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/ani/hackage-test] 33 commits: rts: handle large CONSTR closures in compacting GC
by Apoorv Ingle (@ani) 28 Sep '26
by Apoorv Ingle (@ani) 28 Sep '26
28 Sep '26
Apoorv Ingle pushed to branch wip/ani/hackage-test at Glasgow Haskell Compiler / GHC
Commits:
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.
- - - - -
4e0c306a by Apoorv Ingle at 2026-09-28T09:08:01-06:00
Add new change.d file
- - - - -
98b36f4b by Apoorv Ingle at 2026-09-28T09:08:01-06:00
Edit 27156.md
- - - - -
215 changed files:
- .gitlab-ci.yml
- + changelog.d/25763-cmdline-opt
- + changelog.d/26610
- + changelog.d/27156.md
- + changelog.d/T27629
- + changelog.d/T27788-js-selector
- + changelog.d/boxing-family
- + changelog.d/clc-421
- + changelog.d/fix-compacting-gc-constr-27649
- + changelog.d/fix-control0-mask-trampoline
- + changelog.d/fix-threadlabel-segfault-27618
- + changelog.d/select-io-manager
- 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/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/CoreToStg.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.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/CmdLine.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/Compile.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Session.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/Utils.hs
- compiler/GHC/Iface/Decl.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Rename.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Stg/Lint.hs
- compiler/GHC/StgToJS/Apply.hs
- compiler/GHC/StgToJS/CodeGen.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/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/Solver/Default.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Types/CompleteMatch.hs
- compiler/GHC/Types/Id.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/TyThing.hs
- compiler/GHC/Types/TyThing/Ppr.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/State.hs
- compiler/ghc.cabal.in
- docs/users_guide/runtime_control.rst
- ghc/GHC/Driver/Session/Mode.hs
- hadrian/ghci-multi-cabal.in
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- + libraries/ghc-internal/src/GHC/Internal/Box.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/TypeNats.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
- linters/lint-codes/LintCodes/Static.hs
- rts/ContinuationOps.cmm
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/Prelude.h
- rts/PrimOps.cmm
- rts/RaiseAsync.c
- rts/StgMiscClosures.cmm
- rts/configure.ac
- rts/include/rts/Flags.h
- rts/include/rts/RtsToHsIface.h
- rts/include/rts/storage/Closures.h
- rts/include/stg/MiscClosures.h
- rts/include/stg/Prim.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- + rts/posix/SelectBis.c
- + rts/posix/SelectBis.h
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/prim/bitrev.c
- rts/prim/bswap.c
- rts/rts.cabal
- rts/sm/Compact.c
- rts/sm/Evac.c
- 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/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/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/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- + 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/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/T20718.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- + testsuite/tests/perf/compiler/T25763.stderr
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/primops/should_run/T27299.hs
- + testsuite/tests/primops/should_run/T27299.stdout
- testsuite/tests/primops/should_run/all.T
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.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/simplCore/should_compile/Makefile
- + 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/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/Transform.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- 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/7fa4a3d81213df71f5ba290393a93a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/7fa4a3d81213df71f5ba290393a93a…
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
28 Sep '26
Hannes Siebenhandl pushed new branch wip/fendor/27641 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/fendor/27641
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/27836] Fix #27836, normalise unit database path in ExternalUnitDatabases
by Hannes Siebenhandl (@fendor) 28 Sep '26
by Hannes Siebenhandl (@fendor) 28 Sep '26
28 Sep '26
Hannes Siebenhandl pushed to branch wip/fendor/27836 at Glasgow Haskell Compiler / GHC
Commits:
d15219a9 by Hannes Siebenhandl at 2026-09-28T11:11:18+02:00
Fix #27836, normalise unit database path in ExternalUnitDatabases
Add a regression test, proofing that all variations:
- local-unit-db/my-test.conf
- ./local-unit-db/my-test.conf
- ./local-unit-db/my-test.conf/
are parsed only once.
- - - - -
11 changed files:
- compiler/GHC/Data/OsPath.hs
- compiler/GHC/Unit/External/Database.hs
- + testsuite/tests/driver/T27836/A.hs
- + testsuite/tests/driver/T27836/B.hs
- + testsuite/tests/driver/T27836/C.hs
- + testsuite/tests/driver/T27836/Makefile
- + testsuite/tests/driver/T27836/T27836.stdout
- + testsuite/tests/driver/T27836/all.T
- + testsuite/tests/driver/T27836/unitA
- + testsuite/tests/driver/T27836/unitB
- + testsuite/tests/driver/T27836/unitC
Changes:
=====================================
compiler/GHC/Data/OsPath.hs
=====================================
@@ -13,6 +13,7 @@ module GHC.Data.OsPath
, (<.>)
, splitSearchPath
, isRelative
+ , normalise
, dropTrailingPathSeparator
, takeDirectory
, isSuffixOf
=====================================
compiler/GHC/Unit/External/Database.hs
=====================================
@@ -53,9 +53,15 @@ import System.FilePath as FilePath
-- ExternalUnitDatabases
-- ----------------------------------------------------------------------------
+-- | A normalised 'OsPath' that we use as the key to cache 'UnitDatabase's.
+--
+newtype NormalisedUnitDbLoc = NormalisedUnitDbLoc OsPath
+ deriving (Eq, Ord)
+
-- | Caches unit databases in-memory.
data ExternalUnitDatabases unit = ExternalUnitDatabases
- { eud_cachedDatabases :: !(Map OsPath (UnitDatabase unit))
+ { eud_cachedDatabases :: !(Map NormalisedUnitDbLoc (UnitDatabase unit))
+ -- ^ The 'Map' is keyed by a normalised 'OsPath', ensuring
}
emptyExternalUnitDatabases :: ExternalUnitDatabases unit
@@ -67,18 +73,20 @@ emptyExternalUnitDatabases =
insertExternalUnitDatabases :: UnitDatabase unit -> ExternalUnitDatabases unit -> ExternalUnitDatabases unit
insertExternalUnitDatabases unit_db eud =
ExternalUnitDatabases
- { eud_cachedDatabases = Map.insert (unitDatabasePath unit_db) unit_db (eud_cachedDatabases eud)
+ { eud_cachedDatabases =
+ Map.insert (normalisedUnitDatabaseKey unit_db) unit_db (eud_cachedDatabases eud)
}
deleteExternalUnitDatabases :: OsPath -> ExternalUnitDatabases unit -> ExternalUnitDatabases unit
deleteExternalUnitDatabases unit_db_path eud =
ExternalUnitDatabases
- { eud_cachedDatabases = Map.delete unit_db_path (eud_cachedDatabases eud)
+ { eud_cachedDatabases =
+ Map.delete (normalisedUnitDatabaseKeyOsPath unit_db_path) (eud_cachedDatabases eud)
}
lookupExternalUnitDatabases :: OsPath -> ExternalUnitDatabases unit -> Maybe (UnitDatabase unit)
lookupExternalUnitDatabases key eud =
- Map.lookup key (eud_cachedDatabases eud)
+ Map.lookup (normalisedUnitDatabaseKeyOsPath key) (eud_cachedDatabases eud)
-- ----------------------------------------------------------------------------
-- UnitDatabase
@@ -87,12 +95,38 @@ lookupExternalUnitDatabases key eud =
-- | Unit database entry.
data UnitDatabase unit = UnitDatabase
{ unitDatabasePath :: OsPath
+ -- ^ On-disk location of the unit database.
+ --
, unitDatabaseUnits :: [GenUnitInfo unit]
+ -- ^ Units read from the database located at 'unitDatabasePath'.
+ --
+ -- These units have the @$pkgroot@ variable already resolved.
}
instance (Outputable u) => Outputable (UnitDatabase u) where
ppr (UnitDatabase fp _u) = text "DB:" <+> ppr fp
+normalisedUnitDatabaseKey :: UnitDatabase unit -> NormalisedUnitDbLoc
+normalisedUnitDatabaseKey unit_db = normalisedUnitDatabaseKeyOsPath $ unitDatabasePath unit_db
+
+-- | Normalise the unit database location and drop trailing path separators.
+--
+-- This ensures that two databases that point to the same location can be cached.
+--
+-- We need to remove trailing slash from the 'OsPath' before calculating @$pkgroot@ (Fix #16360)
+normalisedUnitDatabaseKeyOsPath :: OsPath -> NormalisedUnitDbLoc
+normalisedUnitDatabaseKeyOsPath db_path =
+ NormalisedUnitDbLoc $ OsPath.normalise $ OsPath.dropTrailingPathSeparator db_path
+
+-- | Get the root location of a unit database.
+-- Used to replace @$pkgroot@ variables in the 'GenericUnitInfo'.
+unitDatabaseRoot :: OsPath -> OsPath
+unitDatabaseRoot p =
+ let
+ NormalisedUnitDbLoc np = normalisedUnitDatabaseKeyOsPath p
+ in
+ OsPath.takeDirectory np
+
-- ----------------------------------------------------------------------------
--
-- Merging databases
@@ -258,14 +292,12 @@ readUnitDatabase logger cfg conf_file = do
"can't find a package database at " ++ show conf_file
let
- -- Fix #16360: remove trailing slash from conf_file before calculating pkgroot
- conf_file' = OsPath.dropTrailingPathSeparator conf_file
top_dir = OsPath.unsafeEncodeUtf (unitDbConfigGHCDir cfg)
- pkgroot = OsPath.takeDirectory conf_file'
+ pkgroot = unitDatabaseRoot conf_file
pkg_configs1 = map (mungeUnitInfo top_dir pkgroot . mapUnitInfo (\(UnitKey x) -> UnitId x) . mkUnitKeyInfo)
proto_pkg_configs
--
- return $ UnitDatabase conf_file' pkg_configs1
+ return $ UnitDatabase conf_file pkg_configs1
where
readDirStyleUnitInfo :: OsPath -> IO [DbUnitInfo]
readDirStyleUnitInfo conf_dir = do
=====================================
testsuite/tests/driver/T27836/A.hs
=====================================
@@ -0,0 +1,4 @@
+module A where
+
+a :: Int
+a = 1
=====================================
testsuite/tests/driver/T27836/B.hs
=====================================
@@ -0,0 +1,4 @@
+module B where
+
+b :: Int
+b = 1
=====================================
testsuite/tests/driver/T27836/C.hs
=====================================
@@ -0,0 +1,4 @@
+module C where
+
+b :: Int
+b = 1
=====================================
testsuite/tests/driver/T27836/Makefile
=====================================
@@ -0,0 +1,21 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+T27836:
+ mkdir local-unit-db/
+ cd local-unit-db/ && '$(GHC_PKG)' init my-test.conf
+ # All these unit database paths should resolve to the same location
+ # thus after the first time parsing it, we shouldn't parse it again.
+ '$(TEST_HC)' $(TEST_HC_OPTS) --make \
+ -unit @unitA -unit @unitB -unit @unitC \
+ -v2 -fforce-recomp 2>&1 \
+ | grep 'Using binary package database:'
+
+ echo ""
+ echo ""
+ echo "All -package-db variations:"
+ echo "- local-unit-db/my-test.conf"
+ echo "- ./local-unit-db/my-test.conf"
+ echo "- ./local-unit-db/my-test.conf/"
+ echo "Must be parsed only once."
=====================================
testsuite/tests/driver/T27836/T27836.stdout
=====================================
@@ -0,0 +1,8 @@
+Using binary package database: local-unit-db/my-test.conf/package.cache
+
+
+All -package-db variations:
+- local-unit-db/my-test.conf
+- ./local-unit-db/my-test.conf
+- ./local-unit-db/my-test.conf/
+Must be parsed only once.
=====================================
testsuite/tests/driver/T27836/all.T
=====================================
@@ -0,0 +1 @@
+test('T27836', [extra_files(['A.hs', 'B.hs', 'C.hs', 'unitA', 'unitB', 'unitC'])], makefile_test, ['T27836'])
=====================================
testsuite/tests/driver/T27836/unitA
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db local-unit-db/my-test.conf -package base -this-unit-id a-0.0.0 A
=====================================
testsuite/tests/driver/T27836/unitB
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db ./local-unit-db/my-test.conf -package base -this-unit-id b-0.0.0 B
=====================================
testsuite/tests/driver/T27836/unitC
=====================================
@@ -0,0 +1 @@
+-hide-all-packages -no-user-package-db -package-db ./local-unit-db/my-test.conf/ -package base -this-unit-id c-0.0.0 C
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d15219a9f3cf6cfc143231c888a6acd…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d15219a9f3cf6cfc143231c888a6acd…
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/io-manager-deadlock-detection] 2 commits: Add tests for deadlock detection, issue #26408
by Duncan Coutts (@dcoutts) 28 Sep '26
by Duncan Coutts (@dcoutts) 28 Sep '26
28 Sep '26
Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC
Commits:
ba7a6066 by Duncan Coutts at 2026-09-28T09:52:17+01:00
Add tests for deadlock detection, issue #26408
We have three tests:
T26408a: make sure that we can detect a /partial deadlock/ that is not
also a /system deadlock/
T26408b: make sure that we can detect a /system deadlock/ promptly
without relying on idle GC
T26408c: make sure that we do not falsely detect a deadlock when there is
nothing to do except wait on signals
- - - - -
80e2ceee by Duncan Coutts at 2026-09-28T09:52:17+01:00
Update the user guide with the revised idle GC behaviour
It's now not just for the threaded RTS, but general.
Add a new section in the RTS chapter: Idle GC and deadlock detection.
Provide general documentation on both topics. In particular, document
the behaviour of deadlock detection and how it depends on idle GC.
Move the existing entries for -I and -Iw to this section.
Add a changelog entry.
- - - - -
11 changed files:
- + changelog.d/idle-gc-and-deadlock-detection
- docs/users_guide/profiling.rst
- docs/users_guide/runtime_control.rst
- testsuite/tests/rts/Makefile
- + testsuite/tests/rts/T26408a.hs
- + testsuite/tests/rts/T26408a.stderr
- + testsuite/tests/rts/T26408b.hs
- + testsuite/tests/rts/T26408b.stderr
- + testsuite/tests/rts/T26408c.hs
- + testsuite/tests/rts/T26408c.stdout
- testsuite/tests/rts/all.T
Changes:
=====================================
changelog.d/idle-gc-and-deadlock-detection
=====================================
@@ -0,0 +1,22 @@
+section: rts
+issues: #27040 #26408
+mrs: !14820
+synopsis: Idle GC and deadlock detection in the non-threaded RTS now behave the same as in the threaded RTS
+description: {
+Idle GC now works in the non-threaded RTS and is enabled by default.
+
+Deadlock detection in the non-threaded RTS now follows the same design and
+behaviour as in the threaded RTS. In particular, it can detect partial
+deadlocks where some but not all threads are deadlocked. The detection is no
+longer prompt however, as the detection runs as part of idle GC, which (by
+default) is performed 0.3 seconds after all capabilities become idle.
+
+The user guide now documents that deadlock detection relies on idle GC,
+and thus turning off idle GC disables deadlock detection. This is true
+of the threaded RTS in previous GHC releases but it was not previously
+documented behaviour.
+
+Internally, the idle GC and deadlock detection now follow the same design and
+use 99% the same code between the non-threaded and threaded RTS. Having a
+single design improves the comprehension and maintainability of the RTS code.
+}
=====================================
docs/users_guide/profiling.rst
=====================================
@@ -691,11 +691,13 @@ enclosed between ``+RTS ... -RTS`` as usual):
order to increase the resolution of the time profiler.
Using a value of zero disables the RTS clock completely, and has the
- effect of disabling timers that depend on it: the context switch
- timer and the heap profiling timer. Context switches will still
- happen, but deterministically and at a rate much faster than normal.
- Disabling the interval timer is useful for debugging, because it
- eliminates a source of non-determinism at runtime.
+ effect of disabling the timers and features that depend on it: the context
+ switch timer, the heap profiling timer, the eventlog flush timer, and the
+ idle GC timer. Context switches will still happen, but deterministically
+ and at a rate much faster than normal. Disabling the interval timer is
+ useful for debugging, because it eliminates a source of non-determinism at
+ runtime. Note however that disabling the idle GC timer not only disables
+ idle GC but also deadlock detection (see rts-flag:`-I`).
.. rts-flag:: -xc
=====================================
docs/users_guide/runtime_control.rst
=====================================
@@ -731,61 +731,6 @@ performance.
the default small ``-A`` value is suboptimal, as it can be in
programs that create large amounts of long-lived data.
-.. rts-flag:: -I ⟨seconds⟩
-
- :default: 0.3 seconds in the threaded runtime, 0 in the non-threaded runtime
-
- .. index::
- single: idle GC
-
- Set the amount of idle time which must pass before a idle GC is
- performed. Setting ``-I0`` disables the idle GC.
-
- In the threaded and SMP versions of the RTS (see :ghc-flag:`-threaded`,
- :ref:`options-linker`), a major GC is automatically performed if the
- runtime has been idle (no Haskell computation has been running) for a
- period of time.
-
- For an interactive application, it is probably a good idea to use
- the idle GC, because this will allow finalizers to run and
- deadlocked threads to be detected in the idle time when no Haskell
- computation is happening. Also, it will mean that a GC is less
- likely to happen when the application is busy, and so responsiveness
- may be improved. However, if the amount of live data in the heap is
- particularly large, then the idle GC can cause a significant delay,
- and too small an interval could adversely affect interactive
- responsiveness.
-
- The idle period timer only resets after some activity
- by a Haskell thread. If your program is doing literally nothing then
- after the first idle collection is triggered then no more future collections
- will be scheduled until more work is performed.
-
- This is an experimental feature, please let us know if it causes
- problems and/or could benefit from further tuning.
-
-.. rts-flag:: -Iw ⟨seconds⟩
-
- :default: 0 seconds
-
- .. index::
- single: idle GC
-
- Set the minimum wait time between runs of the idle GC.
-
- By default, if idle GC is enabled in the threaded runtime, a major
- GC will be performed every time the process goes idle for a
- sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`). For
- large server processes accepting regular but infrequent requests
- (e.g., once per second), an expensive, major GC may run after
- every request. As an alternative to shutting off idle GC entirely
- (with ``-I0``), a minimum wait time between idle GCs can be
- specified with this flag. For example, ``-Iw60`` will ensure that
- an idle GC runs at most once per minute.
-
- This is an experimental feature, please let us know if it causes
- problems and/or could benefit from further tuning.
-
.. rts-flag:: -ki ⟨size⟩
:default: 1k
@@ -1011,6 +956,116 @@ performance.
``GHC.Stats.getRTSStats`` from Haskell.
+Idle GC and deadlock detection
+------------------------------
+
+The RTS performs idle GC and deadlock detection. These features are more
+closely related and dependent than one might expect. In particular, deadlock
+detection relies on idle GC.
+
+The primary purpose of idle GC is to reclaim memory and return it to the system
+when the application has been idle for a short time. It also gives an
+opportunity for finalisers to run more promptly. See :rts-flag:`-I ⟨seconds⟩`.
+
+Deadlock detection is a debugging feature to try to discover when some or all
+Haskell threads are deadlocked. For the purpose of this explanation we define:
+
+ * a "partial deadlock" to be a set of threads that are deadlocked; and
+ * a "system deadlock" is when all threads are deadlocked.
+
+The deadlock detection is implemented by the GC, and it is capable of detecting
+partial deadlocks. If a deadlock is discovered, the RTS will throw an
+asynchronous exception to (some or all of) the blocked threads to enable
+progress and enable better reporting of the problem. In particular it throws
+the async exceptions ``BlockedIndefinitelyOnMVar`` or
+``BlockedIndefinitelyOnSTM``, as appropriate.
+
+The deadlock detection can be run during a major GC, but it is more expensive
+than a normal major GC so it is not run during normal major GCs. The deadlock
+detection mode of GC is run by the idle GC. This is on the basis that when the
+application is idle is when we are most likely to find a deadlock, and also
+that the extra time cost is unlikely to be a problem because the application is
+already idle.
+
+There are a few consequence of this:
+
+ * Deadlock detection relies on idle GC and will not run if idle GC is disabled
+ with ``-I0``.
+ * Deadlock detection will not run if the application remains busy, and thus
+ idle GC is not invoked.
+ * Deadlock detection is not prompt: it will only occur after the idle GC
+ delay, depending on both :rts-flag:`-I ⟨seconds⟩` and
+ :rts-flag:`-Iw ⟨seconds⟩`.
+
+In the non-threaded RTS there is additional behaviour: system deadlocks will be
+detected promptly, and this works even if idle GC is otherwise disabled.
+
+One cannot rely on deadlock detection as part of the normal program behaviour.
+It should only be used for debugging. Firstly, deadlock detection can be
+disabled or indefinately deferred due to activity. Secondly, the set of threads
+that deadlock detection throws exceptions to is not guaranteed to be minimal:
+a thread waiting on a deadlocked thread may also have an exception thrown to it
+even if it could make progress after the deadlocked thread is terminated.
+
+Deadlock detection can be invoked manually using ``performDeadlockDetection``
+from ``System.Mem.Experimental``.
+
+
+.. rts-flag:: -I ⟨seconds⟩
+
+ :default: 0.3 seconds
+
+ .. index::
+ single: idle GC
+
+ A major GC is automatically performed if the runtime has been idle (no
+ Haskell computation has been running) for a period of time. Set the amount
+ of idle time which must pass before a idle GC is performed.
+
+ Setting ``-I0`` disables the idle GC. This also has the unfortunate side
+ effect of disabling thread deadlock detection (the implementation of which
+ uses the idle GC).
+
+ For an interactive application, it is probably a good idea to use
+ the idle GC, because this will allow finalizers to run and
+ deadlocked threads to be detected in the idle time when no Haskell
+ computation is happening. Also, it will mean that a GC is less
+ likely to happen when the application is busy, and so responsiveness
+ may be improved. However, if the amount of live data in the heap is
+ particularly large, then the idle GC can cause a significant delay,
+ and too small an interval could adversely affect interactive
+ responsiveness.
+
+ The idle period timer only resets after some activity
+ by a Haskell thread. If your program is doing literally nothing then
+ after the first idle collection is triggered then no more future collections
+ will be scheduled until more work is performed.
+
+ Please let us know if it causes problems and/or could benefit from further
+ tuning.
+
+.. rts-flag:: -Iw ⟨seconds⟩
+
+ :default: 0 seconds
+
+ .. index::
+ single: idle GC
+
+ Set the minimum wait time between runs of the idle GC.
+
+ By default (and if idle GC is not disabled) a major
+ GC will be performed every time the process goes idle for a
+ sufficiently long duration (see :rts-flag:`-I ⟨seconds⟩`). For
+ large server processes accepting regular but infrequent requests
+ (e.g., once per second), an expensive, major GC may run after
+ every request. As an alternative to shutting off idle GC entirely
+ (with ``-I0``), a minimum wait time between idle GCs can be
+ specified with this flag. For example, ``-Iw60`` will ensure that
+ an idle GC runs at most once per minute.
+
+ This is an experimental feature, please let us know if it causes
+ problems and/or could benefit from further tuning.
+
.. _rts-options-statistics:
=====================================
testsuite/tests/rts/Makefile
=====================================
@@ -157,3 +157,11 @@ HSC2HS_OPTS = --cc="$(TEST_CC)" $(addprefix --cflag=,$(TEST_CC_OPTS)) --ld=$(TES
IOManager.hs: IOManager.hsc
'$(HSC2HS)' $(HSC2HS_OPTS) $<
+
+.PHONY: T26408c
+T26408c:
+ "$(TEST_HC)" $(TEST_HC_OPTS) -rtsopts T26408c.hs; \
+ ./T26408c +RTS -I0 -T & \
+ sleep 1; \
+ kill -s USR1 $$!; \
+ wait $$!
=====================================
testsuite/tests/rts/T26408a.hs
=====================================
@@ -0,0 +1,54 @@
+import Control.Concurrent
+import Control.Concurrent.STM
+import Control.Exception
+import Control.Monad
+
+-- | Test to make sure that we can detect a /partial deadlock/ that is /not/
+-- also a /system deadlock/: that is we can detect a set of deadlocked threads
+-- even when there are other unrelated threads that are waiting on I\/O or
+-- timeouts.
+--
+-- Historically this did not work in the non-threaded RTS which would only do
+-- deadlock detection if there was a system deadlock: no runnable threads /and/
+-- no pending I\/O or timers.
+--
+-- See Note [Deadlock detection]
+--
+main :: IO ()
+main = do
+
+ -- Set up two threads that are deadlocked on each other
+ aDone <- newTVarIO False
+ bDone <- newTVarIO False
+ let blockingThread theirDone ourDone =
+ atomically $ do
+ done <- readTVar theirDone
+ guard done
+ writeTVar ourDone True
+ _ <- forkIO (blockingThread bDone aDone)
+ _ <- forkIO (blockingThread aDone bDone)
+
+ -- Set up another thread that is blocked on a long timeout.
+ --
+ -- We use a timeout rather than I/O as it's more portable, whereas I/O waits
+ -- are different between posix and windows I/O managers.
+ --
+ -- One gotcha is that when the timeout completes then the deadlock will be
+ -- detected again (since the bug is about I/O or timeouts masking deadlock
+ -- detection). So for a reliable test the timeout used here must be longer
+ -- than the test framework's own timeout. So we use maxBound, and we adjust
+ -- the test framework's timeout to be short (see run_timeout_multiplier).
+ _ <- forkIO (threadDelay maxBound)
+
+ -- Wait on the two deadlocked threads to terminate. This is now a partial
+ -- deadlock but not a system deadlock (the main thread and the first two
+ -- threads are deadlocked, but the one waiting on the timeout is ok).
+ --
+ -- We now expect that the threads that are deadlocked are detected as such
+ -- and an exception is thrown to some or all of them.
+ --
+ -- Note that if this fails, the test itself will effectively deadlock and
+ -- will rely on the test framework's timeout.
+ atomically $ do
+ status <- mapM readTVar [aDone, bDone]
+ guard (or status)
=====================================
testsuite/tests/rts/T26408a.stderr
=====================================
@@ -0,0 +1,3 @@
+T26408a: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
+
+thread blocked indefinitely in an STM transaction
=====================================
testsuite/tests/rts/T26408b.hs
=====================================
@@ -0,0 +1,41 @@
+import Control.Concurrent
+import Control.Concurrent.STM
+import Control.Exception
+import Control.Monad
+
+-- | Test to make sure that we can detect a /system deadlock/ promptly which
+-- means without relying on idle GC. To check this we have to turn off idle GC,
+-- (+RTS -I0) which is what normally triggers deadlock detection.
+--
+-- This is only expected to work in the non-threaded RTS. In principle this
+-- test could also work with the threaded RTS when using only a single
+-- capability, if the I\/O managers can support it. Currently that is not the
+-- case.
+--
+-- See Note [Deadlock detection]
+--
+main :: IO ()
+main = do
+
+ -- Set up two threads that are deadlocked on each other
+ aDone <- newTVarIO False
+ bDone <- newTVarIO False
+ let blockingThread theirDone ourDone =
+ atomically $ do
+ done <- readTVar theirDone
+ guard done
+ writeTVar ourDone True
+ _ <- forkIO (blockingThread bDone aDone)
+ _ <- forkIO (blockingThread aDone bDone)
+
+ -- Wait on the two deadlocked threads to terminate. This is now a system
+ -- deadlock, involving all three threads (the main thread and the other two).
+ --
+ -- We now expect that the threads that are deadlocked are detected as such
+ -- and an exception is thrown to some or all of them.
+ --
+ -- Note that if this fails, the test itself will effectively deadlock and
+ -- will rely on the test framework's timeout.
+ atomically $ do
+ status <- mapM readTVar [aDone, bDone]
+ guard (or status)
=====================================
testsuite/tests/rts/T26408b.stderr
=====================================
@@ -0,0 +1,3 @@
+T26408b: Uncaught exception ghc-internal:GHC.Internal.IO.Exception.BlockedIndefinitelyOnSTM:
+
+thread blocked indefinitely in an STM transaction
=====================================
testsuite/tests/rts/T26408c.hs
=====================================
@@ -0,0 +1,61 @@
+import Control.Concurrent
+import Control.Concurrent.STM
+import Control.Exception
+import Control.Monad
+
+import System.Posix.Signals
+import GHC.Stats.Experimental
+
+-- | Test to make sure that we do not falsely detect a deadlock when there is
+-- nothing to do except wait on signals. This is to make sure that our prompt
+-- system deadlock detection is not too eager.
+--
+-- We arrange a partial deadlock that can be broken if a signal arrives and
+-- the signal handler can unblock things.
+--
+-- This should /not/ result in any deadlock being detected, and no other odd
+-- behaviour, like getting caught in a loop and doing too many idle GCs. So we
+-- count and report the number of major GCs.
+--
+main :: IO ()
+main = do
+
+ -- Set up two threads that are deadlocked on each other
+ aDone <- newTVarIO False
+ bDone <- newTVarIO False
+ let blockingThread theirDone ourDone =
+ atomically $ do
+ done <- readTVar theirDone
+ guard done
+ writeTVar ourDone True
+
+ installHandler userDefinedSignal1
+ (Catch $ do
+ atomically $ writeTVar aDone True
+ >> writeTVar bDone True
+ putStrLn "caught SIGUSR1")
+ Nothing
+
+ _ <- forkIO (blockingThread bDone aDone)
+ _ <- forkIO (blockingThread aDone bDone)
+ yield
+
+ -- Wait on the deadlocked threads to terminate. We now expect that the threads
+ -- that are deadlocked are detected as such and an exception is raised.
+ -- Note that if this fails, the test itself will effectively deadlock and
+ -- will rely on the test framework's timeout.
+ putStrLn "waiting on deadlocked threads..."
+ atomically $ do
+ status <- mapM readTVar [aDone, bDone]
+ guard (or status)
+
+ -- We expect exactly 1 major GC: the one triggered for deadlock detection
+ -- where we find no deadlock. We should then sleep without doing more GC.
+ -- An easy mistake would be looping: detecting probable deadlock, do idle GC.
+ -- See notifyIdleGcDeadlock().
+ stats <- getRTSStats
+ putStrLn ("terminated normally, major GCs = " ++ show (major_gcs stats))
+ -- This test _could_ be sensitive to changes in default heap sizes etc. If
+ -- this number ends up as 2 for example, then adjust the RTS opts (in the
+ -- makefile) so that the heap is big enough that normally no major GC would
+ -- be needed.
=====================================
testsuite/tests/rts/T26408c.stdout
=====================================
@@ -0,0 +1,5 @@
+[1 of 2] Compiling Main ( T26408c.hs, T26408c.o )
+[2 of 2] Linking T26408c
+waiting on deadlocked threads...
+caught SIGUSR1
+terminated normally, major GCs = 1
=====================================
testsuite/tests/rts/all.T
=====================================
@@ -682,6 +682,24 @@ test('T22859',
omit_ways(llvm_ways + ['nonmoving_thr', 'nonmoving_thr_sanity'])],
compile_and_run, ['-with-rtsopts -A8K'])
+# Deadlock detection testsL
+# A: check we can detect a partial deadlock that is not a system deadlock
+test('T26408a', [exit_code(1), run_timeout_multiplier(0.1)], compile_and_run, [''])
+# B: check we can detect system deadlock with idle GC turned off (+RTS -I0)
+test('T26408b', [omit_ways(threaded_ways), extra_run_opts('+RTS -I0 -RTS'),
+ exit_code(1), run_timeout_multiplier(0.1)],
+ compile_and_run, ['-rtsopts'])
+# C: check we do not falsely detect deadlock when blocked only on signals
+# Skip for win32, wasm and js since this test requires posix signals
+test('T26408c', [when(opsys('mingw32'), skip),
+ when(arch('wasm32'), skip), js_skip,
+ run_timeout_multiplier(0.1)
+ ], makefile_test, ['T26408c'])
+# TODO: change omit_ways(threaded_ways) above
+# to all_io_managers_except['mio', 'winio']
+# and test +RTS -N1 with threaded ways, but will be broken for now.
+
+
# These tests need access to the internal RTS headers.
# TODO: there is probably some cleaner way to do this, and it should probably
# be guarded for in-tree tests, since it cannot work against an arbitrary
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e5743b87c87d2a58b1e3c0c2551952…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e5743b87c87d2a58b1e3c0c2551952…
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