
07 Jul '25
Simon Hengel pushed to branch wip/sol-master-patch-99439 at Glasgow Haskell Compiler / GHC
Commits:
a37e3edc by Simon Hengel at 2025-07-08T02:25:40+07:00
Fix typo in using.rst
- - - - -
1 changed file:
- docs/users_guide/using.rst
Changes:
=====================================
docs/users_guide/using.rst
=====================================
@@ -548,7 +548,7 @@ The available mode flags are:
Print ``YES`` if GHC was compiled to use symbols with leading underscores
in object files, ``NO`` otherwise.
- This is usually atarget platform dependent.
+ This is usually target platform dependent.
.. ghc-flag:: --print-libdir
:shortdesc: display GHC library directory
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a37e3edc8efd185892d4157633349dd…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a37e3edc8efd185892d4157633349dd…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/romes/step-out-10] 16 commits: debugger/rts: Allow toggling step-in per thread
by Rodrigo Mesquita (@alt-romes) 07 Jul '25
by Rodrigo Mesquita (@alt-romes) 07 Jul '25
07 Jul '25
Rodrigo Mesquita pushed to branch wip/romes/step-out-10 at Glasgow Haskell Compiler / GHC
Commits:
8ddc7207 by Rodrigo Mesquita at 2025-07-07T16:53:50+00:00
debugger/rts: Allow toggling step-in per thread
The RTS global flag `rts_stop_next_breakpoint` globally sets the
interpreter to stop at the immediate next breakpoint.
With this commit, single step mode can additionally be set per thread in
the TSO flag (TSO_STOP_NEXT_BREAKPOINT).
Being able to toggle "stop at next breakpoint" per thread is an
important requirement for implementing "stepping out" of a function in a
multi-threaded context.
And, more generally, having a per-thread flag for single-stepping paves the
way for multi-threaded debugging.
That said, when we want to enable "single step" mode for the whole
interpreted program we still want to stop at the immediate next
breakpoint, whichever thread it belongs to.
That's why we also keep the global `rts_stop_next_breakpoint` flag, with
`rts_enableStopNextBreakpointAll` and `rts_disableStopNextBreakpointAll` helpers.
Preparation for #26042
- - - - -
5df6757f by Rodrigo Mesquita at 2025-07-07T16:53:53+00:00
docs: Case continuation BCOs
This commit documents a subtle interaction between frames for case BCOs
and their parents frames. Namely, case continuation BCOs may refer to
(non-local) variables that are part of the parent's frame.
The note expanding a bit on these details is called [Case continuation BCOs]
- - - - -
8289de1d by Rodrigo Mesquita at 2025-07-07T16:58:37+00:00
debugger: Implement step-out feature
Implements support for stepping-out of a function (aka breaking right after
returning from a function) in the interactive debugger.
It also introduces a GHCi command :stepout to step-out of a function
being debugged in the interpreter. The feature is described as:
Stop at the first breakpoint immediately after returning from the current
function scope.
Known limitations: because a function tail-call does not push a stack
frame, if step-out is used inside of a function that was tail-called,
execution will not be returned to its caller, but rather its caller's
first non-tail caller. On the other hand, it means the debugger
follows the more realistic execution of the program.
In the following example:
.. code-block:: none
f = do
a
b <--- (1) set breakpoint then step in here
c
b = do
...
d <--- (2) step-into this tail call
d = do
...
something <--- (3) step-out here
...
Stepping-out will stop execution at the `c` invokation in `f`, rather than
stopping at `b`.
The key idea is simple: When step-out is enabled, traverse the runtime
stack until a continuation BCO is found -- and enable the breakpoint
heading that BCO explicitly using its tick-index.
The details are specified in `Note [Debugger: Step-out]` in `rts/Interpreter.c`.
Since PUSH_ALTS BCOs (representing case continuations) were never headed
by a breakpoint (unlike the case alternatives they push), we introduced
the BRK_ALTS instruction to allow the debugger to set a case
continuation to stop at the breakpoint heading the alternative that is
taken. This is further described in `Note [Debugger: BRK_ALTS]`.
Fixes #26042
- - - - -
c7313450 by Rodrigo Mesquita at 2025-07-07T16:58:41+00:00
debugger: Filter step-out stops by SrcSpan
To implement step-out, the RTS looks for the first continuation frame on
the stack and explicitly enables its entry breakpoint. However, some
continuations will be contained in the function from which step-out was
initiated (trivial example is a case expression).
Similarly to steplocal, we will filter the breakpoints at which the RTS
yields to the debugger based on the SrcSpan. When doing step-out, only
stop if the breakpoint is /not/ contained in the function from which we
initiated it.
This is especially relevant in monadic statements such as IO which is
compiled to a long chain of case expressions.
See Note [Debugger: Filtering step-out stops]
- - - - -
37448c97 by Cheng Shao at 2025-07-07T18:35:04+01:00
compiler: make ModBreaks serializable
- - - - -
1973995a by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refactor: "Inspecting the session" moved from GHC
Moved utilities for inspecting the session from the GHC module to
GHC.Driver.Session.Inspect
Purely a clean up
- - - - -
9a8e4245 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Pass the HUG to readModBreaks, not HscEnv
A minor cleanup. The associated history and setupBreakpoint functions
are changed accordingly.
- - - - -
83ba1657 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Move readModBreaks to GHC.Runtime.Interpreter
With some small docs changes
- - - - -
69085e85 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Move interpreterProfiled to Interp.Types
Moves interpreterProfiled and interpreterDynamic to
GHC.Runtime.Interpreter.Types from GHC.Runtime.Interpreter.
- - - - -
5ed6bdcb by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Don't import GHC in Debugger.Breakpoints
Remove the top-level
import GHC
from GHC.Runtime.Debugger.Breakpoints
This makes the module dependencies more granular and cleans up the
qualified imports from the code.
- - - - -
c5037468 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refactor: Use BreakpointId in Core and Ifaces
- - - - -
3065c00d by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
stg2bc: Derive BcM via ReaderT StateT
A small refactor that simplifies GHC.StgToByteCode by deriving-via the
Monad instances for BcM. This is done along the lines of previous
similar refactors like 72b54c0760bbf85be1f73c1a364d4701e5720465.
- - - - -
1e1ad317 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refact: Split InternalModBreaks out of ModBreaks
There are currently two competing ways of referring to a Breakpoint:
1. Using the Tick module + Tick index
2. Using the Info module + Info index
1. The Tick index is allocated during desugaring in `mkModBreaks`. It is
used to refer to a breakpoint associated to a Core Tick. For a given
Tick module, there are N Ticks indexed by Tick index.
2. The Info index is allocated during code generation (in StgToByteCode)
and uniquely identifies the breakpoints at runtime (and is indeed used
to determine which breakpoint was hit at runtime).
Why we need both is described by Note [Breakpoint identifiers].
For every info index we used to keep a `CgBreakInfo`, a datatype containing
information relevant to ByteCode Generation, in `ModBreaks`.
This commit splits out the `IntMap CgBreakInfo` out of `ModBreaks` into
a new datatype `InternalModBreaks`.
- The purpose is to separate the `ModBreaks` datatype, which stores
data associated from tick-level information which is fixed after
desugaring, from the unrelated `IntMap CgBreakInfo` information
accumulated during bytecode generation.
- We move `ModBreaks` to GHC.HsToCore.Breakpoints
The new `InternalModBreaks` simply combines the `IntMap CgBreakInfo`
with `ModBreaks`. After code generation we construct an
`InternalModBreaks` with the `CgBreakInfo`s we accumulated and the
existing `ModBreaks` and store that in the compiled BCO in `bc_breaks`.
- Note that we previously only updated the `modBreaks_breakInfo`
field of `ModBreaks` at this exact location, and then stored the
updated `ModBreaks` in the same `bc_breaks`.
- We put this new datatype in GHC.ByteCode.Breakpoints
The rest of the pipeline for which CgBreakInfo is relevant is
accordingly updated to also use `InternalModBreaks`
- - - - -
34bc244f by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Use BreakpointIds in bytecode gen
Small clean up to use BreakpointId and InternalBreakpointId more
uniformly in bytecode generation rather than using Module + Ix pairs
- - - - -
c568fbd2 by Rodrigo Mesquita at 2025-07-07T18:35:06+01:00
ghci: Allocate BreakArrays at link time only
Previously, a BreakArray would be allocated with a slot for every tick
in a module at `mkModBreaks`, in HsToCore. However, this approach has
a few downsides:
- It interleaves interpreter behaviour (allocating arrays for
breakpoints) within the desugarer
- It is inflexible in the sense it is impossible for the bytecode
generator to add "internal" breakpoints that can be triggered at
runtime, because those wouldn't have a source tick. (This is relevant
for our intended implementation plan of step-out in #26042)
- It ties the BreakArray indices to the *tick* indexes, while at runtime
we would rather just have the *info* indexes (currently we have both
because BreakArrays are indexed by the *tick* one).
Paving the way for #26042 and #26064, this commit moves the allocation
of BreakArrays to bytecode-loading time -- akin to what is done for CCS
arrays.
Since a BreakArray is allocated only when bytecode is linked, if a
breakpoint is set (e.g. `:break 10`) before the bytecode is linked,
there will exist no BreakArray to trigger the breakpoint in.
Therefore, the function to allocate break arrays (`allocateBreakArrays`)
is exposed and also used in GHC.Runtime.Eval to allocate a break array
when a breakpoint is set, if it doesn't exist yet (in the linker env).
- - - - -
89c5a088 by Rodrigo Mesquita at 2025-07-07T18:35:47+01:00
debugger: Uniquely identify breakpoints by internal id
Since b85b11994e0130ff2401dd4bbdf52330e0bcf776 (support inlining
breakpoints), a breakpoint has been identified at runtime by *two* pairs
of <module,index>.
- The first, aka a 'BreakpointId', uniquely identifies a breakpoint in
the source of a module by using the Tick index. A Tick index can index
into ModBreaks.modBreaks_xxx to fetch source-level information about
where that tick originated.
- When a user specifies e.g. a line breakpoint using :break, we'll reverse
engineer what a Tick index for that line
- We update the `BreakArray` of that module (got from the
LoaderState) at that tick index to `breakOn`.
- A BCO we can stop at is headed by a BRK_FUN instruction. This
instruction stores in an operand the `tick index` it is associated
to. We look it up in the associated `BreakArray` (also an operand)
and check wheter it was set to `breakOn`.
- The second, aka the `ibi_info_mod` + `ibi_info_ix` of the
`InternalBreakpointId`, uniquely index into the `imodBreaks_breakInfo`
-- the information we gathered during code generation about the
existing breakpoint *ocurrences*.
- Note that with optimisation there may be many occurrences of the
same source-tick-breakpoint across different modules. The
`ibi_info_ix` is unique per occurrence, but the `bi_tick_ix` may be
shared. See Note [Breakpoint identifiers] about this.
- Note that besides the tick ids, info ids are also stored in
`BRK_FUN` so the break handler can refer to the associated
`CgBreakInfo`.
In light of that, the driving changes come from the desire to have the
info_id uniquely identify the breakpoint at runtime, and the source tick
id being derived from it:
- An InternalBreakpointId should uniquely identify a breakpoint just
from the code-generation identifiers of `ibi_info_ix` and `ibi_info_mod`.
So we drop `ibi_tick_mod` and `ibi_tick_ix`.
- A BRK_FUN instruction need only record the "internal breakpoint id",
not the tick-level id.
So we drop the tick mod and tick index operands.
- A BreakArray should be indexed by InternalBreakpointId rather than
BreakpointId
That means we need to do some more work when setting a breakpoint.
Specifically, we need to figure out the internal ids (occurrences of a
breakpoint) from the source-level BreakpointId we want to set the
breakpoint at (recall :break refers to breaks at the source level).
Besides this change being an improvement to the handling of breakpoints
(it's clearer to have a single unique identifier than two competing
ones), it unlocks the possibility of generating "internal" breakpoints
during Cg (needed for #26042).
It should also be easier to introduce multi-threaded-aware `BreakArrays`
following this change (needed for #26064).
Se also the new Note [ModBreaks vs InternalModBreaks]
- - - - -
83 changed files:
- compiler/GHC.hs
- compiler/GHC/ByteCode/Asm.hs
- + compiler/GHC/ByteCode/Breakpoints.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Linker.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Core/FVs.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Map/Expr.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Ppr.hs
- compiler/GHC/Core/Subst.hs
- compiler/GHC/Core/Tidy.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Config.hs
- + compiler/GHC/Driver/Session/Inspect.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Breakpoints.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/Runtime/Interpreter/Types.hs
- compiler/GHC/Stg/BcPrep.hs
- compiler/GHC/Stg/FVs.hs
- compiler/GHC/StgToByteCode.hs
- − compiler/GHC/Types/Breakpoint.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- compiler/ghc.cabal.in
- docs/users_guide/ghci.rst
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Monad.hs
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-heap/tests/parse_tso_flags.hs
- + libraries/ghci/GHCi/Debugger.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Exception.cmm
- rts/Interpreter.c
- rts/Interpreter.h
- rts/RtsSymbols.c
- rts/StgMiscClosures.cmm
- rts/include/rts/Bytecodes.h
- rts/include/rts/Constants.h
- rts/include/rts/storage/Closures.h
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042b.hs
- + testsuite/tests/ghci.debugger/scripts/T26042b.script
- + testsuite/tests/ghci.debugger/scripts/T26042b.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042c.hs
- + testsuite/tests/ghci.debugger/scripts/T26042c.script
- + testsuite/tests/ghci.debugger/scripts/T26042c.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042d.hs
- + testsuite/tests/ghci.debugger/scripts/T26042d.script
- + testsuite/tests/ghci.debugger/scripts/T26042d.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042e.hs
- + testsuite/tests/ghci.debugger/scripts/T26042e.script
- + testsuite/tests/ghci.debugger/scripts/T26042e.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f.hs
- + testsuite/tests/ghci.debugger/scripts/T26042f.script
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stderr
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f2.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042g.hs
- + testsuite/tests/ghci.debugger/scripts/T26042g.script
- + testsuite/tests/ghci.debugger/scripts/T26042g.stdout
- testsuite/tests/ghci.debugger/scripts/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3a9ac3cd724639cebf8affa75dca1d…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/3a9ac3cd724639cebf8affa75dca1d…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/romes/step-out-9] 15 commits: debugger/rts: Allow toggling step-in per thread
by Rodrigo Mesquita (@alt-romes) 07 Jul '25
by Rodrigo Mesquita (@alt-romes) 07 Jul '25
07 Jul '25
Rodrigo Mesquita pushed to branch wip/romes/step-out-9 at Glasgow Haskell Compiler / GHC
Commits:
8ddc7207 by Rodrigo Mesquita at 2025-07-07T16:53:50+00:00
debugger/rts: Allow toggling step-in per thread
The RTS global flag `rts_stop_next_breakpoint` globally sets the
interpreter to stop at the immediate next breakpoint.
With this commit, single step mode can additionally be set per thread in
the TSO flag (TSO_STOP_NEXT_BREAKPOINT).
Being able to toggle "stop at next breakpoint" per thread is an
important requirement for implementing "stepping out" of a function in a
multi-threaded context.
And, more generally, having a per-thread flag for single-stepping paves the
way for multi-threaded debugging.
That said, when we want to enable "single step" mode for the whole
interpreted program we still want to stop at the immediate next
breakpoint, whichever thread it belongs to.
That's why we also keep the global `rts_stop_next_breakpoint` flag, with
`rts_enableStopNextBreakpointAll` and `rts_disableStopNextBreakpointAll` helpers.
Preparation for #26042
- - - - -
5df6757f by Rodrigo Mesquita at 2025-07-07T16:53:53+00:00
docs: Case continuation BCOs
This commit documents a subtle interaction between frames for case BCOs
and their parents frames. Namely, case continuation BCOs may refer to
(non-local) variables that are part of the parent's frame.
The note expanding a bit on these details is called [Case continuation BCOs]
- - - - -
8289de1d by Rodrigo Mesquita at 2025-07-07T16:58:37+00:00
debugger: Implement step-out feature
Implements support for stepping-out of a function (aka breaking right after
returning from a function) in the interactive debugger.
It also introduces a GHCi command :stepout to step-out of a function
being debugged in the interpreter. The feature is described as:
Stop at the first breakpoint immediately after returning from the current
function scope.
Known limitations: because a function tail-call does not push a stack
frame, if step-out is used inside of a function that was tail-called,
execution will not be returned to its caller, but rather its caller's
first non-tail caller. On the other hand, it means the debugger
follows the more realistic execution of the program.
In the following example:
.. code-block:: none
f = do
a
b <--- (1) set breakpoint then step in here
c
b = do
...
d <--- (2) step-into this tail call
d = do
...
something <--- (3) step-out here
...
Stepping-out will stop execution at the `c` invokation in `f`, rather than
stopping at `b`.
The key idea is simple: When step-out is enabled, traverse the runtime
stack until a continuation BCO is found -- and enable the breakpoint
heading that BCO explicitly using its tick-index.
The details are specified in `Note [Debugger: Step-out]` in `rts/Interpreter.c`.
Since PUSH_ALTS BCOs (representing case continuations) were never headed
by a breakpoint (unlike the case alternatives they push), we introduced
the BRK_ALTS instruction to allow the debugger to set a case
continuation to stop at the breakpoint heading the alternative that is
taken. This is further described in `Note [Debugger: BRK_ALTS]`.
Fixes #26042
- - - - -
c7313450 by Rodrigo Mesquita at 2025-07-07T16:58:41+00:00
debugger: Filter step-out stops by SrcSpan
To implement step-out, the RTS looks for the first continuation frame on
the stack and explicitly enables its entry breakpoint. However, some
continuations will be contained in the function from which step-out was
initiated (trivial example is a case expression).
Similarly to steplocal, we will filter the breakpoints at which the RTS
yields to the debugger based on the SrcSpan. When doing step-out, only
stop if the breakpoint is /not/ contained in the function from which we
initiated it.
This is especially relevant in monadic statements such as IO which is
compiled to a long chain of case expressions.
See Note [Debugger: Filtering step-out stops]
- - - - -
37448c97 by Cheng Shao at 2025-07-07T18:35:04+01:00
compiler: make ModBreaks serializable
- - - - -
1973995a by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refactor: "Inspecting the session" moved from GHC
Moved utilities for inspecting the session from the GHC module to
GHC.Driver.Session.Inspect
Purely a clean up
- - - - -
9a8e4245 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Pass the HUG to readModBreaks, not HscEnv
A minor cleanup. The associated history and setupBreakpoint functions
are changed accordingly.
- - - - -
83ba1657 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Move readModBreaks to GHC.Runtime.Interpreter
With some small docs changes
- - - - -
69085e85 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Move interpreterProfiled to Interp.Types
Moves interpreterProfiled and interpreterDynamic to
GHC.Runtime.Interpreter.Types from GHC.Runtime.Interpreter.
- - - - -
5ed6bdcb by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Don't import GHC in Debugger.Breakpoints
Remove the top-level
import GHC
from GHC.Runtime.Debugger.Breakpoints
This makes the module dependencies more granular and cleans up the
qualified imports from the code.
- - - - -
c5037468 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refactor: Use BreakpointId in Core and Ifaces
- - - - -
3065c00d by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
stg2bc: Derive BcM via ReaderT StateT
A small refactor that simplifies GHC.StgToByteCode by deriving-via the
Monad instances for BcM. This is done along the lines of previous
similar refactors like 72b54c0760bbf85be1f73c1a364d4701e5720465.
- - - - -
1e1ad317 by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
refact: Split InternalModBreaks out of ModBreaks
There are currently two competing ways of referring to a Breakpoint:
1. Using the Tick module + Tick index
2. Using the Info module + Info index
1. The Tick index is allocated during desugaring in `mkModBreaks`. It is
used to refer to a breakpoint associated to a Core Tick. For a given
Tick module, there are N Ticks indexed by Tick index.
2. The Info index is allocated during code generation (in StgToByteCode)
and uniquely identifies the breakpoints at runtime (and is indeed used
to determine which breakpoint was hit at runtime).
Why we need both is described by Note [Breakpoint identifiers].
For every info index we used to keep a `CgBreakInfo`, a datatype containing
information relevant to ByteCode Generation, in `ModBreaks`.
This commit splits out the `IntMap CgBreakInfo` out of `ModBreaks` into
a new datatype `InternalModBreaks`.
- The purpose is to separate the `ModBreaks` datatype, which stores
data associated from tick-level information which is fixed after
desugaring, from the unrelated `IntMap CgBreakInfo` information
accumulated during bytecode generation.
- We move `ModBreaks` to GHC.HsToCore.Breakpoints
The new `InternalModBreaks` simply combines the `IntMap CgBreakInfo`
with `ModBreaks`. After code generation we construct an
`InternalModBreaks` with the `CgBreakInfo`s we accumulated and the
existing `ModBreaks` and store that in the compiled BCO in `bc_breaks`.
- Note that we previously only updated the `modBreaks_breakInfo`
field of `ModBreaks` at this exact location, and then stored the
updated `ModBreaks` in the same `bc_breaks`.
- We put this new datatype in GHC.ByteCode.Breakpoints
The rest of the pipeline for which CgBreakInfo is relevant is
accordingly updated to also use `InternalModBreaks`
- - - - -
34bc244f by Rodrigo Mesquita at 2025-07-07T18:35:05+01:00
cleanup: Use BreakpointIds in bytecode gen
Small clean up to use BreakpointId and InternalBreakpointId more
uniformly in bytecode generation rather than using Module + Ix pairs
- - - - -
c568fbd2 by Rodrigo Mesquita at 2025-07-07T18:35:06+01:00
ghci: Allocate BreakArrays at link time only
Previously, a BreakArray would be allocated with a slot for every tick
in a module at `mkModBreaks`, in HsToCore. However, this approach has
a few downsides:
- It interleaves interpreter behaviour (allocating arrays for
breakpoints) within the desugarer
- It is inflexible in the sense it is impossible for the bytecode
generator to add "internal" breakpoints that can be triggered at
runtime, because those wouldn't have a source tick. (This is relevant
for our intended implementation plan of step-out in #26042)
- It ties the BreakArray indices to the *tick* indexes, while at runtime
we would rather just have the *info* indexes (currently we have both
because BreakArrays are indexed by the *tick* one).
Paving the way for #26042 and #26064, this commit moves the allocation
of BreakArrays to bytecode-loading time -- akin to what is done for CCS
arrays.
Since a BreakArray is allocated only when bytecode is linked, if a
breakpoint is set (e.g. `:break 10`) before the bytecode is linked,
there will exist no BreakArray to trigger the breakpoint in.
Therefore, the function to allocate break arrays (`allocateBreakArrays`)
is exposed and also used in GHC.Runtime.Eval to allocate a break array
when a breakpoint is set, if it doesn't exist yet (in the linker env).
- - - - -
81 changed files:
- compiler/GHC.hs
- compiler/GHC/ByteCode/Asm.hs
- + compiler/GHC/ByteCode/Breakpoints.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Linker.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Core/FVs.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Map/Expr.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Ppr.hs
- compiler/GHC/Core/Subst.hs
- compiler/GHC/Core/Tidy.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Config.hs
- + compiler/GHC/Driver/Session/Inspect.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Breakpoints.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/Runtime/Interpreter/Types.hs
- compiler/GHC/Stg/BcPrep.hs
- compiler/GHC/Stg/FVs.hs
- compiler/GHC/StgToByteCode.hs
- − compiler/GHC/Types/Breakpoint.hs
- compiler/GHC/Types/Tickish.hs
- compiler/GHC/Unit/Module/ModGuts.hs
- compiler/ghc.cabal.in
- docs/users_guide/ghci.rst
- ghc/GHCi/UI.hs
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-heap/tests/parse_tso_flags.hs
- + libraries/ghci/GHCi/Debugger.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Interpreter.c
- rts/Interpreter.h
- rts/RtsSymbols.c
- rts/StgMiscClosures.cmm
- rts/include/rts/Bytecodes.h
- rts/include/rts/Constants.h
- rts/include/rts/storage/Closures.h
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042b.hs
- + testsuite/tests/ghci.debugger/scripts/T26042b.script
- + testsuite/tests/ghci.debugger/scripts/T26042b.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042c.hs
- + testsuite/tests/ghci.debugger/scripts/T26042c.script
- + testsuite/tests/ghci.debugger/scripts/T26042c.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042d.hs
- + testsuite/tests/ghci.debugger/scripts/T26042d.script
- + testsuite/tests/ghci.debugger/scripts/T26042d.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042e.hs
- + testsuite/tests/ghci.debugger/scripts/T26042e.script
- + testsuite/tests/ghci.debugger/scripts/T26042e.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f.hs
- + testsuite/tests/ghci.debugger/scripts/T26042f.script
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stderr
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f2.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042g.hs
- + testsuite/tests/ghci.debugger/scripts/T26042g.script
- + testsuite/tests/ghci.debugger/scripts/T26042g.stdout
- testsuite/tests/ghci.debugger/scripts/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/925552177f8abb540a21e6c3f67f5c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/925552177f8abb540a21e6c3f67f5c…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 15 commits: Consider `PromotedDataCon` in `tyConStupidTheta`
by Marge Bot (@marge-bot) 07 Jul '25
by Marge Bot (@marge-bot) 07 Jul '25
07 Jul '25
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
444701b5 by Berk Özkütük at 2025-07-07T13:11:25-04:00
Consider `PromotedDataCon` in `tyConStupidTheta`
Haddock checks data declarations for the stupid theta so as not to
pretty-print them as empty contexts. Type data declarations end up as
`PromotedDataCon`s by the time Haddock performs this check, causing a
panic. This commit extends `tyConStupidTheta` so that it returns an
empty list for `PromotedDataCon`s. This decision was guided by the fact
that type data declarations never have data type contexts (see (R1) in
Note [Type data declarations]).
Fixes #25739.
- - - - -
32beb9ab by Ryan Hendrickson at 2025-07-07T13:11:30-04:00
haddock: Document instances from other packages
When attaching instances to `Interface`s, it isn't enough just to look
for instances in the list of `Interface`s being processed. We also need
to look in the modules on which they depend, including those outside of
this package.
Fixes #25147.
Fixes #26079.
- - - - -
3af21236 by Rodrigo Mesquita at 2025-07-07T13:11:30-04:00
hadrian: Fallback logic for internal interpreter
When determining whether to build the internal interpreter, the `make`
build system had a fallback case for platforms not in the list of
explicitly-supported operating systems and architectures.
This fallback says we should try to build the internal interpreter if
building dynamic GHC programs (if the architecture is unknown).
Fixes #24098
- - - - -
9bc76073 by Ben Gamari at 2025-07-07T13:11:31-04:00
users-guide: Reference Wasm FFI section
- - - - -
26a51534 by Ben Gamari at 2025-07-07T13:11:31-04:00
users-guide: Fix too-short heading warning
- - - - -
ed8bc569 by Duncan Coutts at 2025-07-07T13:11:34-04:00
Reorganise documentation for allocate* functions
Consolodate interface information into the .h file, keeping just
implementation details in the .c file.
Use Notes stlye in the .h file and refer to notes from the .c file.
- - - - -
482f14cb by Duncan Coutts at 2025-07-07T13:11:34-04:00
Introduce common utilities for allocating arrays
The intention is to share code among the several places that do this
already.
- - - - -
fcbc0505 by Duncan Coutts at 2025-07-07T13:11:34-04:00
Use new array alloc utils in Heap.c
The CMM primop can now report heap overflow.
- - - - -
0ea051e9 by Duncan Coutts at 2025-07-07T13:11:34-04:00
Use new array alloc utils in ThreadLabels.c
Replacing a local utility.
- - - - -
07a02368 by Duncan Coutts at 2025-07-07T13:11:34-04:00
Use new array alloc utils in Threads.c
Replacing local open coded version.
- - - - -
6c6d7440 by Duncan Coutts at 2025-07-07T13:11:34-04:00
Add exitHeapOverflow helper utility
This will be useful with the array alloc functions, since unlike
allocate/allocateMaybeFail, they do not come in two versions. So if it's
not convenient to propagate failure, then one can use this.
- - - - -
5752dae0 by Duncan Coutts at 2025-07-07T13:11:35-04:00
Use new array alloc utils in Weak.c
Also add a cpp macro CCS_SYSTEM_OR_NULL which does what it says. The
benefit of this is that it allows us to referece CCS_SYSTEM even when
we're not in PROFILING mode. That makes abstracting over profiling vs
normal mode a lot easier.
- - - - -
5fd083e7 by Duncan Coutts at 2025-07-07T13:11:35-04:00
Convert the array alloc primops to use the new array alloc utils
- - - - -
88d8d365 by Duncan Coutts at 2025-07-07T13:11:35-04:00
While we're at it, add one missing 'likely' hint
To a cmm primops that raises an exception, like the others now do.
- - - - -
c16670f6 by meooow25 at 2025-07-07T13:11:39-04:00
Keep scanl' strict in the head on rewrite
`scanl'` forces elements to WHNF when the corresponding `(:)`s are
forced. The rewrite rule for `scanl'` missed forcing the first element,
which is fixed here with a `seq`.
- - - - -
32 changed files:
- compiler/GHC/Core/TyCon.hs
- docs/users_guide/exts/doandifthenelse.rst
- docs/users_guide/exts/ffi.rst
- hadrian/src/Oracles/Flag.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Settings/Builders/Cabal.hs
- hadrian/src/Settings/Packages.hs
- hadrian/src/Settings/Program.hs
- libraries/base/changelog.md
- libraries/ghc-internal/src/GHC/Internal/List.hs
- + rts/AllocArray.c
- + rts/AllocArray.h
- rts/Heap.c
- rts/PrimOps.cmm
- rts/RtsUtils.c
- rts/ThreadLabels.c
- rts/Threads.c
- rts/Weak.c
- rts/include/Rts.h
- rts/include/rts/prof/CCS.h
- rts/include/rts/storage/GC.h
- rts/include/rts/storage/Heap.h
- rts/rts.cabal
- rts/sm/Storage.c
- utils/haddock/CHANGES.md
- utils/haddock/haddock-api/src/Haddock/Interface/AttachInstances.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Create.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
- utils/haddock/haddock-test/src/Test/Haddock/Config.hs
- utils/haddock/html-test/ref/Bug1004.html
- + utils/haddock/html-test/ref/Bug25739.html
- + utils/haddock/html-test/src/Bug25739.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4141e4797b87da370c19527a795edb…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4141e4797b87da370c19527a795edb…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/romes/step-out-5] 4 commits: debugger/rts: Allow toggling step-in per thread
by Cheng Shao (@TerrorJack) 07 Jul '25
by Cheng Shao (@TerrorJack) 07 Jul '25
07 Jul '25
Cheng Shao pushed to branch wip/romes/step-out-5 at Glasgow Haskell Compiler / GHC
Commits:
8ddc7207 by Rodrigo Mesquita at 2025-07-07T16:53:50+00:00
debugger/rts: Allow toggling step-in per thread
The RTS global flag `rts_stop_next_breakpoint` globally sets the
interpreter to stop at the immediate next breakpoint.
With this commit, single step mode can additionally be set per thread in
the TSO flag (TSO_STOP_NEXT_BREAKPOINT).
Being able to toggle "stop at next breakpoint" per thread is an
important requirement for implementing "stepping out" of a function in a
multi-threaded context.
And, more generally, having a per-thread flag for single-stepping paves the
way for multi-threaded debugging.
That said, when we want to enable "single step" mode for the whole
interpreted program we still want to stop at the immediate next
breakpoint, whichever thread it belongs to.
That's why we also keep the global `rts_stop_next_breakpoint` flag, with
`rts_enableStopNextBreakpointAll` and `rts_disableStopNextBreakpointAll` helpers.
Preparation for #26042
- - - - -
5df6757f by Rodrigo Mesquita at 2025-07-07T16:53:53+00:00
docs: Case continuation BCOs
This commit documents a subtle interaction between frames for case BCOs
and their parents frames. Namely, case continuation BCOs may refer to
(non-local) variables that are part of the parent's frame.
The note expanding a bit on these details is called [Case continuation BCOs]
- - - - -
8289de1d by Rodrigo Mesquita at 2025-07-07T16:58:37+00:00
debugger: Implement step-out feature
Implements support for stepping-out of a function (aka breaking right after
returning from a function) in the interactive debugger.
It also introduces a GHCi command :stepout to step-out of a function
being debugged in the interpreter. The feature is described as:
Stop at the first breakpoint immediately after returning from the current
function scope.
Known limitations: because a function tail-call does not push a stack
frame, if step-out is used inside of a function that was tail-called,
execution will not be returned to its caller, but rather its caller's
first non-tail caller. On the other hand, it means the debugger
follows the more realistic execution of the program.
In the following example:
.. code-block:: none
f = do
a
b <--- (1) set breakpoint then step in here
c
b = do
...
d <--- (2) step-into this tail call
d = do
...
something <--- (3) step-out here
...
Stepping-out will stop execution at the `c` invokation in `f`, rather than
stopping at `b`.
The key idea is simple: When step-out is enabled, traverse the runtime
stack until a continuation BCO is found -- and enable the breakpoint
heading that BCO explicitly using its tick-index.
The details are specified in `Note [Debugger: Step-out]` in `rts/Interpreter.c`.
Since PUSH_ALTS BCOs (representing case continuations) were never headed
by a breakpoint (unlike the case alternatives they push), we introduced
the BRK_ALTS instruction to allow the debugger to set a case
continuation to stop at the breakpoint heading the alternative that is
taken. This is further described in `Note [Debugger: BRK_ALTS]`.
Fixes #26042
- - - - -
c7313450 by Rodrigo Mesquita at 2025-07-07T16:58:41+00:00
debugger: Filter step-out stops by SrcSpan
To implement step-out, the RTS looks for the first continuation frame on
the stack and explicitly enables its entry breakpoint. However, some
continuations will be contained in the function from which step-out was
initiated (trivial example is a case expression).
Similarly to steplocal, we will filter the breakpoints at which the RTS
yields to the debugger based on the SrcSpan. When doing step-out, only
stop if the breakpoint is /not/ contained in the function from which we
initiated it.
This is especially relevant in monadic statements such as IO which is
compiled to a long chain of case expressions.
See Note [Debugger: Filtering step-out stops]
- - - - -
46 changed files:
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Driver/Config.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- compiler/GHC/StgToByteCode.hs
- docs/users_guide/ghci.rst
- ghc/GHCi/UI.hs
- libraries/ghc-heap/GHC/Exts/Heap/Closures.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-heap/tests/parse_tso_flags.hs
- + libraries/ghci/GHCi/Debugger.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
- libraries/ghci/ghci.cabal.in
- rts/Disassembler.c
- rts/Interpreter.c
- rts/Interpreter.h
- rts/RtsSymbols.c
- rts/StgMiscClosures.cmm
- rts/include/rts/Bytecodes.h
- rts/include/rts/Constants.h
- rts/include/rts/storage/Closures.h
- + testsuite/tests/ghci.debugger/scripts/T26042b.hs
- + testsuite/tests/ghci.debugger/scripts/T26042b.script
- + testsuite/tests/ghci.debugger/scripts/T26042b.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042c.hs
- + testsuite/tests/ghci.debugger/scripts/T26042c.script
- + testsuite/tests/ghci.debugger/scripts/T26042c.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042d.hs
- + testsuite/tests/ghci.debugger/scripts/T26042d.script
- + testsuite/tests/ghci.debugger/scripts/T26042d.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042e.hs
- + testsuite/tests/ghci.debugger/scripts/T26042e.script
- + testsuite/tests/ghci.debugger/scripts/T26042e.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f.hs
- + testsuite/tests/ghci.debugger/scripts/T26042f.script
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stderr
- + testsuite/tests/ghci.debugger/scripts/T26042f1.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042f2.stdout
- + testsuite/tests/ghci.debugger/scripts/T26042g.hs
- + testsuite/tests/ghci.debugger/scripts/T26042g.script
- + testsuite/tests/ghci.debugger/scripts/T26042g.stdout
- testsuite/tests/ghci.debugger/scripts/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/caabafee640ed3cb7e9b0b308638ea…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/caabafee640ed3cb7e9b0b308638ea…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

