[Git][ghc/ghc][wip/fendor/external-unit-db-cache] Fixup: Add graph showing the relation between external Unit types
by Hannes Siebenhandl (@fendor) 22 Jul '26
by Hannes Siebenhandl (@fendor) 22 Jul '26
22 Jul '26
Hannes Siebenhandl pushed to branch wip/fendor/external-unit-db-cache at Glasgow Haskell Compiler / GHC
Commits:
56161185 by fendor at 2026-07-22T11:09:19+02:00
Fixup: Add graph showing the relation between external Unit types
- - - - -
1 changed file:
- compiler/GHC/Unit/External/Index.hs
Changes:
=====================================
compiler/GHC/Unit/External/Index.hs
=====================================
@@ -1,3 +1,45 @@
+-- | The 'UnitIndex' is a 'UnitEnv' wide data structure that shares
+-- external unit information across the 'UnitState' of all home units
+-- (e.g., 'HomeUnitEnv') in a particular 'UnitEnv'.
+--
+-- It caches already read unit databases, all processed 'UnitInfo's and
+-- the 'WireMap'.
+--
+-- This module is meant to be imported as @Index@.
+--
+-- A short overview of how the different types here related to 'UnitState', 'UnitEnv'
+-- and the 'HomeUnitEnv'.
+--
+-- ┌─────────┐
+-- │ UnitEnv │
+-- └────┬────┘
+-- ├───────────────────────┐
+-- │ │
+-- ┌────▼──────┐ ┌─────▼─────┐
+-- │HomeUnitEnv│ │ UnitIndex ├────────────────┐
+-- └────┬──────┘ └───────────┘ │
+-- │ │
+-- │ Reads cached unit DBs │
+-- ┌────▼──────┐ ┌─────────────────────┐ │
+-- │ UnitState ├──────────>ExternalUnitDatabases◄──────┤
+-- └────┬──┬───┘ └─────────────────────┘ │
+-- │ └───────────────────────┐ │
+-- │ Writes new UnitInfos │ │
+-- │ during initialisation │ │
+-- ┌────▼────────┐ ┌────────v──────────┐ │
+-- │ UnitInfoMap │ │ GlobalUnitInfoMap ◄────────┘
+-- └────┬────────┘ └────────^──────────┘
+-- │ │
+-- └──────────────────────────┘
+-- UnitInfoMap references
+-- GlobalUnitInfoMap values
+-- (All UnitInfos are shared)
+--
+-- Open arrow @A ───> B@: A uses B.
+-- Closed arrow @A ◄─── B@: A is a field of B.
+--
+-- Also, see Note [Sharing 'UnitInfo's across the 'UnitEnv'] for more technical discussion
+-- about sharing 'UnitInfo's.
module GHC.Unit.External.Index (
-- * The 'UnitIndexCache'.
-- A mutable wrapper around 'UnitIndex'
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/56161185735edbff60727a62c8b8f69…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/56161185735edbff60727a62c8b8f69…
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/external-unit-db-cache] 2 commits: Fixup: improve docs
by Hannes Siebenhandl (@fendor) 22 Jul '26
by Hannes Siebenhandl (@fendor) 22 Jul '26
22 Jul '26
Hannes Siebenhandl pushed to branch wip/fendor/external-unit-db-cache at Glasgow Haskell Compiler / GHC
Commits:
af0bf825 by fendor at 2026-07-22T11:06:52+02:00
Fixup: improve docs
- - - - -
8e372d55 by fendor at 2026-07-22T11:07:22+02:00
Fixup: Add graph showing the relation between external Unit types
- - - - -
2 changed files:
- compiler/GHC/Unit/External/Index.hs
- compiler/GHC/Unit/State.hs
Changes:
=====================================
compiler/GHC/Unit/External/Index.hs
=====================================
@@ -1,3 +1,43 @@
+-- | The 'UnitIndex' is a 'UnitEnv' wide data structure that shares
+-- external unit information across the 'UnitState' of all home units
+-- (e.g., 'HomeUnitEnv') in a particular 'UnitEnv'.
+--
+-- It caches already read unit databases, all processed 'UnitInfo's and
+-- the 'WireMap'.
+--
+-- This module is meant to be imported as @Index@.
+--
+-- A short overview of how the different types here related to 'UnitState', 'UnitEnv'
+-- and the 'HomeUnitEnv'.
+--
+-- ┌─────────┐
+-- │ UnitEnv │
+-- └────┬────┘
+-- ├───────────────────────┐
+-- │ │
+-- ┌────▼──────┐ ┌─────▼─────┐
+-- │HomeUnitEnv│ │ UnitIndex ├────────────────┐
+-- └────┬──────┘ └───────────┘ │
+-- │ │
+-- │ Reads cached unit DBs │
+-- ┌────▼──────┐ ┌─────────────────────┐ │
+-- │ UnitState ├──────────>ExternalUnitDatabases◄──────┤
+-- └────┬──┬───┘ └─────────────────────┘ │
+-- │ └───────────────────────┐ │
+-- │ Writes new UnitInfos │ │
+-- │ during initialisation │ │
+-- ┌────▼────────┐ ┌────────v──────────┐ │
+-- │ UnitInfoMap │ │ GlobalUnitInfoMap ◄────────┘
+-- └────┬────────┘ └────────^──────────┘
+-- │ │
+-- └──────────────────────────┘
+-- UnitInfoMap references
+-- GlobalUnitInfoMap values
+-- (All UnitInfos are shared)
+--
+-- Open arrow @A ───> B@: A uses B.
+-- Closed arrow @A ◄─── B@: A is a field of B.
+--
module GHC.Unit.External.Index (
-- * The 'UnitIndexCache'.
-- A mutable wrapper around 'UnitIndex'
@@ -71,8 +111,8 @@ import Data.Maybe (catMaybes)
-- The 'UnitIndexCache' ensures that all calls to 'initUnits' will
-- share the 'UnitInfo' if it is possible.
--
--- To share the 'UnitInfo', it needs to be fully-resolved, i.e., its wired-in
--- dependencies and modules need to be resolved.
+-- To share the 'UnitInfo', the 'UnitInfo' needs to be fully-resolved, i.e., its wired-in
+-- dependencies and modules need to be replaced with the 'UnitId' of the wired-in unit.
-- Thus, the 'UnitIndexCache' caches both the global 'WireMap' and the 'UnitInfoMap'.
--
-- The 'WireMap' is globally valid, as other parts of the compiler rely on the fact
=====================================
compiler/GHC/Unit/State.hs
=====================================
@@ -287,14 +287,16 @@ Note [Sharing 'UnitInfo's across the 'UnitEnv']
The 'UnitState' and 'UnitIndex' are closely related.
The 'UnitState' stores all information about the external units referenced by
-a single HomeUnitEnv.
-This includes in particular the 'unitInfoMap', an in-memory representation of
+a single 'HomeUnitEnv'.
+As a reminder, the 'HomeUnitEnv' stores all information specific to a single home unit,
+such as the 'HomePackageTable', 'DynFlags' and the 'UnitState'.
+The 'UnitState' retains the 'unitInfoMap', an in-memory representation of
the unit databases that a 'HomeUnitEnv' depends on.
-Multiple home units can depend on the same unit database, leading to a linear
-increase of 'UnitInfo's per 'HomeUnitEnv'. (It used to be quadratic even, due
-to accidentally retaining old 'UnitInfo's.)
-Thus, we want to share the 'UnitInfo' across multiple 'HomeUnitEnv's.
-This where the 'UnitIndex' is needed.
+Multiple home units can depend on the same unit database, and reference the same
+'UnitInfo's across the GHC session.
+We share all 'UnitInfo's across multiple 'HomeUnitEnv's, saving a lot of
+duplication of the same 'UnitInfo'.
+This what the 'UnitIndex' takes care of.
The 'UnitIndex' stores all fully-resolved 'UnitInfo's that can be referenced
by the 'UnitState'.'unitInfoMap'.
@@ -316,6 +318,9 @@ One instance is stored in 'ExternalUnitDatabases' where variables are resolved,
but the wired-in units haven't been resolved.
The second instance is the fully-resolved 'UnitInfo' stored in the 'UnitIndex'.
+
+See the module documentation for 'GHC.Unit.External.Index' for an overview
+of how the types relate to each other.
-}
-- | The 'UnitState' contains a plethora of information local to a single 'HomeUnitEnv'.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/97fad109104262f5bf28581a38cec6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/97fad109104262f5bf28581a38cec6…
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/spj-reinstallable-base2] base-exports: ignore GHC.Essentials
by sheaf (@sheaf) 22 Jul '26
by sheaf (@sheaf) 22 Jul '26
22 Jul '26
sheaf pushed to branch wip/spj-reinstallable-base2 at Glasgow Haskell Compiler / GHC
Commits:
d075dd77 by sheaf at 2026-07-22T10:40:11+02:00
base-exports: ignore GHC.Essentials
- - - - -
4 changed files:
- 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
- utils/dump-decls/Main.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d075dd77807ca79fbedc5232c9696a6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d075dd77807ca79fbedc5232c9696a6…
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] 45 commits: ci: add missing docker permission workaround in abi-test job
by Duncan Coutts (@dcoutts) 22 Jul '26
by Duncan Coutts (@dcoutts) 22 Jul '26
22 Jul '26
Duncan Coutts pushed to branch wip/io-manager-deadlock-detection at Glasgow Haskell Compiler / GHC
Commits:
0f64f348 by Cheng Shao at 2026-07-16T15:41:08+00:00
ci: add missing docker permission workaround in abi-test job
- - - - -
660cb239 by Cheng Shao at 2026-07-16T19:37:48+00:00
bindist: Fix make install -j race condition on macos/freebsd
This patch fixes make install -j race condition on macos/freebsd. BSD
install fails with EEXIST when multiple install processes concurrently
create the same prefix directory. So we add an `install_dirs`
prerequisite job that sequentially creates the directories for
subsequent jobs to work with. Fixes #27499.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
08130257 by Cheng Shao at 2026-07-16T19:37:48+00:00
ci: run bindist make install with -j
This patch makes the ci scripts run `make install` with `-j` to reduce
wall clock time when installing the bindist, see related issue for
benchmark numbers. This only affects ghc ci logic, the user-facing
default is up to distributors and is still `-j1`. Closes #27029.
- - - - -
d5ae6906 by Adam Gundry at 2026-07-17T04:57:43-04:00
Mark various language extension flags as deprecated (see #27329)
The following language extensions are now deprecated:
- AlternativeLayoutRule
- AlternativeLayoutRuleTransitional
- ParallelArrays
- PolymorphicComponents
- Rank2Types
In addition, the warning `-Walternative-layout-rule-transitional`
has been marked as deprecated, as it is emitted only under the
deprecated extension `XAlternativeLayoutRuleTransitional`.
- - - - -
fe3b059c by Andrew Lelechenko at 2026-07-17T04:58:26-04:00
base: re-export GHC.Environment.getFullArgs from System.Environment
CLC proposal https://github.com/haskell/core-libraries-committee/issues/431
- - - - -
722236dd by sheaf at 2026-07-18T08:48:31-04:00
Coercion optimisation: avoid double-Sym for InstCo
Ticket #27374 pointed out an issue with GHC.Core.Coercion.Opt.optCoercion's
handling of InstCo: it contravened (LC2) in Note [The LiftingContext in optCoercion]
because it applied the ambient 'sym' to a coercion that was then added
to the lifting context substitution.
Fixes #27374
Co-authored-by: Simon Jakobi <simon.jakobi(a)gmail.com>
- - - - -
ff70fc75 by sheaf at 2026-07-18T08:48:31-04:00
Coercion optimisation: avoid exponential behaviour
The change to coercion optimisation of 'InstCo' in the previous commit
introduces exponential behaviour to the coercion optimiser. To avoid
this, this commit provides a way to push in 'Sym' of an already-optimised
coercion: GHC.Core.Coercion.Opt.mkDeepSymCo.
See Note [Pushing Sym without re-optimising] in GHC.Core.Coercion.Opt.
- - - - -
dfef27f0 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Move THREADED_RTS-conditional struct members to end of Capability
Accessing members of the Capability struct from CMM code rely on
accessor macros. (The macros are generated by deriveConstants).
These macros have a single definition. This means that the offsets of
all struct members must *not* vary based on THREADED_RTS vs
!THREADED_RTS. This requires that any struct members that are
conditional on THREADED_RTS must occur after the unconditional struct
members. Hence we move all the ones that are conditional on
THREADED_RTS to the end.
Add a deriveConstants entry for the iomgr member of the Capability
struct, which was the motivation for this change.
Add warning messages to help our future selves. Debugging this took me
a couple hours in gdb!
- - - - -
c254e022 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Make the IOManager API use CapIOManager rather than Capability
This makes the API somewhat more self-contained and more consistent.
Now the IOManager API and each of the backends takes just the I/O
manager structure. Previously we had a bit of a mixture, depending on
whether the function needed access to the Capability or just the
CapIOManager.
We still need access to the cap, so we introduce a back reference to
reach the capability, via iomgr->cap.
Convert all uses in select and poll backends, but not win32 ones.
Convert callers in the scheduler and elsewhere.
Also convert the three CMM primops that call IOManager APIs. They just
need to use Capability_iomgr(MyCapability()).
- - - - -
4f3d8f31 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Split posix/MIO.c out of posix/Signals.c
The MIO I/O manager was secretly living inside the Signals file.
Now it gets its own file, like any other self-respecting I/O manager.
- - - - -
52ce04a9 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Rationalise some scheduler run queue utilities
Move them all to the same place in the file.
Make some static that were used only internally.
Also remove a redundant assignment after calling truncateRunQueue that
is already done within truncateRunQueue.
- - - - -
75bbdebc by Duncan Coutts at 2026-07-18T08:49:12-04:00
Rename initIOManager{AfterFork} to {re}startIOManager
These are more accurate names, since these actions happen after
initialisation and are really about starting (or restarting) background
threads.
- - - - -
724c0517 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Free per-cap I/O managers during shutdown and forkProcess
Historically this was not strictly necessary. The select and win32
legacy I/O managers did not maintain any dynamically allocated
resources. The new poll one does (an auxillary table), and so this
should be freed.
After forkProcess, all threads get deleted. This includes threads
waiting on I/O or timers. So as of this patch, resetting the I/O
manager is just about tidying things up. For example, for the poll
I/O manager this will reset the size of the AIOP table (which
otherwise grows but never shrinks).
In future however the re-initialising will become neeecessary for
functionality, since some I/O managers will need to re-initialise
wakeup fds that are set CLOEXEC.
- - - - -
c007d122 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a TODO to the MIO I/O manager
The direction of travel is to make I/O managers per-capability and have
all their state live in the struct CapIOManager. The MIO I/O manager
however still has a number of global variables.
It's not obvious how handle these globals however.
- - - - -
b65ab7b3 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a FIXME note in the Poll I/O manager
- - - - -
daf2bd6f by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add missing updateRemembSetPushClosure in poll I/O manager
For the non-moving GC.
- - - - -
e33ca830 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Minor doc improvement to struct StgAsyncIOOp member outcome
Mention the enumeration names, as well as their numeric values. The rest
of the code uses the enum names.
- - - - -
4edd2579 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Minor doc improvements for StgTSOBlockInfo
Clarify that certain union members are used only by certain legacy
I/O managers. Hopefully we will be able to remove these at some point.
- - - - -
536bedbb by Duncan Coutts at 2026-07-18T08:49:12-04:00
Avoid exporting various win32-specific rts symbols
The BeginPrivate.h / EndPrivate.h scheme works perfectly well on
Windows, but all of the rts/win32/*.h files were not using it.
- - - - -
8139b5ac by Duncan Coutts at 2026-07-18T08:49:12-04:00
Remove wakeupIOManager, ioManagerWakeup and setIOManagerWakeupFd
We no longer need wakeupIOManager for the threaded RTS case, so we can
remove it and the bits only needed to support it. This includes the
pipe/eventfd fd shared between the RTS and the in-library I/O manager
used for waking up the I/O manager thread. The pipe/eventfd still
exists, but it no longer has to be communicated to the RTS, since the
RTS no longer needs to use it.
So we remove the RTS API export setIOManagerWakeupFd, and remove uses of
it within the I/O managers in ghc-internal.
- - - - -
74fe7c66 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a new interruptIOManager API for the I/O managers
It will be used to interrupt awaitCompletedTimeoutsOrIO. Also update the
return type and docs for awaitCompletedTimeoutsOrIO to have it return
false when it gets interrupted, and have no useful post condition in
that case.
- - - - -
38792843 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for select I/O manager
Uses the FdWakup mechanism.
- - - - -
2f3b00aa by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for poll I/O manager
Uses the FdWakup mechanism.
A quirk we have to cope with is that we now need to poll one more fd --
the wakeup_fd_r -- but this fd has no corresponding entry in the
aiop_table. This is awkward since we have set up our aiop_poll_table to
be an auxilliary table with matching indicies.
The solution this patch uses (and described in the comments) is to have
two tables: struct pollfd *aiop_poll_table, *full_poll_table;
and to have the aiop_poll_table alias the tail of the full_poll_table.
The head entry in the full_poll_table is the extra fd. So we poll the
full_poll_table, while the aiop_poll_table still has matching indicies
with the aiop_table.
Hurrah for C aliasing rules.
- - - - -
cee50131 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for win32 legacy I/O manager
And remove unused related helper resetAbandonRequestWait. It is not
called because the event is created in auto-reset mode, so never needs
to be reset manually.
- - - - -
cf453143 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Note lack of interruptIOManager support for WinIO I/O manager
Though there's a plausible design, we can't sanely test it at the moment
due to related WinIO bugs. Filed as issue #27403.
- - - - -
1b74a0ad by Duncan Coutts at 2026-07-18T08:49:13-04:00
Be more explicit about enum IOReadOrWrite values, and type within cmm
Belt and braces.
- - - - -
b388d093 by Brian McKenna at 2026-07-18T17:51:50-04:00
Ignore ticks in the pattern-match term oracle
The term-oracle in the pattern-match checker is keyed by a canonical
form of the scrutinee, computed by `makeDictsCoherent`. That canonical
form was tick-sensitive: two occurrences of an otherwise identical
expression that happened to carry different ticks were treated as
distinct values, breaking long-distance information.
This shows up in practice under `-finfo-table-map`, because the
desugarer wraps every record-selector use site in a `SourceNote`
carrying that site's span. For example:
data Box = Box { unBox :: Maybe Int }
f b = case unBox b of
Nothing -> 0
Just _ -> let Just x = unBox b in x
The two `unBox b` expressionss carry different SourceNote spans, the
pattern-match checker sees them as different, the long-distance
information from the outer `Just _` branch never reaches the
let-pattern, and `Just x = unBox b` is wrongly reported as
non-exhaustive.
We now strip all ticks in `makeDictsCoherent`. This is documented as
Wrinkle (UD1) of Note [Unique dictionaries in the TmOracle CoreMap].
Fixes #27314
- - - - -
c23e1acb by Mrjtjmn at 2026-07-18T17:52:45-04:00
Add explanations for unsolved Typeable constraints
This commit adds explanations for unsolved 'Typeable' constraints.
GHC will now provide additional explanations for an unsolved constraint
of the form 'Typeable ty', explain why GHC did not solve Typeable constraint.
e.g.:
- 'ty' is a polymorphic type (e.g. forall a. a -> a)
- 'ty' is a qualified type (e.g. Eq Int => Int)
- 'ty' is an unboxed sum type
- 'ty' is an unreduced type family application
- 'ty' whose kind is not typeable
Fixes #26532
- - - - -
cbef021e by Artem Pelenitsyn at 2026-07-19T07:49:55-04:00
ghc-internal: Lock.hs: fix typo and indentation
- - - - -
42918646 by Duncan Coutts at 2026-07-19T07:50:36-04:00
Fix failing test GcStaticPointers for non-moving GC
Minor mistake in asserting something before checking for that same
thing.
Specifically, Bdescr asserts HEAP_ALLOCED_GC, but Bdescr was being used
prior to a guard that checks HEAP_ALLOCED_GC. The solution is just to
move the use of Bdescr after the guard.
Thanks to Simon Jakobi for identifying the problem.
- - - - -
2572c9ae by Duncan Coutts at 2026-07-19T22:03:52+01:00
Make signal handling be a respondibility of the I/O manager(s)
Previously it was scattered between I/O managers and the scheduler, and
especially the scheduler's deadlock detection.
Previously the scheduler would poll for pending signals each iteration
of the scheduler loop. The scheduler also had some hairy signal
functionality in the deadlock detection: in the non-threaded RTS (only)
if there were still no threads running after deadlock detection then it
would block waiting for signals.
But signals can and (in my opinion) should be thought of as just a funny
kind of I/O, and thus should be a responsibility of the I/O manager.
So now we have the I/O managers poll for signals when they are polling
for I/O completion (and removing the separate poll in the scheduler).
And when I/O managers block waiting for I/O then they now also start
signal handlers if they get interrupted by a signal. Crucially, if there
is no pending I/O or timers, the awaitCompletedTimeoutsOrIO will still
block waiting for signals.
This patch puts us into an intermediate state: it temporarily breaks
deadlock detection in the non-threaded RTS. The waiting on I/O currently
happens before deadlock detection. This means we'll now wait forever on
signals before doing deadlock detection. We need to move waiting after
deadlock detection. We'll do that in a later patch.
- - - - -
30411fbb by Duncan Coutts at 2026-07-19T22:03:53+01:00
Clean up the RTS internal signal handling API
Now that the I/O manager is responsible for signals, we can simplify the
API we present for signal handling.
We now just need startPendingSignalHandlers, which is called from the
I/O managers. We can get rid of awaitUserSignals. We also don't need
RtsSignals.h to re-export the platform-specific posix/Signals.h or
win32/ConsoleHandler.h
We can also hide more of the implementation of signals. Less has to be
exposed in posix/Signals.h or win32/ConsoleHandler.h. Indeed,
posix/Signals.h becomes empty and we remove it. Partly this is because
we don't need inline functions (or macros) in the interface.
Also remove signal_handlers from RTS ABI exported symbols list. It does
not appear to have any users in the core libs, and its really an
internal implementation detail. It should not be exposed unless it's
really necessary.
- - - - -
637f1d2f by Duncan Coutts at 2026-07-19T22:03:53+01:00
In the scheduler, move I/O blocking after deadlock detection
To make deadlock detection effective in the non-threaded RTS when there
are deadlocked threads and other unrelated threads waiting on I/O, we
need to arrange to do deadlock detection before we block in scheduler
to wait on I/O.
The solution is to:
1. adjust scheduleFindWork, which runs before deadlock detection, to
only poll for I/O and not block; and
2. add a step after deadlock detection to wait on I/O if there are
still no threads to run (and there's any I/O or timeouts outstanding)
The scheduleCheckBlockedThreads is now so simple that it made more sense
to inline it into scheduleFindWork.
- - - - -
02bd8372 by Duncan Coutts at 2026-07-19T22:03:53+01:00
Remove bogus anyPendingTimeoutsOrIO guard from scheduleDetectDeadlock
The deadlock detection was only invoked if both of these conditions
hold:
1. the run queue is empty
2. there is no pending I/O or timeouts
The second condition is unnecessary. The deadlock detection mechanism
can find deadlocks even if there are other threads waiting on I/O or
timers. Having this extra condition means that we fail to detect
blocked threads if there are any threads waiting on I/O or timers.
Part of fixing issue #26408
- - - - -
29a63cc2 by Duncan Coutts at 2026-07-19T22:03:53+01:00
Don't consider pending I/O for early context switch optimisation
Context switches are normally initiated by the timer signal. If however
the user specifies "context switch as often as possible", with +RTS -C0
then the scheduler arranges for an early context switch (when it's just
about to run a Haskell thread).
Context switching very often is expensive, so as an optimisation there
cases where we do not arrange an early context switch:
1. if there's no other threads to run
2. if there is no pending I/O or timers
This patch eliminates case 2, leaving only case 1.
The rationale is as follows. The use of this was inconsistent across
platforms and threaded/non-threaded RTS ways. It only worked on the
non-threaded RTS and on Windows only worked for the win32-legacy I/O
manager. On all other combinations anyPendingTimeoutsOrIO would always
return false. The fact that nobody noticed and complained about this
inconsistency suggests that the feature is not relied upon.
If however it turns out that applications do rely on this, then the
proper thing to do is not to restore this check, but to add a new I/O
manager hint function that returns if there is any pending events that
are likely to happen *soon*: for example timeouts expiring within one
timeslice, or I/O waits on things likely to complete soon like disk I/O,
but not for example socket/pipe I/O.
The motivation to avoid this use of anyPendingTimeoutsOrIO is to
allow us to eliminate anyPendingTimeoutsOrIO entirely. All other uses
of this are just guards on {await,poll}CompletedTimeoutsOrIO and
the guards can safely be folded into those functions. This will better
cope with some I/O managers having no proper implementation of
anyPendingTimeoutsOrIO.
Ultimately this will let us simplify the scheduler which currently has
to have special #ifdef mingw32_HOST_OS cases to cope with the lack of a
working anyPendingTimeoutsOrIO for some Windows I/O managers
- - - - -
a2cadda6 by Duncan Coutts at 2026-07-19T22:03:53+01:00
Remove anyPendingTimeoutsOrIO guarding {poll,await}CompletedTimeoutsOrIO
Previously the API of the I/O manager used a two step process: check
anyPendingTimeoutsOrIO and then call {poll,await}CompletedTimeoutsOrIO.
This was primarily there as a performance thing, to cheaply check if we
need to do anything.
And then because anyPendingTimeoutsOrIO existed, it was used for other
things too. We have now eliminated the other uses, and are just left
with the performance pattern.
But this was problematic because not all I/O managers correctly
implement anyPendingTimeoutsOrIO (specifically the win32 ones), and now
that we also make I/O managers responsible for signals then we need to
poll/await even if there is no pending I/O or timeouts. If there is no
pending I/O or timeouts then poll/await needs to degenerate to just
waiting forever for any signals.
- - - - -
f3ed5f2d by Duncan Coutts at 2026-07-19T22:03:53+01:00
Remove anyPendingTimeoutsOrIO, it is no longer used
And this avoids the problems arising from the win32 I/O managers having
had a bogus implementation.
- - - - -
c130d363 by Duncan Coutts at 2026-07-19T22:03:53+01:00
Remove second scheduler call to awaitCompletedTimeoutsOrIO
Previously awaitCompletedTimeoutsOrIO was called both before and after
deadlock detection in the scheduler. The reason for that was that the
win32 I/O managers had a bogus implementation of anyPendingTimeoutsOrIO
and this was used to guard the call of awaitCompletedTimeoutsOrIO prior
to deadlock detection. This meant the first call site was never actually
called when using the win32 I/O managers. This was the reason for the
second call: the first one was never used. What a mess.
So now we have a simple design in the scheduler:
1. poll for completed I/O, timers or signals
2. if no runnable threads: do deadlock detection
3. if still no runnable threads: block waiting for I/O, timers or
signals.
- - - - -
f184ccbf by Duncan Coutts at 2026-07-19T22:03:53+01:00
Lift emptyRunQueue guard out of scheduleDetectDeadlock
this improved the clarity of the logic when reading the scheduler code.
- - - - -
02f4c2ba by Duncan Coutts at 2026-07-21T23:44:40+01:00
Make non-threaded deadlock detection also rely on idle GC
Only do deadlock detection GC when idle GC kicks in. This also relies on
using wakeUpRts, so now do this unconditionally. Previously wakeUpRts
was for the threaded rts only.
- - - - -
ffb72ff3 by Duncan Coutts at 2026-07-21T23:44:41+01:00
Enable idle GC by default on non-threaded RTS.
The behaviour is now uniform between threaded and non-threaded. The
deadlock detection now relies on idle GC for both threaded and
non-threaded ways. Previously deadlock detection did not rely on idle
GC for the non-threaded way.
- - - - -
a85e0e7c by Duncan Coutts at 2026-07-21T23:44:41+01:00
Fix state of idle GC control vars with +RTS -V0
Currently when the user uses +RTS -I0, then doIdleGC is set to false.
But if the master tick interval -V is set to 0 then the idleGCDelayTime
was being set to 0 but doIdleGC was not being set to false, which is
inconsistent, and almost certainly buggy.
- - - - -
ff45bc0a by Duncan Coutts at 2026-07-21T23:44:41+01:00
Add a long Note [Deadlock detection]
It describes the historical and modern designs and their trade-offs.
The point is we've now unified the code for deadlock detection between
the threaded and non-threaded ways, by changing the non-threaded to
follow the same design as the threaded.
- - - - -
78f1c4f6 by Duncan Coutts at 2026-07-21T23:44:41+01:00
Add a test for deadlock detection, issue #26408
- - - - -
8c458d21 by Duncan Coutts at 2026-07-21T23:44:41+01:00
Update the user guide with the revised idle GC behaviour
i.e. it's now not just for the threaded RTS, but general.
Also document the fact that disabling idle GC also disables deadlock
detection.
And add a changelog entry.
- - - - -
128 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- + changelog.d/T26532
- + changelog.d/T27314.md
- + changelog.d/T27329
- + changelog.d/T27374
- + changelog.d/fix-make-install-j
- + changelog.d/idle-gc-and-deadlock-detection
- compiler/GHC/Core/Coercion/Opt.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/HsToCore/Pmc/Solver.hs
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Instance/Typeable.hs
- compiler/GHC/Tc/Types/Rank.hs
- docs/users_guide/expected-undocumented-flags.txt
- docs/users_guide/exts/rank_polymorphism.rst
- docs/users_guide/exts/static_pointers.rst
- docs/users_guide/runtime_control.rst
- hadrian/bindist/Makefile
- libraries/base/changelog.md
- libraries/base/src/System/Environment.hs
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/src/GHC/Internal/Event/Control.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Manager.hs
- libraries/ghc-internal/src/GHC/Internal/Event/TimerManager.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock.hs
- rts/Capability.c
- rts/Capability.h
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/Linker.c
- rts/PrimOps.cmm
- rts/RaiseAsync.c
- rts/RtsFlags.c
- rts/RtsSignals.h
- rts/RtsStartup.c
- rts/RtsSymbols.c
- rts/Schedule.c
- rts/Schedule.h
- rts/Timer.c
- rts/include/rts/IOInterface.h
- rts/include/rts/storage/Closures.h
- rts/include/rts/storage/TSO.h
- rts/posix/FdWakeup.h
- + rts/posix/MIO.c
- rts/posix/Signals.h → rts/posix/MIO.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Select.c
- rts/posix/Select.h
- rts/posix/Signals.c
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/rts.cabal
- rts/sm/NonMovingMark.c
- rts/win32/AsyncMIO.c
- rts/win32/AsyncMIO.h
- rts/win32/AsyncWinIO.h
- rts/win32/AwaitEvent.c
- rts/win32/AwaitEvent.h
- rts/win32/ConsoleHandler.c
- rts/win32/ConsoleHandler.h
- rts/win32/MIOManager.h
- rts/win32/ThrIOManager.h
- rts/win32/WorkQueue.h
- rts/win32/veh_excn.h
- testsuite/tests/backpack/should_compile/T13149.bkp
- + testsuite/tests/corelint/T27374.hs
- testsuite/tests/corelint/all.T
- testsuite/tests/determinism/determ017/A.hs
- testsuite/tests/ghci/scripts/T12005.script
- testsuite/tests/haddock/perf/Fold.hs
- testsuite/tests/indexed-types/should_fail/T7354.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/layout/layout001.stdout
- testsuite/tests/layout/layout002.stdout
- testsuite/tests/layout/layout003.stdout
- testsuite/tests/layout/layout004.stdout
- testsuite/tests/layout/layout005.stdout
- testsuite/tests/layout/layout006.stdout
- testsuite/tests/layout/layout007.stdout
- testsuite/tests/layout/layout008.stdout
- testsuite/tests/layout/layout009.stdout
- testsuite/tests/linear/should_compile/T1735Min.hs
- + testsuite/tests/parser/should_compile/T13087.stderr
- testsuite/tests/parser/should_fail/T8431.stderr
- testsuite/tests/parser/should_fail/readFail038.stderr
- testsuite/tests/perf/compiler/T3064.hs
- + testsuite/tests/pmcheck/should_compile/T27314.hs
- testsuite/tests/pmcheck/should_compile/all.T
- testsuite/tests/polykinds/T7594.hs
- testsuite/tests/programs/thurston-modular-arith/Main.hs
- + testsuite/tests/rts/T26408.hs
- + testsuite/tests/rts/T26408.stderr
- testsuite/tests/rts/all.T
- testsuite/tests/rts/ipe/IpeStats/Fold.hs
- testsuite/tests/simplCore/should_compile/T11562.hs
- testsuite/tests/simplCore/should_run/T3591.hs
- testsuite/tests/typecheck/should_compile/DeepSubsumption02.hs
- testsuite/tests/typecheck/should_compile/T12507.hs
- testsuite/tests/typecheck/should_compile/T13951.hs
- testsuite/tests/typecheck/should_compile/T18920.hs
- testsuite/tests/typecheck/should_compile/T2595.hs
- testsuite/tests/typecheck/should_compile/T7541.hs
- testsuite/tests/typecheck/should_fail/T15067.stderr
- + testsuite/tests/typecheck/should_fail/T26532.hs
- + testsuite/tests/typecheck/should_fail/T26532.stderr
- testsuite/tests/typecheck/should_fail/T6069.stderr
- testsuite/tests/typecheck/should_fail/T7368a.hs
- testsuite/tests/typecheck/should_fail/T9858b.stderr
- testsuite/tests/typecheck/should_fail/TcStaticPointersFail02.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_run/T1735_Help/Basics.hs
- testsuite/tests/typecheck/should_run/T3731-short.hs
- testsuite/tests/typecheck/should_run/T3731.hs
- testsuite/tests/typecheck/should_run/church.hs
- testsuite/tests/typecheck/should_run/tcrun008.hs
- testsuite/tests/typecheck/should_run/tcrun017.hs
- testsuite/tests/typecheck/should_run/tcrun026.hs
- testsuite/tests/typecheck/should_run/tcrun035.hs
- testsuite/tests/typecheck/should_run/tcrun036.hs
- utils/deriveConstants/Main.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1120a8675cf03eadd05e2fd6f7587a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/1120a8675cf03eadd05e2fd6f7587a…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] rts: always use StgInt to represent cost center id
by Marge Bot (@marge-bot) 22 Jul '26
by Marge Bot (@marge-bot) 22 Jul '26
22 Jul '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
ab9ab895 by Cheng Shao at 2026-07-21T17:10:53-04:00
rts: always use StgInt to represent cost center id
Currently cost center id is modeled as `Int` and it should be `StgInt`
uniformly in the RTS, hence this patch. Fixes #27524.
- - - - -
5 changed files:
- rts/Profiling.c
- rts/Trace.c
- rts/Trace.h
- rts/eventlog/EventLog.c
- rts/eventlog/EventLog.h
Changes:
=====================================
rts/Profiling.c
=====================================
@@ -44,9 +44,9 @@ static Arena *prof_arena;
* closure_cats
*/
-static unsigned int CC_ID = 1;
-static unsigned int CCS_ID = 1;
-static unsigned int DUMPED_CC_ID = 0; // we have dumped all CCs up to this id to the eventlog
+static StgInt CC_ID = 1;
+static StgInt CCS_ID = 1;
+static StgInt DUMPED_CC_ID = 0; // we have dumped all CCs up to this id to the eventlog
/* Globals for opening the profiling log file(s)
*/
=====================================
rts/Trace.c
=====================================
@@ -757,7 +757,7 @@ void traceIPE(const InfoProvEnt *ipe)
}
#if defined(PROFILING)
-void traceHeapProfCostCentre(StgWord32 ccID,
+void traceHeapProfCostCentre(StgInt ccID,
const char *label,
const char *module,
const char *srcloc,
=====================================
rts/Trace.h
=====================================
@@ -376,7 +376,7 @@ void traceHeapBioProfSampleBegin(StgInt era, StgWord64 time);
void traceHeapProfSampleEnd(StgInt era);
void traceHeapProfSampleString(const char *label, StgWord residency);
#if defined(PROFILING)
-void traceHeapProfCostCentre(StgWord32 ccID,
+void traceHeapProfCostCentre(StgInt ccID,
const char *label,
const char *module,
const char *srcloc,
=====================================
rts/eventlog/EventLog.c
=====================================
@@ -1312,7 +1312,7 @@ void postHeapProfSampleString(const char *label,
}
#if defined(PROFILING)
-void postHeapProfCostCentre(StgWord32 ccID,
+void postHeapProfCostCentre(StgInt ccID,
const char *label,
const char *module,
const char *srcloc,
=====================================
rts/eventlog/EventLog.h
=====================================
@@ -181,7 +181,7 @@ void postHeapProfSampleString(const char *label,
StgWord64 residency);
#if defined(PROFILING)
-void postHeapProfCostCentre(StgWord32 ccID,
+void postHeapProfCostCentre(StgInt ccID,
const char *label,
const char *module,
const char *srcloc,
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ab9ab8952b1ff20ed8b092ff95d6a9f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ab9ab8952b1ff20ed8b092ff95d6a9f…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] Resolving many TTG related orphan type-class instances
by Marge Bot (@marge-bot) 22 Jul '26
by Marge Bot (@marge-bot) 22 Jul '26
22 Jul '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
43dd2b15 by Recursion Ninja at 2026-07-21T17:09:53-04:00
Resolving many TTG related orphan type-class instances
This is part a technical debt removal effort made possible now
that separating out the AST via TTG has come to a close.
As the AST in 'L.H.S' has been incrementally separated from the GHC internals,
there are many accumulated orphan instance of 'Binary', 'NFData', 'Outputable',
and 'Uniquable'. The orphan instance of data-types from within 'L.H.S' have had
their orphan instances moved to either:
1. The module which defines the data-type
2. The module which defines the type-class;
i.e. moving an orphan 'Binary' instance to 'GHC.Utils.Binary'
Orphan instances resolved (37):
| Data-type | Resolved instance(s) | Former orphan module(s) |
| -------------------- | -------------------------- | ------------------------- |
| Role | Binary, NFData, Outputable | GHC.Core.Coercion.Axiom |
| SrcStrictness | Binary, NFData, Outputable | GHC.Core.DataCon |
| SrcUnpackedness | Binary, NFData, Outputable | GHC.Core.DataCon |
| Fixity | Binary, Outputable | GHC.Hs.Basic |
| FixityDirection | Binary, Outputable | GHC.Hs.Basic |
| LexicalFixity | Outputable | GHC.Hs.Basic |
| CCallTarget | NFData | GHC.Hs.Decls.Foreign |
| CType | NFData | GHC.Hs.Decls.Foreign |
| Header | NFData | GHC.Hs.Decls.Foreign |
| OverlapMode | Binary, NFData | GHC.Hs.Decls.Overlap |
| WithHsDocIdentifiers | NFData, Outputable | GHC.Hs.Doc |
| HsDocString | NFData | GHC.Hs.DocString |
| HsDocStringChunk | Binary, Outputable | GHC.Hs.DocString |
| HsDocStringDecorator | Binary, Outputable | GHC.Hs.DocString |
| NamespaceSpecifier | Outputable | GHC.Hs.ImpExp |
| ForAllTyFlag | Binary, NFData, Outputable | GHC.Hs.Specificity |
| Specificity | Binary, NFData | GHC.Hs.Specificity |
| PromotionFlag | Binary, Outputable | GHC.Types.Basic |
| FieldLabelString | Outputable, Uniquable | GHC.Types.FieldLabel |
| InlinePragma | Binary | GHC.Types.InlinePragma |
-------------------------
Metric Decrease:
hard_hole_fits
-------------------------
Closes #21262, #27469
- - - - -
26 changed files:
- compiler/GHC/Core/Coercion/Axiom.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Hs/Basic.hs
- compiler/GHC/Hs/Decls/Overlap.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/DocString.hs
- compiler/GHC/Hs/ImpExp.hs
- − compiler/GHC/Hs/Specificity.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Types/Basic.hs
- compiler/GHC/Types/FieldLabel.hs
- compiler/GHC/Types/Fixity.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Types/InlinePragma.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/Decls/Foreign.hs
- compiler/Language/Haskell/Syntax/Doc.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Specificity.hs
- compiler/ghc.cabal.in
- testsuite/tests/count-deps/CountDepsParser.stdout
Changes:
=====================================
compiler/GHC/Core/Coercion/Axiom.hs
=====================================
@@ -1,5 +1,3 @@
-{-# OPTIONS_GHC -Wno-orphans #-} -- Outputable
-
-- (c) The University of Glasgow 2012
-- | Module for coercion axioms, used to represent type family instances
@@ -22,7 +20,7 @@ module GHC.Core.Coercion.Axiom (
coAxBranchLHS, coAxBranchRHS, coAxBranchSpan, coAxBranchIncomps,
placeHolderIncomps,
- Role(..), fsFromRole,
+ Role(..),
CoAxiomRule(..), BuiltInFamRewrite(..), BuiltInFamInjectivity(..), TypeEqn,
coAxiomRuleArgRoles, coAxiomRuleRole,
@@ -43,7 +41,6 @@ import GHC.Types.Name
import GHC.Types.Unique
import GHC.Types.Var
import GHC.Utils.Misc
-import GHC.Utils.Binary
import GHC.Utils.Panic
import GHC.Data.Pair
import GHC.Types.Basic
@@ -52,7 +49,6 @@ import GHC.Types.SrcLoc
import qualified Data.Data as Data
import Data.Array
import Data.List ( mapAccumL )
-import Control.DeepSeq
{-
Note [Coercion axiom branches]
@@ -521,44 +517,6 @@ instance Outputable CoAxBranch where
, ppUnless (null incomps) $
text "incomps:" <+> vcat (map ppr incomps) ])
-{-
-************************************************************************
-* *
- Roles
-* *
-************************************************************************
-
-Roles are defined here to avoid circular dependencies.
--}
-
--- These names are slurped into the parser code. Changing these strings
--- will change the **surface syntax** that GHC accepts! If you want to
--- change only the pretty-printing, do some replumbing. See
--- mkRoleAnnotDecl in GHC.Parser.PostProcess
-fsFromRole :: Role -> FastString
-fsFromRole Nominal = fsLit "nominal"
-fsFromRole Representational = fsLit "representational"
-fsFromRole Phantom = fsLit "phantom"
-
-instance Outputable Role where
- ppr = ftext . fsFromRole
-
-instance Binary Role where
- put_ bh Nominal = putByte bh 1
- put_ bh Representational = putByte bh 2
- put_ bh Phantom = putByte bh 3
-
- get bh = do tag <- getByte bh
- case tag of 1 -> return Nominal
- 2 -> return Representational
- 3 -> return Phantom
- _ -> panic ("get Role " ++ show tag)
-
-instance NFData Role where
- rnf Nominal = ()
- rnf Representational = ()
- rnf Phantom = ()
-
{-
************************************************************************
* *
=====================================
compiler/GHC/Core/DataCon.hs
=====================================
@@ -5,8 +5,6 @@
\section[DataCon]{@DataCon@: Data Constructors}
-}
-{-# OPTIONS_GHC -Wno-orphans #-} -- Outputable, Binary
-
module GHC.Core.DataCon (
-- * Main data types
DataCon, DataConRep(..),
@@ -109,7 +107,6 @@ import qualified Data.ByteString.Lazy as LBS
import qualified Data.Data as Data
import Data.Char
import Data.List( find )
-import Control.DeepSeq
{-
Note [Data constructor representation]
@@ -1030,16 +1027,6 @@ instance Outputable HsImplBang where
ppr (HsUnpack (Just co)) = text "Unpacked" <> parens (ppr co)
ppr (HsStrict b) = text "StrictNotUnpacked" <> parens (ppr b)
-instance Outputable SrcStrictness where
- ppr SrcLazy = char '~'
- ppr SrcStrict = char '!'
- ppr NoSrcStrict = empty
-
-instance Outputable SrcUnpackedness where
- ppr SrcUnpack = text "{-# UNPACK #-}"
- ppr SrcNoUnpack = text "{-# NOUNPACK #-}"
- ppr NoSrcUnpack = empty
-
instance Outputable StrictnessMark where
ppr MarkedStrict = text "!"
ppr NotMarkedStrict = empty
@@ -1054,40 +1041,6 @@ instance Binary StrictnessMark where
1 -> return MarkedStrict
_ -> panic "Invalid binary format"
-instance Binary SrcStrictness where
- put_ bh SrcLazy = putByte bh 0
- put_ bh SrcStrict = putByte bh 1
- put_ bh NoSrcStrict = putByte bh 2
-
- get bh =
- do h <- getByte bh
- case h of
- 0 -> return SrcLazy
- 1 -> return SrcStrict
- _ -> return NoSrcStrict
-
-instance Binary SrcUnpackedness where
- put_ bh SrcNoUnpack = putByte bh 0
- put_ bh SrcUnpack = putByte bh 1
- put_ bh NoSrcUnpack = putByte bh 2
-
- get bh =
- do h <- getByte bh
- case h of
- 0 -> return SrcNoUnpack
- 1 -> return SrcUnpack
- _ -> return NoSrcUnpack
-
-instance NFData SrcStrictness where
- rnf SrcLazy = ()
- rnf SrcStrict = ()
- rnf NoSrcStrict = ()
-
-instance NFData SrcUnpackedness where
- rnf SrcNoUnpack = ()
- rnf SrcUnpack = ()
- rnf NoSrcUnpack = ()
-
-- | Compare strictness annotations
eqHsBang :: HsImplBang -> HsImplBang -> Bool
eqHsBang HsLazy HsLazy = True
=====================================
compiler/GHC/Hs/Basic.hs
=====================================
@@ -1,52 +1,6 @@
-{-# OPTIONS_GHC -Wno-orphans #-} -- Outputable, Binary
-{-# LANGUAGE TypeFamilies #-}
-
-- | Fixity
module GHC.Hs.Basic
( module Language.Haskell.Syntax.Basic
) where
-import GHC.Prelude
-
-import GHC.Utils.Outputable
-import GHC.Utils.Binary
-
import Language.Haskell.Syntax.Basic
-
-instance Outputable LexicalFixity where
- ppr Prefix = text "Prefix"
- ppr Infix = text "Infix"
-
-instance Outputable FixityDirection where
- ppr InfixL = text "infixl"
- ppr InfixR = text "infixr"
- ppr InfixN = text "infix"
-
-instance Outputable Fixity where
- ppr (Fixity prec dir) = hcat [ppr dir, space, int prec]
-
-
-instance Binary Fixity where
- put_ bh (Fixity aa ab) = do
- put_ bh aa
- put_ bh ab
- get bh = do
- aa <- get bh
- ab <- get bh
- return (Fixity aa ab)
-
-------------------------
-
-instance Binary FixityDirection where
- put_ bh InfixL =
- putByte bh 0
- put_ bh InfixR =
- putByte bh 1
- put_ bh InfixN =
- putByte bh 2
- get bh = do
- h <- getByte bh
- case h of
- 0 -> return InfixL
- 1 -> return InfixR
- _ -> return InfixN
=====================================
compiler/GHC/Hs/Decls/Overlap.hs
=====================================
@@ -1,12 +1,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-} -- XOverlapMode, XXOverlapMode
-
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{- Necessary for the following instances:
- * (type class): Binary OverlapMode
- * (type class): NFData OverlapMode
--}
+{-# OPTIONS_GHC -fno-warn-orphans #-} -- XOverlapMode, XXOverlapMode
{- |
Data-types describing the overlap annotations for instances as well as
@@ -74,34 +69,6 @@ type instance XOverlapMode (GhcPass _) = SourceText
type instance XXOverlapMode (GhcPass _) = DataConCantHappen
-instance NFData (OverlapMode (GhcPass p)) where
- rnf = \case
- NoOverlap s -> rnf s
- Overlappable s -> rnf s
- Overlapping s -> rnf s
- Overlaps s -> rnf s
- Incoherent s -> rnf s
- NonCanonical s -> rnf s
-
-instance Binary (OverlapMode (GhcPass p)) where
- put_ bh = \case
- NoOverlap s -> putByte bh 0 >> put_ bh s
- Overlaps s -> putByte bh 1 >> put_ bh s
- Incoherent s -> putByte bh 2 >> put_ bh s
- Overlapping s -> putByte bh 3 >> put_ bh s
- Overlappable s -> putByte bh 4 >> put_ bh s
- NonCanonical s -> putByte bh 5 >> put_ bh s
-
- get bh = do
- h <- getByte bh
- case h of
- 0 -> get bh >>= \s -> return $ NoOverlap s
- 1 -> get bh >>= \s -> return $ Overlaps s
- 2 -> get bh >>= \s -> return $ Incoherent s
- 3 -> get bh >>= \s -> return $ Overlapping s
- 4 -> get bh >>= \s -> return $ Overlappable s
- _ -> get bh >>= \s -> return $ NonCanonical s
-
pprSafeOverlap :: Bool -> SDoc
pprSafeOverlap True = text "[safe]"
pprSafeOverlap False = empty
=====================================
compiler/GHC/Hs/Doc.hs
=====================================
@@ -63,16 +63,6 @@ type instance Anno (WithHsDocIdentifiers (HsDocString (GhcPass pass)) (GhcPass p
deriving instance (Data pass, Data (LIdP pass), Data a) => Data (WithHsDocIdentifiers a pass)
deriving instance (Eq (LIdP pass), Eq a) => Eq (WithHsDocIdentifiers a pass)
-instance (UnXRec pass, NFData (IdP pass), NFData a) => NFData (WithHsDocIdentifiers a pass) where
- rnf (WithHsDocIdentifiers d i) = rnf d `seq` rnf (map (unXRec @pass) i)
-
--- | For compatibility with the existing @-ddump-parsed' output, we only show
--- the docstring.
---
--- Use 'pprHsDoc' to show `HsDoc`'s internals.
-instance Outputable a => Outputable (WithHsDocIdentifiers a pass) where
- ppr (WithHsDocIdentifiers s _ids) = ppr s
-
instance Binary a => Binary (WithHsDocIdentifiers a GhcRn) where
put_ bh (WithHsDocIdentifiers s ids) = do
put_ bh s
=====================================
compiler/GHC/Hs/DocString.hs
=====================================
@@ -5,6 +5,8 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
+-- Binary HsDocString
+-- Outputable HsDocString
module GHC.Hs.DocString
( LHsDocString
@@ -44,7 +46,6 @@ import GHC.Hs.Extension.Pass (GhcPass, GhcPs, GhcRn, GhcTc)
import Language.Haskell.Syntax.Doc
import Language.Haskell.Syntax.Extension
-import Control.DeepSeq
import Data.Data
import Data.List.NonEmpty (NonEmpty(..))
import Data.List (intercalate)
@@ -82,11 +83,6 @@ instance (Show (LHsDocStringChunk pass), XXHsDocString pass ~ DataConCantHappen)
instance Outputable (HsDocString (GhcPass p)) where
ppr = text . renderHsDocString
-instance NFData (HsDocString (GhcPass p)) where
- rnf (MultiLineDocString _ a b) = rnf a `seq` rnf b
- rnf (NestedDocString _ a b) = rnf a `seq` rnf b
- rnf (GeneratedDocString _ a) = rnf a
-
-- | Annotate a pretty printed thing with its doc.
-- The docstring comes after if it is 'HsDocStringPrevious'.
-- Otherwise it comes before.
@@ -120,37 +116,12 @@ instance Binary (HsDocString (GhcPass p)) where
2 -> GeneratedDocString noExtField <$> get bh
t -> fail $ "HsDocString: invalid tag " ++ show t
-instance Outputable HsDocStringDecorator where
- ppr = text . printDecorator
-
printDecorator :: HsDocStringDecorator -> String
printDecorator HsDocStringNext = "|"
printDecorator HsDocStringPrevious = "^"
printDecorator (HsDocStringNamed n) = '$':n
printDecorator (HsDocStringGroup n) = replicate n '*'
-instance Binary HsDocStringDecorator where
- put_ bh x = case x of
- HsDocStringNext -> putByte bh 0
- HsDocStringPrevious -> putByte bh 1
- HsDocStringNamed n -> putByte bh 2 >> put_ bh n
- HsDocStringGroup n -> putByte bh 3 >> put_ bh n
- get bh = do
- tag <- getByte bh
- case tag of
- 0 -> pure HsDocStringNext
- 1 -> pure HsDocStringPrevious
- 2 -> HsDocStringNamed <$> get bh
- 3 -> HsDocStringGroup <$> get bh
- t -> fail $ "HsDocStringDecorator: invalid tag " ++ show t
-
-instance Binary HsDocStringChunk where
- put_ bh (HsDocStringChunk bs) = put_ bh bs
- get bh = HsDocStringChunk <$> get bh
-
-instance Outputable HsDocStringChunk where
- ppr = text . unpackHDSC
-
mkGeneratedHsDocStringGhc :: String -> HsDocString (GhcPass p)
mkGeneratedHsDocStringGhc = mkGeneratedHsDocString noExtField . mkHsDocStringChunk
=====================================
compiler/GHC/Hs/ImpExp.hs
=====================================
@@ -447,8 +447,3 @@ coveredByNamespaceSpecifier DataNamespaceSpecifier{} = isValNameSpace
filterByNamespaceSpecifierGREs :: NamespaceSpecifier (GhcPass p) -> [GlobalRdrElt] -> [GlobalRdrElt]
filterByNamespaceSpecifierGREs NoNamespaceSpecifier{} = id
filterByNamespaceSpecifierGREs ns_spec = filterByNamespaceGREs (coveredByNamespaceSpecifier ns_spec)
-
-instance Outputable (NamespaceSpecifier (GhcPass p)) where
- ppr NoNamespaceSpecifier{} = empty
- ppr TypeNamespaceSpecifier{} = text "type"
- ppr DataNamespaceSpecifier{} = text "data"
=====================================
compiler/GHC/Hs/Specificity.hs deleted
=====================================
@@ -1,51 +0,0 @@
-{-# OPTIONS_GHC -Wno-orphans #-}
-module GHC.Hs.Specificity where
-
-import Prelude
-import Control.DeepSeq (NFData(..))
-
-import GHC.Utils.Outputable
-import GHC.Utils.Binary
-
-import Language.Haskell.Syntax.Specificity
-
-{- *********************************************************************
-* *
-* ForAllTyFlag
-* *
-********************************************************************* -}
-
-instance Outputable ForAllTyFlag where
- ppr Required = text "[req]"
- ppr Specified = text "[spec]"
- ppr Inferred = text "[infrd]"
-
-instance Binary Specificity where
- put_ bh SpecifiedSpec = putByte bh 0
- put_ bh InferredSpec = putByte bh 1
-
- get bh = do
- h <- getByte bh
- case h of
- 0 -> return SpecifiedSpec
- _ -> return InferredSpec
-
-instance Binary ForAllTyFlag where
- put_ bh Required = putByte bh 0
- put_ bh Specified = putByte bh 1
- put_ bh Inferred = putByte bh 2
-
- get bh = do
- h <- getByte bh
- case h of
- 0 -> return Required
- 1 -> return Specified
- _ -> return Inferred
-
-instance NFData Specificity where
- rnf SpecifiedSpec = ()
- rnf InferredSpec = ()
-instance NFData ForAllTyFlag where
- rnf (Invisible spec) = rnf spec
- rnf Required = ()
-
=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -137,7 +137,6 @@ import GHC.Hs -- Lots of it
import GHC.Core.TyCon ( TyCon, isTupleTyCon, tyConSingleDataCon_maybe )
import GHC.Core.DataCon ( DataCon, dataConTyCon, dataConName )
import GHC.Core.ConLike ( ConLike(..) )
-import GHC.Core.Coercion.Axiom ( fsFromRole )
import GHC.Types.Name.Reader
import GHC.Types.Name
import GHC.Types.Basic
@@ -425,7 +424,7 @@ mkRoleAnnotDecl loc tycon roles anns
where
role_data_type = dataTypeOf (undefined :: Role)
all_roles = map fromConstr $ dataTypeConstrs role_data_type
- possible_roles = [(fsFromRole role, role) | role <- all_roles]
+ possible_roles = [(strFromRole role, role) | role <- all_roles]
parse_role (L loc_role Nothing) = return $ L (noAnnSrcSpan loc_role) Nothing
parse_role (L loc_role (Just role))
=====================================
compiler/GHC/Types/Basic.hs
=====================================
@@ -14,14 +14,6 @@ types that
\end{itemize}
-}
-{-# OPTIONS_GHC -Wno-orphans #-}
-{-
-Above flag is necessary for these instances:
- * Binary Boxity
- * Binary PromotionFlag
- * Outputable Boxity
- * Outputable PromotionFlag
--}
{-# LANGUAGE DerivingVia #-}
module GHC.Types.Basic (
@@ -377,27 +369,6 @@ unSwap NotSwapped f a b = f a b
unSwap IsSwapped f a b = f b a
-{- *********************************************************************
-* *
- Promotion flag
-* *
-********************************************************************* -}
-
-instance Outputable PromotionFlag where
- ppr NotPromoted = text "NotPromoted"
- ppr IsPromoted = text "IsPromoted"
-
-instance Binary PromotionFlag where
- put_ bh NotPromoted = putByte bh 0
- put_ bh IsPromoted = putByte bh 1
-
- get bh = do
- n <- getByte bh
- case n of
- 0 -> return NotPromoted
- 1 -> return IsPromoted
- _ -> fail "Binary(IsPromoted): fail)"
-
{-
************************************************************************
* *
=====================================
compiler/GHC/Types/FieldLabel.hs
=====================================
@@ -1,5 +1,4 @@
{-# LANGUAGE UndecidableInstances #-}
-{-# OPTIONS_GHC -Wno-orphans #-} -- Outputable FieldLabelString
{-
%
@@ -48,7 +47,6 @@ import GHC.Prelude
import {-# SOURCE #-} GHC.Types.Name
-import GHC.Types.Unique (Uniquable(..))
import GHC.Utils.Outputable
import GHC.Utils.Binary
import GHC.Data.FastString
@@ -89,12 +87,6 @@ instance Outputable FieldLabel where
<> ppr (flHasDuplicateRecordFields fl)
<> ppr (flHasFieldSelector fl))
-instance Outputable FieldLabelString where
- ppr (FieldLabelString l) = ppr l
-
-instance Uniquable FieldLabelString where
- getUnique (FieldLabelString fs) = getUnique (mkFastStringShortText fs)
-
-- | Flag to indicate whether the DuplicateRecordFields extension is enabled.
data DuplicateRecordFields
= DuplicateRecordFields -- ^ Fields may be duplicated in a single module
=====================================
compiler/GHC/Types/Fixity.hs
=====================================
@@ -1,5 +1,3 @@
-{-# OPTIONS_GHC -Wno-dodgy-exports #-} -- For re-export of GHC.Hs.Basic instances
-
-- | Fixity
module GHC.Types.Fixity
( Fixity (..)
@@ -11,14 +9,12 @@ module GHC.Types.Fixity
, negateFixity
, funTyFixity
, compareFixity
- , module GHC.Hs.Basic
)
where
import GHC.Prelude
import Language.Haskell.Syntax.Basic (LexicalFixity(..), FixityDirection(..), Fixity(..) )
-import GHC.Hs.Basic () -- For instances only
------------------------
=====================================
compiler/GHC/Types/ForeignCall.hs
=====================================
@@ -319,13 +319,6 @@ type instance XXHeader (GhcPass p) = DataConCantHappen
deriving instance Eq (Header (GhcPass p))
-instance NFData (CType (GhcPass p)) where
- rnf (CType ext mh fs) =
- rnf ext `seq` rnf mh `seq` rnf fs
-
-instance NFData (Header (GhcPass p)) where
- rnf (Header s h) =
- rnf s `seq` rnf h
instance NFData CCallStaticTargetUnit where
rnf = \case
@@ -388,14 +381,6 @@ instance forall p. IsPass p => Eq (CCallTarget (GhcPass p)) where
GhcTc -> x1 == x2
_ -> False
-instance forall p. IsPass p => NFData (CCallTarget (GhcPass p)) where
- rnf = \case
- DynamicTarget NoExtField -> ()
- StaticTarget x a b -> rnf a `seq` rnf b `seq` case ghcPass @p of
- GhcPs -> rnf x
- GhcRn -> rnf x
- GhcTc -> rnf x
-
instance forall p. IsPass p => Binary (CCallTarget (GhcPass p)) where
put_ bh = \case
StaticTarget x a b -> do
=====================================
compiler/GHC/Types/InlinePragma.hs
=====================================
@@ -9,16 +9,8 @@
-}
{-# OPTIONS_GHC -Wno-orphans #-}
-{-
-Suppression of warnings are required for instances:
- - Binary Activation
- - Binary CompilerPhase
- - Binary InlinePragma
- - Binary InlineSaturation
- - Binary XActivation
- - Binary XInlinePragmaGhc
- - Outputable CompilerPhase
--}
+-- Required for TTG type-family definitions,
+-- There are no orphan type-class instances
module GHC.Types.InlinePragma
( -- * Inline Pragma Encoding
@@ -494,10 +486,6 @@ no harm.
always returns 'False' when its second argument is 'NeverActive'.
-}
-{- TODO: These orphan instance should be moved to the GHC.Utils.{Binary,Outputable}
-modules once TTG has progressed and the Language.Haskell.Syntax.Types module
-no longer depends on importing GHC.Hs.Doc.
--}
instance Binary XInlinePragmaGhc where
put_ bh (XInlinePragmaGhc s a) = do
put_ bh s
@@ -508,26 +496,6 @@ instance Binary XInlinePragmaGhc where
a <- get bh
return (XInlinePragmaGhc s a)
-instance forall p. IsPass p => Binary (InlinePragma (GhcPass p)) where
- put_ bh (InlinePragma s a b c) = do
- put_ bh a
- put_ bh b
- put_ bh c
- case ghcPass @p of
- GhcPs -> put_ bh s
- GhcRn -> put_ bh s
- GhcTc -> put_ bh s
-
- get bh = do
- a <- get bh
- b <- get bh
- c <- get bh
- s <- case ghcPass @p of
- GhcPs -> get bh
- GhcRn -> get bh
- GhcTc -> get bh
- return (InlinePragma s a b c)
-
instance Binary InlineSaturation where
put_ bh AnySaturation = putByte bh 0
put_ bh (AppliedToAtLeast w) = putByte bh 1 *> put_ bh w
@@ -620,5 +588,24 @@ pprInline' emptyInline (InlinePragma
AnySaturation -> empty
AppliedToAtLeast ar -> parens (text "sat-args=" <> int ar)
+{- TODO: This orphan instance should be moved to GHC.Utils.Outputable once that
+module can import 'GhcPass' without causing an import cycle.
+@
+┌──────▶ GHC.Utils.Outputable
+│ │
+│ │ Needs to access GhcPass for instance:
+│ │ Outputable (InlinePragma (GhcPass p))
+│ ▼
+│ GHC.Hs.Extension.Pass
+│ │
+│ │ For GenLocated, SrcSpan, unLoc
+│ ▼
+│ GHC.Types.SrcLoc
+│ │
+│ │ for Outputable, SDoc,
+│ │ pprFastFilePath, ppr combinators
+└───────────────┘
+@
+-}
instance forall p. IsPass p => Outputable (InlinePragma (GhcPass p)) where
ppr = pprInline
=====================================
compiler/GHC/Types/Unique.hs
=====================================
@@ -68,7 +68,8 @@ import GHC.Exts (indexCharOffAddr#, Char(..), Int(..))
import GHC.Word ( Word64 )
import Data.Char ( chr, ord, isPrint )
-import Language.Haskell.Syntax.Module.Name
+import Language.Haskell.Syntax.Basic ( FieldLabelString(..) )
+import Language.Haskell.Syntax.Module.Name ( ModuleName(..) )
{-
************************************************************************
@@ -419,6 +420,8 @@ instance Uniquable Word64 where
instance Uniquable ModuleName where
getUnique (ModuleName nm) = getUnique nm
+instance Uniquable FieldLabelString where
+ getUnique (FieldLabelString fs) = getUnique (mkFastStringShortText fs)
{-
************************************************************************
=====================================
compiler/GHC/Types/Var.hs
=====================================
@@ -129,7 +129,6 @@ import GHC.Utils.Binary
import GHC.Utils.Outputable
import GHC.Utils.Panic
-import GHC.Hs.Specificity ()
import Language.Haskell.Syntax.Specificity
import Control.DeepSeq
=====================================
compiler/GHC/Utils/Binary.hs
=====================================
@@ -1,5 +1,8 @@
{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE DerivingVia #-}
@@ -119,8 +122,13 @@ import GHC.Prelude
import Language.Haskell.Syntax.Basic
import Language.Haskell.Syntax.Binds.InlinePragma
+import Language.Haskell.Syntax.Decls.Overlap
+import Language.Haskell.Syntax.Doc
+import Language.Haskell.Syntax.Extension
import Language.Haskell.Syntax.Module.Name (ModuleName(..))
import Language.Haskell.Syntax.ImpExp.IsBoot (IsBootInterface(..))
+import Language.Haskell.Syntax.Specificity
+import Language.Haskell.Syntax.Type (PromotionFlag(..))
import {-# SOURCE #-} GHC.Types.Name (Name)
import GHC.Data.ShortText (ShortText)
@@ -164,7 +172,7 @@ import qualified Data.Map.Strict as Map
import Data.Proxy
import Data.Set ( Set )
import qualified Data.Set as Set
-import Data.Time
+import Data.Time hiding ( Nominal )
import Data.List (unfoldr)
import System.IO as IO
import System.IO.Error ( mkIOError, eofErrorType )
@@ -1926,6 +1934,85 @@ instance Binary ModuleName where
put_ bh (ModuleName fs) = put_ bh fs
get bh = do fs <- get bh; return (ModuleName fs)
+instance Binary Specificity where
+ put_ bh SpecifiedSpec = putByte bh 0
+ put_ bh InferredSpec = putByte bh 1
+
+ get bh = do
+ h <- getByte bh
+ case h of
+ 0 -> return SpecifiedSpec
+ _ -> return InferredSpec
+
+instance Binary ForAllTyFlag where
+ put_ bh Required = putByte bh 0
+ put_ bh Specified = putByte bh 1
+ put_ bh Inferred = putByte bh 2
+
+ get bh = do
+ h <- getByte bh
+ case h of
+ 0 -> return Required
+ 1 -> return Specified
+ _ -> return Inferred
+
+instance Binary HsDocStringDecorator where
+ put_ bh x = case x of
+ HsDocStringNext -> putByte bh 0
+ HsDocStringPrevious -> putByte bh 1
+ HsDocStringNamed n -> putByte bh 2 >> put_ bh n
+ HsDocStringGroup n -> putByte bh 3 >> put_ bh n
+
+ get bh = do
+ tag <- getByte bh
+ case tag of
+ 0 -> pure HsDocStringNext
+ 1 -> pure HsDocStringPrevious
+ 2 -> HsDocStringNamed <$> get bh
+ 3 -> HsDocStringGroup <$> get bh
+ t -> fail $ "HsDocStringDecorator: invalid tag " ++ show t
+
+instance Binary HsDocStringChunk where
+ put_ bh (HsDocStringChunk bs) = put_ bh bs
+ get bh = HsDocStringChunk <$> get bh
+
+instance ( Binary (XInlinePragma p)
+ , Binary (Activation p)
+ , XXInlinePragma p ~ DataConCantHappen
+ ) => Binary (InlinePragma p) where
+ put_ bh (InlinePragma s a b c) = do
+ put_ bh a
+ put_ bh b
+ put_ bh c
+ put_ bh s
+
+ get bh = do
+ a <- get bh
+ b <- get bh
+ c <- get bh
+ s <- get bh
+ return (InlinePragma s a b c)
+
+instance ( Binary (XOverlapMode p)
+ , XXOverlapMode p ~ DataConCantHappen
+ ) => Binary (OverlapMode p) where
+ put_ bh (NoOverlap s) = putByte bh 0 >> put_ bh s
+ put_ bh (Overlaps s) = putByte bh 1 >> put_ bh s
+ put_ bh (Incoherent s) = putByte bh 2 >> put_ bh s
+ put_ bh (Overlapping s) = putByte bh 3 >> put_ bh s
+ put_ bh (Overlappable s) = putByte bh 4 >> put_ bh s
+ put_ bh (NonCanonical s) = putByte bh 5 >> put_ bh s
+
+ get bh = do
+ h <- getByte bh
+ case h of
+ 0 -> get bh >>= \s -> return $ NoOverlap s
+ 1 -> get bh >>= \s -> return $ Overlaps s
+ 2 -> get bh >>= \s -> return $ Incoherent s
+ 3 -> get bh >>= \s -> return $ Overlapping s
+ 4 -> get bh >>= \s -> return $ Overlappable s
+ _ -> get bh >>= \s -> return $ NonCanonical s
+
newtype BinLocated a = BinLocated { unBinLocated :: Located a }
instance Binary a => Binary (BinLocated a) where
@@ -2088,6 +2175,26 @@ instance Binary Boxity where -- implemented via isBoxed-isomorphism to Bool
b <- get bh
pure $ if b then Boxed else Unboxed
+instance Binary Fixity where
+ put_ bh (Fixity aa ab) = do
+ put_ bh aa
+ put_ bh ab
+ get bh = do
+ aa <- get bh
+ ab <- get bh
+ return (Fixity aa ab)
+
+instance Binary FixityDirection where
+ put_ bh InfixL = putByte bh 0
+ put_ bh InfixR = putByte bh 1
+ put_ bh InfixN = putByte bh 2
+ get bh = do
+ h <- getByte bh
+ case h of
+ 0 -> return InfixL
+ 1 -> return InfixR
+ _ -> return InfixN
+
instance Binary ConInfoTable where
get bh = Binary.decode <$> get bh
@@ -2150,3 +2257,49 @@ instance Binary RuleMatchInfo where
h <- getByte bh
if h == 1 then pure ConLike
else pure FunLike
+
+instance Binary Role where
+ put_ bh Nominal = putByte bh 1
+ put_ bh Representational = putByte bh 2
+ put_ bh Phantom = putByte bh 3
+
+ get bh = do tag <- getByte bh
+ case tag of 1 -> return Nominal
+ 2 -> return Representational
+ 3 -> return Phantom
+ _ -> panic ("get Role " ++ show tag)
+
+instance Binary SrcStrictness where
+ put_ bh SrcLazy = putByte bh 0
+ put_ bh SrcStrict = putByte bh 1
+ put_ bh NoSrcStrict = putByte bh 2
+
+ get bh =
+ do h <- getByte bh
+ case h of
+ 0 -> return SrcLazy
+ 1 -> return SrcStrict
+ _ -> return NoSrcStrict
+
+instance Binary SrcUnpackedness where
+ put_ bh SrcNoUnpack = putByte bh 0
+ put_ bh SrcUnpack = putByte bh 1
+ put_ bh NoSrcUnpack = putByte bh 2
+
+ get bh =
+ do h <- getByte bh
+ case h of
+ 0 -> return SrcNoUnpack
+ 1 -> return SrcUnpack
+ _ -> return NoSrcUnpack
+
+instance Binary PromotionFlag where
+ put_ bh NotPromoted = putByte bh 0
+ put_ bh IsPromoted = putByte bh 1
+
+ get bh = do
+ n <- getByte bh
+ case n of
+ 0 -> return NotPromoted
+ 1 -> return IsPromoted
+ _ -> fail "Binary(IsPromoted): fail)"
=====================================
compiler/GHC/Utils/Outputable.hs
=====================================
@@ -115,12 +115,17 @@ import {-# SOURCE #-} GHC.Types.Name.Occurrence( OccName )
import Language.Haskell.Syntax.Basic
import Language.Haskell.Syntax.Binds.InlinePragma
import Language.Haskell.Syntax.Decls.Overlap ( OverlapMode(..) )
+import Language.Haskell.Syntax.Doc
+import Language.Haskell.Syntax.ImpExp ( NamespaceSpecifier(..) )
import Language.Haskell.Syntax.Module.Name ( ModuleName(..) )
+import Language.Haskell.Syntax.Specificity
import Language.Haskell.Syntax.Text
+import Language.Haskell.Syntax.Type ( PromotionFlag(..) )
import GHC.Prelude.Basic
import GHC.Utils.BufHandle (BufHandle, bPutChar, bPutStr, bPutFS, bPutFZS)
+import GHC.Utils.Encoding ( utf8DecodeByteString )
import GHC.Data.FastString
import qualified GHC.Utils.Ppr as Pretty
import qualified GHC.Utils.Ppr.Colour as Col
@@ -1108,6 +1113,28 @@ instance Outputable Extension where
instance Outputable ModuleName where
ppr = pprModuleName
+instance Outputable FieldLabelString where
+ ppr (FieldLabelString l) = ppr l
+
+instance Outputable ForAllTyFlag where
+ ppr Required = text "[req]"
+ ppr Specified = text "[spec]"
+ ppr Inferred = text "[infrd]"
+
+instance Outputable HsDocStringDecorator where
+ ppr HsDocStringNext = text "|"
+ ppr HsDocStringPrevious = text "^"
+ ppr (HsDocStringNamed n) = char '$' <> text n
+ ppr (HsDocStringGroup n) = text (replicate n '*')
+
+instance Outputable HsDocStringChunk where
+ ppr (HsDocStringChunk bs) = text (utf8DecodeByteString bs)
+
+-- | For compatibility with the existing @-ddump-parsed@ output, we only show
+-- the docstring.
+instance Outputable a => Outputable (WithHsDocIdentifiers a pass) where
+ ppr (WithHsDocIdentifiers s _ids) = ppr s
+
instance Outputable OsPath where
ppr p = text $ either show id (decodeUtf p)
@@ -2039,6 +2066,35 @@ instance Outputable TopLevelFlag where
ppr TopLevel = text "<TopLevel>"
ppr NotTopLevel = text "<NotTopLevel>"
+instance Outputable LexicalFixity where
+ ppr Prefix = text "Prefix"
+ ppr Infix = text "Infix"
+
+instance Outputable FixityDirection where
+ ppr InfixL = text "infixl"
+ ppr InfixR = text "infixr"
+ ppr InfixN = text "infix"
+
+instance Outputable Fixity where
+ ppr (Fixity prec dir) = hcat [ppr dir, space, int prec]
+
+instance Outputable SrcStrictness where
+ ppr SrcLazy = char '~'
+ ppr SrcStrict = char '!'
+ ppr NoSrcStrict = empty
+
+instance Outputable SrcUnpackedness where
+ ppr SrcUnpack = text "{-# UNPACK #-}"
+ ppr SrcNoUnpack = text "{-# NOUNPACK #-}"
+ ppr NoSrcUnpack = empty
+
+instance Outputable PromotionFlag where
+ ppr NotPromoted = text "NotPromoted"
+ ppr IsPromoted = text "IsPromoted"
+
+instance Outputable Role where
+ ppr = ftext . strFromRole
+
instance Outputable (OverlapMode p) where
ppr (NoOverlap _) = empty
ppr (Overlappable _) = text "[overlappable]"
@@ -2047,3 +2103,9 @@ instance Outputable (OverlapMode p) where
ppr (Incoherent _) = text "[incoherent]"
ppr (NonCanonical _) = text "[noncanonical]"
ppr (XOverlapMode _) = text "[user TTG extension]"
+
+instance Outputable (NamespaceSpecifier p) where
+ ppr NoNamespaceSpecifier{} = empty
+ ppr TypeNamespaceSpecifier{} = text "type"
+ ppr DataNamespaceSpecifier{} = text "data"
+ ppr (XNamespaceSpecifier _) = text "[user TTG extension]"
=====================================
compiler/Language/Haskell/Syntax/Basic.hs
=====================================
@@ -8,6 +8,7 @@ import Data.Data (Data)
import Data.Eq
import Data.Ord
import Data.Bool
+import Data.String (IsString(..))
import Prelude
{-
@@ -93,6 +94,20 @@ Field Labels
data Role = Nominal | Representational | Phantom
deriving (Eq, Ord, Data)
+instance NFData Role where
+ rnf Nominal = ()
+ rnf Representational = ()
+ rnf Phantom = ()
+
+-- These names are slurped into the parser code. Changing these strings
+-- will change the **surface syntax** that GHC accepts! If you want to
+-- change only the pretty-printing, do some replumbing. See
+-- mkRoleAnnotDecl in GHC.Parser.PostProcess
+strFromRole :: IsString s => Role -> s
+strFromRole Nominal = fromString "nominal"
+strFromRole Representational = fromString "representational"
+strFromRole Phantom = fromString "phantom"
+
{-
************************************************************************
* *
@@ -109,6 +124,11 @@ data SrcStrictness = SrcLazy -- ^ Lazy, ie '~'
| NoSrcStrict -- ^ no strictness annotation
deriving (Eq, Data)
+instance NFData SrcStrictness where
+ rnf SrcLazy = ()
+ rnf SrcStrict = ()
+ rnf NoSrcStrict = ()
+
-- | Source Unpackedness
--
-- What unpackedness the user requested
@@ -117,6 +137,11 @@ data SrcUnpackedness = SrcUnpack -- ^ {-# UNPACK #-} specified
| NoSrcUnpack -- ^ no unpack pragma
deriving (Eq, Data)
+instance NFData SrcUnpackedness where
+ rnf SrcNoUnpack = ()
+ rnf SrcUnpack = ()
+ rnf NoSrcUnpack = ()
+
{-
************************************************************************
* *
=====================================
compiler/Language/Haskell/Syntax/Decls/Foreign.hs
=====================================
@@ -74,7 +74,7 @@ import Control.DeepSeq
import Data.Data hiding (TyCon, Fixity, Infix)
import Data.Maybe
import Data.Eq
-import Prelude (Enum, Show)
+import Prelude (Enum, Show, seq)
{-
************************************************************************
@@ -211,6 +211,12 @@ data CCallTarget pass
| DynamicTarget (XDynamicTarget pass)
| XCCallTarget !(XXCCallTarget pass)
+instance (NFData (XStaticTarget pass), NFData (XDynamicTarget pass), NFData (XXCCallTarget pass))
+ => NFData (CCallTarget pass) where
+ rnf (StaticTarget x a b) = rnf a `seq` rnf b `seq` rnf x
+ rnf (DynamicTarget x) = rnf x
+ rnf (XCCallTarget x) = rnf x
+
data CExportSpec
-- | foreign export ccall foo :: ty
= CExportStatic
@@ -228,6 +234,11 @@ data CType pass
HText
| XCType !(XXCType pass)
+instance (NFData (XCType pass), NFData (Header pass), NFData (XXCType pass))
+ => NFData (CType pass) where
+ rnf (CType ext mh fs) = rnf ext `seq` rnf mh `seq` rnf fs
+ rnf (XCType x) = rnf x
+
-- | The filename for a C header file
data Header pass
= Header
@@ -235,6 +246,10 @@ data Header pass
HText
| XHeader !(XXHeader pass)
+instance (NFData (XHeader pass), NFData (XXHeader pass)) => NFData (Header pass) where
+ rnf (Header s h) = rnf s `seq` rnf h
+ rnf (XHeader x) = rnf x
+
data Safety
= PlaySafe -- ^ Might invoke Haskell GC, or do a call back, or
-- switch threads, etc. So make sure things are
=====================================
compiler/Language/Haskell/Syntax/Doc.hs
=====================================
@@ -65,6 +65,18 @@ data HsDocString pass
| XHsDocString
!(XXHsDocString pass)
+instance
+ ( NFData (XMultiLineDocString pass)
+ , NFData (XNestedDocString pass)
+ , NFData (XGeneratedDocString pass)
+ , NFData (XXHsDocString pass)
+ , NFData (LHsDocStringChunk pass)
+ ) => NFData (HsDocString pass) where
+ rnf (MultiLineDocString x a b) = rnf x `seq` rnf a `seq` rnf b
+ rnf (NestedDocString x a b) = rnf x `seq` rnf a `seq` rnf b
+ rnf (GeneratedDocString x a) = rnf x `seq` rnf a
+ rnf (XHsDocString x) = rnf x
+
mkGeneratedHsDocString :: XGeneratedDocString p -> HsDocStringChunk -> HsDocString p
mkGeneratedHsDocString x = GeneratedDocString x
@@ -110,3 +122,6 @@ data WithHsDocIdentifiers a pass = WithHsDocIdentifiers
{ hsDocString :: !a
, hsDocIdentifiers :: ![LIdP pass]
}
+
+instance (UnXRec pass, NFData (IdP pass), NFData a) => NFData (WithHsDocIdentifiers a pass) where
+ rnf (WithHsDocIdentifiers d i) = rnf d `seq` rnf (map (unXRec @pass) i)
=====================================
compiler/Language/Haskell/Syntax/Extension.hs
=====================================
@@ -9,6 +9,7 @@ module Language.Haskell.Syntax.Extension where
-- This module captures the type families to precisely identify the extension
-- points for GHC.Hs syntax
+import Control.DeepSeq
import Data.Type.Equality (type (~))
import Data.Data hiding ( Fixity )
@@ -16,6 +17,7 @@ import Data.Kind (Type)
import Data.Eq
import Data.Ord
+import Text.Show
{-
Note [Trees That Grow]
@@ -62,6 +64,9 @@ See also Note [IsPass] and Note [NoGhcTc] in GHC.Hs.Extension.
data NoExtField = NoExtField
deriving (Data,Eq,Ord)
+instance NFData NoExtField where
+ rnf NoExtField = ()
+
-- | Used when constructing a term with an unused extension point.
noExtField :: NoExtField
noExtField = NoExtField
@@ -95,7 +100,10 @@ can only do that if the extension field was strict (#18764).
See also [DataConCantHappen and strict fields].
-}
data DataConCantHappen
- deriving (Data,Eq,Ord)
+ deriving (Data,Eq,Ord,Show)
+
+instance NFData DataConCantHappen where
+ rnf = dataConCantHappen
-- | Eliminate a 'DataConCantHappen'. See Note [Constructor cannot occur].
dataConCantHappen :: DataConCantHappen -> a
=====================================
compiler/Language/Haskell/Syntax/ImpExp.hs
=====================================
@@ -1,4 +1,6 @@
{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-} -- Wrinkle in Note [Trees That Grow]
+ -- in module Language.Haskell.Syntax.Extension
module Language.Haskell.Syntax.ImpExp ( module Language.Haskell.Syntax.ImpExp, IsBootInterface(..) ) where
import Language.Haskell.Syntax.Doc (LHsDoc)
@@ -6,9 +8,9 @@ import Language.Haskell.Syntax.Extension
import Language.Haskell.Syntax.Module.Name
import Language.Haskell.Syntax.ImpExp.IsBoot ( IsBootInterface(..) )
-import Data.Eq (Eq)
+import Data.Eq (Eq(..))
import Data.Data (Data)
-import Data.Bool (Bool)
+import Data.Bool (Bool(..))
import Data.Maybe (Maybe)
import Data.String (String)
import Data.Int (Int)
=====================================
compiler/Language/Haskell/Syntax/Specificity.hs
=====================================
@@ -14,6 +14,7 @@ module Language.Haskell.Syntax.Specificity (
import Prelude
+import Control.DeepSeq (NFData(..))
import Data.Data
-- | ForAllTyFlag
@@ -27,6 +28,10 @@ data ForAllTyFlag = Invisible !Specificity
deriving (Eq, Ord, Data)
-- (<) on ForAllTyFlag means "is less visible than"
+instance NFData ForAllTyFlag where
+ rnf (Invisible spec) = rnf spec
+ rnf Required = ()
+
-- | Whether an 'Invisible' argument may appear in source Haskell.
data Specificity = InferredSpec
-- ^ the argument may not appear in source Haskell, it is
@@ -36,6 +41,10 @@ data Specificity = InferredSpec
-- required.
deriving (Eq, Ord, Data)
+instance NFData Specificity where
+ rnf SpecifiedSpec = ()
+ rnf InferredSpec = ()
+
pattern Inferred, Specified :: ForAllTyFlag
pattern Inferred = Invisible InferredSpec
pattern Specified = Invisible SpecifiedSpec
=====================================
compiler/ghc.cabal.in
=====================================
@@ -566,7 +566,6 @@ Library
GHC.Hs.Instances
GHC.Hs.Lit
GHC.Hs.Pat
- GHC.Hs.Specificity
GHC.Hs.Stats
GHC.HsToCore
GHC.HsToCore.Arrows
=====================================
testsuite/tests/count-deps/CountDepsParser.stdout
=====================================
@@ -113,7 +113,6 @@ GHC.Hs.ImpExp
GHC.Hs.Instances
GHC.Hs.Lit
GHC.Hs.Pat
-GHC.Hs.Specificity
GHC.Hs.Type
GHC.Hs.Utils
GHC.HsToCore.Breakpoints.Types
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/43dd2b15a2145d342cddc907a274ce6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/43dd2b15a2145d342cddc907a274ce6…
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
22 Jul '26
Simon Jakobi pushed new branch wip/sjakobi/T27534 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sjakobi/T27534
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/az/exactprint-annotation-rationalisation] 23 commits: EPA: ClsInstDecl as list in GhcPs
by Alan Zimmerman (@alanz) 22 Jul '26
by Alan Zimmerman (@alanz) 22 Jul '26
22 Jul '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
76eaab2b by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: ClsInstDecl as list in GhcPs
- - - - -
4902d4d8 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedP from OverlapMode
- - - - -
1c2136a2 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedP from CType
- - - - -
02fa4694 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedP, last use in WarningTxt
- - - - -
47e2bd6e by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedE from WarningCategory
- - - - -
e51cda17 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocateE from XCImport and XCExport
- - - - -
f1573020 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedE from HsRecFields dot
- - - - -
e0fd0448 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedE completely, last usage for pats
- - - - -
bd898429 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove AnnList (EpToken "where") usages
This is moving toward removing the parameter from AnnList completely
- - - - -
d2191400 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA remove AnnList (EpToken "rec") usages
- - - - -
2d925fec by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove last parameterised AnnList usage (EpaLocation)
Also remove the parameter
- - - - -
ff7c00da by Alan Zimmerman at 2026-07-21T20:45:24+01:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print annotations next
- - - - -
78f9383d by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Remove LocatedBC / SrcSpanBF
- - - - -
f572ffe7 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: remove unused addTrailingAnnToL. Squash appropriately
- - - - -
a534d96b by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPS: Remove NoEpTok/NoEpUniTok, using an unhelpful SrcSpan instead
Also introduce helper functions noEpTok and noEpUniTok to serve
as simple replacements in code inserting an token annotation without
location information.
- - - - -
340eba04 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Some haddock processing tweaks
- - - - -
7f4ddb2c by Alan Zimmerman at 2026-07-21T20:45:24+01:00
Some haddock exactprint tests
- - - - -
34062b4b by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: When adding comments honour trailing anns
- - - - -
377a4c01 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Uses Parsers.parseModule for exactprint tests
This is the advertised way to parse for use for exact printing in the
ghc-exactprint library, make sure we test using it.
- - - - -
4b3225af by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA Fix HsCmdDo exact print with comments
TODO: add test based on proc-do-complex-four-out.hs
- - - - -
027448d3 by Alan Zimmerman at 2026-07-21T20:45:24+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
6a0c2b0e by Alan Zimmerman at 2026-07-21T20:45:24+01:00
EPA: Add an overview doc for exact printing
- - - - -
78995316 by Simon Peyton Jones at 2026-07-21T20:45:24+01:00
Added an intro section
- - - - -
79 changed files:
- + ExactPrint.md
- compiler/GHC/Builtin/Utils.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Decls/Overlap.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Warnings.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Env.hs
- compiler/GHC/Tc/Utils/Instantiate.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Unit/Module/Warnings.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- testsuite/tests/ghc-api/T25121_status.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/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.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/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Create.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/cd5e0673aecddc426e9012482062ad…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/cd5e0673aecddc426e9012482062ad…
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/az/exactprint-annotation-rationalisation] 3 commits: WIP on removing NoEpAnn. Likely abandon
by Alan Zimmerman (@alanz) 22 Jul '26
by Alan Zimmerman (@alanz) 22 Jul '26
22 Jul '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
d981d339 by Alan Zimmerman at 2026-07-21T19:24:01+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
79c819d5 by Alan Zimmerman at 2026-07-21T19:24:01+01:00
EPA: Add an overview doc for exact printing
- - - - -
cd5e0673 by Simon Peyton Jones at 2026-07-21T19:24:01+01:00
Added an intro section
- - - - -
6 changed files:
- + ExactPrint.md
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/Language/Haskell/Syntax/Expr.hs
Changes:
=====================================
ExactPrint.md
=====================================
@@ -0,0 +1,553 @@
+# GHC Exact Print Annotations — Technical Overview
+
+## Motivation
+
+A central goal of Haskell tooling — refactoring engines, formatters, language servers, code
+generators — is to make targeted edits to a Haskell source file and then emit the result with
+_only the intended changes_ applied. A naive approach of pretty-printing the parsed AST loses
+all original layout, comments, and stylistic choices, producing diffs that are far larger than
+the semantic change.
+
+GHC's **Exact Print Annotation (EPA)** subsystem solves this by embedding enough positional
+information directly into every AST node to allow the tree to be reprinted _exactly_ as the
+original source, byte-for-byte (modulo tab expansion). When a tool modifies the AST it adjusts
+only the annotations it needs to change; all surrounding nodes reprint themselves unchanged.
+
+## Background: the syntax tree
+
+The AST of the source program is represented using the data types defined in `Language.Haskell.Syntax.*`.
+This data type uses the "Trees That Grow (TTG)" plan;
+see [Implementing trees that grow](https://gitlab.haskell.org/ghc/ghc/-/wikis/implementing-trees-that-gr…
+
+For example, in (GHC-independent) `Language.Haskell.Syntax.Expr`:
+```
+data HsExpr p
+ = ... many constructors including ...
+ | HsLet (XLet p)
+ (HsLocalBinds p)
+ (LHsExpr p)
+
+type family XLet p
+type LHsExpr p = XRec p (HsExpr p)
+
+```
+Note that
+* The `(XLet p)` field is the *extension field* of the `HsLet` data constructor,
+ where `XLet` is a type family.
+* Almost every node is wrapped in an `XRec`, another type family. That makes it easy
+ for clients to attach arbitrary information to each node.
+
+GHC specialises this data a type in `GHC.Hs.*`, as follows:
+```
+data Pass = Parsed | Renamed | Typechecked
+
+data GhcPass (c :: Pass) where
+ GhcPs :: GhcPass 'Parsed
+ GhcRn :: GhcPass 'Renamed
+ GhcTc :: GhcPass 'Typechecked
+
+type family XRec p a = r | r -> a
+type instance XRec (GhcPass p) t = XRecGhc t
+
+-- (XRecGhc tree) wraps `tree` in a GHC-specific,
+-- but pass-independent, source location
+type XRecGhc t = GenLocated (Anno t) t
+
+data GenLocated l e = L l e
+type family Anno t
+```
+So, via `XRec`, every node `e :: t` in the GHC-specific version of HsSyn is wrapped in
+a `L ann e`, where `ann :: Anno t` is the annotation on the node.
+
+Notice that there are two places we can hang information:
+* (EXT) **Constructor-specific punctuation**: the extension field of each data constructor
+ can contain information that is specific to that constructor. Example: the location
+ of the keywords `let` and `in` for the `HsLet` construct.
+
+* (XREC) **Entire-node information**: the `Anno t` field that wraps almost every node in the
+ syntax tree can contain information that is needed for *every* node. Classic example:
+ the `SrcSpan` of the node.
+
+**NOTE**: currently there is information in (XREC) that more properly belongs in (EXT).
+A refactoring project is under way to put this right.
+
+
+## File layout
+
+The subsystem lives primarily in `utils/check-exact/` and is exposed through three main layers:
+
+| Layer | Key operation | Purpose |
+|---|---|---|
+| **Parser** | `getCommentsFor` / `getPriorCommentsFor` | Attaches comments to AST nodes |
+| **Printer** | `exactPrint` / `makeDeltaAst` | Reprints the AST; converts absolute spans to relative deltas |
+| **Transform** | `Transform` monad | Edits the AST while keeping annotations consistent |
+
+---
+
+## Core Concepts
+
+### Positions: Absolute and Relative
+
+The parser records every token's position as an **absolute** source span (file, line, column).
+Before reprinting a modified AST the printer converts these to **relative** (delta) form.
+
+```
+EpaLocation
+ ├─ EpaSpan (absolute) — original (line, col) span from the parser
+ └─ EpaDelta (relative) — DeltaPos + retained original span + leading comments
+```
+
+A `DeltaPos` encodes a position as an offset from a _reference point_:
+
+```
+DeltaPos
+ ├─ SameLine col — rightward gap from the end of the preceding token; col ≥ 0
+ └─ DifferentLine lines col
+ — lines below the preceding token;
+ col is a SIGNED offset from the enclosing layout block's
+ left margin (layout_lhs + col = absolute column)
+```
+
+The different reference points for the two constructors are deliberate and are what makes
+layout-preserving moves possible (see _Splice Invariance_ below).
+
+---
+
+## Invariants
+
+### 1. Every Node Is Self-Contained
+
+Each located AST node carries a `NodeAnnotation` (the `EpAnn ann` wrapper) containing:
+
+- **`anchor`** — an `EpaLocation` marking the top-left corner of the node's bounding rectangle.
+ This is the coordinate origin for the node's internal layout.
+- **`anns`** — node-kind-specific annotation payload (keyword token positions, bracket tokens,
+ pragma text, etc.).
+- **`comments`** — all `EpaComment` values logically owned by this node.
+
+No separate annotation map exists. The annotation is embedded directly in the `GenLocated`
+wrapper via the `Anno` type family (the "Trees That Grow" mechanism). The annotation type for
+each node kind is determined statically by its Haskell type.
+
+### 2. Trailing Annotations Are Separate from the Bounding Box
+
+Punctuation that separates list elements — trailing commas, semicolons, vertical bars, and
+constraint arrows — is stored in a `TrailingAnn` list that is _outside_ the node's bounding box.
+
+```
+TrailingAnn
+ ├─ semi — ';'
+ ├─ comma — ','
+ ├─ vbar — '|'
+ └─ darrow — '=>' or '⇒'
+```
+
+The `HasTrailing` typeclass exposes a uniform `trailing` / `setTrailing` interface over all
+annotation types. The printer uses this interface to extract trailing glue without inspecting
+the concrete annotation type.
+
+**Consequence for tooling:** when moving, copying, or deleting a node, a tool operates on the
+anchor span and can transfer or discard trailing annotations independently. There is no risk
+of accidentally duplicating or losing a separator when restructuring a list.
+
+### 3. The Bounding Box Invariant
+
+The `anchor` span is the bounding box of everything the node _owns_:
+
+- **Leading comments** are printed _before_ the anchor and lie _outside_ the bounding box.
+ They may appear at any indentation.
+- **Trailing annotations** are printed _after_ the anchor's end and also lie outside it.
+- The anchor's start position is the top-left corner of the box; the parser widens the span
+ (via `widenAnchorT` / `widenAnchorS`) to cover every token that syntactically belongs to
+ the node.
+
+```
+ leading comment ← outside bounding box; before anchor
+ [anchor start]
+ token token token ← inside bounding box
+ token token
+ [anchor end]
+ , ← trailing annotation; outside bounding box
+```
+
+### 4. Splice Invariance
+
+Moving an entire subtree to a different column requires updating _only the root node's entry
+delta_. No descendant delta needs to change.
+
+This works because `DifferentLine` deltas store the column as `absolute_col − layout_lhs`, where
+`layout_lhs` is the left margin of the enclosing layout block. When the root anchor moves, the
+printer adopts the new column as the new `layout_lhs`. Every child that has a `DifferentLine`
+delta recomputes its absolute column as `new_layout_lhs + stored_col`, sliding the entire subtree
+to the right or left by the same amount.
+
+`SameLine` deltas are unaffected by relocation because they record a gap from the immediately
+preceding token, not from the layout margin.
+
+**Known exceptions:** layout-block terminators such as `in`, `where`, and `of` intentionally
+appear to the _left_ of the block they close. Their `DifferentLine.col` is negative. The
+printer's validity check (`isGoodDelta`) accepts this: for `DifferentLine` it only requires
+`lines > 0`, not that `col ≥ 0`.
+
+### 5. The Zero-Column Invariant for List Items
+
+Nodes that appear as direct children of a layout list (a `where` clause, a `do` block, an
+export list, etc.) always store `DifferentLine(n, 0)` — a zero column offset from the list's
+layout origin.
+
+The list's layout origin is recorded in `AnnList.al_anchor`. The absolute column of each item
+is therefore `al_anchor.col + 0 = al_anchor.col`. This has two consequences:
+
+1. **Reordering is free.** Items can be moved within the list without updating any individual
+ delta; all items already share the same column.
+2. **Relocation is cheap.** Moving the entire list to a new indentation requires updating only
+ `al_anchor`; all items self-correct automatically.
+
+---
+
+## Data Flow
+
+```
+Source file
+ │
+ ▼
+┌─────────────┐
+│ Parser │ getCommentsFor / getPriorCommentsFor / getFinalCommentsFor
+│ (GHC.Parser│ → allocates EpaComments to each AST node (unbalanced form)
+│ + Lexer) │ → all EpaLocations are absolute (EpaSpan)
+└──────┬──────┘
+ │ ParsedSource (absolute annotations)
+ ▼
+┌──────────────────┐
+│ makeDeltaAst │ converts EpaSpan → EpaDelta for every can_update node
+│ (exactPrint.hs) │ adjustDeltaForOffset stores col as (absolute - layout_lhs)
+└──────┬───────────┘
+ │ ParsedSource (relative annotations)
+ ▼
+┌──────────────────────┐
+│ Transform monad │ structural edits: insert/remove/reorder declarations
+│ (Transform.hs) │ setEntryDP, transferEntryDP, balanceCommentsList, …
+└──────┬───────────────┘
+ │ modified ParsedSource
+ ▼
+┌──────────────────┐
+│ exactPrint │ single-pass traversal; undelta(prior_end, dp, layout_lhs)
+│ (ExactPrint.hs) │ comment interleaving; layout block tracking
+└──────┬───────────┘
+ │
+ ▼
+ Source text (byte-for-byte faithful to the original, modulo edits)
+```
+
+---
+
+## The Parser Phase
+
+### Comment Allocation
+
+The GHC parser maintains a comment queue (`comment_q`) of `EpaComment` values seen in the token
+stream but not yet attached to any node. As each grammar production is reduced, the parser drains
+matching comments from the queue:
+
+- `getCommentsFor span node` — moves all comments whose span falls _inside_ `span` into the
+ node's `EpAnn.comments.prior` list.
+- `getPriorCommentsFor span node` — additionally captures comments that fall _just before_ the
+ span (used for top-level declarations to capture preceding Haddock comments).
+- `getFinalCommentsFor module_node` — at EOF, drains the entire queue into the module node.
+
+**Guarantee:** each comment is allocated to exactly one node; the queue is drained monotonically.
+All `EpaComments` produced by the parser are in the `unbalanced` state (only `prior`, no
+`following`).
+
+### `Anno` Type Family
+
+The annotation type for each AST node kind is chosen by the `Anno` type family. Common
+specialisations:
+
+| Type alias | Annotation payload | Used for |
+|---|---|---|
+| `SrcSpanAnnA` | `AnnListItem` | Most expressions, patterns, declarations |
+| `SrcSpanAnnN` | `NameAnn` | Name occurrences |
+| `SrcSpanAnnL` | `AnnList ()` | Plain layout lists |
+| `SrcSpanAnnLW` | `AnnList EpToken` | `where`-clause lists |
+| `SrcSpanAnnP` | `AnnPragma` | `{-# … #-}` pragmas |
+| `SrcSpanAnnC` | `AnnContext` | Constraint contexts `C a =>` |
+
+---
+
+## The Printing Phase
+
+### The `ExactPrint` Typeclass
+
+Every AST constructor that can appear in a located position has an `ExactPrint` instance:
+
+```haskell
+class (Typeable a) => ExactPrint a where
+ getAnnotationEntry :: a -> Entry -- extract anchor + trailing + comments
+ setAnnotationAnchor :: a -> EpaLocation
+ -> [TrailingAnn] -> EpAnnComments -> a -- write anchor back
+ exact :: (Monad m, Monoid w) => a -> EP w m a -- print this constructor
+```
+
+`markAnnotated a = enterAnn (getAnnotationEntry a) a` is the single recursive call site.
+`enterAnn` handles all the cross-cutting concerns (comments, layout, anchor write-back) and
+then delegates to `exact` for the constructor-specific tokens and child traversals. There are
+several hundred `ExactPrint` instances, one for each GHC AST constructor.
+
+The same instance serves both `exactPrint` and `makeDeltaAst`: the output options (`EPOptions`)
+determine whether tokens are accumulated into a string or discarded.
+
+### `makeDeltaAst`
+
+Converts every `EpaSpan` anchor to `EpaDelta` form in a single pass over the AST. The delta
+is computed as:
+
+```
+raw_delta = ss2delta(prior_end_pos, anchor_start)
+stored_delta = adjustDeltaForOffset(layout_lhs, raw_delta)
+ = DifferentLine(lines, absolute_col - layout_lhs)
+```
+
+The inverse during printing (`undelta`) recovers the absolute column:
+
+```
+absolute_col = layout_lhs + stored_delta.col -- DifferentLine
+absolute_col = prior_col + stored_delta.col -- SameLine
+```
+
+This shared traversal runs with no-op output options; no source text is accumulated.
+
+### `exactPrint`
+
+Runs the same traversal as `makeDeltaAst` but with string-accumulating options. For each node:
+
+1. Leading comments (in `EpaDelta.leading_comments`) are printed before the anchor.
+2. The anchor position is resolved (absolute from `EpaSpan`, or via `undelta` for `EpaDelta`).
+3. If `mark_layout` is set, the anchor column becomes the new `layout_lhs` for the block.
+4. Children are visited recursively with the updated layout state.
+
+### Comment Interleaving
+
+Two strategies are used depending on the anchor form:
+
+- **Absolute anchor (`EpaSpan`):** the printer maintains a pool of pending comments sorted by
+ source position. Before each token it emits all pooled comments whose position precedes the
+ token (`printCommentsBefore`, via `commentAllocationBefore`).
+- **Relative anchor (`EpaDelta`):** comments are pre-attached to the node's annotation by
+ `makeDeltaAst` or `balanceCommentsList`. The entire pool is flushed unconditionally via
+ `flushComments`, then the node's own attached comments are printed in order via `printOneComment`.
+
+CPP-injected comments may carry fake filenames ("CPP", "LINE", "SHEBANG") in their spans.
+Ordering always uses `ss2pos` (line, column only) rather than the full `RealSrcSpan` comparator,
+which would sort by filename first and produce incorrect relative ordering.
+
+### Parentheses in Infix Declaration Heads via the Comment Machinery
+
+Infix type-level declarations — type synonyms, data types, class declarations, type families, and
+GADT constructors — may carry optional parentheses around the operator/constructor in the
+declaration head. For example:
+
+```haskell
+type (a `MyOp` b) = ...
+class (a `MyClass` b) where ...
+data (a `MyData` b) = ...
+```
+
+These parentheses are stored as _lists_ of `EpToken` values (`ops`, `cps`) on the declaration's
+annotation (`AnnSynDecl`, `AnnClassDecl`, `AnnFamilyDecl`, `AnnDataDefn`, `AnnConDeclGADT`, and
+`AnnFunRhs` for infix function patterns). They cannot be emitted at a fixed point in the
+structural traversal because the infix head visits the type constructor and its arguments in an
+order that does not naturally interleave with the surrounding parens.
+
+The solution is `epTokensToComments "(" ops` / `epTokensToComments ")" cps`: each present
+`EpToken` is converted to a synthetic `Comment` (with `keyword_origin = "("` or `")"`) and
+injected into the comment pool. The pool is ordered by source position, so these synthetic
+parens are automatically emitted at exactly the right location relative to the other tokens,
+without requiring any explicit position tracking in the `exact` instance.
+
+During `makeDeltaAst`, each synthetic paren's computed delta is captured via `applyComment` and
+embedded in the adjacent token's `EpaDelta` constructor, so the round-trip preserves paren
+positions in delta form too.
+
+### Mixed Bind/Sig Lists
+
+The GHC AST stores function bindings and type signatures in separate sub-lists. The printer
+merges them using a sort key on the `ValBinds` extension point:
+
+- **`NoAnnSortKey` (span order):** sorts by original source span — correct immediately after
+ parsing while spans are still trustworthy.
+- **`AnnSortKey [BindTag]` (tag order):** a sequence of `bind_tag` / `sig_tag` tokens recording
+ the exact interleaving. Set whenever `replaceDeclarations` is called; the printer follows the
+ sequence exactly, enabling caller-controlled reordering.
+
+Class and instance bodies use the same mechanism with four sub-lists and `DeclTag`.
+
+---
+
+## The Transform Phase
+
+The `Transform` monad wraps a state carrying a unique-span counter and a debug log. It exposes
+operations for structural edits while keeping annotations consistent.
+
+The unique-span counter exists to support `uniqueSrcSpanT`, which allocated synthetic `SrcSpan`
+values (line = -1) for freshly created AST nodes. That function is now dead code: it is exported
+for API compatibility but is called nowhere in GHC or `check-exact`. New nodes are given
+`EpaDelta` locations directly, which need no unique span. The counter field in `TransformState`
+is therefore vestigial and should be removed together with `uniqueSrcSpanT` and `isUniqueSrcSpan`
+in a future cleanup.
+
+### Declaration Access
+
+- **`getDeclarations node`** — returns the declaration list of a module, match, let-binding,
+ pattern-binding where clause, or class/instance body as a `DeclarationList`.
+- **`replaceDeclarations node new_decls`** — replaces the list; implicitly records the new
+ ordering as a tag-order `AnnSortKey`, so the printer will honour the caller's order regardless
+ of original source spans. **Passing an empty list collapses the underlying `HsLocalBinds` to
+ `EmptyLocalBinds`, which drops the `where` keyword annotation entirely** — this is not a no-op
+ on the container structure.
+
+A key responsibility of `getDeclarations` is **normalising `ValBinds`**. Inside a
+`HsLocalBinds` / `ValBinds` node (a `where` clause, `let` expression, `let` statement, or
+pattern-binding where clause), GHC stores function bindings and type signatures in two _separate_
+sub-collections: a `Bag` of `LHsBind` and a `[LSig]`. These are physically disjoint from each
+other and have no guaranteed ordering relative to each other.
+
+`getDeclarations` (via `hsDeclsLocalBinds` → `hsDeclsValBinds` → `orderedDeclsBinds`) merges
+them back into a single `[LHsDecl]` in source order:
+
+- **`NoAnnSortKey` (freshly parsed):** sorts the combined list by `RealSrcSpan`, recovering the
+ original interleaved order from the absolute source positions.
+- **`AnnSortKey [BindTag]` (after any structural edit):** replays the tag sequence recorded by
+ `captureOrderBinds`, drawing from the binds bag or sigs list according to each tag.
+
+The result is a physically ordered, uniform list of `LHsDecl` values — binds wrapped in `ValD`,
+sigs wrapped in `SigD` — that the exact-print and transformation machinery can traverse and edit
+without knowing or caring that the underlying storage splits them across two separate containers.
+`replaceDeclarations` is the inverse: it re-separates the uniform list back into the two
+sub-collections via `decl2Bind` / `decl2Sig` and records a fresh `AnnSortKey` for the new order.
+
+### Positioning Nodes
+
+- **`setEntryDP node dp`** — sets the entry delta of a node, converting its anchor to relative
+ form. Must be called before inserting a freshly constructed node.
+- **`getEntryDP node`** — reads the current entry delta (default `SameLine 0` if absent).
+- **`transferEntryDP source target`** — moves the entry delta _and_ leading comments from one
+ node to another. Used when a removed declaration's successor needs to inherit its spacing.
+
+### Inserting and Removing Declarations
+
+**`insertDeclaration node decl point`** inserts `decl` at `point` (start, end, before/after a
+named item). The inserted node must already have a relative anchor. All existing declarations
+are comment-balanced before the structural change.
+
+**`removeDeclaration node decl`** removes `decl`. The caller must then call `transferEntryDP`
+or `setEntryDP` on the new first declaration to absorb the gap.
+
+### Comment Balancing
+
+After parsing, all comments are in the `prior` list of the nodes that saw them first. The
+`balanceComments` / `balanceCommentsList` operations redistribute them:
+
+- A comment ≤ 1 blank line below declaration D stays with D as a `following` comment.
+- A comment > 1 blank line below D migrates to the `prior` list of D's successor.
+
+The operation is **idempotent**: running it twice produces the same result because `balanced` is
+a terminal state (`unbalanced → balanced`; no transition out of `balanced` is defined).
+
+For `FunBind` nodes the balancing is hierarchical: comments are first balanced at the binding
+level, then distributed among the individual match equations within the binding.
+
+### Capturing Spacing Before Edits
+
+Before removing or reordering declarations, spacing must be encoded in relative form so it
+survives the structural change. If `makeDeltaAst` has **not** been called, the AST still
+carries absolute `EpaSpan` anchors; the spacing functions below derive relative deltas from those
+absolute positions and write them back, making subsequent structural edits safe. If `makeDeltaAst`
+**has** already been called, the anchors are already in `EpaDelta` form and these functions are
+idempotent — they recompute the same delta values that `makeDeltaAst` already stored.
+
+- **`captureLineSpacing decls`** — sets each declaration's entry delta lines to the actual blank
+ lines between it and its predecessor.
+- **`captureMatchLineSpacing decl`** — same, within the match equations of a `FunBind`.
+- **`captureTypeSigSpacing sig`** — captures spacing within a multi-name type signature.
+- **`addModuleCommentOrigDeltas module`** — converts module-level comment spans from absolute to
+ relative form before the module's declaration list is replaced.
+
+---
+
+## CPP Support
+
+Source files using `{-# LANGUAGE CPP #-}` present a special challenge: the GHC parser sees
+the _preprocessed_ text, which may differ substantially from the original source (directives
+removed, macros expanded).
+
+### Pipeline
+
+```
+Original source
+ │
+ ├─ getPreprocessorAsComments ─→ directive line tokens (real filename)
+ │
+ ├─ stripLinePragmas / tokeniseOriginalSrc ─→ directive-stripped token stream
+ │
+ └─ getPreprocessedSrcDirect ─→ C-preprocessed text
+ └─ lexTokenStream ─→ post-CPP token stream
+ │
+ └─ getCppTokens ─→ three-way merge
+ ─→ MergedCppComments
+```
+
+`getCppTokens` identifies directive tokens present in the original source but absent from the
+preprocessed output (consumed by CPP) and converts them to synthetic `ITlineComment` tokens.
+These are merged with the directive lines extracted directly to form `injected_comments`.
+
+**Limitation:** the three-way merge is correct only in `-nomacro` mode. Macro expansion
+introduces expanded tokens that are not recoverable by span-based matching.
+
+### Insertion
+
+`insertCppComments module injected_comments` splices the synthetic comments back into the parsed
+AST using a bottom-up traversal (`everywhereM`): each `EpAnn` node claims the injected comments
+whose source span it encloses. Remaining comments are distributed to module-level positions by
+`insertTopLevelCppComments`. CPP-aware ordering (`ss2pos`, ignoring filenames) is used
+throughout.
+
+After `insertCppComments`, the round-trip guarantee extends to CPP-enabled sources:
+`exactPrint(insertCppComments(parseModuleEpAnnsWithCpp(f)))` reproduces `f` byte-for-byte.
+
+---
+
+## Correctness Guarantees
+
+| Guarantee | Statement |
+|---|---|
+| **RoundTripFidelity** | For any file `f` parsed by GHC, `exactPrint(makeDeltaAst(parse(f)))` is byte-identical to `f` (modulo tab expansion). |
+| **AllCommentsEmitted** | After a complete `exactPrint` traversal, every comment attached to the AST has been emitted exactly once and the pending pool is empty. |
+| **CursorNonDecreasing** | The output cursor only moves forward; tokens are emitted in source order. |
+| **CommentsAllocatedAtMostOnce** | Each comment in the parser's queue is moved to exactly one AST node; once removed it is never re-added. |
+| **BalanceIsIdempotent** | `balanceCommentsList` can be called multiple times on the same list without duplicating or losing comments. |
+| **ReplacePreservesCallerOrder** | After `replaceDeclarations(node, new_decls)`, the printer outputs declarations in the same order as `new_decls`. |
+| **RoundTripWithCpp** | For CPP-enabled files, `exactPrint(insertCppComments(parseModuleEpAnnsWithCpp(f)))` reproduces `f` byte-for-byte (no-macro mode only). |
+
+---
+
+## Typical Tool Workflow
+
+A refactoring tool that wants to add a new top-level declaration does the following:
+
+```
+1. parseModuleEpAnnsWithCpp opts libdir file -- parse with CPP support
+2. insertCppComments parsed_source comments -- re-insert CPP/LINE/SHEBANG comments
+3. makeDeltaAst parsed_source -- convert absolute spans to deltas
+4. runTransform do
+ addModuleCommentOrigDeltas module -- protect module-level comments
+ decls ← getDeclarations module -- read current declaration list
+ captureLineSpacing decls -- encode spacing before edits
+ balanceCommentsList decls -- redistribute comments
+ setEntryDP new_decl (DifferentLine 2 0) -- position the new declaration
+ replaceDeclarations module (decls ++ [new_decl])
+5. exactPrint modified_source -- emit the result
+```
+
+The result is the original source with the new declaration appended, all original comments and
+layout preserved, and a diff that contains only the added lines.
=====================================
compiler/GHC/Hs/Expr.hs
=====================================
@@ -2620,7 +2620,7 @@ FieldLabelStrings
instance (UnXRec p, Outputable (XRec p FieldLabelString)) => Outputable (FieldLabelStrings p) where
ppr (FieldLabelStrings flds) =
- hcat (punctuate dot (toList $ NE.map (ppr . unXRec @p) flds))
+ hcat (punctuate dot (toList $ NE.map ppr flds))
instance (UnXRec p, Outputable (XRec p FieldLabelString)) => OutputableBndr (FieldLabelStrings p) where
pprInfixOcc = pprFieldLabelStrings
@@ -2632,7 +2632,7 @@ instance (UnXRec p, Outputable (XRec p FieldLabelString)) => OutputableBndr (Lo
pprFieldLabelStrings :: forall p. (UnXRec p, Outputable (XRec p FieldLabelString)) => FieldLabelStrings p -> SDoc
pprFieldLabelStrings (FieldLabelStrings flds) =
- hcat (punctuate dot (toList $ NE.map (ppr . unXRec @p) flds))
+ hcat (punctuate dot (toList $ NE.map ppr flds))
pprPrefixFastString :: FastString -> SDoc
pprPrefixFastString fs = pprPrefixOcc (mkVarUnqual fs)
@@ -2677,8 +2677,6 @@ type instance Anno FastString = EpAnnCO
type instance Anno HText = EpAnnCO
-- Used in HsQuasiQuote and perhaps elsewhere
-type instance Anno (DotFieldOcc (GhcPass p)) = EpAnnCO
-
instance (HasAnnotation (Anno a))
=> WrapXRec (GhcPass p) a where
wrapXRec = noLocA
=====================================
compiler/GHC/Parser.y
=====================================
@@ -3283,12 +3283,12 @@ aexp2 :: { ECP }
amsA' (sLL $1 $> $ HsCmdArrForm (AnnList (glRM $1) (ListBanana (epUniTok $1) (epUniTok $4)) [] []) $2 Prefix
(reverse $3)) }
-projection :: { Located (NonEmpty (LocatedAn NoEpAnns (DotFieldOcc GhcPs))) }
+projection :: { Located (NonEmpty (DotFieldOcc GhcPs)) }
projection
-- See Note [Whitespace-sensitive operator parsing] in GHC.Parsing.Lexer
: projection TIGHT_INFIX_PROJ field
- { sLL $1 $> ((sLLa $2 $> $ DotFieldOcc (AnnFieldLabel (Just $ epTok $2)) $3) `NE.cons` unLoc $1) }
- | PREFIX_PROJ field { sLL $1 $> ((sLLa $1 $> $ DotFieldOcc (AnnFieldLabel (Just $ epTok $1)) $2) :| [])}
+ { sLL $1 $> ((DotFieldOcc (AnnFieldLabel (Just $ epTok $2)) $3) `NE.cons` unLoc $1) }
+ | PREFIX_PROJ field { sLL $1 $> ((DotFieldOcc (AnnFieldLabel (Just $ epTok $1)) $2) :| [])}
splice_exp :: { LHsExpr GhcPs }
: splice_untyped { fmap (HsUntypedSplice noExtField) (reLoc $1) }
@@ -3772,11 +3772,11 @@ fbind :: { forall b. DisambECP b => PV (Fbind b) }
fmap Right $ mkHsProjUpdatePV l (L l fields) var isPun Nothing
}
-fieldToUpdate :: { Located [LocatedAn NoEpAnns (DotFieldOcc GhcPs)] }
+fieldToUpdate :: { Located [DotFieldOcc GhcPs] }
fieldToUpdate
-- See Note [Whitespace-sensitive operator parsing] in Lexer.x
- : fieldToUpdate TIGHT_INFIX_PROJ field { sLL $1 $> ((sLLa $2 $> (DotFieldOcc (AnnFieldLabel $ Just $ epTok $2) $3)) : unLoc $1) }
- | field { sL1 $1 [sL1a $1 (DotFieldOcc (AnnFieldLabel Nothing) $1)] }
+ : fieldToUpdate TIGHT_INFIX_PROJ field { sLL $1 $> ((DotFieldOcc (AnnFieldLabel $ Just $ epTok $2) $3) : unLoc $1) }
+ | field { sL1 $1 [DotFieldOcc (AnnFieldLabel Nothing) $1] }
-----------------------------------------------------------------------------
-- Implicit Parameter Bindings
=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -3768,7 +3768,7 @@ starSym NormalSyntax = fsLit "*"
-----------------------------------------
-- Bits and pieces for RecordDotSyntax.
-mkRdrGetField :: LHsExpr GhcPs -> LocatedAn NoEpAnns (DotFieldOcc GhcPs)
+mkRdrGetField :: LHsExpr GhcPs -> DotFieldOcc GhcPs
-> HsExpr GhcPs
mkRdrGetField arg field =
HsGetField {
@@ -3777,11 +3777,11 @@ mkRdrGetField arg field =
, gf_field = field
}
-mkRdrProjection :: NonEmpty (LocatedAn NoEpAnns (DotFieldOcc GhcPs)) -> AnnProjection -> HsExpr GhcPs
+mkRdrProjection :: NonEmpty (DotFieldOcc GhcPs) -> AnnProjection -> HsExpr GhcPs
mkRdrProjection flds anns =
HsProjection {
proj_ext = anns
- , proj_flds = fmap unLoc flds
+ , proj_flds = flds
}
mkRdrProjUpdate :: SrcSpanAnnA -> Located (NonEmpty (LocatedAn NoEpAnns (DotFieldOcc GhcPs)))
=====================================
compiler/GHC/Tc/Types/Origin.hs
=====================================
@@ -627,9 +627,9 @@ exprCtOrigin (HsQual {}) = Shouldn'tHappenOrigin "constraint context" -
exprCtOrigin (HsFunArr {}) = Shouldn'tHappenOrigin "function arrow" -- See Note [Types in terms]
exprCtOrigin (ExplicitList {}) = ListOrigin
exprCtOrigin (HsIf {}) = IfThenElseOrigin
-exprCtOrigin (HsProjection _ p) = RecordFieldProjectionOrigin (FieldLabelStrings $ fmap noLocA p)
+exprCtOrigin (HsProjection _ p) = RecordFieldProjectionOrigin (FieldLabelStrings p)
exprCtOrigin (RecordUpd{}) = RecordUpdOrigin
-exprCtOrigin (HsGetField _ _ f) = GetFieldOrigin (fmap (mkFastStringShortText . field_label) $ dfoLabel (unLoc f))
+exprCtOrigin (HsGetField _ _ f) = GetFieldOrigin (fmap (mkFastStringShortText . field_label) $ dfoLabel f)
exprCtOrigin (XExpr (ExpandedThingRn (HSE o _))) = hsCtxtCtOrigin o
exprCtOrigin (XExpr (HsRecSelRn f)) = OccurrenceOfRecSel $ L (getLoc $ foLabel f) (foExt f)
=====================================
compiler/Language/Haskell/Syntax/Expr.hs
=====================================
@@ -117,7 +117,7 @@ values (see function @mkRdrRecordUpd@ in 'GHC.Parser.PostProcess').
type LFieldLabelStrings p = XRec p (FieldLabelStrings p)
newtype FieldLabelStrings p =
- FieldLabelStrings (NonEmpty (XRec p (DotFieldOcc p)))
+ FieldLabelStrings (NonEmpty (DotFieldOcc p))
-- Field projection updates (e.g. @foo.bar.baz = 1@). See Note
-- [RecordDotSyntax field updates].
@@ -452,7 +452,7 @@ data HsExpr p
| HsGetField {
gf_ext :: XGetField p
, gf_expr :: LHsExpr p
- , gf_field :: XRec p (DotFieldOcc p)
+ , gf_field :: DotFieldOcc p
}
-- | Record field selector. e.g. @(.x)@ or @(.x.y)@
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b387aa7adc89a4c18a8190d45d43e1…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b387aa7adc89a4c18a8190d45d43e1…
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/az/exactprint-annotation-rationalisation] 23 commits: Improve error messages for invalid record wildcards
by Alan Zimmerman (@alanz) 22 Jul '26
by Alan Zimmerman (@alanz) 22 Jul '26
22 Jul '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
c2f6dcd4 by Sasha Bogicevic at 2026-07-20T10:31:56+02:00
Improve error messages for invalid record wildcards
Record wildcard hints are now shown in more contexts and include
constructor arity; matching with `..` on a fieldless constructor
now produces a dedicated error message.
Fixes #21101
- - - - -
4c02e76b by Duncan Coutts at 2026-07-21T10:37:21-04:00
Mark test T27105 as fragile, citing issue #27522
Scheduler fairness is fine, except when it isn't. And it isn't on CI
machines surprisingly often! See the issue for details.
- - - - -
d29ecece by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Keep decls together in ClassDecl
Similar to 1718230f4d3d19d8c49c0e5d496cb0fb6f399528 for HsValBindsLR,
this commit updates ClassDecl so that it no longer splits out the
assorted `LHsDecl GhcPs` until the renamer.
It does this by inserting a type family (separate from the classic TTG one) for this.
So
data TyClDecl
...
| ClassDecl {
...
tcdDecls :: XClassDecls pass
with
type instance XClassDecls GhcPs = [LHsDecl GhcPs]
type instance XClassDecls GhcRn = ClassDeclX GhcRn
type instance XClassDecls GhcTc = ClassDeclX GhcTc
data ClassDeclX pass
= ClassDeclX { tcdSigs :: [LSig pass], -- ^ Methods' signatures
tcdMeths :: LHsBinds pass, -- ^ Default methods
tcdATs :: [LFamilyDecl pass], -- ^ Associated types;
tcdATDefs :: [LTyFamDefltDecl pass], -- ^ Associated type defaults
tcdDocs :: [LDocDecl pass] -- ^ Haddock docs
}
- - - - -
43a54fd3 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: ClsInstDecl as list in GhcPs
- - - - -
fbfc2751 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedP from OverlapMode
- - - - -
feb416ac by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedP from CType
- - - - -
e6581225 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedP, last use in WarningTxt
- - - - -
87426abf by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedE from WarningCategory
- - - - -
6d9d97b2 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocateE from XCImport and XCExport
- - - - -
d1e07582 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedE from HsRecFields dot
- - - - -
07f2acce by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedE completely, last usage for pats
- - - - -
76a461a7 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove AnnList (EpToken "where") usages
This is moving toward removing the parameter from AnnList completely
- - - - -
3c4c9362 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA remove AnnList (EpToken "rec") usages
- - - - -
79e2f451 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove last parameterised AnnList usage (EpaLocation)
Also remove the parameter
- - - - -
1807bcc6 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print annotations next
- - - - -
577ba7c2 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Remove LocatedBC / SrcSpanBF
- - - - -
336a113f by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: remove unused addTrailingAnnToL. Squash appropriately
- - - - -
78957abb by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPS: Remove NoEpTok/NoEpUniTok, using an unhelpful SrcSpan instead
Also introduce helper functions noEpTok and noEpUniTok to serve
as simple replacements in code inserting an token annotation without
location information.
- - - - -
53d0b7ce by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Some haddock processing tweaks
- - - - -
33660fae by Alan Zimmerman at 2026-07-21T18:30:13+01:00
Some haddock exactprint tests
- - - - -
0808acd9 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: When adding comments honour trailing anns
- - - - -
c66d7de8 by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA: Uses Parsers.parseModule for exactprint tests
This is the advertised way to parse for use for exact printing in the
ghc-exactprint library, make sure we test using it.
- - - - -
b387aa7a by Alan Zimmerman at 2026-07-21T18:30:13+01:00
EPA Fix HsCmdDo exact print with comments
TODO: add test based on proc-do-complex-four-out.hs
- - - - -
99 changed files:
- + changelog.d/21101
- compiler/GHC/Builtin/Utils.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Decls/Overlap.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Warnings.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/Utils/Env.hs
- compiler/GHC/Tc/Utils/Instantiate.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Types/GREInfo.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Unit/Module/Warnings.hs
- compiler/Language/Haskell/Syntax/Binds.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- testsuite/tests/concurrent/should_run/T27105.hs
- testsuite/tests/concurrent/should_run/all.T
- testsuite/tests/ghc-api/T25121_status.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/haddock_examples/haddock.Test.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/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.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/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- + testsuite/tests/rename/should_fail/T21101.hs
- + testsuite/tests/rename/should_fail/T21101.stderr
- testsuite/tests/rename/should_fail/T9815.stderr
- testsuite/tests/rename/should_fail/T9815b.stderr
- testsuite/tests/rename/should_fail/T9815bghci.stderr
- testsuite/tests/rename/should_fail/T9815ghci.stderr
- testsuite/tests/rename/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Backends/LaTeX.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Create.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/cf57b346bb530d37ca67b9bc942f3a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/cf57b346bb530d37ca67b9bc942f3a…
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