This is the twenty-seventh edition of our GHC activities report, which describes the work Well-Typed are doing on GHC, Cabal, HLS and other parts of the core Haskell toolchain. The current edition covers roughly the months of March 2025 to May 2025. You can find the previous editions collected under the ghc-activities-report tag.

Sponsorship

We offer Haskell Ecosystem Support Packages to provide commercial users with support from Well-Typed’s experts, while investing in the Haskell community and its technical ecosystem including through the work described in this report. To find out more, read our recent announcement of these packages in partnership with the Haskell Foundation. We need funding to continue this essential maintenance work!

Many thanks to our Haskell Ecosystem Supporters: Channable and QBayLogic; to our existing clients who also contribute to making this work possible: Anduril, Juspay and Mercury; and to the HLS Open Collective for supporting HLS release management.

Team

The Haskell toolchain team at Well-Typed currently includes:

In addition, many others within Well-Typed contribute to GHC, Cabal and HLS occasionally, or contribute to other open source Haskell libraries and tools.

GHC

Highlights

Explicit level imports

Following on from our best paper prize at TFP 2025, Matthew implemented Explicit Level Imports (GHC proposal #682, !14241).

This feature allows one to specify whether imports are needed for running Template Haskell splices, or for generating Template Haskell quotes. This cleanly separates which modules are required at compile-time vs those that are required at runtime. For example, the pandoc package uses the Template Haskell deriveJSON function from the aeson package. This function can be imported using a splice import:

{-# LANGUAGE ExplicitLevelImports #-}
{-# LANGUAGE TemplateHaskell #-}
module Text.Pandoc.App.Opt where
import splice Data.Aeson.TH (deriveJSON, defaultOptions)
-- + many other non-splice imports

data XYZ = ...
$(deriveJSON defaultOptions ''XYZ)

Declaring the Data.Aeson.TH import as a splice import informs GHC that this module is required only at compile-time, and (crucially) that other, non-splice, imports, are not needed at compile time. This hugely improves the performance of tools that use -fno-code (such as HLS), as GHC is no longer required to pessimistically assume that all modules imported in a module enabling TemplateHaskell are required at compile-time.

GHCi support for primops

Andreas significantly improved GHCi performance by implementing certain GHC primops (such as integer arithmetic operations) directly in the bytecode interpreter (!13978).

Reductions in runtime of up to 50% have been observed, with GHC-in-GHCi speeding up by about 15%.

Improvements to the debugger

Rodrigo has made numerous improvements to the GHCi debugger, which had accumulated many bugs over the years due to lack of maintenance (!14246, !14195, !14160, !14106, !14196, !14195, !13997). Usability is improved across the board, with quality-of-life fixed such as adding breakpoints to all statements in a do block to make debugging more predictable (#25932) to significant performance improvements to :steplocal (#25779).

Rodrigo also published the ghc-debugger package including an executable ghc-debug-adapter. This implements the Debug Adapter Protocol, enabling Haskell programs to be stepped-through and debugged from editors such as Visual Studio Code. ghc-debug-adapter depends on many recent changes to GHC, so it is compatible only with the upcoming GHC 9.14.

Expressions in SPECIALISE pragmas

Sam worked with Simon Peyton Jones to finalise MR !12319 “Expressions in SPECIALISE pragmas”. This change means that a SPECIALISE pragma is no longer required to simply be a type signature, it can be an arbitrary expression. For full details, see GHC proposal #493, but two particular idioms are worth noting. Firstly, the type at which to specialise can now be specified by a type application, e.g.

myFunction :: forall a. Num a => a -> Maybe a -> (a, a)
myFunction = ...
{-# SPECIALISE myFunction @Int #-}

This specialise pragma is much more concise than:

{-# SPECIALISE :: Int -> Maybe Int -> (Int, Int) #-}

and less prone to breakage when the type of myFunction changes.

Secondly, the syntax enables value specialisation, for example:

mainFunction :: Bool -> ...
mainFunction debug = if debug then ... else ...
{-# SPECIALISE mainFunction False #-}

This tells GHC to optimise the non-debug code path, without the debug logic potentially getting in the way.

Multiple Home Units support in GHCi

GHC 9.14 is fully compatible with multiple home units, including all GHCi commands and the GHCi debugger, thanks to work by Hannes about which we recently published a blog post (!14231). Our new design generalises the architecture of GHCi so that multi-unit and single-unit sessions are handled in the same way. The uniform handling will make sure that multi-unit sessions work correctly as GHCi evolves.

GHC Releases

Frontend

  • Sam fixed a regression in the implementation of QuickLook in GHC 9.12 that would cause valid programs to be rejected (#26030, #25950, !14235).

  • Sam fixed a problem in which HasCallStack evidence was incorrectly cached in GHC, causing GHC to bogusly report identical call stacks (#25529, !14084).

  • Sam rectified several oversights in the initial implementation of the NamedDefaults language extension laid out in GHC proposal #409:

    • an issue with exporting named defaults (#25857, !14142),
    • lack of support for named default declarations for poly-kinded typeclasses such as Typeable (#25882, !14143),
    • an oversight in which NamedDefaults changed the behaviour of existing programs (#25775, !14075, ghc-proposals#694).
  • Sam fixed duplicate record fields sometimes being reported as unused when they are actually used (#24035, !14066).

  • Sam improved the error message emitted by GHC when one attempts to write a non-class at the head of a typeclass instance (#22688, !14105).

  • Sam fixed several issues with the renaming of export lists:

    • one issue involved the TypeData extension (#24027, !14119),
    • another was to do with bundled pattern synonyms (#25892, !14154).
  • Sam made “illegal term-level use” error messages more user friendly (#23982, !14122). That MR also improved the way GHC reports name qualification to the user, preferring to display the user-written qualification in error messages.

  • Sam fixed GHC creating unnecessary cycle-breaker variables, which could cause problems for type-checking plugins that weren’t expecting them (#25933, !14206).

  • Sam implemented the deprecation described in GHC proposal #448: the combination of ScopedTypeVariables and TypeApplications no longer enables the use of type applications in constructor patterns, requiring instead the TypeAbstractions extension (!13551).

  • Sam fixed an issue in which equal types compared non-equal under TypeRep-equality by implementing a suggestion by Krzysztof Gogolewski (#25998, !14281).

  • Sam improved the documentation surrounding defaulting in the user’s guide, providing a high-level overview of the different mechanisms in GHC for defaulting ambiguous type variables (#25807, !14057).

Backend

  • Ben and Sam investigated testsuite failures in the LLVM backend (#25769). They identified many different issues:

    • #25730 concerned incorrect type annotations in the generated LLVM, fixed in !13936.
    • #25770, #25773 were symptoms of a serious bug in the implementation of floating-point register padding (fixed in !14134),
    • !14129 fixed incorrect type annotations in the LLVM for atomic operations, adding new tests to Cmm Lint to avoid similar bugs in the future.
    • Most of the other bugs involved initializers/finalizers, which were due to incorrect linkage annotation for builtin arrays (fixed in !14157).
  • Rodrigo worked with Simon Peyton Jones to fix an issue in which the presence or absence of unrelated RULES could affect compilation, leading to non-deterministic compilation (#25170, !13884).

  • Andreas fixed a bug in which GHC would construct over-saturated constructor applications, which caused a panic when building the xmonad-contrib package (#23865, !14036).

  • Andreas made GHC constant-fold away invalid tagToEnum# calls to a particular error expression, which unlocks dead-code elimination opportunities and makes it easier to debug issues that arise from invalid use of tagToEnum# (#25976, !14254)

  • Andreas added -fhuge-code-sections, an off-by-default flag that provides a workaround for AArch64 users running into bug #24648.

  • Matthew overhauled the driver to bring one-shot compilation and make mode in line with each other, by consistently using the module graph to answer queries related to the module import structure (!14198, !14209). This was partly motivated by implementation requirements of the “Explicit Splice Imports” proposal, for which module graph queries are a central component.

  • Matthew added support for “fixed” nodes in the module graph, which can be used for modules without corresponding source-files that are e.g. generated via the GHC API (#25920, !14187).

  • Rodrigo moved some DynFlags consistency checks in order to consolidate the logic into the core makeDynFlagsConsistent function.

  • Ben changed how GHC prints Uniques to the user to avoid NULL characters (#25989, !14265).

Compiler performance

  • Matthew improved the performance of the bytecode assembler by ensuring the code is properly specialised (!13983).

  • Matthew made sure that forceModIface properly forced all fields of ModIface in order to avoid space leaks (!14078).

  • Matthew removed unused mi_used_th and mi_hpc fields from interfaces, which were needlessly bloating interface files (!14073).

  • Matthew avoided allocation of intermediate ByteStrings when serialising FastStrings (#25861, !14107).

Recompilation checking

  • Matthew overhauled the ModIface datatype, splitting it up in a more logical way which makes it easier to identify which parts contribute to recompilation checking (!14102). This allowed fixing several issues with recompilation checking in !14118, such as:

    • it ignored changes in exported named default declarations (#25855),
    • it did not take into account changes to COMPLETE pragmas (#25854).
  • Matthew added the -fwrite-if-self-recomp flag which controls whether to include self-recompilation information, which avoids writing recompilation information in cases such as producing binary distributions for which recompilation is not a concern (#10424, #22188, !8604).

  • Matthew refactored the implementation of recompilation-checking to ensure that all flags that influence recompilations are correctly taken into account (#25837, !14085).

  • Sam improved recompilation checking for export lists in !14178 (#25881). In practice, this means that modules with explicit import lists will no longer always trigger the recompilation of a module they depend on when that module’s export list changes, as long as the explicitly imported items are preserved.

  • Matthew improved the output of -dump-hi-diff to properly display the precise change in flags which caused recompilation (#25571, !13792).

Runtime system

  • Ben fixed a bug in which the WinIO I/O manager was being inconsistently selected (#25838, !14088).

  • Ben diagnosed and fixed a linking issue affecting global offset table usage on macOS that manifested in incorrect runtime results when using the GHC API (#25577, !13991).

  • Ben fixed an issue in which GHC’s RTS linker was too eager to load shared objects which refer to undefined symbols (#25943, !14290).

  • Ben significantly improved the performance of the RTS linker, culminating in a reduction in GHCi startup time from 2.5s to 250ms on Windows (#26052, #26009, !14339).

GHCi & bytecode interpreter

  • Andreas fixed several endianness issues in the interpreter (#25791, !14172).

  • Matthew implemented a fix for the mishandling of stack underflow frames (#25750, !13957). A remaining issue was subsequently identified (#25865) and fixed by Andreas’ work on the interpreter (!13978).

  • Matthew ensured that all top-level functions are visible when loading a module in the interpreter, not only exported functions (!14032).

  • Matthew fixed a bug in the simplifier that caused Core Lint failures when compiling certain programs (#25790, !14019).

  • Matthew fixed a regression in the way that GHCi would import modules that involved Cabal mixins stanzas (#25951, !14222).

Libraries

  • Ben exposed the constructors and fields of the Backtrace datatype in base (#26049, !14351).

  • Ben brought base changelog entries up to date in !14320.

Build system & packaging

  • Sam fixed GHC not working properly if the installation path contains spaces on Windows (#25204, !14137).

  • Ben fixed a couple of issues relating to the llvm-as flag:

    • the value of the field was incorrectly set (#25856, !14104),
    • the information in the field was passed incorrectly to clang (#25793, !14025).

Testsuite

  • Andreas fixed a bug in which tests requiring the interpreter would be run even if the compiler didn’t support it (#25533, !14201).

  • Matthew fixed an issue with tests that used Template Haskell in the profiled dynamic way (#25947, !14215).

Cabal

  • Mikolaj prepared the 3.14.2.0 bugfix release to the Cabal package suite (including the Cabal library and cabal-install).

  • Matthew fixed all known regressions in the 3.14.1.0 release of cabal-install:

    • Issue #10759 to do with picking up unwanted environment files #10828.
    • Duplication of environment variables (#10718, #10827).
    • Interaction of multi-repl with internal dependencies (#10775, #10841).
    • A working directory oversight (#10772, #10800).
    • The pkgname_datadir environment variable incorrectly using a relative path (#10717, #10830).
  • Matthew updated the outdated and gen-bounds commands to work with the v2- project infrastructure (#10878, #10840).

  • Matthew ensured that C++ environment variables are passed to configure scripts (#10797, #10844).

  • Matthew added a module name validity check to the cabal check command (#10295, #10816).

  • Matthew updated the Cabal CI to use GHC 9.12.2 and GHC 9.6.7 (#10893).

  • Matthew improved the testsuite output to make it more readable (#8419, #10837).

  • Matthew fixed an issue in which changes to the PATH environment variable would incorrectly not trigger recompilation (#2015, #10817).

HLS

  • Hannes prepared the HLS release 2.10.0.0 (#4448)

  • Zubin prepared the HLS release 2.11.0.0 (#4585)

  • Zubin added support for GHC 9.12.2 in HLS (#4527)

  • Zubin reworked the HLS release CI infrastructure (#4481)

Haskell.org infrastructure

Ben worked to refactor and migrate a variety of core haskell.org services from Equinix Metal to new infrastructure at OpenCape:

  • hoogle.haskell.org has been Nixified and now periodically reindexes automatically.

  • Haskell.org’s primary mail server, mail.haskell.org, has been Nixified and updated.

  • Haskell.org’s many mailing lists have been migrated to Mailman 3

  • gitlab.haskell.org has been migrated to OpenCape and updated

  • The Hackage documentation builder has been completely revamped with a more maintainable deployment strategy and a broader set of native packages available, enabling more Hackage packages to benefit from automatically-built documentation.

With these maintainability improvements we hope that haskell.org’s core infrastructure team can be more easily grown in the future.