07 Jul '25
Simon Peyton Jones pushed to branch wip/T23162-spj at Glasgow Haskell Compiler / GHC
Commits:
c92f3ede by Simon Peyton Jones at 2025-07-07T17:43:01+01:00
Import wibbles
- - - - -
1 changed file:
- compiler/GHC/Tc/Solver/Dict.hs
Changes:
=====================================
compiler/GHC/Tc/Solver/Dict.hs
=====================================
@@ -13,7 +13,6 @@ module GHC.Tc.Solver.Dict (
import GHC.Prelude
import GHC.Tc.Errors.Types
-import GHC.Tc.Instance.FunDeps
import GHC.Tc.Instance.Class( safeOverlap, matchEqualityInst )
import GHC.Tc.Types.Evidence
import GHC.Tc.Types.Constraint
@@ -30,9 +29,9 @@ import GHC.Hs.Type( HsIPName(..) )
import GHC.Core
import GHC.Core.Type
-import GHC.Core.InstEnv ( InstEnvs, DFunInstType, ClsInst(..) )
import GHC.Core.Class
import GHC.Core.Predicate
+import GHC.Core.InstEnv( DFunInstType )
import GHC.Core.Multiplicity ( scaledThing )
import GHC.Core.Unify ( ruleMatchTyKiX )
@@ -56,7 +55,6 @@ import GHC.Driver.DynFlags
import qualified GHC.LanguageExtensions as LangExt
-import Data.Foldable( foldrM )
import Data.Maybe ( listToMaybe, mapMaybe, isJust )
import Data.Void( Void )
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c92f3ede6845358857164bb61211259…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/c92f3ede6845358857164bb61211259…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
Simon Peyton Jones pushed to branch wip/T23162-spj at Glasgow Haskell Compiler / GHC
Commits:
3c1f315c by Simon Peyton Jones at 2025-07-07T17:07:35+01:00
Wibble
- - - - -
1 changed file:
- compiler/GHC/Tc/Solver/Monad.hs
Changes:
=====================================
compiler/GHC/Tc/Solver/Monad.hs
=====================================
@@ -153,7 +153,6 @@ import qualified GHC.Tc.Zonk.TcType as TcM
import GHC.Driver.DynFlags
import GHC.Tc.Instance.Class( safeOverlap, instanceReturnsDictCon )
-import GHC.Tc.Instance.FunDeps( FunDepEqn(..) )
import GHC.Utils.Misc
@@ -214,7 +213,6 @@ import Data.IORef
import Data.List ( mapAccumL )
import Data.List.NonEmpty ( nonEmpty )
import qualified Data.List.NonEmpty as NE
-import qualified Data.Semigroup as S
import GHC.Types.SrcLoc
import GHC.Rename.Env
import GHC.LanguageExtensions as LangExt
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3c1f315c8665631db8adea3dfa84ad5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3c1f315c8665631db8adea3dfa84ad5…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/haanss/depdir] Fix test for addDependentDirectory, hopefully.
by Hassan Al-Awwadi (@hassan.awwadi) 07 Jul '25
by Hassan Al-Awwadi (@hassan.awwadi) 07 Jul '25
07 Jul '25
Hassan Al-Awwadi pushed to branch wip/haanss/depdir at Glasgow Haskell Compiler / GHC
Commits:
d4ae1992 by Hassan Al-Awwadi at 2025-07-07T17:52:23+02:00
Fix test for addDependentDirectory, hopefully.
The rest of the changes are just reversions of accidental additions and spelling/grammer changes.
- - - - -
7 changed files:
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Utils/Fingerprint.hs
- libraries/base/src/GHC/Fingerprint.hs
- libraries/ghc-internal/src/GHC/Internal/Fingerprint.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
- testsuite/tests/th/Makefile
Changes:
=====================================
compiler/GHC/HsToCore/Usage.hs
=====================================
@@ -2,7 +2,7 @@ module GHC.HsToCore.Usage (
-- * Dependency/fingerprinting code (used by GHC.Iface.Make)
mkUsageInfo, mkUsedNames,
- UsageConfig(..)
+ UsageConfig(..),
) where
import GHC.Prelude
=====================================
compiler/GHC/Unit/Finder.hs
=====================================
@@ -32,7 +32,7 @@ module GHC.Unit.Finder (
findObjectLinkableMaybe,
findObjectLinkable,
- -- important that GHC.HsToCore.Usage uses the same hashing method for usage dirs as is done here.
+ -- important that GHC.HsToCore.Usage uses the same hashing method for usage dirs as is used here.
getDirHash,
) where
=====================================
compiler/GHC/Utils/Fingerprint.hs
=====================================
@@ -20,7 +20,7 @@ module GHC.Utils.Fingerprint (
fingerprintData,
fingerprintString,
fingerprintStrings,
- getFileHash,
+ getFileHash
) where
import GHC.Prelude.Basic
=====================================
libraries/base/src/GHC/Fingerprint.hs
=====================================
@@ -5,7 +5,7 @@ module GHC.Fingerprint (
fingerprintData,
fingerprintString,
fingerprintFingerprints,
- getFileHash,
+ getFileHash
) where
import GHC.Internal.Fingerprint
=====================================
libraries/ghc-internal/src/GHC/Internal/Fingerprint.hs
=====================================
@@ -16,7 +16,7 @@ module GHC.Internal.Fingerprint (
fingerprintData,
fingerprintString,
fingerprintFingerprints,
- getFileHash,
+ getFileHash
) where
import GHC.Internal.IO
=====================================
libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
=====================================
@@ -830,7 +830,7 @@ getPackageRoot = Q qGetPackageRoot
-- The compiler can then recognize that it should re-compile the Haskell file
-- when a directory changes.
--
--- Expects an absolute file path.
+-- Expects an absolute directory path.
--
-- Notes:
--
=====================================
testsuite/tests/th/Makefile
=====================================
@@ -45,12 +45,12 @@ TH_Depends:
.PHONY: TH_Depends_Dir
TH_Depends_Dir:
- $(RM) TH_Depends_external/dummy.txt
+ rm -rf TH_Depends_external
$(RM) TH_Depends TH_Depends.exe
$(RM) TH_Depends.o TH_Depends.hi
$(RM) TH_Depends_External.o TH_Depends_External.hi
-
- mk_DIR TH_Depends_external
+
+ mkdir TH_Depends_external
'$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends_Dir
./TH_Depends_Dir
sleep 2
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d4ae1992198aea378e5c0fefae1b66f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/d4ae1992198aea378e5c0fefae1b66f…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/T23162-spj] 2 commits: Make FunDeps into a new module
by Simon Peyton Jones (@simonpj) 07 Jul '25
by Simon Peyton Jones (@simonpj) 07 Jul '25
07 Jul '25
Simon Peyton Jones pushed to branch wip/T23162-spj at Glasgow Haskell Compiler / GHC
Commits:
46241879 by Simon Peyton Jones at 2025-07-07T16:05:46+01:00
Make FunDeps into a new module
- - - - -
e970a6b8 by Simon Peyton Jones at 2025-07-07T16:39:04+01:00
Solve new_eqs rather than adding them to WantedConstraints
- - - - -
6 changed files:
- compiler/GHC/Tc/Solver/Dict.hs
- compiler/GHC/Tc/Solver/Equality.hs
- + compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Solver/Solve.hs
- compiler/ghc.cabal.in
Changes:
=====================================
compiler/GHC/Tc/Solver/Dict.hs
=====================================
@@ -8,9 +8,6 @@ module GHC.Tc.Solver.Dict (
matchLocalInst, chooseInstance,
makeSuperClasses, mkStrictSuperClasses,
solveCallStack, -- For GHC.Tc.Solver
-
- -- * Functional dependencies
- doDictFunDepImprovement
) where
import GHC.Prelude
@@ -1379,346 +1376,6 @@ with the least superclass depth (see Note [Replacement vs keeping]),
but that doesn't work for the example from #22216.
-}
-{- *********************************************************************
-* *
-* Functional dependencies, instantiation of equations
-* *
-************************************************************************
-
-When we spot an equality arising from a functional dependency,
-we now use that equality (a "wanted") to rewrite the work-item
-constraint right away. This avoids two dangers
-
- Danger 1: If we send the original constraint on down the pipeline
- it may react with an instance declaration, and in delicate
- situations (when a Given overlaps with an instance) that
- may produce new insoluble goals: see #4952
-
- Danger 2: If we don't rewrite the constraint, it may re-react
- with the same thing later, and produce the same equality
- again --> termination worries.
-
-To achieve this required some refactoring of GHC.Tc.Instance.FunDeps (nicer
-now!).
-
-Note [FunDep and implicit parameter reactions]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Currently, our story of interacting two dictionaries (or a dictionary
-and top-level instances) for functional dependencies (including implicit
-parameters), is that we simply produce new Wanted equalities. So for example
-
- class D a b | a -> b where ...
- Inert:
- [G] d1 : D Int Bool
- WorkItem:
- [W] d2 : D Int alpha
-
- We generate the extra work item
- [W] cv : alpha ~ Bool
- where 'cv' is currently unused. However, this new item can perhaps be
- spontaneously solved to become given and react with d2,
- discharging it in favour of a new constraint d2' thus:
- [W] d2' : D Int Bool
- d2 := d2' |> D Int cv
- Now d2' can be discharged from d1
-
-We could be more aggressive and try to *immediately* solve the dictionary
-using those extra equalities.
-
-If that were the case with the same inert set and work item we might discard
-d2 directly:
-
- [W] cv : alpha ~ Bool
- d2 := d1 |> D Int cv
-
-But in general it's a bit painful to figure out the necessary coercion,
-so we just take the first approach. Here is a better example. Consider:
- class C a b c | a -> b
-And:
- [G] d1 : C T Int Char
- [W] d2 : C T beta Int
-In this case, it's *not even possible* to solve the wanted immediately.
-So we should simply output the functional dependency and add this guy
-[but NOT its superclasses] back in the worklist. Even worse:
- [G] d1 : C T Int beta
- [W] d2: C T beta Int
-Then it is solvable, but its very hard to detect this on the spot.
-
-It's exactly the same with implicit parameters, except that the
-"aggressive" approach would be much easier to implement.
-
-Note [Fundeps with instances, and equality orientation]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-This Note describes a delicate interaction that constrains the orientation of
-equalities. This one is about fundeps, but the /exact/ same thing arises for
-type-family injectivity constraints: see Note [Improvement orientation].
-
-doTopFunDepImprovement compares the constraint with all the instance
-declarations, to see if we can produce any equalities. E.g
- class C2 a b | a -> b
- instance C Int Bool
-Then the constraint (C Int ty) generates the equality [W] ty ~ Bool.
-
-There is a nasty corner in #19415 which led to the typechecker looping:
- class C s t b | s -> t
- instance ... => C (T kx x) (T ky y) Int
- T :: forall k. k -> Type
-
- work_item: dwrk :: C (T @ka (a::ka)) (T @kb0 (b0::kb0)) Char
- where kb0, b0 are unification vars
-
- ==> {doTopFunDepImprovement: compare work_item with instance,
- generate /fresh/ unification variables kfresh0, yfresh0,
- emit a new Wanted, and add dwrk to inert set}
-
- Suppose we emit this new Wanted from the fundep:
- [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
-
- ==> {solve that equality kb0 := kfresh0, b0 := yfresh0}
- Now kick out dwrk, since it mentions kb0
- But now we are back to the start! Loop!
-
-NB1: This example relies on an instance that does not satisfy the
- coverage condition (although it may satisfy the weak coverage
- condition), and hence whose fundeps generate fresh unification
- variables. Not satisfying the coverage condition is known to
- lead to termination trouble, but in this case it's plain silly.
-
-NB2: In this example, the third parameter to C ensures that the
- instance doesn't actually match the Wanted, so we can't use it to
- solve the Wanted
-
-We solve the problem by (#21703):
-
- carefully orienting the new Wanted so that all the
- freshly-generated unification variables are on the LHS.
-
- Thus we call unifyWanteds on
- T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
- and /NOT/
- T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
-
-Now we'll unify kfresh0:=kb0, yfresh0:=b0, and all is well. The general idea
-is that we want to preferentially eliminate those freshly-generated
-unification variables, rather than unifying older variables, which causes
-kick-out etc.
-
-Keeping younger variables on the left also gives very minor improvement in
-the compiler performance by having less kick-outs and allocations (-0.1% on
-average). Indeed Historical Note [Eliminate younger unification variables]
-in GHC.Tc.Utils.Unify describes an earlier attempt to do so systematically,
-apparently now in abeyance.
-
-But this is is a delicate solution. We must take care to /preserve/
-orientation during solving. Wrinkles:
-
-(W1) We start with
- [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
- Decompose to
- [W] kfresh0 ~ kb0
- [W] (yfresh0::kfresh0) ~ (b0::kb0)
- Preserve orientation when decomposing!!
-
-(W2) Suppose we happen to tackle the second Wanted from (W1)
- first. Then in canEqCanLHSHetero we emit a /kind/ equality, as
- well as a now-homogeneous type equality
- [W] kco : kfresh0 ~ kb0
- [W] (yfresh0::kfresh0) ~ (b0::kb0) |> (sym kco)
- Preserve orientation in canEqCanLHSHetero!! (Failing to
- preserve orientation here was the immediate cause of #21703.)
-
-(W3) There is a potential interaction with the swapping done by
- GHC.Tc.Utils.Unify.swapOverTyVars. We think it's fine, but it's
- a slight worry. See especially Note [TyVar/TyVar orientation] in
- that module.
-
-The trouble is that "preserving orientation" is a rather global invariant,
-and sometimes we definitely do want to swap (e.g. Int ~ alpha), so we don't
-even have a precise statement of what the invariant is. The advantage
-of the preserve-orientation plan is that it is extremely cheap to implement,
-and apparently works beautifully.
-
---- Alternative plan (1) ---
-Rather than have an ill-defined invariant, another possiblity is to
-elminate those fresh unification variables at birth, when generating
-the new fundep-inspired equalities.
-
-The key idea is to call `instFlexiX` in `emitFunDepWanteds` on only those
-type variables that are guaranteed to give us some progress. This means we
-have to locally (without calling emitWanteds) identify the type variables
-that do not give us any progress. In the above example, we _know_ that
-emitting the two wanteds `kco` and `co` is fruitless.
-
- Q: How do we identify such no-ops?
-
- 1. Generate a matching substitution from LHS to RHS
- ɸ = [kb0 :-> k0, b0 :-> y0]
- 2. Call `instFlexiX` on only those type variables that do not appear in the domain of ɸ
- ɸ' = instFlexiX ɸ (tvs - domain ɸ)
- 3. Apply ɸ' on LHS and then call emitWanteds
- unifyWanteds ... (subst ɸ' LHS) RHS
-
-Why will this work? The matching substitution ɸ will be a best effort
-substitution that gives us all the easy solutions. It can be generated with
-modified version of `Core/Unify.unify_tys` where we run it in a matching mode
-and never generate `SurelyApart` and always return a `MaybeApart Subst`
-instead.
-
-The same alternative plan would work for type-family injectivity constraints:
-see Note [Improvement orientation] in GHC.Tc.Solver.Equality.
---- End of Alternative plan (1) ---
-
---- Alternative plan (2) ---
-We could have a new flavour of TcTyVar (like `TauTv`, `TyVarTv` etc; see GHC.Tc.Utils.TcType.MetaInfo)
-for the fresh unification variables introduced by functional dependencies. Say `FunDepTv`. Then in
-GHC.Tc.Utils.Unify.swapOverTyVars we could arrange to keep a `FunDepTv` on the left if possible.
-Looks possible, but it's one more complication.
---- End of Alternative plan (2) ---
-
-
---- Historical note: Failed Alternative Plan (3) ---
-Previously we used a flag `cc_fundeps` in `CDictCan`. It would flip to False
-once we used a fun dep to hint the solver to break and to stop emitting more
-wanteds. This solution was not complete, and caused a failures while trying
-to solve for transitive functional dependencies (test case: T21703)
--- End of Historical note: Failed Alternative Plan (3) --
-
-Note [Do fundeps last]
-~~~~~~~~~~~~~~~~~~~~~~
-Consider T4254b:
- class FD a b | a -> b where { op :: a -> b }
-
- instance FD Int Bool
-
- foo :: forall a b. (a~Int,FD a b) => a -> Bool
- foo = op
-
-(DFL1) Try local fundeps first.
- From the ambiguity check on the type signature we get
- [G] FD Int b
- [W] FD Int beta
- Interacting these gives beta:=b; then we start again and solve without
- trying fundeps between the new [W] FD Int b and the top-level instance.
- If we did, we'd generate [W] b ~ Bool, which fails.
-
-(DFL2) Try solving from top-level instances before fundeps
- From the definition `foo = op` we get
- [G] FD Int b
- [W] FD Int Bool
- We solve this from the top level instance before even trying fundeps.
- If we did try fundeps, we'd generate [W] b ~ Bool, which fails.
-
-
-Note [Weird fundeps]
-~~~~~~~~~~~~~~~~~~~~
-Consider class Het a b | a -> b where
- het :: m (f c) -> a -> m b
-
- class GHet (a :: * -> *) (b :: * -> *) | a -> b
- instance GHet (K a) (K [a])
- instance Het a b => GHet (K a) (K b)
-
-The two instances don't actually conflict on their fundeps,
-although it's pretty strange. So they are both accepted. Now
-try [W] GHet (K Int) (K Bool)
-This triggers fundeps from both instance decls;
- [W] K Bool ~ K [a]
- [W] K Bool ~ K beta
-And there's a risk of complaining about Bool ~ [a]. But in fact
-the Wanted matches the second instance, so we never get as far
-as the fundeps.
-
-#7875 is a case in point.
--}
-
-doDictFunDepImprovement :: Cts -> TcS (Cts, Bool)
--- (doDictFunDepImprovement inst_envs cts)
--- * Generate the fundeps from interacting the
--- top-level `inst_envs` with the constraints `cts`
--- * Do the unifications and return any unsolved constraints
--- See Note [Fundeps with instances, and equality orientation]
-doDictFunDepImprovement unsolved_wanteds
- = do { inerts <- getInertCans -- The inert_dicts are all Givens
- ; inst_envs <- getInstEnvs
- ; (_, new_eqs, unifs) <- foldrM (do_one_dict inst_envs)
- (inert_dicts inerts, emptyBag, False)
- unsolved_wanteds
- ; return (new_eqs, unifs) }
-
-do_one_dict :: InstEnvs -> Ct
- -> (DictMap DictCt, Cts, Bool)
- -> TcS (DictMap DictCt, Cts, Bool)
-do_one_dict inst_envs (CDictCan dict_ct) (local_dicts, new_eqs, unifs)
- = do { (new_eqs1, unifs1) <- do_one_top inst_envs dict_ct
- ; (local_dicts2, new_eqs2, unifs2) <- do_one_local local_dicts dict_ct
- ; return ( local_dicts2
- , new_eqs1 `unionBags` new_eqs2 `unionBags` new_eqs
- , unifs1 || unifs2 || unifs ) }
-
-do_one_dict _ _ acc -- Non-DictCt constraints
- = return acc
-
-do_one_top :: InstEnvs -> DictCt -> TcS (Cts, Bool)
-do_one_top inst_envs (DictCt { di_ev = ev, di_cls = cls, di_tys = xis })
- = unifyFunDepWanteds ev eqns
- where
- eqns :: [FunDepEqn (CtLoc, RewriterSet)]
- eqns = improveFromInstEnv inst_envs mk_ct_loc cls xis
-
- dict_pred = mkClassPred cls xis
- dict_loc = ctEvLoc ev
- dict_origin = ctLocOrigin dict_loc
- dict_rewriters = ctEvRewriters ev
-
- mk_ct_loc :: ClsInst -- The instance decl
- -> (CtLoc, RewriterSet)
- mk_ct_loc ispec
- = (dict_loc { ctl_origin = new_orig }, dict_rewriters)
- where
- inst_pred = mkClassPred cls (is_tys ispec)
- inst_loc = getSrcSpan (is_dfun ispec)
- new_orig = FunDepOrigin2 dict_pred dict_origin
- inst_pred inst_loc
-
-do_one_local :: DictMap DictCt -> DictCt -> TcS (DictMap DictCt, Cts, Bool)
--- Using functional dependencies, interact the unsolved Wanteds
--- against each other and the inert Givens, to produce new equalities
-do_one_local locals dict_ct@(DictCt { di_cls = cls, di_ev = wanted_ev })
- -- locals contains all the Givens and earlier Wanteds
- = do { (new_eqs, unifs) <- foldrM do_interaction (emptyBag, False) $
- findDictsByClass locals cls
- ; return (addDict dict_ct locals, new_eqs, unifs) }
- where
- wanted_pred = ctEvPred wanted_ev
- wanted_loc = ctEvLoc wanted_ev
-
- do_interaction :: DictCt -> (Cts,Bool) -> TcS (Cts,Bool)
- do_interaction (DictCt { di_ev = all_ev }) (new_eqs, unifs) -- This can be Given or Wanted
- = do { traceTcS "doLocalFunDepImprovement" $
- vcat [ ppr wanted_ev
- , pprCtLoc wanted_loc, ppr (isGivenLoc wanted_loc)
- , pprCtLoc all_loc, ppr (isGivenLoc all_loc)
- , pprCtLoc deriv_loc, ppr (isGivenLoc deriv_loc) ]
-
- ; (new_eqs1, unifs1) <- unifyFunDepWanteds wanted_ev $
- improveFromAnother (deriv_loc, all_rewriters)
- all_pred wanted_pred
- ; return (new_eqs1 `unionBags` new_eqs, unifs1 || unifs) }
- where
- all_pred = ctEvPred all_ev
- all_loc = ctEvLoc all_ev
- all_rewriters = ctEvRewriters all_ev
- deriv_loc = wanted_loc { ctl_depth = deriv_depth
- , ctl_origin = deriv_origin }
- deriv_depth = ctl_depth wanted_loc `maxSubGoalDepth`
- ctl_depth all_loc
- deriv_origin = FunDepOrigin1 wanted_pred
- (ctLocOrigin wanted_loc)
- (ctLocSpan wanted_loc)
- all_pred
- (ctLocOrigin all_loc)
- (ctLocSpan all_loc)
-
{- *********************************************************************
* *
=====================================
compiler/GHC/Tc/Solver/Equality.hs
=====================================
@@ -14,6 +14,7 @@ import GHC.Tc.Solver.Irred( solveIrred )
import GHC.Tc.Solver.Dict( matchLocalInst, chooseInstance )
import GHC.Tc.Solver.Rewrite
import GHC.Tc.Solver.Monad
+import GHC.Tc.Solver.FunDeps( unifyAndEmitFunDepWanteds )
import GHC.Tc.Solver.InertSet
import GHC.Tc.Solver.Types( findFunEqsByTyCon )
import GHC.Tc.Types.Evidence
@@ -3108,13 +3109,17 @@ improve_wanted_top_fun_eqs fam_tc lhs_tys rhs_ty
| Just ops <- isBuiltInSynFamTyCon_maybe fam_tc
= return (map snd $ tryInteractTopFam ops fam_tc lhs_tys rhs_ty)
+ -- ToDo: use ideas in #23162 for closed type families; injectivity only for open
+
-- See Note [Type inference for type families with injectivity]
+ -- Open, so look for inj
| Injective inj_args <- tyConInjectivityInfo fam_tc
= do { fam_envs <- getFamInstEnvs
; top_eqns <- improve_injective_wanted_top fam_envs inj_args fam_tc lhs_tys rhs_ty
; let local_eqns = improve_injective_wanted_famfam inj_args fam_tc lhs_tys rhs_ty
; traceTcS "improve_wanted_top_fun_eqs" $
vcat [ ppr fam_tc, text "local_eqns" <+> ppr local_eqns, text "top_eqns" <+> ppr top_eqns ]
+ -- xxx ToDo: this does both local and top => bug?
; return (local_eqns ++ top_eqns) }
| otherwise -- No injectivity
=====================================
compiler/GHC/Tc/Solver/FunDeps.hs
=====================================
@@ -0,0 +1,486 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE MultiWayIf #-}
+
+-- | Solving Class constraints CDictCan
+module GHC.Tc.Solver.FunDeps (
+ unifyAndEmitFunDepWanteds,
+ doDictFunDepImprovement,
+ ImprovementResult, noImprovement
+ ) where
+
+import GHC.Prelude
+
+import GHC.Tc.Instance.FunDeps
+import GHC.Tc.Types.Evidence
+import GHC.Tc.Types.Constraint
+import GHC.Tc.Types.CtLoc
+import GHC.Tc.Types.Origin
+import GHC.Tc.Solver.InertSet
+import GHC.Tc.Solver.Monad
+import GHC.Tc.Solver.Types
+import GHC.Tc.Utils.TcType
+import GHC.Tc.Utils.Unify( UnifyEnv(..) )
+import GHC.Tc.Utils.Monad as TcM
+
+import GHC.Core.Type
+import GHC.Core.InstEnv ( InstEnvs, ClsInst(..) )
+import GHC.Core.Coercion.Axiom( TypeEqn )
+
+import GHC.Types.Name
+import GHC.Types.Var.Set
+
+import GHC.Utils.Outputable
+
+import GHC.Data.Bag
+import GHC.Data.Pair
+
+import qualified Data.Semigroup as S
+
+import Control.Monad
+
+{- *********************************************************************
+* *
+* Functional dependencies, instantiation of equations
+* *
+************************************************************************
+
+When we spot an equality arising from a functional dependency,
+we now use that equality (a "wanted") to rewrite the work-item
+constraint right away. This avoids two dangers
+
+ Danger 1: If we send the original constraint on down the pipeline
+ it may react with an instance declaration, and in delicate
+ situations (when a Given overlaps with an instance) that
+ may produce new insoluble goals: see #4952
+
+ Danger 2: If we don't rewrite the constraint, it may re-react
+ with the same thing later, and produce the same equality
+ again --> termination worries.
+
+To achieve this required some refactoring of GHC.Tc.Instance.FunDeps (nicer
+now!).
+
+Note [FunDep and implicit parameter reactions]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Currently, our story of interacting two dictionaries (or a dictionary
+and top-level instances) for functional dependencies (including implicit
+parameters), is that we simply produce new Wanted equalities. So for example
+
+ class D a b | a -> b where ...
+ Inert:
+ [G] d1 : D Int Bool
+ WorkItem:
+ [W] d2 : D Int alpha
+
+ We generate the extra work item
+ [W] cv : alpha ~ Bool
+ where 'cv' is currently unused. However, this new item can perhaps be
+ spontaneously solved to become given and react with d2,
+ discharging it in favour of a new constraint d2' thus:
+ [W] d2' : D Int Bool
+ d2 := d2' |> D Int cv
+ Now d2' can be discharged from d1
+
+We could be more aggressive and try to *immediately* solve the dictionary
+using those extra equalities.
+
+If that were the case with the same inert set and work item we might discard
+d2 directly:
+
+ [W] cv : alpha ~ Bool
+ d2 := d1 |> D Int cv
+
+But in general it's a bit painful to figure out the necessary coercion,
+so we just take the first approach. Here is a better example. Consider:
+ class C a b c | a -> b
+And:
+ [G] d1 : C T Int Char
+ [W] d2 : C T beta Int
+In this case, it's *not even possible* to solve the wanted immediately.
+So we should simply output the functional dependency and add this guy
+[but NOT its superclasses] back in the worklist. Even worse:
+ [G] d1 : C T Int beta
+ [W] d2: C T beta Int
+Then it is solvable, but its very hard to detect this on the spot.
+
+It's exactly the same with implicit parameters, except that the
+"aggressive" approach would be much easier to implement.
+
+Note [Fundeps with instances, and equality orientation]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This Note describes a delicate interaction that constrains the orientation of
+equalities. This one is about fundeps, but the /exact/ same thing arises for
+type-family injectivity constraints: see Note [Improvement orientation].
+
+doTopFunDepImprovement compares the constraint with all the instance
+declarations, to see if we can produce any equalities. E.g
+ class C2 a b | a -> b
+ instance C Int Bool
+Then the constraint (C Int ty) generates the equality [W] ty ~ Bool.
+
+There is a nasty corner in #19415 which led to the typechecker looping:
+ class C s t b | s -> t
+ instance ... => C (T kx x) (T ky y) Int
+ T :: forall k. k -> Type
+
+ work_item: dwrk :: C (T @ka (a::ka)) (T @kb0 (b0::kb0)) Char
+ where kb0, b0 are unification vars
+
+ ==> {doTopFunDepImprovement: compare work_item with instance,
+ generate /fresh/ unification variables kfresh0, yfresh0,
+ emit a new Wanted, and add dwrk to inert set}
+
+ Suppose we emit this new Wanted from the fundep:
+ [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
+
+ ==> {solve that equality kb0 := kfresh0, b0 := yfresh0}
+ Now kick out dwrk, since it mentions kb0
+ But now we are back to the start! Loop!
+
+NB1: This example relies on an instance that does not satisfy the
+ coverage condition (although it may satisfy the weak coverage
+ condition), and hence whose fundeps generate fresh unification
+ variables. Not satisfying the coverage condition is known to
+ lead to termination trouble, but in this case it's plain silly.
+
+NB2: In this example, the third parameter to C ensures that the
+ instance doesn't actually match the Wanted, so we can't use it to
+ solve the Wanted
+
+We solve the problem by (#21703):
+
+ carefully orienting the new Wanted so that all the
+ freshly-generated unification variables are on the LHS.
+
+ Thus we call unifyWanteds on
+ T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
+ and /NOT/
+ T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)
+
+Now we'll unify kfresh0:=kb0, yfresh0:=b0, and all is well. The general idea
+is that we want to preferentially eliminate those freshly-generated
+unification variables, rather than unifying older variables, which causes
+kick-out etc.
+
+Keeping younger variables on the left also gives very minor improvement in
+the compiler performance by having less kick-outs and allocations (-0.1% on
+average). Indeed Historical Note [Eliminate younger unification variables]
+in GHC.Tc.Utils.Unify describes an earlier attempt to do so systematically,
+apparently now in abeyance.
+
+But this is is a delicate solution. We must take care to /preserve/
+orientation during solving. Wrinkles:
+
+(W1) We start with
+ [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)
+ Decompose to
+ [W] kfresh0 ~ kb0
+ [W] (yfresh0::kfresh0) ~ (b0::kb0)
+ Preserve orientation when decomposing!!
+
+(W2) Suppose we happen to tackle the second Wanted from (W1)
+ first. Then in canEqCanLHSHetero we emit a /kind/ equality, as
+ well as a now-homogeneous type equality
+ [W] kco : kfresh0 ~ kb0
+ [W] (yfresh0::kfresh0) ~ (b0::kb0) |> (sym kco)
+ Preserve orientation in canEqCanLHSHetero!! (Failing to
+ preserve orientation here was the immediate cause of #21703.)
+
+(W3) There is a potential interaction with the swapping done by
+ GHC.Tc.Utils.Unify.swapOverTyVars. We think it's fine, but it's
+ a slight worry. See especially Note [TyVar/TyVar orientation] in
+ that module.
+
+The trouble is that "preserving orientation" is a rather global invariant,
+and sometimes we definitely do want to swap (e.g. Int ~ alpha), so we don't
+even have a precise statement of what the invariant is. The advantage
+of the preserve-orientation plan is that it is extremely cheap to implement,
+and apparently works beautifully.
+
+--- Alternative plan (1) ---
+Rather than have an ill-defined invariant, another possiblity is to
+elminate those fresh unification variables at birth, when generating
+the new fundep-inspired equalities.
+
+The key idea is to call `instFlexiX` in `emitFunDepWanteds` on only those
+type variables that are guaranteed to give us some progress. This means we
+have to locally (without calling emitWanteds) identify the type variables
+that do not give us any progress. In the above example, we _know_ that
+emitting the two wanteds `kco` and `co` is fruitless.
+
+ Q: How do we identify such no-ops?
+
+ 1. Generate a matching substitution from LHS to RHS
+ ɸ = [kb0 :-> k0, b0 :-> y0]
+ 2. Call `instFlexiX` on only those type variables that do not appear in the domain of ɸ
+ ɸ' = instFlexiX ɸ (tvs - domain ɸ)
+ 3. Apply ɸ' on LHS and then call emitWanteds
+ unifyWanteds ... (subst ɸ' LHS) RHS
+
+Why will this work? The matching substitution ɸ will be a best effort
+substitution that gives us all the easy solutions. It can be generated with
+modified version of `Core/Unify.unify_tys` where we run it in a matching mode
+and never generate `SurelyApart` and always return a `MaybeApart Subst`
+instead.
+
+The same alternative plan would work for type-family injectivity constraints:
+see Note [Improvement orientation] in GHC.Tc.Solver.Equality.
+--- End of Alternative plan (1) ---
+
+--- Alternative plan (2) ---
+We could have a new flavour of TcTyVar (like `TauTv`, `TyVarTv` etc; see GHC.Tc.Utils.TcType.MetaInfo)
+for the fresh unification variables introduced by functional dependencies. Say `FunDepTv`. Then in
+GHC.Tc.Utils.Unify.swapOverTyVars we could arrange to keep a `FunDepTv` on the left if possible.
+Looks possible, but it's one more complication.
+--- End of Alternative plan (2) ---
+
+
+--- Historical note: Failed Alternative Plan (3) ---
+Previously we used a flag `cc_fundeps` in `CDictCan`. It would flip to False
+once we used a fun dep to hint the solver to break and to stop emitting more
+wanteds. This solution was not complete, and caused a failures while trying
+to solve for transitive functional dependencies (test case: T21703)
+-- End of Historical note: Failed Alternative Plan (3) --
+
+Note [Do fundeps last]
+~~~~~~~~~~~~~~~~~~~~~~
+Consider T4254b:
+ class FD a b | a -> b where { op :: a -> b }
+
+ instance FD Int Bool
+
+ foo :: forall a b. (a~Int,FD a b) => a -> Bool
+ foo = op
+
+(DFL1) Try local fundeps first.
+ From the ambiguity check on the type signature we get
+ [G] FD Int b
+ [W] FD Int beta
+ If we ineract that Wanted with /both/ the t0p-level instance, /and/ the
+ local Given, we'll get
+ beta ~ Int and beta ~ b
+ respectively. That would generate (b~Bool), which would fai. I think
+ it doesn't matter which of the two we pick, but historically we have
+ picked teh local-fundeps firs.
+
+(DFL2) Try solving from top-level instances before fundeps.
+ From the definition `foo = op` we get
+ [G] FD Int b
+ [W] FD Int Bool
+ We solve this from the top level instance before even trying fundeps.
+ If we did try fundeps, we'd generate [W] b ~ Bool, which fails.
+
+ (DFL2) is achieved by trying fundeps only on /unsolved/ Wanteds.
+
+
+Note [Weird fundeps]
+~~~~~~~~~~~~~~~~~~~~
+Consider class Het a b | a -> b where
+ het :: m (f c) -> a -> m b
+
+ class GHet (a :: * -> *) (b :: * -> *) | a -> b
+ instance GHet (K a) (K [a])
+ instance Het a b => GHet (K a) (K b)
+
+The two instances don't actually conflict on their fundeps,
+although it's pretty strange. So they are both accepted. Now
+try [W] GHet (K Int) (K Bool)
+This triggers fundeps from both instance decls;
+ [W] K Bool ~ K [a]
+ [W] K Bool ~ K beta
+And there's a risk of complaining about Bool ~ [a]. But in fact
+the Wanted matches the second instance, so we never get as far
+as the fundeps.
+
+#7875 is a case in point.
+-}
+
+doDictFunDepImprovement :: Cts -> TcS ImprovementResult
+-- (doDictFunDepImprovement inst_envs cts)
+-- * Generate the fundeps from interacting the
+-- top-level `inst_envs` with the constraints `cts`
+-- * Do the unifications and return any unsolved constraints
+-- See Note [Fundeps with instances, and equality orientation]
+-- foldrM :: (Foldable t, Monad m) => (a -> b -> m b) -> b -> t a -> m b
+doDictFunDepImprovement unsolved_wanteds
+ = do { inerts <- getInertCans -- The inert_dicts are all Givens
+ ; inst_envs <- getInstEnvs
+ ; (_, imp_res) <- foldM (do_one_dict inst_envs)
+ (inert_dicts inerts, noopImprovement)
+ unsolved_wanteds
+ ; return imp_res }
+
+do_one_dict :: InstEnvs
+ -> (DictMap DictCt, ImprovementResult)
+ -> Ct
+ -> TcS (DictMap DictCt, ImprovementResult)
+-- The `local_dicts` accumulator starts life as just the Givens, but
+-- as we encounter each Wanted we augment it. Result: each Wanted
+-- is interacted with all the Givens, and all prededing Wanteds.
+-- This is worst-case quadratic because we have to compare each
+-- constraint with all the others, to find all the pairwise interactions
+do_one_dict inst_envs (local_dicts, imp_res) (CDictCan dict_ct)
+ = do { (local_dicts1, imp_res1) <- do_one_local local_dicts dict_ct
+ ; if noImprovement imp_res1
+ then do { imp_res2 <- do_one_top inst_envs dict_ct
+ ; return (local_dicts1, imp_res `plusImprovements` imp_res2) }
+ else return (local_dicts1, imp_res `plusImprovements` imp_res1) }
+
+do_one_dict _ acc _ -- Non-DictCt constraints
+ = return acc
+
+do_one_top :: InstEnvs -> DictCt -> TcS ImprovementResult
+do_one_top inst_envs (DictCt { di_ev = ev, di_cls = cls, di_tys = xis })
+ = unifyFunDepWanteds ev eqns
+ where
+ eqns :: [FunDepEqn (CtLoc, RewriterSet)]
+ eqns = improveFromInstEnv inst_envs mk_ct_loc cls xis
+
+ dict_pred = mkClassPred cls xis
+ dict_loc = ctEvLoc ev
+ dict_origin = ctLocOrigin dict_loc
+ dict_rewriters = ctEvRewriters ev
+
+ mk_ct_loc :: ClsInst -- The instance decl
+ -> (CtLoc, RewriterSet)
+ mk_ct_loc ispec
+ = (dict_loc { ctl_origin = new_orig }, dict_rewriters)
+ where
+ inst_pred = mkClassPred cls (is_tys ispec)
+ inst_loc = getSrcSpan (is_dfun ispec)
+ new_orig = FunDepOrigin2 dict_pred dict_origin
+ inst_pred inst_loc
+
+do_one_local :: DictMap DictCt -> DictCt -> TcS (DictMap DictCt, ImprovementResult)
+-- Using functional dependencies, interact the unsolved Wanteds
+-- against each other and the inert Givens, to produce new equalities
+do_one_local locals dict_ct@(DictCt { di_cls = cls, di_ev = wanted_ev })
+ -- locals contains all the Givens and earlier Wanteds
+ = do { imp_res <- foldM do_interaction noopImprovement $
+ findDictsByClass locals cls
+ ; return (addDict dict_ct locals, imp_res) }
+ where
+ wanted_pred = ctEvPred wanted_ev
+ wanted_loc = ctEvLoc wanted_ev
+
+ do_interaction :: (Cts,Bool) -> DictCt -> TcS (Cts,Bool)
+ do_interaction (new_eqs, unifs) (DictCt { di_ev = all_ev }) -- This can be Given or Wanted
+ = do { traceTcS "doLocalFunDepImprovement" $
+ vcat [ ppr wanted_ev
+ , pprCtLoc wanted_loc, ppr (isGivenLoc wanted_loc)
+ , pprCtLoc all_loc, ppr (isGivenLoc all_loc)
+ , pprCtLoc deriv_loc, ppr (isGivenLoc deriv_loc) ]
+
+ ; (new_eqs1, unifs1) <- unifyFunDepWanteds wanted_ev $
+ improveFromAnother (deriv_loc, all_rewriters)
+ all_pred wanted_pred
+ ; return (new_eqs1 `unionBags` new_eqs, unifs1 || unifs) }
+ where
+ all_pred = ctEvPred all_ev
+ all_loc = ctEvLoc all_ev
+ all_rewriters = ctEvRewriters all_ev
+ deriv_loc = wanted_loc { ctl_depth = deriv_depth
+ , ctl_origin = deriv_origin }
+ deriv_depth = ctl_depth wanted_loc `maxSubGoalDepth`
+ ctl_depth all_loc
+ deriv_origin = FunDepOrigin1 wanted_pred
+ (ctLocOrigin wanted_loc)
+ (ctLocSpan wanted_loc)
+ all_pred
+ (ctLocOrigin all_loc)
+ (ctLocSpan all_loc)
+
+
+{-
+************************************************************************
+* *
+ Emitting equalities arising from fundeps
+* *
+************************************************************************
+-}
+
+type ImprovementResult = (Cts, Bool)
+ -- The Cts are the new equality constraints
+ -- The Bool is True if we unified any meta-ty-vars on when
+ -- generating those new equality constraints
+
+noopImprovement :: ImprovementResult
+noopImprovement = (emptyBag, False)
+
+noImprovement :: ImprovementResult -> Bool
+noImprovement (cts,unifs) = not unifs && isEmptyBag cts
+
+plusImprovements :: ImprovementResult -> ImprovementResult -> ImprovementResult
+plusImprovements (cts1,unif1) (cts2,unif2)
+ = (cts1 `unionBags` cts2, unif1 || unif2)
+
+
+unifyAndEmitFunDepWanteds :: CtEvidence -- The work item
+ -> [FunDepEqn (CtLoc, RewriterSet)]
+ -> TcS Bool -- True <=> some unification happened
+unifyAndEmitFunDepWanteds ev fd_eqns
+ = do { (new_eqs, unifs) <- unifyFunDepWanteds ev fd_eqns
+
+ ; -- Emit the deferred constraints
+ -- See Note [Work-list ordering] in GHC.Tc.Solved.Equality
+ --
+ -- All the constraints in `cts` share the same rewriter set so,
+ -- rather than looking at it one by one, we pass it to
+ -- extendWorkListChildEqs; just a small optimisation.
+ ; unless (isEmptyBag new_eqs) $
+ updWorkListTcS (extendWorkListChildEqs ev new_eqs)
+
+ ; return unifs }
+
+unifyFunDepWanteds :: CtEvidence -- The work item
+ -> [FunDepEqn (CtLoc, RewriterSet)]
+ -> TcS ImprovementResult
+
+unifyFunDepWanteds _ [] = return noopImprovement -- common case noop
+-- See Note [FunDep and implicit parameter reactions]
+
+unifyFunDepWanteds ev fd_eqns
+ = do { (fresh_tvs_s, cts, unified_tvs) <- wrapUnifierX ev Nominal do_fundeps
+
+ -- Figure out if a "real" unification happened: See Note [unifyFunDeps]
+ ; let unif_happened = any is_old_tv unified_tvs
+ fresh_tvs = mkVarSet (concat fresh_tvs_s)
+ is_old_tv tv = not (tv `elemVarSet` fresh_tvs)
+
+ ; return (cts, unif_happened) }
+ where
+ do_fundeps :: UnifyEnv -> TcM [[TcTyVar]]
+ do_fundeps env = mapM (do_one env) fd_eqns
+
+ do_one :: UnifyEnv -> FunDepEqn (CtLoc, RewriterSet) -> TcM [TcTyVar]
+ do_one uenv (FDEqn { fd_qtvs = tvs, fd_eqs = eqs, fd_loc = (loc, rewriters) })
+ = do { (fresh_tvs, eqs') <- instantiateFunDepEqn tvs (reverse eqs)
+ -- (reverse eqs): See Note [Reverse order of fundep equations]
+ ; uPairsTcM env_one eqs'
+ ; return fresh_tvs }
+ where
+ env_one = uenv { u_rewriters = u_rewriters uenv S.<> rewriters
+ , u_loc = loc }
+
+instantiateFunDepEqn :: [TyVar] -> [TypeEqn] -> TcM ([TcTyVar], [TypeEqn])
+instantiateFunDepEqn tvs eqs
+ | null tvs
+ = return ([], eqs)
+ | otherwise
+ = do { TcM.traceTc "emitFunDepWanteds 2" (ppr tvs $$ ppr eqs)
+ ; (tvs', subst) <- instFlexiXTcM emptySubst tvs -- Takes account of kind substitution
+ ; return (tvs', map (subst_pair subst) eqs) }
+ where
+ subst_pair subst (Pair ty1 ty2)
+ = Pair (substTyUnchecked subst' ty1) ty2
+ -- ty2 does not mention fd_qtvs, so no need to subst it.
+ -- See GHC.Tc.Instance.Fundeps Note [Improving against instances]
+ -- Wrinkle (1)
+ where
+ subst' = extendSubstInScopeSet subst (tyCoVarsOfType ty1)
+ -- The free vars of ty1 aren't just fd_qtvs: ty1 is the result
+ -- of matching with the [W] constraint. So we add its free
+ -- vars to InScopeSet, to satisfy substTy's invariants, even
+ -- though ty1 will never (currently) be a poytype, so this
+ -- InScopeSet will never be looked at.
+
=====================================
compiler/GHC/Tc/Solver/Monad.hs
=====================================
@@ -105,10 +105,9 @@ module GHC.Tc.Solver.Monad (
-- Unification
wrapUnifierX, wrapUnifierTcS, unifyFunDeps, uPairsTcM, unifyForAllBody,
- unifyFunDepWanteds, unifyAndEmitFunDepWanteds,
-- MetaTyVars
- newFlexiTcSTy, instFlexiX,
+ newFlexiTcSTy, instFlexiX, instFlexiXTcM,
cloneMetaTyVar,
tcInstSkolTyVarsX,
@@ -129,8 +128,7 @@ module GHC.Tc.Solver.Monad (
pprEq,
-- Enforcing invariants for type equalities
- checkTypeEq,
- instantiateFunDepEqn
+ checkTypeEq
) where
import GHC.Prelude
@@ -2235,83 +2233,6 @@ solverDepthError loc ty
where
depth = ctLocDepth loc
-{-
-************************************************************************
-* *
- Emitting equalities arising from fundeps
-* *
-************************************************************************
--}
-
-unifyAndEmitFunDepWanteds :: CtEvidence -- The work item
- -> [FunDepEqn (CtLoc, RewriterSet)]
- -> TcS Bool -- True <=> some unification happened
-unifyAndEmitFunDepWanteds ev fd_eqns
- = do { (new_eqs, unifs) <- unifyFunDepWanteds ev fd_eqns
-
- ; -- Emit the deferred constraints
- -- See Note [Work-list ordering] in GHC.Tc.Solved.Equality
- --
- -- All the constraints in `cts` share the same rewriter set so,
- -- rather than looking at it one by one, we pass it to
- -- extendWorkListChildEqs; just a small optimisation.
- ; unless (isEmptyBag new_eqs) $
- updWorkListTcS (extendWorkListChildEqs ev new_eqs)
-
- ; return unifs }
-
-unifyFunDepWanteds :: CtEvidence -- The work item
- -> [FunDepEqn (CtLoc, RewriterSet)]
- -> TcS (Cts, Bool) -- True <=> some unification happened
-
-unifyFunDepWanteds _ [] = return (emptyBag, False) -- common case noop
--- See Note [FunDep and implicit parameter reactions]
-
-unifyFunDepWanteds ev fd_eqns
- = do { (fresh_tvs_s, cts, unified_tvs) <- wrapUnifierX ev Nominal do_fundeps
-
- -- Figure out if a "real" unification happened: See Note [unifyFunDeps]
- ; let unif_happened = any is_old_tv unified_tvs
- fresh_tvs = mkVarSet (concat fresh_tvs_s)
- is_old_tv tv = not (tv `elemVarSet` fresh_tvs)
-
- ; return (cts, unif_happened) }
- where
- do_fundeps :: UnifyEnv -> TcM [[TcTyVar]]
- do_fundeps env = mapM (do_one env) fd_eqns
-
- do_one :: UnifyEnv -> FunDepEqn (CtLoc, RewriterSet) -> TcM [TcTyVar]
- do_one uenv (FDEqn { fd_qtvs = tvs, fd_eqs = eqs, fd_loc = (loc, rewriters) })
- = do { (fresh_tvs, eqs') <- instantiateFunDepEqn tvs (reverse eqs)
- -- (reverse eqs): See Note [Reverse order of fundep equations]
- ; uPairsTcM env_one eqs'
- ; return fresh_tvs }
- where
- env_one = uenv { u_rewriters = u_rewriters uenv S.<> rewriters
- , u_loc = loc }
-
-instantiateFunDepEqn :: [TyVar] -> [TypeEqn] -> TcM ([TcTyVar], [TypeEqn])
-instantiateFunDepEqn tvs eqs
- | null tvs
- = return ([], eqs)
- | otherwise
- = do { TcM.traceTc "emitFunDepWanteds 2" (ppr tvs $$ ppr eqs)
- ; (tvs', subst) <- instFlexiXTcM emptySubst tvs -- Takes account of kind substitution
- ; return (tvs', map (subst_pair subst) eqs) }
- where
- subst_pair subst (Pair ty1 ty2)
- = Pair (substTyUnchecked subst' ty1) ty2
- -- ty2 does not mention fd_qtvs, so no need to subst it.
- -- See GHC.Tc.Instance.Fundeps Note [Improving against instances]
- -- Wrinkle (1)
- where
- subst' = extendSubstInScopeSet subst (tyCoVarsOfType ty1)
- -- The free vars of ty1 aren't just fd_qtvs: ty1 is the result
- -- of matching with the [W] constraint. So we add its free
- -- vars to InScopeSet, to satisfy substTy's invariants, even
- -- though ty1 will never (currently) be a poytype, so this
- -- InScopeSet will never be looked at.
-
{-
************************************************************************
* *
=====================================
compiler/GHC/Tc/Solver/Solve.hs
=====================================
@@ -15,6 +15,7 @@ module GHC.Tc.Solver.Solve (
import GHC.Prelude
import GHC.Tc.Solver.Dict
+import GHC.Tc.Solver.FunDeps( doDictFunDepImprovement )
import GHC.Tc.Solver.Equality( solveEquality )
import GHC.Tc.Solver.Irred( solveIrred )
import GHC.Tc.Solver.Rewrite( rewrite, rewriteType )
@@ -119,13 +120,13 @@ simplify_loop n limit definitely_redo_implications
, int (lengthBag simples) <+> text "simples to solve" ])
; traceTcS "simplify_loop: wc =" (ppr wc)
- ; (unifs1, wc1) <- reportUnifications $ -- See Note [Superclass iteration]
- solveSimpleWanteds simples
+ ; (n_unifs, wc1) <- reportUnifications $ -- See Note [Superclass iteration]
+ solveSimpleWanteds simples
-- Any insoluble constraints are in 'simples' and so get rewritten
-- See Note [Rewrite insolubles] in GHC.Tc.Solver.InertSet
; wc2 <- if not definitely_redo_implications -- See Note [Superclass iteration]
- && unifs1 == 0 -- for this conditional
+ && n_unifs == 0 -- for this conditional
&& isEmptyBag (wc_impl wc1)
then return (wc { wc_simple = wc_simple wc1 }) -- Short cut
else do { implics2 <- solveNestedImplications $
@@ -207,10 +208,19 @@ maybe_simplify_again n limit unif_happened wc@(WC { wc_simple = simples })
try_fundeps :: TcS (Maybe NextAction)
try_fundeps
- = do { (new_eqs, unifs) <- doDictFunDepImprovement simples
- ; if null new_eqs && not unifs
+ = do { (new_eqs, unifs1) <- doDictFunDepImprovement simples
+ ; if null new_eqs && not unifs1
then return Nothing
- else return (Just (NA_TryAgain (wc `addSimples` new_eqs) unifs)) }
+ else
+ -- We solve new_eqs immediately, hoping to get some unifications
+ -- If instead we just added them to `wc` we'll iterate and (in case when
+ -- that doesn't solve it) we'll add the same constraint again... loop!
+ do { traceTcS "try_fundeps" (ppr unifs1 $$ ppr new_eqs)
+ ; (n_unifs2, _wc) <- reportUnifications $
+ solveSimpleWanteds new_eqs
+ ; if (unifs1 || n_unifs2 > 0)
+ then return (Just (NA_TryAgain wc True))
+ else return Nothing } }
{- Note [Superclass iteration]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
=====================================
compiler/ghc.cabal.in
=====================================
@@ -854,6 +854,7 @@ Library
GHC.Tc.Solver.Irred
GHC.Tc.Solver.Equality
GHC.Tc.Solver.Dict
+ GHC.Tc.Solver.FunDeps
GHC.Tc.Solver.Monad
GHC.Tc.Solver.Types
GHC.Tc.TyCl
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e9fc82be442304fc913469f2e41690…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e9fc82be442304fc913469f2e41690…
You're receiving this email because of your account on gitlab.haskell.org.
1
0

[Git][ghc/ghc][wip/haanss/depdir] added a test for qAddDependentDirectory
by Hassan Al-Awwadi (@hassan.awwadi) 07 Jul '25
by Hassan Al-Awwadi (@hassan.awwadi) 07 Jul '25
07 Jul '25
Hassan Al-Awwadi pushed to branch wip/haanss/depdir at Glasgow Haskell Compiler / GHC
Commits:
5c81a09c by Hassan Al-Awwadi at 2025-07-07T16:39:55+02:00
added a test for qAddDependentDirectory
- - - - -
6 changed files:
- testsuite/.gitignore
- testsuite/tests/th/Makefile
- + testsuite/tests/th/TH_Depends_Dir.hs
- + testsuite/tests/th/TH_Depends_Dir.stdout
- + testsuite/tests/th/TH_Depends_External_Dir.hs
- testsuite/tests/th/all.T
Changes:
=====================================
testsuite/.gitignore
=====================================
@@ -1523,6 +1523,7 @@ mk/ghcconfig*_test___spaces_ghc*.exe.mk
/tests/th/T8633
/tests/th/TH_Depends
/tests/th/TH_Depends_external.txt
+/tests/th/TH_Depends_external/dummy.txt
/tests/th/TH_StringPrimL
/tests/th/TH_import_loop/ModuleA.hi-boot
/tests/th/TH_import_loop/ModuleA.o-boot
=====================================
testsuite/tests/th/Makefile
=====================================
@@ -43,6 +43,20 @@ TH_Depends:
'$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends
./TH_Depends
+.PHONY: TH_Depends_Dir
+TH_Depends_Dir:
+ $(RM) TH_Depends_external/dummy.txt
+ $(RM) TH_Depends TH_Depends.exe
+ $(RM) TH_Depends.o TH_Depends.hi
+ $(RM) TH_Depends_External.o TH_Depends_External.hi
+
+ mk_DIR TH_Depends_external
+ '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends_Dir
+ ./TH_Depends_Dir
+ sleep 2
+ echo "dummy" > TH_Depends_external/dummy.txt
+ '$(TEST_HC)' $(TEST_HC_OPTS) $(ghcThWayFlags) --make -v0 TH_Depends_Dir
+ ./TH_Depends_Dir
T8333:
'$(TEST_HC)' $(TEST_HC_OPTS_INTERACTIVE) $(ghcThWayFlags) T8333.hs < /dev/null
=====================================
testsuite/tests/th/TH_Depends_Dir.hs
=====================================
@@ -0,0 +1,9 @@
+
+{-# LANGUAGE TemplateHaskell #-}
+
+module Main where
+
+import TH_Depends_External (checkDirectoryContent)
+
+main :: IO ()
+main = putStrLn $checkDirectoryContent
=====================================
testsuite/tests/th/TH_Depends_Dir.stdout
=====================================
@@ -0,0 +1,3 @@
+no files?
+
+yes files!
\ No newline at end of file
=====================================
testsuite/tests/th/TH_Depends_External_Dir.hs
=====================================
@@ -0,0 +1,18 @@
+
+module TH_Depends_External where
+
+import Language.Haskell.TH.Syntax
+import Language.Haskell.TH.Lib
+import System.Directory (listDirectory)
+import Control.Monad.IO.Class (liftIO)
+
+checkDirectoryContent :: Q Exp
+checkDirectoryContent = do
+ let externalDependency = "TH_Depends_external"
+ qAddDependentDirectory externalDependency
+ files <- liftIO $ listDirectory externalDependency
+
+ let s = case files
+ [] -> "no files?"
+ _ -> "yes files!"
+ stringE s
\ No newline at end of file
=====================================
testsuite/tests/th/all.T
=====================================
@@ -214,6 +214,7 @@ test('T5434', [], multimod_compile,
['T5434', '-v0 -Wall ' + config.ghc_th_way_flags])
test('T5508', normal, compile, ['-v0 -ddump-splices -dsuppress-uniques'])
test('TH_Depends', [only_ways(['normal'])], makefile_test, ['TH_Depends'])
+test('TH_Depends_Dir', [only_ways(['normal'])], makefile_test, ['TH_Depends_Dir'])
test('T5597', [], multimod_compile, ['T5597', '-v0 ' + config.ghc_th_way_flags])
test('T5665', [], multimod_compile, ['T5665', '-v0 ' + config.ghc_th_way_flags])
test('T5700', [], multimod_compile,
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5c81a09cbaefacac51022d9f484d25f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5c81a09cbaefacac51022d9f484d25f…
You're receiving this email because of your account on gitlab.haskell.org.
1
0