Duncan Coutts pushed to branch wip/dcoutts/issue-26717 at Glasgow Haskell Compiler / GHC Commits: 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@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. - - - - - 565c2554 by Duncan Coutts at 2026-07-21T01:22:14+01:00 Eliminate STM_AWOKEN It was used as nullary closure for the block_info.closure in the case of a thread being awoken after an STM transaction. However, while it was written, it was never read, so contributed nothing to the behaviour. Furthermore, in the only place it was set (in tryWakeupThread) the why_blocked was immediately overwritten by the NotBlocked status, and the block_info was updated accordingly (by appendToRunQueue). So it didn't even serve a purpose of clarifying an intermediate state, there really was no such intermediate state. Cleaning this up will allow the BlockedOnSTM case to follow the same pattern as the other why_blocked cases that do not use the block_info, and in turn this reduces the number of different categories. - - - - - c8bd011e by Duncan Coutts at 2026-07-21T01:22:14+01:00 Document that eventlog thread stop code ThreadBlocked is no longer used It has not been used since GHC 7.0.x (2011). In 7.2 all the BlockedOn* codes were added, and these were and are used instead of ThreadBlocked. - - - - - 4acb24c4 by Duncan Coutts at 2026-07-21T01:25:14+01:00 Add a proper mapping to eventlog external thread stop status That is the mapping from rts-internal codes, to the coes used in the status field in the eventlog EVENT_STOP_THREAD event. See issue #9003 for what goes wrong when we mess this up. In that ticket, people note that we should really not require the internal tso->why_blocked codes to leak into the external eventlog thread stop codes. The same principle applies to the StgThreadReturnCode. This change properly separates them, and explicitly maps between them using a pair of (compact, constant) tables. These tables are pretty small (with no alignment constraints) and will soon shrink so it seems a sensible tradeoff. We also introduce and use proper EVENT_STOP_THREAD constants in the event log format header. Previously there was not specification in the code for these (only in the docs): the values were encoded into the conversion code. This will allow us to renumber the internal why_blockd codes without breaking the eventlog output. - - - - - bc5f211f by Duncan Coutts at 2026-07-21T01:25:17+01:00 Remove unused tso->block_info.wakeup member. Presumably it was used once, but not now. - - - - - 4c722f11 by Duncan Coutts at 2026-07-21T01:29:34+01:00 Document StgTSOBlockInfo to say what cases use what members In principle, tso->why_blocked is the tag for the StgTSOBlockInfo union, so we should be able to say for each union member the why_blocked cases that use that member. - - - - - d08d988b by Duncan Coutts at 2026-07-21T01:29:37+01:00 Add a tso->block_info.mvar member and use it in preference to the generic block_info.closure union member, with casts. The plan is that when we know what case we're in (via tso->why_blocked) then we can always access the correct union member, and so we will only need to access block_info.closure for generic cases where we don't know or don't care. - - - - - 0d414ba9 by Duncan Coutts at 2026-07-21T01:29:37+01:00 Add a tso->block_info.unused member and use it in preference to the generic block_info.closure union member, with casts. The plan is that when we know what case we're in (via tso->why_blocked) then we can always access the correct union member, and so we will only need to access block_info.closure for generic cases where we don't know or don't care. - - - - - 0ed45905 by Duncan Coutts at 2026-07-21T01:31:39+01:00 Avoid storing to tso->block_info.closure In one case we can use a specific union member (.prev) instead. In several cases the stores were in fact redundant because of subsequent overwrites. In scavengeTSO we replace setting tso->block_info.closure to a valid closure, with an assertion that the block_info.unused is already set to END_TSO_QUEUE which is a valid (static) closure. - - - - - 306824c9 by Duncan Coutts at 2026-07-21T01:31:42+01:00 Renumber the tso->why_blocked constants We can do this now because we have separated the internal values from the external ones used in the eventlog. This lets us put them back into a deliberate order and consolodate some gaps. More importantly, it is a prepation for a slightly more sophisticated encoding. - - - - - 7cbb653d by Duncan Coutts at 2026-07-21T01:31:42+01:00 Define constants for the existing stg_threadStatuszh return codes The stg_threadStatuszh reuses the internal tso->why_blocked codes but also extends them with a couple previously magic values. This is awkward since we need to know what those magic values are so we don't accidentally use those values to mean something else. By pulling a definition up to where the why_blocked codes are defined we will be able to avoid mistakenly assining those codes some meaning (or just changing the BlockedThreadComplete, BlockedThreadKilled code if necessary). - - - - - 1e749764 by Duncan Coutts at 2026-07-21T01:31:42+01:00 Extend the tso->why_blocked encoding to indicate block_info closures We use some bit tricks to cheaply and generically test if a tso->why_blocked tag implies that the corresponding tso->block_info will contain a non-trivial valid closure (i.e. not just block_info.unused set to END_TSO_QUEUE). In particular we arrange for most why_blocked values to naturally have a distinguishing bit, but for the BlockedOn{Read,Write,Delay} cases, they can come in either non-closure or closure forms. We allow an additional bit to distinguish these cases. The non-closure forms are only from legacy I/O managers: select and win32-legacy. So this extra bit mechanism will be able to be retired once the legacy I/O managers are themselves retired. This means in a few places we need to untag the why_blocked value before inspecting it, but in most places we do not. - - - - - 45a2291f by Duncan Coutts at 2026-07-21T01:33:25+01:00 Use BlockInfoForceNonClosure in the select I/O manager - - - - - 56e1e50a by Duncan Coutts at 2026-07-21T01:33:28+01:00 Use BlockInfoForceNonClosure in the win32-legacy I/O manager for the BlockedOn{Read,Write} since these use the non-heap allocated StgAsyncIOResult. - - - - - 02687c66 by Duncan Coutts at 2026-07-21T01:33:29+01:00 Enforce the why_blocked and block_info rules in checkTSO We now check the cases wher IsBlockInfoClosure should hold, the cases that are supposed to use block_info.unused == END_TSO_QUEUE, and which cases are allowed to use BlockInfoForceNonClosure. This partially enforces the use of why_blocked as a tag for the block_info union. We could be stricter and check for the correct expected info table for the closure cases. - - - - - e29ede4b by Duncan Coutts at 2026-07-21T01:33:29+01:00 Use IsBlockInfoClosure to simplify several tests In GC and generic traversal we need to know if we should look at the block_info.closure or not. Now we can do just that using a cheap bit test on the why_blocked tag. This fixes issue 26717, where the problem was that some GC modes did not know when to look at block_info.closure, because the poll I/O manager uses a closure for BlockedOn{Read,Write} while the select I/O manager uses a non-closure. Now this information is in the why_blocked tag itself. - - - - - 834b7253 by Duncan Coutts at 2026-07-21T01:33:29+01:00 Remove the now-unused scavengeTSOIOManager The GC no longer has to delegate to the I/O manager, since it can use IsBlockInfoClosure to decide things itself. - - - - - bacf5f15 by Duncan Coutts at 2026-07-21T01:34:20+01:00 Remove duplicate assertion - - - - - acdc8a8b by Duncan Coutts at 2026-07-21T01:43:35+01:00 Follow atomic access rules more consistently for tso->why_blocked The rule is this: store block_info *before* why_blocked store why_blocked using store release load why_blocked using load acquire load block_info *after* why_blocked This is a an atomic store release / load acquire pair and (if the reads are in a separate thread to the writes, and the read receives the value stored) then this guarantees a full "happens before" relationship of these stores and loads. In some cases, we do not need a full load acquire, because we don't read the block_info at all and so do not need any ordering. In this case we just need an atomic relaxed load. This was being followed in most places, but not all. If there's good reason in any case that we don't need atomic access, then we should document that in a comment. In the absence of that I think it's easier to follow the rule everywhere. - - - - - e76e9134 by Duncan Coutts at 2026-07-21T01:43:37+01:00 Add a changelog entry - - - - - 78 changed files: - + changelog.d/T26532 - + changelog.d/T26716 - + changelog.d/T27314.md - + changelog.d/T27374 - compiler/GHC/Core/Coercion/Opt.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 - docs/users_guide/eventlog-formats.rst - libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs - 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/PrimOps.cmm - rts/RaiseAsync.c - rts/RaiseAsync.h - rts/RtsStartup.c - rts/RtsSymbols.c - rts/STM.c - rts/Schedule.c - rts/Schedule.h - rts/StgMiscClosures.cmm - rts/Threads.c - rts/Trace.c - rts/Trace.h - rts/TraverseHeap.c - rts/include/Cmm.h - rts/include/rts/Constants.h - rts/include/rts/EventLogFormat.h - rts/include/rts/IOInterface.h - rts/include/rts/storage/Closures.h - rts/include/rts/storage/TSO.h - rts/include/stg/MiscClosures.h - rts/posix/FdWakeup.h - + rts/posix/MIO.c - + 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/Signals.h - rts/posix/Timeout.c - rts/posix/Timeout.h - rts/rts.cabal - rts/sm/Compact.c - rts/sm/NonMovingMark.c - rts/sm/Sanity.c - rts/sm/Scav.c - rts/win32/AsyncMIO.c - rts/win32/AsyncMIO.h - rts/win32/AsyncWinIO.h - rts/win32/AwaitEvent.c - rts/win32/AwaitEvent.h - rts/win32/ConsoleHandler.h - rts/win32/MIOManager.h - rts/win32/ThrIOManager.h - rts/win32/WorkQueue.h - rts/win32/veh_excn.h - + testsuite/tests/corelint/T27374.hs - testsuite/tests/corelint/all.T - + testsuite/tests/pmcheck/should_compile/T27314.hs - testsuite/tests/pmcheck/should_compile/all.T - 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/T9858b.stderr - testsuite/tests/typecheck/should_fail/TcStaticPointersFail02.stderr - testsuite/tests/typecheck/should_fail/all.T - 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/2476509ee56feac50d886f5f81817dc... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2476509ee56feac50d886f5f81817dc... 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
participants (1)
-
Duncan Coutts (@dcoutts)