-
d07a5176
by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Remove the Eq and Ord instances for ForAllTyFlag
In Note [Comparing visibility] in GHC.Core.TyCo.Compare we read that
"we always compare ForAllTyFlags with `eqForAllVis` or `cmpForAllVis`"
But alas there was an Eq and Ord instance for ForAllTyFlag, which made it
impossible to check this claim. So I made these changes:
* Move the definition of ForAllTyFlag and its related functions from
L.H.S.Specificity (where it did not belong, since L.H.S didn't use it)
into GHC.Types.Var.
* Remove the Eq and Ord instances for ForAllTyFlag
* Deal with the fallout, by using `eqForAllVis` or related functions.
* In interface files we use IfaceType as a key for a de-deduplication map
See Note [Deduplication during iface binary serialisation]
in GHC.Iface.Binary. For this we need /exact/ comparison, so we use a
newtype wrapper for IfaceForAllTyFlag, and give use `cmpForAllTyFlagExactly`
to implement its Ord instance. Tiresome but easy.
* Rename `toIfaceForAllBndrs` to `toIfaceForAllTyBndrs`
-
ea5c054d
by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Refactor type and coercion substitution
This MR refactors type and coercion substitution so that they use
TyCoMapper, rather than hand-rolled code. Fundamentally it's a simple
idea, but I had to do quite a bit of refactoring to make it fly.
I got Claude to help me this this refactoring, but I paid careful
attention to every line of code.
Main changes:
* New module GHC.Core.TyCo.Make which defines TyCoMapper, and a bunch of other
simple "smart constructors" for types and coercions. See
GHC.Core.TyCo.Rep Note [The Type-related module hierarchy], esp (TRM1)
* The new TyCoMapper has new fields `tcm_tcapp_ty` and `tcm_tcapp_co` to handle
type-constructor applications. These replace `tcm_tycon`.
* Rewrite GHC.Core.TyCo.Subst to use TyCoMapper
* Rewrite GHC.Core.Type.expandTypeSynonyms to use TyCoMapper.
* Implement `seqType` and `seqCo` with TyCoFolder. To to this I had to add
a `tcf_lit` field to TyCoFolder.
* A new module GHC.Utils.StrictIdentity to define a strict identity monad,
which itself is used for
substTyCoMapper :: TyCoMapper Subst StrictIdentity
* I removed an apparently-unused instance for Binary Levity
Refactoring `seq` functions
* In GHC.Core.Seq, redefine `seqOneShot`, `seqRuleInfo`, and friends to return
`SeqResult`, a new type defined in GHC.Types.Baseic, rather than `()`. We
can define a Monoid instance for `SeqResult` that does the right thing.
* This has knock-on effects across the compiler.
-
34165e23
by Simon Peyton Jones at 2026-10-13T18:10:39-04:00
Refactor TyCoMapper to take a single monad; add StrictReaderT
TyCoMapper used to be parameterised over both an environment and a
monad, with fields like
tcm_tyvar :: env -> TyVar -> m Type
Now it is parameterised over just the monad:
tcm_tyvar :: TyVar -> m Type
If the mapping needs an environment, `m` is a reader monad over that
environment. Many uses (e.g. zonkTcTypeMapper, swizzleMapper) need no
environment at all, and used to pass a dummy (). The binder field
becomes
tcm_tycobinder :: forall r. TyCoVar -> ForAllTyFlag
-> (TyCoVar -> m r) -> m r
and runs the continuation in the extended environment. mapTyCoX is gone;
only mapTyCo remains.
To make this efficient, the reader monad must use the one-shot trick and
be strict in the environment. So:
* New module GHC.Utils.Monad.StrictReader defines
newtype StrictReaderT r m a
with hand-written instances. The builder of the pattern synonym
`StrictReaderT` is a smart constructor that adds both `oneShot` and a
bang on the environment, so every StrictReaderT computation is strict
in the environment. See Note [Instances for StrictReaderT].
* ZonkT becomes type ZonkT = StrictReaderT ZonkEnv
ZonkM becomes type ZonkM = StrictReaderT ZonkGblEnv IO
replacing their own hand-written instances.
* Substitution uses
type SubstM = StrictReaderT Subst StrictIdentity
substTyCoMapper :: TyCoMapper SubstM
and similarly expandTypeSynonymMapper.
* zonk_tycomapper :: TyCoMapper (ZonkT TcM) is now much simpler.
* swizzleMapper uses StrictIdentity rather than Identity.
* Module renaming, so that the strict monads are all peers in GHC.Utils.Monad:
GHC.Utils.StrictIdentity ==> GHC.Utils.Monad.StrictIdentity
GHC.Utils.Monad.State.Strict ==> GHC.Utils.Monad.StrictState
alongside the new GHC.Utils.Monad.StrictReader.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-
c0299e15
by Simon Jakobi at 2026-10-13T18:11:23-04:00
testsuite: Line-buffer the driver's stdout
Python block-buffers stdout when it is a pipe. This is the case for
example in CI. As a result there is often no indication of progress at
all until the run ends.
To fix this, we configure it explicitly to use line buffering instead.
stderr is reconfigured too, for consistency: Python line-buffers it by
default since 3.9.
The type-ignores are for mypy, which types these handles as TextIO โ an
interface without reconfigure.
Fixes #12934.
Assisted-by: Claude Fable 5
-
9b5beaa2
by Florian Ragwitz at 2026-10-13T18:12:05-04:00
hpc: Improve coverage semantics for instance methods and instances
Users care about which of their explicitly written methods are or aren't
covered, not whether all of the inherited methods are covered as well. Those
should be covered where they are implemented.
We therefore skip generating top-level boxes for inherited methods which will
appear as uncovered with no clear indication as to why, and no way to fix other
than exercising code the user has not written.
Previously, top-level boxes for those inherited methods would use the instance
head as their source span. Now that those boxes are gone, we can use that
source span for another purpose:
We introduce a new top-level box for each class instance with its instance head
as the source span, and use that to indicate whether or not an instance has
been covered at all.
We do this by making any method of the instance, inherited/generated or
otherwise, tick the box for the instance head. Excluded from this are
methodless classes, which could never be covered using the current mechanism.
This could be improved upon further.
These changes apply to ticking for program coverage only, and don't affect
source notes. Additionally, we update the scope of record selector ticking to
be limited to program coverage as well, as the oversight to also extend it to
source notes has previously caused bugs and because record selector ticking is
coverage-specific.
Fixes #17155, #15932.
-
836ec50a
by Simon Peyton Jones at 2026-10-13T18:12:54-04:00
Refactor fundeps again
This MR fixes #27828; there was a bad scoping error in `trim_qtvs`.
The main changes are
* Refactor `trim_qtvs` completely to eliminate the bug.
* Remove a redundant `apartnessCheck` from `mkTopOpenFamEqFDs`.
* Lots of improvements to the Notes in FunDeps.hs
I also did a couple of pure refactors:
* I realised that we were carefully caching `cab_incomps` in each
`CoAxBranch`, to make it more efficient to reduce a type family application,
but we were not doing the same thing when computing relevance for functional
dependencies; see Note [Relevance] in GHC.Tc.Solver.FunDeps. So I added
`cab_overlaps`, which is now used in `noEarlierMatch`.
* Use a proper data types, `AmIUnifying` and `OuterKindsFlag`, for two
previously-Boolean arguments to `tc_unify_tys`. This is a pure refactor.
-
889ef371
by Simon Peyton Jones at 2026-10-13T18:13:36-04: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.
* `kickOutAfterFillingCoercionHole` should account for equalities with a
TyFamLHS. See (KOC5) in Note [Kick out after filling a coercion hole]
* No need to gather coercion holes in the `PuOK` case of `canEqCanLHSFinish_no_unification`.
See documentation with that code.
-
f9193e53
by Simon Peyton Jones at 2026-10-13T18:13:37-04:00
One line fix to fallout from
...#27731, !16564, simplifying Tc.Errors machinery
There was an over-eager assert!
-
cdd56b7a
by Florian Ragwitz at 2026-10-13T18:14:19-04:00
Fix several sphinx errors and warnings from rendering the release notes
The extension references which are removed here are for extension which were
already previously undocumented, so just removing them seems fine for now.
-
4f00da25
by Florian Ragwitz at 2026-10-13T18:14:19-04:00
Link to QualifiedStrings documentation
-
97adaf89
by Simon Peyton Jones at 2026-10-14T00:07:27+01:00
Line up simpleUnifyCheck and check_ty_eq_rhs
Even after #26543 was allegedly fixed, the original repo case in the
Description continued to fail. The reason was that the QuickLook
unifier, `qlUnify`, used `simpleUnifyCheck` for checking unification
invariants; and `simpleUnifyCheck` conservatively rejected any RHS
with a coercion hole in it. The upshot was that QuickLook was not
as clever as it should be, wrongly failing to do an impredicative
instantiation.
Interestingly `check_ty_eq_rhs`, which does the same job during
constraint solving, was more liberal: it just looked at the free vars
of the coercion, and allowed coercion holes.
This MR lines them up, adding some careful notes. See
Note [simpleUnifyCheck]
Note [Occurs check and coercions]
Note [Unification preconditions] esp (COERCIONS)
Things are better than before, but I am still uncomfortable about the
possibilty that a hole might be filled with a coercion that mentions
the LHS type variable; for now I have left this discomfort documented
in Note [Occurs check and coercions].
Main changes
* `qlUnify` now only unifies /instantiation variables/ not regular unification
variables. This makes QuickLook more predictable. See (UQL6) in
Note [QuickLook unification].
* `qlUnify` no longer needs to call `simpleUnifyCheck`, so we can get rid of
`UC_QuickLook` and its code in `simpleUnifyCheck`.
* I found I could get rid of `mkOccFolders` in favour of `anyFreeVarsOfType`.
Hooray.
* Improve documentation of CycleBreakerTv, and rename it to GivenCycleBreakerTv
because they are only uesd for Givens!
* I accepted a 9% worsening in compile time allocation on T5030
because the newly-consistent story triggered #27934.
Other changes
* I refactored `atvFolder` so that it pairs the "interesting-var" function
with the in-scope set, to avoid building a closure with a free variable.
Now we get nice tight loops for `anyFreeVarsOfType` and friends.
See Note [Any-free-var folder].
Metric Increase:
T5030
-
5f7b3047
by Simon Peyton Jones at 2026-10-14T00:07:27+01:00
Fix order-dependence in QuickLook
Ticket #27917 pointed out that QuickLook was order-dependent
-- very bad!
The solution is in `qlUnify`: spot when we are unifying a type with a
definite monotype, and then monomorphise any free instantiation
variables of that type.
See (UQL8) and (UQL9) in Note [QuickLook unification].
-
c391c70c
by Simon Peyton Jones at 2026-10-14T00:07:28+01:00
Add some comments about RuntimeUnk
This commit just adds comments explaining why we don't need to worry about
RuntimeUnkTv in the constraint solver.
It also tightens up docmentation of earlier (overlapping) commits in
this MR.
-
7df3ba2f
by Andrea Vezzosi at 2026-10-14T06:15:48-04:00
add runGhcWithSignalHandlers and remove handling from runGhc[T]
it only ever makes sense to call `withSignalHandlers` from `main`, otherwise
signals can be too easily swallowed without notification to the rest of your
process.
This commit takes withSignalHandlers out of `runGhc/runGhcT` and provides
`runGhcWithSignalHandlers` for the old behaviour.
-
3a72b8a2
by Hannes Siebenhandl at 2026-10-14T13:48:32-04: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.
-
8ddbc6f9
by Florian Ragwitz at 2026-10-14T13:49:28-04:00
Remove obsolete HPC types
Fixes #27946.
-
f34c79ee
by Alan Zimmerman at 2026-10-15T04:54:47-04:00
EPA: Start using vocurly as basis for AnnListLayout
For the existing cases that populate AnnList.
This uses AnnListBraces when braces are explicitly supplied, and
AnnListLayout when vocurly is used
The change also shows up AnnList usages that can never capture layout,
these are cleared up later.
-
033ba59c
by sheaf at 2026-10-15T04:55:44-04:00
Make exprFreeVars deep
exprFreeVars used to be a shallow free-variable finder (it didn't look
into kinds), but most callers wanted it to be deep. In particular,
'exprFreeVars' is often used to seed an 'InScopeSet', and that must
contain the /deep/ free variables.
This commit makes it deep, which allows us to get rid of calls to
'closeOverKinds' or other workarounds for shallowness in:
- 'GHC.Core.Opt.Simplify.Utils.abstractFloats'
- 'GHC.HsToCore.Binds.dsSpec_help'
- 'GHC.Core.Opt.Arity.etaBodyForJoinPoint'
- 'GHC.Core.FVs.freeVars'
- 'GHC.Core.Lint.lintCoreRule'
We also remove 'deepExprsFreeVarsDSet' and 'closeOverKindsDSet'.
Other places needed a little bit more care, in particular to properly
handle coercion variables free in kinds:
- 'GHC.Core.Opt.Specialise.specHeader' quantifies over the binder of
an unspecialised argument directly, instead of computing the free Ids
of the argument.
- 'GHC.HsToCore.dsWarnRuleShadowing' filters out 'CoVar's.
- 'GHC.Core.Opt.SpecConstr.callToPat' had some ad-hoc logic to compute
argument pattern types and a workaround with 'expandTypeSynonyms'.
That logic is removed in favour of a new 'argsToPats' function which
directly computes the argument pattern types by instantiating the
type of the head of the call pattern.
Fixes #27936
-
f93d523c
by Luite Stegeman at 2026-10-15T04:56:34-04:00
testsuite: Use mkWeakMVar in memo001/memo002
mkWeakPtr in the memo001/memo001 tests created weak pointers
with the MVar box as the key, which is fragile in the presence
of optimization. The fix is using mkWeakMVar, keying on the
primitive MVar# instead.
Fixes #27924 and #27396
-
8c947573
by Simon Peyton Jones at 2026-10-16T13:11:53+01:00
Improve exprOkForSpeculation
Fixes #27910, which exposed an infelicity in exprOkForSpeculation
The new story is carefully described in new Note [exprOkForSpeculation],
esp (a-c) and (EOKS1).
-
7dedcf13
by Alan Zimmerman at 2026-10-16T19:03:17-04:00
EPA: Add TTG fields to FamilyInfo
This commit is a simple add, with unused extension fields.
It lays the groundwork for using them for exact print annotations
-
26b9a8a0
by Arkissa at 2026-10-17T16:04:10+00:00
ghci: Prefer exact source spans in :type-at
Prefer expression types for identical source spans.
Fixes #27412.
-
70dc4e61
by sheaf at 2026-10-18T18:15:15-04:00
Carefully implement coercion zapping
This commit implements coercion zapping in the rewriter, controlled by
the -fzap-coercions flag (on by default). This significantly reduces
the size of coercions produced when rewriting type family applications,
resulting in much faster compilation. See Note [Coercion zapping]
in GHC.Tc.Solver.Rewrite for an explanation and further details.
The rewriter now keeps track of the `CoVar`s and `CoercionHole`s it uses
when rewriting, which allows computing the dependencies of a zapped
coercion without traversing the coercion the rewriter produced, as
explained in Note [Computing coercion dependencies] in GHC.Tc.Solver.Rewrite.
Accompanying changes:
- Rework how dependencies of 'UnivCo's are tracked, purely for
performance reasons.
We now use the 'UnivCoDeps' datatype instead of '[Coercion]', and
we now insist that 'uco_deps' also contains the CoVars that appear
in the LHS and RHS types of the UnivCo (as a caching mechanism).
- Several performance improvements to the rewriter (e.g. increased
strictness).
- Improvements to the family application cache, including:
- Better documentation.
- Only cache a whole chain of reduction steps.
- Don't put coercion holes in the cache, fixing #27518.
- Allow Core Lint to be called from within the typechecker (and from
within the coercion optimiser).
- Move out "annotation linting" from Core Lint into the general Core
pipeline, to avoid the dependency GHC.Core.Coercion.Opt->GHC.Core.Lint
causing a regression in CountDepsParser due to transitive
dependencies of GHC.Core.Opt.Monad and GHC.Unit.Module.ModGuts.
Fixes #8095 #27336 #27518
Summary of performance numbers
Compile-time byte allocated in GHC's testsuite (0 regressions)
CoOpt_Singletons(normal) ghc/alloc 634,040,332 595,448,728 -6.1% GOOD
FamAppCachePerf(normal) ghc/alloc 495,629,812 191,612,920 -61.3% GOOD
LargeRecord(normal) ghc/alloc 1,226,806,434 966,260,976 -21.2% GOOD
SimplCastPerf(normal) ghc/alloc 160,505,678 148,112,688 -7.7% GOOD
T12227(normal) ghc/alloc 244,668,982 233,037,216 -4.8% GOOD
T12545(normal) ghc/alloc 697,804,842 424,705,152 -39.1% GOOD
T13386(normal) ghc/alloc 422,871,076 35,213,656 -91.7% GOOD
T15703(normal) ghc/alloc 288,402,364 273,692,560 -5.1% GOOD
T26426(normal) ghc/alloc 86,002,052 78,152,104 -9.1% GOOD
T27336(normal) ghc/alloc 498,932,268 35,886,016 -92.8% GOOD
T5030(normal) ghc/alloc 117,057,706 88,785,648 -24.2% GOOD
T5321Fun(normal) ghc/alloc 252,552,628 243,820,176 -3.5% GOOD
T8095(normal) ghc/alloc 768,198,564 127,779,520 -83.4% GOOD
T9872a(normal) ghc/alloc 1,655,675,020 1,406,017,008 -15.1% GOOD
T9872b(normal) ghc/alloc 1,864,180,628 1,644,706,696 -11.8% GOOD
T9872b_defer(normal) ghc/alloc 2,834,585,468 1,657,729,760 -41.5% GOOD
T9872c(normal) ghc/alloc 1,572,573,116 1,354,055,424 -13.9% GOOD
T9872d(normal) ghc/alloc 351,516,004 331,071,424 -5.8% GOOD
TcPlugin_RewritePerf(normal) ghc/alloc 2,063,065,680 1,847,464,272 -10.5% GOOD
Chesskell (github.com/Proxymoron461/chesskell/issues)
Typechecking that the first N plies of kholmovVsBronsteinShort
are a valid chess game:
Plies | GHC 9.14 time/alloc | This commit time/alloc |
-----:|---------------------|------------------------|
2 | 41s/20GB | 0.2s/19MB |
4 | 95s/49GB | 0.3s/25MB |
6 | timed out | 0.6s/522MB |
8 | timed out | 7s/673MB |
10 | timed out | timed out |
Compilation of singletons-base: compile time -28%, allocations -25%
-------------------------
Metric Decrease:
CoOpt_Singletons
FamAppCachePerf
LargeRecord
SimplCastPerf
T12227
T12545
T13386
T15703
T24984
T26426
T27336
T5030
T5321Fun
T8095
T9872a
T9872b
T9872b_defer
T9872c
T9872d
TcPlugin_RewritePerf
-------------------------
Co-Authored-By: Sylvain Henry <sylvain@haskus.fr>
-
3d326d7d
by Sylvain Henry at 2026-10-18T18:16:09-04:00
JS: fix stack corruption in h$ap_gen_fast's pushAllRegs (#27624)
The loop added in 9e46990976 ("JS: support more than 128 registers
(#26558)") pushed register N to stack[sp - N - 2] while the switch it
falls through into, and every reader of the h$ap_gen frame, uses
stack[sp - (N - 2)]. Registers >= R32 were therefore written 4 slots
too low: the slots the readers consult were left stale and the frame
below was silently corrupted.
Fix the loop to use the same slot formula as the switch, and add a
regression test: an unknown call to a thunk with 35 argument register
slots, which goes through the generic apply.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-
6bbca569
by Andreas Klebinger at 2026-10-21T10:01:10+00:00
Fix a few tag inference bugs.
* Treat bottoming functions and nullary join points as functions.
* When generating bytecode don't rely on imported tag sigs.
Fixes #27831, #27841 and #27842.
-
71808e29
by Sylvain Henry at 2026-10-22T04:50:24-04:00
Add GHC.Internal.Unboxed.Do: do-notation for unboxed values (#27944)
QualifiedDo operators can be representation-polymorphic: a do-block is
expanded into ordinary applications before typechecking, and the only
check on a bound variable is the deferred fixed-runtime-rep check. A
representation-polymorphic (>>=) cannot be defined directly, though,
because its body would bind a variable of unknown representation.
The new module provides such operators for State#-passing code as
methods of a class indexed by RuntimeRep, with one instance per
representation. With optimisation the dictionaries disappear and a
block compiles to the usual nested case expressions:
Ubx.do { x <- readWordArray# mba 0# -- x :: Word#
; writeWordArray# mba 1# x -- unit actions are statements
; Ubx.pure x }
Also add users guide documentation and tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
33247780
by Sylvain Henry at 2026-10-22T04:50:24-04:00
Bignum: use Ubx.do for State#-passing code (#27944)
Rewrite the State#-passing code of the GHC.Internal.Bignum modules
(Primitives, WordArray, BigNat, Natural, Integer, Backend.Native,
Backend.GMP) with the qualified do-notation of GHC.Internal.Unboxed.Do.
Nested case chains such as
case newWordArray# sz s of { (# s, mwa #) ->
case act mwa s of { s ->
unsafeFreezeByteArray# mwa s }}
become
Ubx.do
mwa <- newWordArray# sz
act mwa
unsafeFreezeByteArray# mwa
The optimised Core is unchanged (compared per top-level binding).
BigNat now enables MonoLocalBinds: local loops written with Ubx.do
must stay monomorphic in the state type, otherwise GHC does not turn
them into join points.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
593f5f5b
by Sylvain Henry at 2026-10-22T04:51:16-04:00
JS: remove archive padding after wasm objects (#25665)
On Darwin hosts, emar (llvm-ar) writes archives in the Darwin format when
the first member is not a known object file, which is the case for the
archives of the JS backend. This format pads each member to 8 bytes with
'\n' and includes the padding in the member size. Wasm objects do not
accept trailing bytes: wasm-ld failed with "section too large".
The JS linker now removes this padding when it extracts a wasm object from
an archive. In addition, Hadrian passes --format=gnu to emar for the JS
target. See Note [Wasm objects in Darwin archives].
Also document the format of Cc (wasm) objects in Note [Cc object format].
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-
13dff5d0
by L0neGamer at 2026-10-22T21:37:31-04:00
add options for resizing bytearrays (#27244)
add five mutable bytearray resizing operations
for various kinds of pinnedness and aligning.
- prefer unpinned when allocating new array (``resizeMutableByteArrayPreferUnpinned#``; the ``resizeMutableByteArray#`` default)
- prefer pinned when allocating new array (``resizeMutableByteArrayPreferPinned#``)
- prefer aligned and pinned when allocating new array (``resizeMutableByteArrayPreferAlignedPinned#``)
- prefer same as input bytearray when allocating new array (``resizeMutableByteArrayKeepPinnedness#``)
- ensure the output array is aligned and pinned after operation (``resizeMutableByteArrayMakeAlignedPinned#``)
-
0c17201c
by Sylvain Henry at 2026-10-22T21:38:23-04:00
JS: implement performMinorGC and performBlockingMajorGC (#28012)
GHC.Internal.System.Mem imports three C entry points: performMajorGC,
performGC (for performMinorGC) and performBlockingMajorGC. The JS RTS
only defined h$performMajorGC, so calling performMinorGC or
performBlockingMajorGC threw "ReferenceError: h$performGC is not
defined" at runtime.
The JS RTS has a single non-generational collector, so both missing
entry points are now aliases of h$performMajorGC.
Add a test that calls every GC entry point of System.Mem.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
-
7a631ff2
by L0neGamer at 2026-10-23T09:11:39-04:00
Mon(a|oi)d of No (Return|Mappend) progress (!15795)
Make non-canonical definitions of return and mappend
non-suppressible errors, and make canonical definitions
of those methods warnings.
CLC proposal: https://github.com/haskell/core-libraries-committee/issues/328
-
14656fd4
by Simon Jakobi at 2026-10-23T09:12:27-04:00
Cmm: keep the CBE substitution idempotent
When blocks are merged over multiple rounds during common block
elimination, a block that won a merge in an earlier round can still
lose a merge in a later round. The substitution map (Subst) used to
record this as a chain: the earlier loser mapped to a block that was
itself eliminated. In PLT terminology, the substitution became
*non-idempotent*. Applying such a substitution with a single lookup
left edges to unreachable blocks in the Cmm graph, eventually leading
to a setInfoTableStackMap panic (#27368).
With this patch, each round's fresh substitutions are applied to the old
ones as they are added (extendSubst), so every eliminated block maps
directly to its final replacement and the substitution stays idempotent.
Fixes #27368.
Assisted-by: Claude Fable 5.1
-
92848e61
by Evgeny Malyshev at 2026-10-23T20:26:04-04:00
hadrian: Include transitive dependencies in Haddock builds
Haddock needs indirect dependency interfaces to resolve references to
definitions outside a package's direct dependencies. Expose Cabal's
configured dependency closure through ContextData and use it alongside
the direct list, preserving direct-interface precedence.
Bump Shake's cache version for the serialized oracle change. Add a
Hadrian selftest requiring template-haskell's indirect ghc-prim interface.
Fixes #25850.
Fixes #27786.
Assisted-By: OpenAI Codex
Metric Increase:
haddock.Cabal
-
5e8ae81b
by Zubin Duggal at 2026-10-23T20:26:47-04:00
WinIO: Don't confuse Win32 errors and NTSTATUS codes
WinIO misinterpreted NTSTATUS codes as Win32 errors, and had some ad hoc logic to deal with this.
However, this logic was incomplete and resulted in the mishandling of results of certain I/O operations
(see testcase).
Now the `ErrCode` argument in `CompletionCallback` is always the Win32 error code,
and we convert the NTSTATUS using RtlNtStatusToDosError.
Also when an overlapped/async operation such as ReadFile fails immediately,
with a Win32 error such as ERROR_BROKEN_PIPE, we don't get a further completion
notification, so we never recorded the failure. This worked by coincidence
earlier, because `STATUS_PENDING` happened to have the same numerical code as
`ERROR_NO_MORE_ITEMS`, but it doesn't now that we translate `STATUS_PENDING`
into the correct Win32 error code (`ERROR_IO_PENDING`). Fix this by using
the Win32 error value we obtained when an operation returns `STATUS_PENDING`.
Fixes #27971
-
c42ed66a
by Martijn Bastiaan at 2026-10-24T16:00:42+02:00
Add single-threaded test for #27526 + #27123
Issue #27526 is a duplicate of #27123, which has already been fixed in
HEAD. The proposed patch in the former carries a "nicer" regression test
in that it doesn't rely on multi-threading to trigger.
Closes #27526
-
7f73f515
by Simon Peyton Jones at 2026-10-24T17:29:19-04:00
Refactor QuickLook a bit
This MR simply refactors QuickLook a bit. In particular, it
simplifies `tcApp`, and moves shared code into `finishApp`.
It also adds `Note [QuickLook: arguments before result]`
responding to #26599.
There should be no user-visible changes, except perhaps in error messages,
which improve marginally.
For some reason T24984 compiles 8% faster. I'll take the win!
Metric Decrease:
T24984
-
a4a3a964
by Alan Zimmerman at 2026-10-24T17:30:03-04:00
EPA: Use AnnList for XClosedTypeFamily
We now have TTG annotations for FamilyInfo.
Make use of it to store the AnnList annotations for a closed type
family.
-
953e4c7f
by sheaf at 2026-10-26T18:19:10-04:00
Rework LogQueue to be a proper queue
Now 'LogQueue' uses a 'TQueue' with a separate 'TVar' to indicate
whether the log queue is finished logging, instead of the previous weird
data representation using a reverse-order '[Maybe Msg]' with 'Nothing'
denoting the end of the queue.
-
ec29c5fa
by Rodrigo Mesquita at 2026-10-26T18:19:10-04:00
Refactor GHC.Driver.MakeAction
Pull out of 'runParPipelines' the logic for creating a 'MakeEnv' ready
to be used by multiple threads, as that will be useful for downsweep as
well (which doesn't fit the 'runPipelines' flow), rather than being just
for upsweep.
The code is moved and re-structured to match the export list, simplify
the sequentiality checks previously both in 'runPipelines' and
'runAllPipelines', which were weirdly similar and confusing; into the
two part step where we need these checks: (1) to construct the MakeEnv,
(2) to run the MakeActions in parallel. These two steps are separate and
used to be too mixed up.
Some additional little simplifications or clean ups here and there.
-
3fb22073
by Rodrigo Mesquita at 2026-10-26T18:19:11-04:00
Refactor GHC.Driver.Downsweep in preparation for parallel downsweep
Pure refactor to improve the code to facilitate implementing parallel
downsweep in the next commit.
This commit puts a MakeEnv into the DownsweepEnv, gives the fields
proper names and uses RecordWildcards to simplify, rather than passing
around all diagnostic wrappers, driver-message-things and using 10s of
positional fields.
No behavior changes here!
-
52f385a7
by sheaf at 2026-10-26T18:19:11-04:00
Driver: enableCodeGenForTH in load', not downsweep
enableCodeGenForTH modifies the module graph for planning, creating
fresh temporary paths. So it should only be run when we are about to
execute a build plan (e.g. in load'), so that consumers that want to
know facts about the module graph don't go looking for files that don't
exist yet (in particular 'haddock --no-compilation').
-
82402d14
by sheaf at 2026-10-26T18:19:11-04:00
2-phase Cache/Search Finder monad
This commit restructures the finder abstraction by introducing the
'FinderM' monad, which splits module lookup operations into two phases:
- a cache-only phase, performing no filesystem access,
- from the first cache miss onwards, a search action which may access
the filesystem.
This allows consumers to distinguish between quick cached results versus
more expensive filesystem search operations.
-
5c302b40
by sheaf at 2026-10-26T18:19:11-04:00
Separate Home/External finder caches
The caches for home modules and modules from external units have
different lifetimes, needing to be invalidated at different points.
This commit moves the cache for modules from external units into the
'UnitState' type, which keeps the cache correct whenever package flags
or package databases change (setSessionDynFlags, :set -package ...,
Backpack's addUnit).
This also makes it easier to clear the cache of home modules, as they
are now stored separately (obviating the 'isUnitEnvInstalledModule'
check we used to perform).
-
605fbb96
by sheaf at 2026-10-26T18:19:11-04:00
Add known home modules to the finder cache
The finder looks in the file system for the on-disk source that
corresponds to a Haskell module name. There are two kinds of modules
where we need to bypass this search:
- a file passed directly in the command line, which may not be in any
search path and whose module name may differ from the file name;
- Backpack signatures and modules, which do not exist as files at all.
This commit handles these two situations by adding an immutable
'KnownHomeModules' in the finder cache, which is always consulted first
before doing any search.
This removes the ad-hoc imperative logic which relied on mutating the
finder cache at careful points.
'setModuleGraph' now also takes the known home modules, so that the
module graph and the known home modules are always set consistently.
See Note [Known home modules] in GHC.Unit.Finder.Types.
-
82718f44
by sheaf at 2026-10-26T18:19:11-04:00
Make 'UnresolvedImport' into a suitable cache key
The goal of this commit is to make 'UnresolvedImport' suitable for use
as a cache key. Specifically, during downsweep we want to be able to
cache module resolution queries. We want to do this using some kind of
map, e.g. (roughly speaking):
Map UnresolvedImport ResolvedImport
For this to work, 'UnresolvedImport' must contain **precisely** the
input data to each resolution computation. In other words:
1. If something changes that materially affects resolution, it must
be stored in the cache key.
2. If changing some input does not materially affect the outcome,
then it must not be stored in the cache key.
To satisfy (1), we change 'UnitNode' to also keep track of the home
unit in whose unit state the unit's dependencies were looked up.
For (2), 'ImportResolution' now stores a 'ModuleLookupScope' instead of an
'UnresolvedImportOrigin'. Plugin imports are expressed with a new
'LookupPlugin' scope. The source location of an import, which is
immaterial to the resolution, is stored out of band outside
'UnresolvedImport', in 'ModSummary'.
-
d4635c7c
by sheaf at 2026-10-26T18:19:11-04:00
Add GHC.Data.Dependent
This commit adds a minimal implementation of dependent sums
('Some', 'DSum') and dependent maps ('DMap').
The implementation is not entirely performance optimal, as there are a
few extraneous allocations compared to the full-blown implementation in
the 'dependent-map' package. For our use case (caching downsweep
computations), this does not matter much.
See GHC.Data.Dependent.
-
6759533e
by sheaf at 2026-10-26T18:19:11-04:00
Driver: structured concurrent worker abstraction
This commit introduces a structured concurrency framework in the style
of the 'ki' library: a collection of threads within a scope.
We implement two kinds of concurrent workers on top of this framework:
- Independent workers cannot wait for one another at all. The only
scheduling operation is to wait for quiescence.
- Coordinating workers declare an STM readiness condition (waiting on
other workers to complete) which gates their start.
See Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
This commit ports upsweep to this new framework, with downsweep being
left as subsequent work.
Further changes along the way:
- Refactoring of how concurrency is acquired to avoid the footgun of
trying to use a no-op 'AbstractSem' as a lock in the serial case.
- The "re-run with -j1" logic for semaphore opening errors no longer
triggers on late semaphore failures (part-way through a lengthy
computation).
- Logger threads are properly cleaned up on exception, with each
concurrent worker's log queue and local TmpFs properly bracketed.
- The 'GhcMessage -> AnyGhcDiagnostic' and 'Maybe Messager'
arguments of 'depanalE', 'depanalPartial' and 'downsweep', which
were all dead in practice, have been dropped.
- Concurrent logging has been completely overhauled. Each worker has
its own log queue. Workers whose logs are printed as we go are
spawned in the order in which their logs must be printed, now using
a TQueue instead of the old complicated LogQueueQueue mechanism.
'logThread' is replaced by 'withLogPrinter', which runs the thread
that prints the log queues within a concurrent worker scope.
These changes allow us to fix issues with the exception handling:
we now always flush remaining log queues before exiting (even when
the action throws an exception), and exceptions that arise while
printing are properly rethrown. This fixes #28009.
-
38a99006
by sheaf at 2026-10-26T18:19:11-04:00
Rule-based deterministic concurrent downsweep
This commit rewrites downsweep as a single query-answering rule
(see 'DownsweepRule') that can be executed by concurrent worker threads.
The design allows every expensive operation (preprocessing files with CPP,
parsing headers, reading interfaces) to be performed concurrently
according to the -j<N>/-jsem flags.
See Note [Rules-based downsweep] in GHC.Driver.Downsweep.
The rules are run by "GHC.Driver.Concurrency.runRules" in a demand-driven
way: each worker can demand other workers to run, but never wait on
another worker. Every piece of work is done at most once.
Fixes #27514
Memory usage can increase due to doing more work concurrently:
-------------------------
Metric Increase:
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
mhu-perf
-------------------------
-
9fa7b7be
by sheaf at 2026-10-26T18:19:11-04:00
Acquire/release semaphore tokens more readily
This commit reworks GHC.Driver.MakeSem to use the scoped worker
abstraction of GHC.Utils.Concurrent.Scope. This allows us to get rid of
a lot of the tricky logic in GHC.Driver.MakeSem involving manual
exception handling, thread spawning and lifetime management, etc.
The main user-visible change is that token acquisition is no longer
rate limited (due to a bug, there previously was a 1s rate limit on
acquisition of semaphore tokens), with the release debounce period
shortened from 1s to 10ms.
Other changes:
- With -jsem, the number of capabilities is now capped at the number
of processors, bringing it in line with the -jN behaviour.
- Pending jobs whose wait for a token is interrupted by an exception
are now withdrawn from the jobserver, instead of acquiring a token
that it hung onto without doing useful work.
Fixes #27763
-
c83c6733
by Alan Zimmerman at 2026-10-26T18:19:55-04:00
EPA: Update AnnClsInstDecl to contain AnnList
It was already separately tracking the '{', '}' and leading ';' values.
This aligns it with layout-introducing parser productions ('vocurly'),
so the layout processing can be handled uniformly
-
4c252691
by Rodrigo Mesquita at 2026-10-27T10:57:42+00:00
Asynchronous RPC with external interpreter
There's a single pipe to connect the host compiler to the external
interpreter. On this pipe, we have historically done synchronous
request/reply communication to invoke methods on the external
interpreter.
However, with the advent of multi-threaded debugging, it has become
necessary to do asynchronous calls on the external interpreter, where we
may block on the host waiting for a reply, but the external interpreter
should handle the request asynchronously and reply when ready. For
instance, we want to start the evaluation of the debuggee but not block
the interpreter waiting for it to finish or hit a breakpoint. Or e.g. we
want to resume two threads of the debuggee simulatenously instead of
blocking waiting for the resume result of each one sequentially.
And it turns out, we were already doing some ad-hoc asynchronous
communication to evaluate Template Haskell on the external interpreter.
It used to go as an ad-hoc "send request without expecting reply",
followed by handling on the host a set of 'THMessage's sent by the
interpreter, followed by "expect a response to arrive now". It worked
because there was still a fixed order of messages, but it fits an async
model in a non-ad-hoc way.
Asynchronous communication that unblocks the external interpreter main
thread will also make it possible to resolve issues such as #18852
(exceptions leaving the external interpreter in an unrecoverrable state)
The new design
--------------
The new invariant: every single request should receive a matching reply.
The pipe is bidirectional (the ext. interpreter can receive requests,
but it can also send requests for TH evaluation; the host can send
requests, but it can also receive TH requests from the interpreter).
When a request is sent in whichever direction, it always contains a
correlation-id header. The Sender places an empty MVar on a
pending-reply map keyed by id and waits on it for a response.
See 'remoteCall' and 'remoteTHCall'.
The pipe is associated to a thread which drains the read end. Because
it's bidirectional, two things may be read off the pipe:
- A Reply, with a correlation-id header, which we match against the
pending requests map to unblock the corresponding pending-reply MVar
- A Request, which is queued to the pending-request queue.
To handle a message, we take a Request from the pending-request queue
and run a handler that produces a Reply. Currently, the handler is run
synchronously in the thread, but it is trivial to make the handler run
in a forked thread for certain ("interruptible") messages in an upcoming
patch (when it is necessary for the multi-threaded debugger).
See 'handleRemoteCall' and 'handleRemoteTHCall'.
The old interp lock now just protect the pipe writing (reads don't lock
bc they are done exclusively by drainPipe), it doesn't need nor should
lock the full remote procedure call.
Fixes #27960
-
23f928a6
by Rodrigo Mesquita at 2026-10-27T10:57:42+00:00
interp: Add separate mailboxes for TH async RPC
When the host sends a `RunTH` command to the interpreter, it expects a
few "reverse requests" from the interpreter (e.g. `NewName`) which must
be run in the same context that initiated the `RunTH`.
When the host is evaluating many splices at once, it will send many
`RunTH` commands to the interpreter and start listening for reverse
requests in the meantime. However, a received request previously had no
way to be associated to the scope of a particular `RunTH` invocation, so
the reverse commands could get replied to from incorrect contexts, or
e.g. a `RunTHDone` from the interp. could terminate early the wrong
`RunTH` computation.
In this commit we add independent mailboxes to the interpreter
asynchronous RPC infrastructure, allowing a RunTH to announce a
particular `InboxId` that all reverse requests for that RunTH should
use. On the host, we create that mailbox id and have
`handleRemoteTHCall` listen on that specific mailbox rather than for
requests without a specific mailbox address.
-
380cd1c4
by Rodrigo Mesquita at 2026-10-27T10:57:42+00:00
refactor: Move Async RPC abstraction into GHCi.Message.Async
Carves out the Async RPC over the Pipe abstraction to a new module,
making the GHCi.Message more about the specific interpreter messages
rather than about the IPC protocol.
-
5dc6dd97
by Rodrigo Mesquita at 2026-10-27T10:57:42+00:00
interp: If we have pending reads in the queue we can deliver them
We may still be able to reply even if the read end was closed.
As long as we have pending messages we should try to deliver them.
-
45129774
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
interp: close Handles when done with Pipe
Ensures we close the pipe handles (if created using 'mkPipeFromHandles')
on `closePipe`, and do `closePipe` on `Shutdown`.
The thread draining the read end always `'finally'` closes the read end.
And `closePipe` kills the draining thread and closes the write end.
When `remoteAsyncReq`, we throw PipeReadClosedException if the pipe read is
closed, since we wouldn't be able to receive the reply.
It's fine for the drain thread not to close the write end, even if it
dies without `closePipe` (which does close the write end), because:
- requests pending a reply will get PipeClosedException (we'll not be
able to read any further "real" reply)
- new calls to `remoteAsyncReq` will throw PipeReadClosedException
- pending pipe writes, in response to an already read request, might still succeed
And the clean up code should call close pipe to ensure the write end is
closed as well.
So, if the drain thread dies naturally and the read end is closed, may
still be attempted to be used, but it will throw accordingly. The user
likely wants to catch this to close the write end as well.
-
7f15068c
by Zubin Duggal at 2026-10-27T10:57:43+00:00
interp: fix external interpreter shutdown deadlock on Windows
On shutdown both GHC and iserv call `closePipe`, which stopped the drain
thread before closing the write end. On Windows the drain thread reads
with a safe foreign call to _read, which `throwTo' can't interrupt, so
the two sides were stuck in the deadlock deadlock.
Closing the write end first is not enough on its own. On Windows a read
on a pipe only unblocks once all write handles to it are closed [1].
However, all all four pipe ends were inheritable and we didn't close the
child's ends in the parent. This means a close on any one side wouldn't
unblock any reads, becuase the other side had another copy open.
* runWithPipes now uses System.Process.CommunicationHandle [2], which is
built for passing pipes to a child process: only the child's ends are
inheritable, and it simplifies our mess of CPP. Additionally, it is
important to close our copy of the endpoints passed to the child so
each side only keeps open copies it needs to allow a close to unblock
reads.
* readGhcHandle makes the handle non-inheritable so we don't have
additional references floating around that stop reads from unblocking
on close.
* In closePipe the ordering is subtle and important. We first fail any
requests still waiting for a reply, then close the write end so the
other side sees EOF. We take the write lock to close it, so we never
close under a concurrent write [3]. Only then do we stop the drain
thread, which closes the read end itself once its read has finished.
Bump bound to process >= 1.6.20 as this is the version with
System.Process.CommunicationHandle
[1] https://learn.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations
[2] https://github.com/haskell/process/pull/308
[3] https://gitlab.haskell.org/ghc/ghc/-/work_items/27801
-
3940d78f
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
GHCi.Server: handle RunTH and RunModFinalizers in forked thread
This commit makes the external interpreter able to many interpret
template haskell splices in parallel without blocking the main server
thread. Simply: fork off a thread to handle a RunTH request. The handler
will issue the reverse-requests on the given RunTH mailbox, to keep each
RunTH isolated from the others.
`handleRemoteCall` was generalised slightly by allowing `WriteReply` to
be used linearly in the handler directly, so that the caller of
`handleRemoteCall` may `WriteReply` in the forked thread rather than in
the main thread.
The only question is how can we guarantee `WriteReply` is used linearly
if we want to use it inside a forked thread? We need two guarantees:
1. `WriteReply` is used in the forked thread regardless of whether an
exception is thrown to that thread.
2. The forked thread is always awaited for, so that the main program
doesn't exit before the thread has a chance to actually use
`WriteReply`.
We introduced a small `ThreadManager` which provides a `spawnFinally`
that provides these two guarantees:
1. is guaranteed by `forkFinally`, which always runs the clean up action
exactly once.
2. is guaranteed by `ThreadManager` being abstract and the only exposed
function that introduces `ThreadManager` being `withThreadManager`,
which always awaits for all spawned threads.
Co-authored-by: Rodrigo Mesquita <rodrigo.m.mesquita@gmail.com>
-
3067f05a
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
interp: spawn fillPipe thread, writePipe now safely interruptible
Previously an async exception during a call to `writePipe` could have caused a
partial write, making any further write impossible to parse from the other end.
Now `writePipe` merely pushes the message onto a queue, and the actual write
happens in a separate `fillPipe` thread. As a result of this and previous
changes, `interpCmd` no longer needs `interruptibleMask` to preserve the state
of the protocol.
`writePipe` waits for the actual write to complete, and rethrows exceptions
raised in the write attempt, if any.
-
9c0010fe
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
Follow `Note [Exceptions in TH]` in `qReport` implementation
`qReport b msg` should use `seqList msg` to trigger any exceptions in the
`String`, as explained in the note.
Through refactors the `seqList msg` call followed the bulk of the implementation
in `GHC.Tc.Gen.Splice.report`, but the call should be in `qReport` itself, so
that, even with -fexternal-interpreter, `msg` gets forced right away and any
exceptions get reported as a compilation error.
Follow up to 'interp: spawn fillPipe thread, writePipe now safely
interruptible'.
-
ebe700ac
by Rodrigo Mesquita at 2026-10-27T10:57:43+00:00
test: make T3171 not fragile and add version for ext interp too
- actually test the sleep of the user-given expression gets interrupted
- the ext interp version tests two things:
- Requests can be cancelled without terminating the ext interp (#18852)
- The stmt being evaluated gets interrupted in the ext interp (that
is, ^C gets sent to the thread running the user-given expression
and doesn't affect the ext interp main thread)
- testing ext interp requires sending ^C to whole process group
Fixes #15383
-
33fe4002
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
interp: cancel requests if async exceptions while waiting for reply.
-
87f07310
by Andrea Vezzosi at 2026-10-27T10:57:43+00:00
interp: do not terminate ext. interp. process on exception
`sendMessage` throwing an exception does not warrant terminating the external
interpreter process anymore, because communication can keep going no matter what.
`stopInterp` now resets the whole `Interp` state, so that a new
ExtInterpInstance can be later created and work correctly.
`withExtInterp` now calls `stopInterp` on a `PipeClosedException` and rethrows.
Fixes #18852