Skip to content

Implement Explicit Level Imports

Matthew Pickering requested to merge wip/splice-imports-2025 into master

Implement Explicit Level Imports for Template Haskell

This commit introduces the `ExplicitLevelImports` and
`ImplicitStagePersistence` language extensions as proposed in GHC
Proposal #682.

Key Features
------------

- `ExplicitLevelImports` adds two new import modifiers - `splice` and
  `quote` - allowing precise control over the level at which imported
  identifiers are available
- `ImplicitStagePersistence` (enabled by default) preserves existing
  path-based cross-stage persistence behavior
- `NoImplicitStagePersistence` disables implicit cross-stage
  persistence, requiring explicit level imports

Benefits
--------

- Improved compilation performance by reducing unnecessary code generation
- Enhanced IDE experience with faster feedback in `-fno-code` mode
- Better dependency tracking by distinguishing compile-time and runtime dependencies
- Foundation for future cross-compilation improvements

This implementation enables the separation of modules needed at
compile-time from those needed at runtime, allowing for more efficient
compilation pipelines and clearer code organization in projects using
Template Haskell.

Implementation Notes
--------------------

The level which a name is availble at is stored in the 'GRE', in the normal
GlobalRdrEnv. The function `greLevels` returns the levels which a specific GRE
is imported at. The level information for a 'Name' is computed by `getStageAndBindLevel`.
The level validity is checked by `checkCrossStageLifting`.

Instances are checked by `checkWellStagedDFun`, which computes the level an
instance by calling `checkWellStagedInstanceWhat`, which sees what is
available at by looking at the module graph.

Modifications to downsweep
--------------------------

Code generation is now only enabled for modules which are needed at
compile time.
See the Note [-fno-code mode] for more information.

Uniform error messages for level errors
---------------------------------------

All error messages to do with levels are now reported uniformly using
the `TcRnBadlyStaged` constructor.

Lift derivation changed
-----------------------

Derived lift instances will now not generate code with expression
quotations.

Before:

```
data A = A Int deriving Lift

=>
lift (A x) = [| A $(lift x) |]
```

After:

```
lift (A x) = conE 'A `appE` (lift x)
```

This is because if you attempt to derive `Lift` in a module where
`NoImplicitStagePersistence` is enabled, you would get an infinite loop
where a constructor was attempted to be persisted using the instance you
are currently defining.

GHC API Changes
---------------

The ModuleGraph now contains additional information about the type of
the edges (normal, quote or splice) between modules. This is abstracted
using the `ModuleGraphEdge` data type.

Fixes #25828
Edited by Matthew Pickering

Merge request reports

Loading