[Git][ghc/ghc][wip/sjakobi/udfm-placement] Use a pigeonhole sort for deterministic UniqDFM iteration
by Simon Jakobi (@sjakobi) 08 Aug '26
by Simon Jakobi (@sjakobi) 08 Aug '26
08 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/udfm-placement at Glasgow Haskell Compiler / GHC
Commits:
17ce2922 by Simon Jakobi at 2026-08-08T17:19:52+02:00
Use a pigeonhole sort for deterministic UniqDFM iteration
Deterministic UniqDFM iteration used a list mergesort, allocating O(n
log n) cons cells and contributing significantly to compiler allocations
(#27459).
Use a pigeonhole sort where appropriate, while retaining the mergesort
fallback. See Note [Sorting a UDFM] and Note [Cost of deterministic
iteration].
The peak_megabytes_allocated increase for LinkableUsage02 is probably
due to GC timing noise. See #27613.
-------------------------
Metric Decrease:
InstanceMatching
InstanceMatching1
ManyAlternatives
T12707
T13379
T13719
T24471
T27336
T5321FD
T5321Fun
T783
Metric Increase 'peak_megabytes_allocated':
LinkableUsage02
-------------------------
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
5 changed files:
- compiler/GHC/Data/Word64Map/Internal.hs
- compiler/GHC/Data/Word64Map/Lazy.hs
- compiler/GHC/Data/Word64Map/Strict.hs
- compiler/GHC/Data/Word64Map/Strict/Internal.hs
- compiler/GHC/Types/Unique/DFM.hs
Changes:
=====================================
compiler/GHC/Data/Word64Map/Internal.hs
=====================================
@@ -170,6 +170,7 @@ module GHC.Data.Word64Map.Internal (
, map
, mapWithKey
, traverseWithKey
+ , traverseWithKey_
, traverseMaybeWithKey
, mapAccum
, mapAccumWithKey
@@ -2520,6 +2521,16 @@ traverseWithKey f = go
| otherwise = liftA2 (Bin p m) (go l) (go r)
{-# INLINE traverseWithKey #-}
+-- | \(O(n)\). Visit each key\/value pair in ascending key order, discarding
+-- the results.
+traverseWithKey_ :: Applicative t => (Key -> a -> t ()) -> Word64Map a -> t ()
+traverseWithKey_ f = go
+ where
+ go Nil = pure ()
+ go (Tip k v) = f k v
+ go (Bin _ _ l r) = go l *> go r
+{-# INLINE traverseWithKey_ #-}
+
-- | \(O(n)\). The function @'mapAccum'@ threads an accumulating
-- argument through the map in ascending order of keys.
--
=====================================
compiler/GHC/Data/Word64Map/Lazy.hs
=====================================
@@ -149,6 +149,7 @@ module GHC.Data.Word64Map.Lazy (
, WM.map
, mapWithKey
, traverseWithKey
+ , traverseWithKey_
, traverseMaybeWithKey
, mapAccum
, mapAccumWithKey
=====================================
compiler/GHC/Data/Word64Map/Strict.hs
=====================================
@@ -166,6 +166,7 @@ module GHC.Data.Word64Map.Strict (
, map
, mapWithKey
, traverseWithKey
+ , traverseWithKey_
, traverseMaybeWithKey
, mapAccum
, mapAccumWithKey
=====================================
compiler/GHC/Data/Word64Map/Strict/Internal.hs
=====================================
@@ -168,6 +168,7 @@ module GHC.Data.Word64Map.Strict.Internal (
, map
, mapWithKey
, traverseWithKey
+ , traverseWithKey_
, traverseMaybeWithKey
, mapAccum
, mapAccumWithKey
@@ -330,6 +331,7 @@ import GHC.Data.Word64Map.Internal
, toAscList
, toDescList
, toList
+ , traverseWithKey_
, union
, unions
, withoutKeys
=====================================
compiler/GHC/Types/Unique/DFM.hs
=====================================
@@ -14,6 +14,9 @@ See Note [Unique Determinism] in GHC.Types.Unique for explanation why @Unique@ o
is not deterministic.
-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+
{-# OPTIONS_GHC -Wall #-}
module GHC.Types.Unique.DFM (
@@ -79,6 +82,9 @@ import Data.Functor.Classes (Eq1 (..))
import Data.List (sortBy)
import Data.Function (on)
import GHC.Types.Unique.FM (UniqFM, nonDetUFMToList, ufmToIntMap, unsafeIntMapToUFM)
+import GHC.Data.SmallArray
+import GHC.Exts (State#, build)
+import GHC.ST (ST(..), runST)
import Unsafe.Coerce
import qualified GHC.Data.Word64Set as W
@@ -96,10 +102,10 @@ import qualified GHC.Data.Word64Set as W
-- This means `alterUDFM` consistent with `addToUDFM` and `adjustUDFM`,
-- so that for example `alterUDFM id k = id` and `alterUDFM (fmap f) k = adjustUDFM f k`
--
--- There is an implementation cost: each element is given a serial number
--- as it is added, and `udfmToList` sorts its result by this serial
--- number. So you should only use `UniqDFM` if you need the deterministic
--- property.
+-- There is an implementation cost: each element is given an insertion tag
+-- as it is added, and functions like `udfmToList` or `eltsUDFM` order their
+-- results by this tag (see Note [Cost of deterministic iteration]). So you
+-- should only use `UniqDFM` if you need the deterministic property.
--
-- `foldUDFM` also preserves determinism.
--
@@ -112,7 +118,7 @@ import qualified GHC.Data.Word64Set as W
--
--
-- There's more than one way to implement this. The implementation here tags
--- every value with the insertion time that can later be used to sort the
+-- every value with its insertion tag that can later be used to sort the
-- values when asked to convert to a list.
--
-- Updating an existing key keeps the old tag. This keeps the order stable for
@@ -125,7 +131,7 @@ import qualified GHC.Data.Word64Set as W
--
-- An alternative would be to have
--
--- data UniqDFM ele = UDFM (M.IntMap ele) [ele]
+-- data UniqDFM ele = UDFM (Word64Map ele) [ele]
--
-- where the list determines the order. This makes deletion tricky as we'd
-- only accumulate elements in that list, but makes merging easier as you
@@ -133,11 +139,11 @@ import qualified GHC.Data.Word64Set as W
-- Deletion can probably be done in amortized fashion when the size of the
-- list is twice the size of the set.
--- | A type of values tagged with insertion time
+-- | A type of values carrying an insertion tag
data TaggedVal val =
TaggedVal
!val
- {-# UNPACK #-} !Int -- ^ insertion time
+ {-# UNPACK #-} !Int -- ^ insertion tag
deriving stock (Data, Functor, Foldable, Traversable)
taggedFst :: TaggedVal val -> val
@@ -159,18 +165,30 @@ instance Eq val => Eq (TaggedVal val) where
data UniqDFM key ele =
UDFM
!(M.Word64Map (TaggedVal ele)) -- A map where keys are Unique's values and
- -- values are tagged with insertion time.
- -- The invariant is that all the tags will
- -- be distinct within a single map
- {-# UNPACK #-} !Int -- Upper bound on the values' insertion
- -- time. See Note [Overflow on plusUDFM]
+ -- values carry an insertion tag.
+ {-# UNPACK #-} !Int -- Upper bound on the values' insertion
+ -- tags. See Note [Overflow on plusUDFM]
+ -- See Note [UDFM invariants]
deriving (Data, Functor)
--- | Deterministic, in O(n log n).
+{- Note [UDFM invariants]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+In a map (UDFM m ub):
+
+ (a) The insertion tags of the elements of m are distinct.
+ (b) Every tag lies in [0, ub).
+
+Consequently ub >= size m.
+
+The tags determine the order of deterministic iteration (eltsUDFM,
+udfmToList). See Note [Sorting a UDFM].
+-}
+
+-- | Deterministic. See Note [Cost of deterministic iteration].
instance Foldable (UniqDFM key) where
foldr = foldUDFM
--- | Deterministic, in O(n log n).
+-- | Deterministic. See Note [Cost of deterministic iteration].
instance Traversable (UniqDFM key) where
traverse f = fmap listToUDFM_Directly
. traverse (\(u,a) -> (u,) <$> f a)
@@ -264,8 +282,8 @@ plusUDFM_CK f udfml@(UDFM _ i) udfmr@(UDFM _ j)
-- Note [Overflow on plusUDFM]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- There are multiple ways of implementing plusUDFM.
--- The main problem that needs to be solved is overlap on times of
--- insertion between different keys in two maps.
+-- The main problem that needs to be solved is overlap on insertion
+-- tags between different keys in two maps.
-- Consider:
--
-- A = fromList [(a, (x, 1))]
@@ -325,13 +343,27 @@ elemUDFM :: Uniquable key => key -> UniqDFM key elt -> Bool
elemUDFM k (UDFM m _i) = M.member (getKey $ getUnique k) m
-- | Performs a deterministic fold over the UniqDFM.
--- It's O(n log n) while the corresponding function on `UniqFM` is O(n).
+--
+-- O(n) in the common case, with an O(n log n) fallback.
+--
+-- See Note [Cost of deterministic iteration].
foldUDFM :: (elt -> a -> a) -> a -> UniqDFM key elt -> a
{-# INLINE foldUDFM #-}
--- This INLINE prevents a regression in !10568
-foldUDFM k z m = foldr k z (eltsUDFM m)
-
--- | Like 'foldUDFM' but the function also receives a key
+-- Specialises k and z into M.foldr on the small-map path.
+foldUDFM k z (UDFM m ub)
+ | M.compareSize m 1 /= GT = M.foldr (k . taggedFst) z m
+ | otherwise = fold_udfm k z m ub
+
+fold_udfm :: (elt -> a -> a) -> a -> M.Word64Map (TaggedVal elt) -> Int -> a
+{-# NOINLINE fold_udfm #-}
+-- Kept out of line so that foldUDFM's consumers don't inline the sort machinery.
+fold_udfm k z m ub
+ | usePigeonholeSort m ub = foldr k z (pigeonholeSort ub (\_ tv -> tv) m)
+ | otherwise = foldr k z (map taggedFst (sort_it m))
+
+-- | Like 'foldUDFM' but the function also receives a key.
+--
+-- See Note [Cost of deterministic iteration].
foldWithKeyUDFM :: (Unique -> elt -> a -> a) -> a -> UniqDFM key elt -> a
{-# INLINE foldWithKeyUDFM #-}
-- This INLINE was copied from foldUDFM
@@ -346,14 +378,113 @@ nonDetStrictFoldUDFM k z (UDFM m _i) = foldl' k' z m
where
k' acc (TaggedVal v _) = k v acc
+{- Note [Cost of deterministic iteration]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Deterministic iteration -- foldUDFM, eltsUDFM, udfmToList, and everything
+built on them -- orders elements by insertion tag. The element with the
+smallest tag can sit anywhere in the map, so every tag must be inspected,
+and, given a @UDFM m ub@ on the pigeonhole-sort path, an array with ub slots
+must be filled, before the first element can be emitted (see
+Note [Sorting a UDFM]). So beyond maps of a single element, deterministic
+iteration cannot stream: demanding any of the result processes the whole
+map. #27459 shows that cost hitting a consumer that only needed to know
+whether the result was non-empty.
+
+So: to test for emptiness, use isNullUDFM rather than null on eltsUDFM;
+for order-oblivious queries, prefer short-circuiting anyUDFM/allUDFM; and
+if you don't need the deterministic order at all, use nonDetStrictFoldUDFM.
+-}
+
+-- | Deterministic, in order of insertion.
+--
+-- See Note [Sorting a UDFM] and Note [Cost of deterministic iteration].
eltsUDFM :: UniqDFM key elt -> [elt]
-{-# INLINE eltsUDFM #-}
--- The INLINE makes it a good producer (from the map)
-eltsUDFM (UDFM m _i) = map taggedFst (sort_it m)
+{-# INLINE eltsUDFM #-} -- so the small case is a good producer
+ -- This matters for T13719.
+eltsUDFM (UDFM m ub)
+ | M.compareSize m 1 /= GT = build (\c n -> M.foldr (c . taggedFst) n m)
+ | otherwise = elts_udfm m ub
+
+elts_udfm :: M.Word64Map (TaggedVal elt) -> Int -> [elt]
+{-# NOINLINE elts_udfm #-}
+-- Kept out of line so that eltsUDFM's consumers don't inline the sort machinery.
+elts_udfm m ub
+ | usePigeonholeSort m ub = pigeonholeSort ub (\_ tv -> tv) m
+ | otherwise = map taggedFst (sort_it m)
sort_it :: M.Word64Map (TaggedVal elt) -> [TaggedVal elt]
sort_it m = sortBy (compare `on` taggedSnd) (M.elems m)
+
+{- Note [Sorting a UDFM]
+~~~~~~~~~~~~~~~~~~~~~~~~
+Deterministic iteration must yield a map's elements in order of their
+insertion tags. The obvious way is to sort on the tags, but we can do better:
+in (UDFM m ub) the tags are distinct indices into [0, ub) (see
+Note [UDFM invariants]), so each element can simply be placed at its own
+tag in an ub-slot array, which is then read back in index order. This is
+pigeonhole sort, with one element per hole.
+
+Cost: writing the elements is O(n) for n = M.size m, while allocating the
+array and reading it back are O(ub). Since n <= ub the total is O(ub). No
+comparisons are made.
+
+So the method wins only while the array stays dense, and ub never shrinks
+(overwrites keep bumping it, delete/filter shrink n but not ub).
+usePigeonholeSort therefore takes this path only when ub <= 4 * n, which
+bounds its cost at O(n), and falls back to the O(n log n) comparison sort
+otherwise.
+
+Unfilled slots contain a TaggedVal with tag -1 and value
+@unsafeCoerce () :: r@. This is safe because the value is never used: only
+slots with non-negative tags are read.
+
+pigeonholeSort also avoids intermediate lists: it fills the array by
+traversing the map directly, and emits its readout with 'build', so the foldr
+in fold_udfm fuses with it. This contributes significantly to the allocation
+reductions in InstanceMatching1 in !16292.
+-}
+
+-- | @ub <= 4 * size m@, computed without a full 'M.size' traversal.
+usePigeonholeSort :: M.Word64Map a -> Int -> Bool
+usePigeonholeSort m ub = M.compareSize m ceil_ub_div_4 /= LT
+ where
+ ceil_ub_div_4 = (ub + 3) `div` 4 -- ceil(ub/4): ub <= 4*n iff n >= ceil(ub/4)
+
+-- | Order the map's elements by tag. The tags must be distinct and in
+-- @[0, ub)@, and @mk@ must preserve them. See Note [Sorting a UDFM].
+pigeonholeSort :: forall e r. Int
+ -> (M.Key -> TaggedVal e -> TaggedVal r)
+ -> M.Word64Map (TaggedVal e)
+ -> [r]
+{-# INLINE pigeonholeSort #-} -- Specialise mk and enable foldr/build fusion.
+pigeonholeSort ub mk m = build gen
+ where
+ -- The tag -1 marks unfilled slots; the value field is never read, but it
+ -- is strict, so it needs a WHNF value of type r. See Note [Sorting a UDFM].
+ hole :: TaggedVal r
+ hole = TaggedVal (unsafeCoerce ()) (-1)
+
+ fill :: SmallMutableArray s (TaggedVal r) -> State# s -> (# State# s, () #)
+ fill marr s = case M.traverseWithKey_ write m of ST st -> st s
+ where
+ write k tv = ST (\s' ->
+ (# writeSmallArray marr (taggedSnd tv) (mk k tv) s', () #))
+
+ gen :: forall b. (r -> b -> b) -> b -> b
+ gen cons nil = runST (ST (\s0 ->
+ case newSmallArray ub hole s0 of
+ (# s1, marr #) -> case fill marr s1 of
+ (# s2, () #) -> case unsafeFreezeSmallArray marr s2 of
+ (# s3, arr #) -> (# s3, readout arr 0 #)))
+ where
+ readout :: SmallArray (TaggedVal r) -> Int -> b
+ readout arr j
+ | j >= ub = nil
+ | t < 0 = readout arr (j + 1)
+ | otherwise = cons v (readout arr (j + 1))
+ where TaggedVal v t = indexSmallArray arr j
+
filterUDFM :: (elt -> Bool) -> UniqDFM key elt -> UniqDFM key elt
filterUDFM p (UDFM m i) = UDFM (M.filter (\(TaggedVal v _) -> p v) m) i
@@ -371,11 +502,22 @@ udfmRestrictKeysSet (UDFM val_set i) set =
in UDFM (M.restrictKeys val_set key_set) i
-- | Converts `UniqDFM` to a list, with elements in deterministic order.
--- It's O(n log n) while the corresponding function on `UniqFM` is O(n).
+--
+-- O(n) in the common case, with an O(n log n) fallback.
+--
+-- See Note [Cost of deterministic iteration].
udfmToList :: UniqDFM key elt -> [(Unique, elt)]
-udfmToList (UDFM m _i) =
- [ (mkUniqueGrimily k, taggedFst v)
- | (k, v) <- sortBy (compare `on` (taggedSnd . snd)) $ M.toList m ]
+-- NB: no INLINE, unlike eltsUDFM. udfmToList's one hot consumer is
+-- traverseUSDFM in the pattern-match checker, which doesn't fuse. Inlining
+-- the size dispatch into it regresses T17836.
+udfmToList (UDFM m ub)
+ | M.compareSize m 1 /= GT =
+ M.foldrWithKey (\k tv r -> (mkUniqueGrimily k, taggedFst tv) : r) [] m
+ | usePigeonholeSort m ub = pigeonholeSort ub
+ (\k tv -> TaggedVal (mkUniqueGrimily k, taggedFst tv) (taggedSnd tv)) m
+ | otherwise =
+ [ (mkUniqueGrimily k, taggedFst v)
+ | (k, v) <- sortBy (compare `on` (taggedSnd . snd)) $ M.toList m ]
-- Determines whether two 'UniqDFM's contain the same keys.
equalKeysUDFM :: UniqDFM key a -> UniqDFM key b -> Bool
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/17ce29220fc43ab8856dfb402cca3f9…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/17ce29220fc43ab8856dfb402cca3f9…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27653] 2 commits: testsuite: Migrate perf tests off collect_compiler_stats('all')
by Simon Jakobi (@sjakobi) 08 Aug '26
by Simon Jakobi (@sjakobi) 08 Aug '26
08 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27653 at Glasgow Haskell Compiler / GHC
Commits:
1324f1f0 by Simon Jakobi at 2026-08-08T15:39:20+02:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, whose noise
profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates all users of 'all' (and of the 'all' default)
to explicit per-metric collection, ahead of removing 'all' from the
driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing -- in #27489 it drifted by -5.3% while max_bytes_used
moved by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425, LinkableUsage01/02), residency remains
gated via max_bytes_used, now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all -- one is a simplifier-ticks blowup,
the other is stated entirely in allocation numbers -- and the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric; they are already gated at 1% via
collect_compiler_runtime. The ghc/max gate has previously broken CI
spuriously (widened 1% -> 10% in 9fd11585eb for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
b28f7e48 by Simon Jakobi at 2026-08-08T15:39:20+02:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' (also the default) gated bytes allocated, max_bytes_used and
peak_megabytes_allocated at a single tolerance, although their noise
profiles are incompatible, making such tests either flaky or toothless
(#27653). Metric collection composes -- one collect_stats call per
metric -- so 'all' had no use beyond the footgun.
All users were migrated in the previous commit; this removes the
argument and the default from the driver and updates the documentation
accordingly.
Assisted-by: Claude Fable 5
- - - - -
5 changed files:
- testsuite/driver/README.md
- testsuite/driver/testlib.py
- testsuite/tests/bytecode/TLinkable/all.T
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/space_leaks/all.T
Changes:
=====================================
testsuite/driver/README.md
=====================================
@@ -35,11 +35,13 @@ differences, and things such as that are not necessary to be considered by the
test writer anymore. This is due to the fact that the test comparison relies
entirely on locally collected metrics on the testing machine.
-As such, it is perfectly sufficient to write `collect_stats('all',20)` in the
-".T" files to measure the 3 potential stats that can be collected for that test
-and automatically test them for regressions, failing if there is more than a 20%
-change in any direction. In fact, even that is not necessary as
-`collect_stats()` defaults to 'all', and 20% deviation allowed.
+A test states which metric to measure and how much deviation to allow, e.g.
+`collect_stats('bytes allocated', 2)` in the ".T" files; the test fails if the
+metric changes by more than 2% in either direction. To gate several metrics,
+use one `collect_stats` call per metric, so that each gets a tolerance matched
+to its noise profile: allocations are nearly deterministic and support tight
+windows, while the residency metrics need considerably slacker ones (see
+Note [Measuring residency] in testlib.py).
The function `collect_compiler_stats()` is completely equivalent in every way to
`collect_stats` except that it measures the performance of the compiler itself
=====================================
testsuite/driver/testlib.py
=====================================
@@ -24,7 +24,7 @@ import subprocess
from testglobals import config, ghc_env, default_testopts, brokens, t, \
TestRun, TestResult, TestOptions, PerfMetric
from testutil import strip_quotes, lndir, link_or_copy_file, passed, \
- failBecause, testing_metrics, residency_testing_metrics, \
+ failBecause, residency_testing_metrics, \
stable_perf_counters, \
PassFail, badResult, str_warn, str_removeprefix
from term_color import Color, colored
@@ -816,28 +816,20 @@ def _collect_generic_stat(name : TestName, opts, metric_infos):
# -----
-# Defaults to "test everything, and only break on extreme cases"
-#
-# The inputs to this function are slightly interesting:
-# metric can be either:
-# - 'all', in which case all 3 possible metrics are collected and compared.
-# - The specific metric one wants to use in the test.
-# - A set of the metrics one wants to use in the test.
-#
-# Deviation defaults to 20% because the goal is correctness over performance.
-# The testsuite should avoid breaking when there is not an actual error.
-# Instead, the testsuite should notify of regressions in a non-breaking manner.
+# metric is either a single metric name or a set of metric names. Use one
+# call per metric so each gets a tolerance matched to its noise profile;
+# see Note [Measuring residency] for the residency metrics.
#
# collect_compiler_stats is used when the metrics collected are about the compiler.
# collect_stats is used in the majority case when the metrics to be collected
# are about the performance of the runtime code generated by the compiler.
-def collect_compiler_stats(metric='all',deviation=20):
+def collect_compiler_stats(metric, deviation=20):
def f(name, opts, m=metric, d=deviation):
no_lint(name, opts)
return _collect_stats(name, opts, m, d, None, True)
return f
-def collect_stats(metric='all', deviation=20, static_stats_file=None):
+def collect_stats(metric, deviation=20, static_stats_file=None):
return lambda name, opts, m=metric, d=deviation, s=static_stats_file: _collect_stats(name, opts, m, d, s)
def statsFile(comp_test: bool, name: str) -> str:
@@ -864,12 +856,9 @@ def _collect_stats(name: TestName, opts, metrics, deviation: Optional[int],
# This is a bit weird, though.
return
- # Normalize metrics to a list of strings.
+ # Normalize metrics to a set of strings.
if isinstance(metrics, str):
- if metrics == 'all':
- metrics = testing_metrics()
- else:
- metrics = { metrics }
+ metrics = { metrics }
opts.is_stats_test = True
if is_compiler_stats_test:
=====================================
testsuite/tests/bytecode/TLinkable/all.T
=====================================
@@ -3,7 +3,8 @@
# after they have been loaded into the `LoaderState`.
# However, this property is currently not validated automatically.
test('LinkableUsage01'
- , [ collect_compiler_stats('all', 2)
+ , [ collect_compiler_stats('bytes allocated', 2)
+ , collect_compiler_stats('max_bytes_used', 5)
, extra_files(['genLinkables.sh', 'BCOTemplate.hs'])
, pre_cmd('$MAKE -s --no-print-directory LinkableUsage01_Prep')
, req_bco
@@ -18,7 +19,8 @@ test('LinkableUsage01'
# Performance test for bytecode `Linkable`s.
test('LinkableUsage02'
- , [ collect_compiler_stats('all', 2)
+ , [ collect_compiler_stats('bytes allocated', 2)
+ , collect_compiler_stats('max_bytes_used', 5)
, extra_files(['genLinkables.sh', 'BCOTemplate.hs'])
, pre_cmd('$MAKE -s --no-print-directory LinkableUsage02_Prep')
, req_bco
=====================================
testsuite/tests/perf/compiler/all.T
=====================================
@@ -640,13 +640,16 @@ test ('T15164',
],
compile,
['-v0 -O'])
+# T15630/T15630a guard against exponential simplifier blowup when inlining
+# join points (#15630). T15630a is a monomorphic variant that has blown up
+# even when T15630 was fine; see the comment in T15630.hs.
test('T15630',
- [collect_compiler_stats()
+ [collect_compiler_stats('bytes allocated', 2)
],
compile,
['-O2'])
test('T15630a',
- [collect_compiler_stats()
+ [collect_compiler_stats('bytes allocated', 2)
],
compile,
['-O2'])
@@ -770,12 +773,19 @@ test ('T9198',
compile,
[''])
+# Guards against quadratic demand-analysis cost on wide recursive data
+# types, which manifested as a compile-time memory blowup (#11545).
test('T11545',
- [ collect_compiler_stats('all', 15) ],
+ [ collect_compiler_stats('bytes allocated', 2)
+ , collect_compiler_residency(15) ],
compile, ['-O'])
+# Guards against the compile-time memory blowup of #15304, caused by
+# over-keen inlining and demand-analysis memory usage on a module with
+# many wide strict constructors.
test('T15304',
- [ collect_compiler_stats('all', 10) ],
+ [ collect_compiler_stats('bytes allocated', 2)
+ , collect_compiler_residency(10) ],
compile, ['-O'])
test ('T20049',
[ collect_compiler_stats('bytes allocated',2) ],
@@ -797,8 +807,10 @@ test('T16875', # Testing one hole-fit with a lot in scope for #16875
collect_compiler_runtime(4),
compile, ['-fdefer-type-errors -fno-max-valid-hole-fits -package ghc'])
+# Guards against renamer/typechecker allocation regressions on very large
+# generated modules (#20261, a Happy-generated parser).
test ('T20261',
- [collect_compiler_stats('all')],
+ [collect_compiler_stats('bytes allocated', 2)],
compile,
[''])
@@ -807,8 +819,7 @@ test ('T20261',
# does not sensibly handle one test acting as both
# a compile-time and a run-time performance test
test('T21839c',
- [ collect_compiler_stats('all', 10),
- collect_compiler_runtime(1),
+ [ collect_compiler_runtime(1),
only_ways(['normal'])],
compile,
['-O'])
@@ -874,8 +885,12 @@ test('interpreter_steplocal',
ghci_script,
['interpreter_steplocal.script'])
+# Guards against the compile-time memory blowup of #26425; primarily
+# stresses OccAnal and unfolding performance on a long chain of nested
+# join points and cases.
test ('T26425',
- [ collect_compiler_stats('all',20) ],
+ [ collect_compiler_stats('bytes allocated', 2)
+ , collect_compiler_residency(20) ],
compile,
['-O'])
=====================================
testsuite/tests/perf/space_leaks/all.T
=====================================
@@ -1,9 +1,6 @@
setTestOpts(js_skip)
test('space_leak_001',
- # This could potentially be replaced with
- # collect_stats('all',5) to test all 3 with
- # 5% possible deviation.
[ collect_stats('bytes allocated',5),
collect_runtime_residency(15),
omit_ways(['profasm','profthreaded','threaded1','threaded2',
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b747e20aeee4f4fc7d08bc25b4d868…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b747e20aeee4f4fc7d08bc25b4d868…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
08 Aug '26
Simon Jakobi pushed new branch wip/sjakobi/T27653 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sjakobi/T27653
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
08 Aug '26
Simon Jakobi pushed new branch wip/sjakobi/T27613 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sjakobi/T27613
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/az/exactprint-annotation-rationalisation] 30 commits: hie files: Dump the type table when dumping with -ddump-hie
by Alan Zimmerman (@alanz) 08 Aug '26
by Alan Zimmerman (@alanz) 08 Aug '26
08 Aug '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
b18c8dd5 by Zubin Duggal at 2026-08-06T15:51:01-04:00
hie files: Dump the type table when dumping with -ddump-hie
- - - - -
f839d0fb by Zubin Duggal at 2026-08-06T15:51:01-04:00
hie files: Take evidence for quantified constraints into account when saving evidence terms to the hie ast
Fixes #25709
- - - - -
5753ebaa by Simon Jakobi at 2026-08-06T15:51:43-04:00
testsuite: fix stale paths for the ghc-config build artifacts
ghc-config.hs moved from testsuite/mk/ to testsuite/ghc-config/ in
6c7a49139c, but the .gitignore entry and the clean rule still referred to
the old location. As a result the compiled ghc-config binary, which
boilerplate.mk rebuilds on every make-driven test run, showed up as an
untracked file and was never cleaned.
Assisted-by: Claude Opus 5
- - - - -
246d4d72 by Simon Peyton Jones at 2026-08-06T15:52:25-04:00
Documentation only
...driven by my investigation of #27591
- - - - -
be69e9df by Alan Zimmerman at 2026-08-06T15:53:05-04:00
EPA: Replace AnnPragma with individual types
We introduced AnnPragma as a common type for all pragma usages wrapped
in LocatedP / SrcSpanAnnP. Now that those are gone, and the AnnPragma
moved into the TTG points for the given items, we can ensure that each
carries only the annotations it needs.
So we remove AnnPragma, and in its place bring in
AnnCType
AnnWarningTxt
AnnOverlap
AnnAnnDecl
AnnPragSCC
- - - - -
0779e12c by Simon Jakobi at 2026-08-07T12:36:11-04:00
Cmm: print unreachable blocks under -dppr-debug (#27368)
Unreachable blocks linger in a CmmGraph's block map for most of the Cmm
pipeline, but pprCmmGraph only ever printed the blocks reachable from the
entry, so dumps looked consistent while the graph was not. Issues like
#27368 were hard to debug due to this.
pprCmmGraph now appends the stored-but-unreachable blocks under a
"// unreachable blocks:" heading when -dppr-debug is on.
See Note [unreachable blocks] in GHC.Cmm.Pipeline.
Assisted-by: Claude Opus 5
- - - - -
3a0f9a51 by Simon Peyton Jones at 2026-08-07T12:36:54-04:00
Fix three bugs related to required type args and INLINE pragmas
* `GHC.Core.Opt.Arity.mkEtaForAllMCo` got the visibility flags back to front,
leading to a Lint error (#27557)
* The arity in an InlineSaturation is the VisArity not the Arity; the
two can differ when we have "required" type arguments. This made the
INLINE pragma argument counting go wrong in `makeCorePair` (#27590).
* When a simple binding has a type signature, we take special path in `tcPolyCheck`,
leading to an outer `AbsBinds` that has no dictionaries, even when the binding
is in fact overloaded. That confused the inline-arity computation in
`makeCorePair` (#27589).
The latter two are fixed using the new function `GHC.HsToCore.Binds.findSatArity`.
That actually simplifies the API of `makeCorePair`, which is nice.
The first bug is fixed by swapping the visiblity flags in
`GHC.Core.Opt.Arity.mkEtaForAllMCo`
Getting the INLINE behaviour right led to some perf changes:
* Runtime /halved/ on T7954 due to better specialisation
* Compile time increased by 6% in T21839c because a bit more inlining
happened, as it always should have done.
* For some reason compile-time max-bytes-used dropped by 30% on
T27336, but only on one build configuration; and it increased
on LinkableUsage02 by 6% on another configuration
Geometric mean effect on our compile time benchmarks is +0.1%.
Metric Decrease:
T27336
T7954
Metric Increase:
LinkableUsage02
T21839c
- - - - -
4f985108 by Vladislav Zavialov at 2026-08-07T17:49:50-04:00
Discard type arguments in tcPatToExpr (#27440, #27583)
The builder expression of an implicitly bidirectional pattern synonym must not
mention types written in the RHS:
* Invisible type arguments led to a panic (#27440)
* Required type arguments failed with out-of-scope variables (#27583)
Both are now discarded, following the precedent established by pattern
signatures (#9867).
Discarding type arguments takes some care: a type pattern cannot be told from a
value pattern by syntax alone, as the `type` keyword may be omitted. Consider:
data T a b c where
MkT :: forall a. forall b c -> a -> T a b c
pattern P :: x -> T x y z
pattern P x = MkT @a (type b) c x
In P's right-hand side, `@a` and `type b` are clearly type arguments, but what
about `c` and `x`? We can only tell by matching the patterns against MkT's
type. So tcPatToExpr now runs in TcM and matches the arguments against the
constructor's TyVarBinders using zipPatsBndrs, which is made public for this
purpose. The resulting builder is $bP x = MkT _ _ x.
See Note [Discarding types in the builder expression].
Test cases: T27440a T27440b T27440c T27440d T27440e
T27583a T27583b T27583c T27583d T27583e T27583f T27583g
Metric Increase: LinkableUsage02
Metric Decrease: T27336
Assisted-by: Claude Opus 5
- - - - -
eb1dcd4d by sheaf at 2026-08-07T17:50:40-04:00
mkWpFun_FRR: fix ordering of coercion composition
When the subsumption machinery generates an eta-expansion, we must
perform a representation polymorphism check to ensure the lambda binder
it introduces has a fixed runtime representation.
This is done in GHC.Tc.Utils.mkWpFun_FRR.
This check involves composing quite a few coercions, arising from
representation-polymorphism checks on both the actual and expected
argument types. These coercions are then chained using HsWrapper
composition, <.>. The ordering of composition was incorrect, leading to
the Core Lint failure reported in #27639. This commit fixes that.
Fixes #27639
- - - - -
4bbe0b57 by Alan Zimmerman at 2026-08-08T12:16:42+01:00
EPA: Remove LocatedE, replace with LocatedA
This gets rid of one more LocatedXXX occurrence
- - - - -
95b7f1fe by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Remove AnnList (EpToken "where") usages
This is moving toward removing the parameter from AnnList completely
- - - - -
bd656553 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA remove AnnList (EpToken "rec") usages
- - - - -
bf897cb1 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Remove last parameterised AnnList usage (EpaLocation)
Also remove the parameter
- - - - -
c4b87716 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print annotations next
- - - - -
bc3a508f by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Remove LocatedBC / SrcSpanBF
- - - - -
c6e5509a by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: remove unused addTrailingAnnToL. Squash appropriately
- - - - -
bf2ded42 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPS: Remove NoEpTok/NoEpUniTok, using an unhelpful SrcSpan instead
Also introduce helper functions noEpTok and noEpUniTok to serve
as simple replacements in code inserting an token annotation without
location information.
- - - - -
72e61d12 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Some haddock processing tweaks
- - - - -
3cd8a679 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
Some haddock exactprint tests
- - - - -
b6b5e93a by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: When adding comments honour trailing anns
- - - - -
af109cd3 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Uses Parsers.parseModule for exactprint tests
This is the advertised way to parse for use for exact printing in the
ghc-exactprint library, make sure we test using it.
- - - - -
6212bd1f by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA Fix HsCmdDo exact print with comments
TODO: add test based on proc-do-complex-four-out.hs
- - - - -
60190a10 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Add comments about remaining Anno SrcSpan instances
- - - - -
cb285894 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Plan for Fixing AnnList Layout Properly
- - - - -
83eefeba by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: First pass implementation of HsList, for ClassDecls
Just as a straight list replacement to start with, no payload.
This shows the scope and invasiveness of the initial change
- - - - -
71b09537 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: HsList attempt WIP
- - - - -
21958733 by Alan Zimmerman at 2026-08-08T12:17:29+01:00
Enable ppr test for Haddock1. It currently fails
- - - - -
2ad6d1df by Alan Zimmerman at 2026-08-08T12:17:29+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
f1e9072c by Alan Zimmerman at 2026-08-08T12:17:29+01:00
EPA: Add an overview doc for exact printing
- - - - -
a9d725e4 by Simon Peyton Jones at 2026-08-08T12:17:29+01:00
Added an intro section
- - - - -
148 changed files:
- + ANNLIST-LAYOUT-PLAN.md
- + ExactPrint.md
- + changelog.d/T27368-ppr-unreachable-cmm-blocks.md
- + changelog.d/T27440
- + changelog.d/T27557
- + changelog.d/T27583
- + changelog.d/T27589
- + changelog.d/T27639
- compiler/GHC/Cmm.hs
- compiler/GHC/Cmm/Pipeline.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Decls/Overlap.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/DocString.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Binds.hs
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Tc/Gen/Bind.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Sig.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Arity.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/InlinePragma.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Unit/Module/Warnings.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- docs/users_guide/debugging.rst
- ghc/GHCi/UI.hs
- libraries/base/tests/perf/ElemFusionUnknownList_O1.stderr
- libraries/base/tests/perf/ElemFusionUnknownList_O2.stderr
- testsuite/.gitignore
- testsuite/Makefile
- testsuite/tests/cmm/should_compile/Makefile
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.cmm
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.stdout
- testsuite/tests/cmm/should_compile/all.T
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/hiefile/should_compile/T24493.stderr
- + testsuite/tests/hiefile/should_run/T25709.hs
- + testsuite/tests/hiefile/should_run/T25709.stdout
- testsuite/tests/hiefile/should_run/all.T
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- + testsuite/tests/patsyn/should_compile/T27440a.hs
- + testsuite/tests/patsyn/should_compile/T27440b.hs
- + testsuite/tests/patsyn/should_compile/T27440c.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/patsyn/should_fail/T27440d.hs
- + testsuite/tests/patsyn/should_fail/T27440d.stderr
- testsuite/tests/patsyn/should_fail/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- + testsuite/tests/printer/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- + testsuite/tests/rep-poly/T27639.hs
- testsuite/tests/rep-poly/all.T
- + testsuite/tests/simplCore/should_compile/T27589.hs
- + testsuite/tests/simplCore/should_compile/T27589.stderr
- + testsuite/tests/simplCore/should_compile/T27590.hs
- + testsuite/tests/simplCore/should_compile/T27590.stderr
- testsuite/tests/simplCore/should_compile/all.T
- + testsuite/tests/typecheck/should_compile/T27557.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/vdq-rta/should_compile/T27583a.hs
- + testsuite/tests/vdq-rta/should_compile/T27583b.hs
- + testsuite/tests/vdq-rta/should_compile/T27583c.hs
- + testsuite/tests/vdq-rta/should_compile/T27583d.hs
- + testsuite/tests/vdq-rta/should_compile/T27583e.hs
- + testsuite/tests/vdq-rta/should_compile/T27583g.hs
- testsuite/tests/vdq-rta/should_compile/all.T
- + testsuite/tests/vdq-rta/should_fail/T27440e.hs
- + testsuite/tests/vdq-rta/should_fail/T27440e.stderr
- + testsuite/tests/vdq-rta/should_fail/T27583f.hs
- + testsuite/tests/vdq-rta/should_fail/T27583f.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Parsers.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Xhtml/Decl.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/6f49d29fb54a8a94deb13af361eb5f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/6f49d29fb54a8a94deb13af361eb5f…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc] Pushed new branch wip/az/epa-tidy-locatedxxx-14
by Alan Zimmerman (@alanz) 08 Aug '26
by Alan Zimmerman (@alanz) 08 Aug '26
08 Aug '26
Alan Zimmerman pushed new branch wip/az/epa-tidy-locatedxxx-14 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/az/epa-tidy-locatedxxx-14
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/supersven/hadrian-cross-stage3] WIP: Self-reviewed
by Sven Tennie (@supersven) 08 Aug '26
by Sven Tennie (@supersven) 08 Aug '26
08 Aug '26
Sven Tennie pushed to branch wip/supersven/hadrian-cross-stage3 at Glasgow Haskell Compiler / GHC
Commits:
ceb01b44 by Sven Tennie at 2026-08-08T10:32:02+02:00
WIP: Self-reviewed
- - - - -
2 changed files:
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Generate.hs
Changes:
=====================================
hadrian/src/Rules/BinaryDist.hs
=====================================
@@ -267,12 +267,8 @@ buildBinDistDir root conf@BindistConfig{..} = do
--
-- N.B. the ghc-pkg executable may be prefixed with a target triple
-- (c.f. #20267).
- -- Recache using the stage1 ghc-pkg executable. This is the unprefixed
- -- host ghc-pkg for native bindists and the target-triple-prefixed cross
- -- ghc-pkg for cross bindists; both run on the build host and can handle
- -- the target package database. The stage3 bindist's package DB is also
- -- built by the stage1 cross compiler, so stage1 ghc-pkg is correct there
- -- too.
+ -- Stage1 is also correct for cross-compiler scenarios, because it runs on
+ -- the build host.
ghcPkgPath <- programPath =<< programContext Stage1 ghcPkg
cmd_ ghcPkgPath ["recache", "--package-db", bindistFilesDir -/- "lib" -/- "package.conf.d" ]
@@ -370,6 +366,7 @@ bindistRules = do
buildBinDistDir root cfg
phony "binary-dist-dir-cross" $ buildBinDistDir root crossBindist
+
phony "binary-dist-dir-stage3" $ buildBinDistDir root targetBindist
let buildBinDist compressor = do
@@ -411,7 +408,7 @@ bindistRules = do
need [distribConfigure]
copyFile distribConfigure configurePath
- -- Generate the Makefile that enables the "make install" part
+ -- Copy the Makefile that enables the "make install" part
root -/- bindistFolderName -/- "ghc-*" -/- "Makefile" %> \makefilePath -> do
top <- topDirectory
copyFile (top -/- "hadrian" -/- "bindist" -/- "Makefile") makefilePath
=====================================
hadrian/src/Rules/Generate.hs
=====================================
@@ -25,7 +25,6 @@ import Utilities
import GHC.Toolchain as Toolchain hiding (HsCpp(HsCpp))
import GHC.Platform.ArchOS
import Settings.Program (ghcWithInterpreter)
-import UserSettings (finalStage)
-- | Track this file to rebuild generated files whenever it changes.
trackGenerateHs :: Expr ()
@@ -252,51 +251,30 @@ generateRules = do
(root -/- "ghc-stage2") <~+ ghcWrapper Stage2
(root -/- "ghc-stage3") <~+ ghcWrapper Stage3
- forM_ allStages $ \buildStage -> do
- let -- Two stages are in play per rule iteration:
- --
- -- * @buildStage@ — loop variable; the settings file is written
- -- into @_build/<buildStage>/lib/settings@ and
- -- describes the compiler at @compilerStage@.
- -- * @compilerStage@ — the stage whose @bin/@ holds the compiler
- -- the settings file describes; also the
- -- ambient 'Expr' stage passed to
- -- 'generateSettings' (via 'semiEmptyTarget'),
- -- so it is the value of @executableStage@
- -- inside that function.
- --
- -- For a cross-compiler the libs it links against live in the
- -- /successor/ stage's lib dir; @libraryStage@ (computed in the
- -- rule body below) is that successor. @compilerStage@ normally
- -- equals @buildStage@, but at @finalStage@ there is no successor
- -- to hold its libs, so @compilerStage@ drops to the predecessor
- -- (the final stage's lib dir merely hosts the predecessor
- -- cross-compiler's target-arch libs).
- compilerStage = if buildStage == finalStage
- then predStage buildStage
- else buildStage
- prefix = root -/- stageString buildStage -/- "lib"
+ forM_ allStages $ \compilerStage -> do
+ let
+ prefix = root -/- stageString compilerStage -/- "lib"
go gen file = generate file (semiEmptyTarget compilerStage) gen
(prefix -/- "settings") %> \out -> do
-- Stage0 has no library or package DB of its own (the
-- bootstrapping compiler uses Stage1's); for any other stage the
-- package DB lives where the LibDir redirect points (this stage's
- -- own lib dir, or the successor's when @buildStage@ is a cross
+ -- own lib dir, or the successor's when @compilerStage@ is a cross
-- stage).
- isCross <- crossStage buildStage
- let libraryStage = case buildStage of
+ isCross <- crossStage compilerStage
+ let libraryStage = case compilerStage of
Stage0 {} -> Stage1
- _ -> if isCross then succStage buildStage else buildStage
+ _ -> if isCross then succStage compilerStage else compilerStage
pkgDb <- packageDbPath (PackageDbLoc libraryStage Final)
-- addTrailingPathSeparator needed: makeRelativeNoSysLink uses
-- splitPath where "lib" and "lib/" are distinct components.
let libTopDir = addTrailingPathSeparator $
- if isStage0 buildStage
+ if isStage0 compilerStage
then prefix
else root -/- stageString libraryStage -/- "lib"
relPkgDb = makeRelativeNoSysLink libTopDir pkgDb
go (generateSettings out True relPkgDb libraryStage) out
- (prefix -/- "targets" -/- "default.target") %> \out -> go (show <$> expr (targetStage (succStage buildStage))) out
+ (prefix -/- "targets" -/- "default.target") %> \out -> go (show <$> expr (targetStage (succStage compilerStage))) out
where
file <~+ gen = file %> \out -> generate out emptyTarget gen >> makeExecutable out
@@ -610,13 +588,10 @@ generateSettings settingsFile includeLibDir rel_pkg_db libraryStage = do
, ("Use interpreter", expr $ yesNo <$> ghcWithInterpreter executableStage)
-- Advertise the RTS ways that will actually ship with the compiler
-- described by this settings file, i.e. the ways the @libraryStage@
- -- RTS is built with. Cabal queries this to decide which library ways
- -- the compiler supports (see
- -- 'Distribution.Simple.Compiler.waySupported'); under-advertising
- -- causes Cabal to silently drop flags like
- -- @--enable-profiling-shared@.
+ -- RTS is built with.
-- The settings file is regenerated at install time when installing a bindist.
- , ("RTS ways", unwords . map show . Set.toList <$> expr (interpretInContext (vanillaContext libraryStage rts) getRtsWays))
+ , ("RTS ways", unwords . map show . Set.toList <$>
+ expr (interpretInContext (vanillaContext libraryStage rts) getRtsWays))
, ("Relative Global Package DB", pure rel_pkg_db)
, ("base unit-id", pure base_unit_id)
]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ceb01b44aac4baaf8751cb2bde9ab17…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ceb01b44aac4baaf8751cb2bde9ab17…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/supersven/hadrian-cross-stage3] 19 commits: testsuite: Don't crash on non-UTF-8 test output
by Sven Tennie (@supersven) 08 Aug '26
by Sven Tennie (@supersven) 08 Aug '26
08 Aug '26
Sven Tennie pushed to branch wip/supersven/hadrian-cross-stage3 at Glasgow Haskell Compiler / GHC
Commits:
8fc6f882 by Simon Jakobi at 2026-08-05T14:53:41-04:00
testsuite: Don't crash on non-UTF-8 test output
read_stdout, read_stderr_for, read_comp_stderr and read_diff decoded
strictly (the first three with UTF-8, read_diff with the locale
encoding), so a test emitting invalid UTF-8 (binary output, or a crash
truncating a multi-byte character) raised UnicodeDecodeError and was
reported as a framework failure instead of its actual result.
Decode with errors='replace', like read_no_crs and safe_print.
Assisted-by: Claude Fable 5
- - - - -
56534866 by Simon Jakobi at 2026-08-05T14:53:41-04:00
testsuite: Colorize the test summary, also in CI
The summary headings were plain, and SUMMARY was colored unconditionally,
so the escapes also ended up in the file written by --summary-file.
Color is now decided per output sink via term_color.colored_if; see the
comments in term_color.
CI logs are not a tty, but GitLab's log viewer renders ANSI colors, so
add --force-colors and pass it in .gitlab/ci.sh.
Assisted-by: Claude Opus 5
- - - - -
bceb541a by Simon Jakobi at 2026-08-05T14:53:42-04:00
testsuite: Repeat unexpected failure output in the summary
Finding out why a test failed meant scrolling back through a possibly
very long log to the point where the test ran. The summary now repeats
the captured output of unexpected failures, before the statistics, so
the most interesting part is at the end of the log (#16720).
Output mismatches report their diff instead of the mismatching stream
(see Note [Redundant output in test results]). The repeated output is
bounded per stream, and skipped altogether beyond
MAX_SUMMARY_OUTPUT_TESTS failure blocks. Tests failing identically in
several ways share one block.
Test results now report a source-relative directory, stable regardless
of where the run was started from.
Assisted-by: Claude Fable 5
- - - - -
2ab02c57 by Ben Gamari at 2026-08-05T14:54:24-04:00
base: Don't drop exception context in SomeException(toException)
For reasons that are lost to time, the implementation of [CLC #200]
that was merged inappropriately dropped `ExceptionContext` in the
`toException` implementation given to `SomeException`.
Fix this infelicity.
[CLC #200]: https://github.com/haskell/core-libraries-committee/issues/200
- - - - -
126ce574 by Vladislav Zavialov at 2026-08-05T14:55:05-04:00
Test case for #20902
Starting with GHC 9.14.1 (the first major release to include 51e3ec83),
and from point releases GHC 9.10.2 and GHC 9.12.3 (backports cc4470be68
and b30f25591e), all examples in this ticket are handled as expected.
- - - - -
b14d8d59 by Alan Zimmerman at 2026-08-05T14:55:46-04:00
EPA: Remove LocatedP, last use in WarningTxt
The last step of removing LocatedP, by moving the AnnPragma for
WarningTxt into its TTG extension point instead.
This also allows us to remove LocatedP and SrcSpanAnnP
- - - - -
70b58c8f by Vladislav Zavialov at 2026-08-05T14:56:27-04:00
Test cases for #18725
Starting with GHC 9.4 (the first release to include 268efcc9a4), the program in
this ticket no longer panics. A standalone kind signature breaks the recursive
loop, so the type constructor can be used in a kind within its own group.
T18725a checks that this is accepted with the signature present, while T18725b
confirms it is still rejected without it.
- - - - -
b18c8dd5 by Zubin Duggal at 2026-08-06T15:51:01-04:00
hie files: Dump the type table when dumping with -ddump-hie
- - - - -
f839d0fb by Zubin Duggal at 2026-08-06T15:51:01-04:00
hie files: Take evidence for quantified constraints into account when saving evidence terms to the hie ast
Fixes #25709
- - - - -
5753ebaa by Simon Jakobi at 2026-08-06T15:51:43-04:00
testsuite: fix stale paths for the ghc-config build artifacts
ghc-config.hs moved from testsuite/mk/ to testsuite/ghc-config/ in
6c7a49139c, but the .gitignore entry and the clean rule still referred to
the old location. As a result the compiled ghc-config binary, which
boilerplate.mk rebuilds on every make-driven test run, showed up as an
untracked file and was never cleaned.
Assisted-by: Claude Opus 5
- - - - -
246d4d72 by Simon Peyton Jones at 2026-08-06T15:52:25-04:00
Documentation only
...driven by my investigation of #27591
- - - - -
be69e9df by Alan Zimmerman at 2026-08-06T15:53:05-04:00
EPA: Replace AnnPragma with individual types
We introduced AnnPragma as a common type for all pragma usages wrapped
in LocatedP / SrcSpanAnnP. Now that those are gone, and the AnnPragma
moved into the TTG points for the given items, we can ensure that each
carries only the annotations it needs.
So we remove AnnPragma, and in its place bring in
AnnCType
AnnWarningTxt
AnnOverlap
AnnAnnDecl
AnnPragSCC
- - - - -
0779e12c by Simon Jakobi at 2026-08-07T12:36:11-04:00
Cmm: print unreachable blocks under -dppr-debug (#27368)
Unreachable blocks linger in a CmmGraph's block map for most of the Cmm
pipeline, but pprCmmGraph only ever printed the blocks reachable from the
entry, so dumps looked consistent while the graph was not. Issues like
#27368 were hard to debug due to this.
pprCmmGraph now appends the stored-but-unreachable blocks under a
"// unreachable blocks:" heading when -dppr-debug is on.
See Note [unreachable blocks] in GHC.Cmm.Pipeline.
Assisted-by: Claude Opus 5
- - - - -
3a0f9a51 by Simon Peyton Jones at 2026-08-07T12:36:54-04:00
Fix three bugs related to required type args and INLINE pragmas
* `GHC.Core.Opt.Arity.mkEtaForAllMCo` got the visibility flags back to front,
leading to a Lint error (#27557)
* The arity in an InlineSaturation is the VisArity not the Arity; the
two can differ when we have "required" type arguments. This made the
INLINE pragma argument counting go wrong in `makeCorePair` (#27590).
* When a simple binding has a type signature, we take special path in `tcPolyCheck`,
leading to an outer `AbsBinds` that has no dictionaries, even when the binding
is in fact overloaded. That confused the inline-arity computation in
`makeCorePair` (#27589).
The latter two are fixed using the new function `GHC.HsToCore.Binds.findSatArity`.
That actually simplifies the API of `makeCorePair`, which is nice.
The first bug is fixed by swapping the visiblity flags in
`GHC.Core.Opt.Arity.mkEtaForAllMCo`
Getting the INLINE behaviour right led to some perf changes:
* Runtime /halved/ on T7954 due to better specialisation
* Compile time increased by 6% in T21839c because a bit more inlining
happened, as it always should have done.
* For some reason compile-time max-bytes-used dropped by 30% on
T27336, but only on one build configuration; and it increased
on LinkableUsage02 by 6% on another configuration
Geometric mean effect on our compile time benchmarks is +0.1%.
Metric Decrease:
T27336
T7954
Metric Increase:
LinkableUsage02
T21839c
- - - - -
4f985108 by Vladislav Zavialov at 2026-08-07T17:49:50-04:00
Discard type arguments in tcPatToExpr (#27440, #27583)
The builder expression of an implicitly bidirectional pattern synonym must not
mention types written in the RHS:
* Invisible type arguments led to a panic (#27440)
* Required type arguments failed with out-of-scope variables (#27583)
Both are now discarded, following the precedent established by pattern
signatures (#9867).
Discarding type arguments takes some care: a type pattern cannot be told from a
value pattern by syntax alone, as the `type` keyword may be omitted. Consider:
data T a b c where
MkT :: forall a. forall b c -> a -> T a b c
pattern P :: x -> T x y z
pattern P x = MkT @a (type b) c x
In P's right-hand side, `@a` and `type b` are clearly type arguments, but what
about `c` and `x`? We can only tell by matching the patterns against MkT's
type. So tcPatToExpr now runs in TcM and matches the arguments against the
constructor's TyVarBinders using zipPatsBndrs, which is made public for this
purpose. The resulting builder is $bP x = MkT _ _ x.
See Note [Discarding types in the builder expression].
Test cases: T27440a T27440b T27440c T27440d T27440e
T27583a T27583b T27583c T27583d T27583e T27583f T27583g
Metric Increase: LinkableUsage02
Metric Decrease: T27336
Assisted-by: Claude Opus 5
- - - - -
eb1dcd4d by sheaf at 2026-08-07T17:50:40-04:00
mkWpFun_FRR: fix ordering of coercion composition
When the subsumption machinery generates an eta-expansion, we must
perform a representation polymorphism check to ensure the lambda binder
it introduces has a fixed runtime representation.
This is done in GHC.Tc.Utils.mkWpFun_FRR.
This check involves composing quite a few coercions, arising from
representation-polymorphism checks on both the actual and expected
argument types. These coercions are then chained using HsWrapper
composition, <.>. The ordering of composition was incorrect, leading to
the Core Lint failure reported in #27639. This commit fixes that.
Fixes #27639
- - - - -
1e4e9ba4 by Sven Tennie at 2026-08-08T09:51:07+02:00
hadrian: Add Stage3 cross-compiled target bindist (#26924)
Add a `binary-dist-stage3` Hadrian target that packages target
executables (produced by the stage2 compiler) into a separate
'_build/bindist-stage3/' folder, distinct from the stage2 regular or
cross-compiler bindist in '_build/bindist/'.
This allows a single CI pipeline job to produce both a cross-compiler
bindist (e.g. x86_64 -> RISC-V) and a target-architecture bindist (e.g.
RISC-V -> RISC-V) that can be installed and run natively on the target.
To avoid issues with stale files or race-conditions on them, generate
the `configure` script per stage in `_build/<stage>/distrib`
directories.
- - - - -
2fc3431b by Sven Tennie at 2026-08-08T09:52:01+02:00
ci: Build Stage3 and Stage2 bindists for cross targets in one job (#26924)
Building the stage3 target bindist already produces most of the stage2
cross-compiler bindist as a byproduct, so building them in separate jobs
duplicates the build efforts for no benefit. Instead of a separate
CROSS_STAGE=3 job, the stage3 job now also builds and publishes the
stage2 bindist.
The stage3 tarball is named and versioned as if it were built natively
on the target (target triple prefix, non-cross opsys), so its artifact
name stays what downstream consumers already expect and no changes are
needed on their side.
For now, this is only enabled for the RISC-V job. Others can easily
follow.
- - - - -
37cff7f6 by Sven Tennie at 2026-08-08T09:53:02+02:00
WIP: Self-reviewed
- - - - -
104 changed files:
- .gitignore
- .gitlab/ci.sh
- .gitlab/generate-ci/gen_ci.hs
- .gitlab/jobs.yaml
- + changelog.d/T27368-ppr-unreachable-cmm-blocks.md
- + changelog.d/T27440
- + changelog.d/T27455
- + changelog.d/T27557
- + changelog.d/T27583
- + changelog.d/T27589
- + changelog.d/T27639
- + changelog.d/stage3-cross-bindists
- compiler/GHC/Builtin/Utils.hs
- compiler/GHC/Cmm.hs
- compiler/GHC/Cmm/Pipeline.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Decls/Overlap.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/HsToCore/Binds.hs
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Warnings.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Tc/Gen/Bind.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Sig.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Types/Arity.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/InlinePragma.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Unit/Module/Warnings.hs
- distrib/configure.ac.in
- docs/users_guide/debugging.rst
- hadrian/src/BindistConfig.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/Generate.hs
- libraries/base/changelog.md
- libraries/base/tests/perf/ElemFusionUnknownList_O1.stderr
- libraries/base/tests/perf/ElemFusionUnknownList_O2.stderr
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
- testsuite/.gitignore
- testsuite/Makefile
- testsuite/driver/runtests.py
- testsuite/driver/term_color.py
- testsuite/driver/testlib.py
- testsuite/tests/cmm/should_compile/Makefile
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.cmm
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.stdout
- testsuite/tests/cmm/should_compile/all.T
- testsuite/tests/ghc-e/should_fail/T18441fail7.stderr
- testsuite/tests/ghc-e/should_run/ghc-e005.stderr
- testsuite/tests/hiefile/should_compile/T24493.stderr
- + testsuite/tests/hiefile/should_run/T25709.hs
- + testsuite/tests/hiefile/should_run/T25709.stdout
- testsuite/tests/hiefile/should_run/all.T
- + testsuite/tests/patsyn/should_compile/T27440a.hs
- + testsuite/tests/patsyn/should_compile/T27440b.hs
- + testsuite/tests/patsyn/should_compile/T27440c.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/patsyn/should_fail/T27440d.hs
- + testsuite/tests/patsyn/should_fail/T27440d.stderr
- testsuite/tests/patsyn/should_fail/all.T
- + testsuite/tests/rep-poly/T27639.hs
- testsuite/tests/rep-poly/all.T
- + testsuite/tests/saks/should_compile/T18725a.hs
- testsuite/tests/saks/should_compile/all.T
- + testsuite/tests/saks/should_fail/T18725b.hs
- + testsuite/tests/saks/should_fail/T18725b.stderr
- testsuite/tests/saks/should_fail/all.T
- + testsuite/tests/simplCore/should_compile/T27589.hs
- + testsuite/tests/simplCore/should_compile/T27589.stderr
- + testsuite/tests/simplCore/should_compile/T27590.hs
- + testsuite/tests/simplCore/should_compile/T27590.stderr
- testsuite/tests/simplCore/should_compile/all.T
- + testsuite/tests/th/T20902.hs
- testsuite/tests/th/all.T
- + testsuite/tests/typecheck/should_compile/T27557.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/vdq-rta/should_compile/T27583a.hs
- + testsuite/tests/vdq-rta/should_compile/T27583b.hs
- + testsuite/tests/vdq-rta/should_compile/T27583c.hs
- + testsuite/tests/vdq-rta/should_compile/T27583d.hs
- + testsuite/tests/vdq-rta/should_compile/T27583e.hs
- + testsuite/tests/vdq-rta/should_compile/T27583g.hs
- testsuite/tests/vdq-rta/should_compile/all.T
- + testsuite/tests/vdq-rta/should_fail/T27440e.hs
- + testsuite/tests/vdq-rta/should_fail/T27440e.stderr
- + testsuite/tests/vdq-rta/should_fail/T27583f.hs
- + testsuite/tests/vdq-rta/should_fail/T27583f.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/29ffc8368db986c3312cae93636f49…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/29ffc8368db986c3312cae93636f49…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
sheaf pushed new branch wip/tc-rewrite-spec-constr at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/tc-rewrite-spec-constr
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] Discard type arguments in tcPatToExpr (#27440, #27583)
by Marge Bot (@marge-bot) 07 Aug '26
by Marge Bot (@marge-bot) 07 Aug '26
07 Aug '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
4f985108 by Vladislav Zavialov at 2026-08-07T17:49:50-04:00
Discard type arguments in tcPatToExpr (#27440, #27583)
The builder expression of an implicitly bidirectional pattern synonym must not
mention types written in the RHS:
* Invisible type arguments led to a panic (#27440)
* Required type arguments failed with out-of-scope variables (#27583)
Both are now discarded, following the precedent established by pattern
signatures (#9867).
Discarding type arguments takes some care: a type pattern cannot be told from a
value pattern by syntax alone, as the `type` keyword may be omitted. Consider:
data T a b c where
MkT :: forall a. forall b c -> a -> T a b c
pattern P :: x -> T x y z
pattern P x = MkT @a (type b) c x
In P's right-hand side, `@a` and `type b` are clearly type arguments, but what
about `c` and `x`? We can only tell by matching the patterns against MkT's
type. So tcPatToExpr now runs in TcM and matches the arguments against the
constructor's TyVarBinders using zipPatsBndrs, which is made public for this
purpose. The resulting builder is $bP x = MkT _ _ x.
See Note [Discarding types in the builder expression].
Test cases: T27440a T27440b T27440c T27440d T27440e
T27583a T27583b T27583c T27583d T27583e T27583f T27583g
Metric Increase: LinkableUsage02
Metric Decrease: T27336
Assisted-by: Claude Opus 5
- - - - -
23 changed files:
- + changelog.d/T27440
- + changelog.d/T27583
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- + testsuite/tests/patsyn/should_compile/T27440a.hs
- + testsuite/tests/patsyn/should_compile/T27440b.hs
- + testsuite/tests/patsyn/should_compile/T27440c.hs
- testsuite/tests/patsyn/should_compile/all.T
- + testsuite/tests/patsyn/should_fail/T27440d.hs
- + testsuite/tests/patsyn/should_fail/T27440d.stderr
- testsuite/tests/patsyn/should_fail/all.T
- + testsuite/tests/vdq-rta/should_compile/T27583a.hs
- + testsuite/tests/vdq-rta/should_compile/T27583b.hs
- + testsuite/tests/vdq-rta/should_compile/T27583c.hs
- + testsuite/tests/vdq-rta/should_compile/T27583d.hs
- + testsuite/tests/vdq-rta/should_compile/T27583e.hs
- + testsuite/tests/vdq-rta/should_compile/T27583g.hs
- testsuite/tests/vdq-rta/should_compile/all.T
- + testsuite/tests/vdq-rta/should_fail/T27440e.hs
- + testsuite/tests/vdq-rta/should_fail/T27440e.stderr
- + testsuite/tests/vdq-rta/should_fail/T27583f.hs
- + testsuite/tests/vdq-rta/should_fail/T27583f.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
Changes:
=====================================
changelog.d/T27440
=====================================
@@ -0,0 +1,8 @@
+section: compiler
+issues: #27440
+mrs: !16434
+synopsis:
+ Fix a panic on ``@ty`` in a pattern synonym RHS
+description:
+ An invisible type argument (``@ty``) in the right-hand side of an implicitly
+ bidirectional pattern synonym no longer causes a panic.
=====================================
changelog.d/T27583
=====================================
@@ -0,0 +1,9 @@
+section: compiler
+issues: #27583
+mrs: !16434
+synopsis:
+ Fix spurious out-of-scope errors from ``type ty`` in a pattern synonym RHS
+description:
+ A required type argument with an explicit namespace specifier (``type ty``)
+ in the right-hand side of an implicitly bidirectional pattern synonym no
+ longer reports variables bound by the pattern as out of scope.
=====================================
compiler/GHC/Tc/Gen/Pat.hs
=====================================
@@ -16,6 +16,7 @@ module GHC.Tc.Gen.Pat
, tcCheckPat, tcCheckPat_O, tcInferPat
, tcMatchPats
, addDataConStupidTheta
+ , zipPatsBndrs
)
where
@@ -1727,7 +1728,7 @@ split_con_ty_args :: LexicalFixity -- How to wrap value arguments
, [(HsTyPat GhcRn, TyVar)] -- Existentials
, HsConPatDetails GhcRn ) -- Value arguments
split_con_ty_args fixity con_like arg_pats = do
- (bndr_ty_arg_prs, value_args) <- zip_pats_bndrs arg_pats (conLikeUserTyVarBinders con_like)
+ (bndr_ty_arg_prs, value_args) <- zipPatsBndrs arg_pats (conLikeUserTyVarBinders con_like)
return $ if null ex_tvs -- Short cut common case
then (bndr_ty_arg_prs, [], mk_details fixity value_args)
else let (ex_prs, univ_prs) = partition is_existential bndr_ty_arg_prs
@@ -1743,24 +1744,75 @@ split_con_ty_args fixity con_like arg_pats = do
-- InfixCon becomes PrefixCon if there are fewer than 2 value arguments.
-- Test case: T25127_infix
-zip_pats_bndrs :: [LPat GhcRn] -> [TyVarBinder] -> TcM ([(HsTyPat GhcRn, TyVar)], [LPat GhcRn])
-zip_pats_bndrs (L loc pat : pats) (Bndr tv vis : tvbs)
+-- | Line the arguments of a 'ConPat' up against the 'TyVarBinder's of its
+-- 'ConLike', returning the type arguments with the binders they instantiate,
+-- and the remaining value arguments.
+--
+-- See Note [Zipping ConPat arguments with TyVarBinders]
+--
+-- Precondition: 'check_con_pat_arity' has passed for these arguments, so that
+-- we never run out of patterns while a required binder remains.
+zipPatsBndrs :: [LPat GhcRn] -> [TyVarBinder] -> TcM ([(HsTyPat GhcRn, TyVar)], [LPat GhcRn])
+zipPatsBndrs (L loc pat : pats) (Bndr tv vis : tvbs)
| isVisibleForAllTyFlag vis
= do { tp <- setSrcSpanA loc $ pat_to_type_pat pat
- ; (prs, pats') <- zip_pats_bndrs pats tvbs
+ ; (prs, pats') <- zipPatsBndrs pats tvbs
; return ((tp, tv) : prs, pats') }
| InvisPat pat_spec tp <- pat
, Invisible spec <- vis
, pat_spec == spec
- = do { (prs, pats') <- zip_pats_bndrs pats tvbs
+ = do { (prs, pats') <- zipPatsBndrs pats tvbs
; return ((tp, tv):prs, pats') }
-zip_pats_bndrs pats (Bndr _ vis : tvbs)
- -- zip_pats_bndrs [] (Bndr _ Required : tvbs)
+zipPatsBndrs pats (Bndr _ vis : tvbs)
+ -- zipPatsBndrs [] (Bndr _ Required : tvbs)
-- is ruled out by the arity check in splitConTyArgs,
-- so we can assume (isInvisibleForAllTyFlag vis)
= do { massert (isInvisibleForAllTyFlag vis)
- ; zip_pats_bndrs pats tvbs }
-zip_pats_bndrs pats [] = return ([], pats)
+ ; zipPatsBndrs pats tvbs }
+zipPatsBndrs pats [] = return ([], pats)
+
+{- Note [Zipping ConPat arguments with TyVarBinders]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The zipPatsBndrs function allows us to line up the arguments of a constructor
+pattern `MkT p1 p2 ... pn` with the TyVarBinders of MkT's type. Consider
+
+ MkT :: forall a b. forall c d -> a -> T a b c d
+
+Then, in a pattern match `MkT @s (type t) p q`, we have the following
+correspondence:
+
+ * pattern `@s` with the TyVarBinder `forall a.`
+ * no pattern with the TyVarBinder `forall b.`
+ * pattern `type t` with the TyVarBinder `forall c ->`
+ * pattern `p` with the TyVarBinder `forall d ->`
+ * pattern `q` is the one remaining value argument
+
+Note how `p` and `q` are exactly alike, and only the TyVarBinder can tell us
+that `p` is a required type argument rather than a value argument.
+
+So the call (zipPatsBndrs pats tvbs) with the inputs
+
+ pats = [@s, type t, p, q]
+ tvbs = [Bndr a Spec, Bndr b Spec, Bndr c Req, Bndr d Req]
+
+produces the following (ty_pats, val_pats) outputs:
+
+ ty_pats = [(s, a), (t, c), (p, d)]
+ val_pats = [q] -- remaining value arguments
+
+Note that the ty_pats components are stripped: the type patterns have lost the
+`@` or the `type` herald, and the binders have been reduced to their TyVars.
+
+This is currently used in two ways:
+
+1. In tcDataConPat/tcPatSynPat (via splitConTyArgs) to pass the type arguments
+ onto tcConTyArgs and the value arguments onto tcConValArgs.
+ See Note [Type applications in patterns] for more details.
+
+2. In tcPatToExpr, to discard the type arguments in the RHS of an implicitly
+ bidirectional pattern synonym, keeping only the value arguments.
+ See Note [Discarding types in the builder expression] in GHC.Tc.TyCl.PatSyn.
+-}
tcConTyArgs :: Subst -> PatEnv -> [(HsTyPat GhcRn, TyVar)]
-> TcM a -> TcM a
=====================================
compiler/GHC/Tc/TyCl/PatSyn.hs
=====================================
@@ -940,11 +940,9 @@ tcPatSynBuilderBind prag_fn (PSB { psb_id = ps_lname@(L loc ps_name)
| isUnidirectional dir
= return []
- | Left why <- mb_match_group -- Can't invert the pattern
- = setSrcSpan (getLocA lpat) $ failWithTc $ TcRnPatSynInvalidRhs ps_name lpat args why
-
- | Right match_group <- mb_match_group -- Bidirectional
- = do { patsyn <- tcLookupPatSyn ps_name
+ | otherwise -- Bidirectional
+ = do { match_group <- get_match_group
+ ; patsyn <- tcLookupPatSyn ps_name
; case patSynBuilder patsyn of {
Nothing -> return [] ;
-- This case happens if we found a type error in the
@@ -985,10 +983,10 @@ tcPatSynBuilderBind prag_fn (PSB { psb_id = ps_lname@(L loc ps_name)
; return builder_binds } } }
where
- mb_match_group
+ get_match_group
= case dir of
- ExplicitBidirectional explicit_mg -> Right explicit_mg
- ImplicitBidirectional -> fmap mk_mg (tcPatToExpr args lpat)
+ ExplicitBidirectional explicit_mg -> return explicit_mg
+ ImplicitBidirectional -> mk_mg <$> tcPatToExpr ps_name args lpat
Unidirectional -> panic "tcPatSynBuilderBind"
mk_mg :: LHsExpr GhcRn -> MatchGroup GhcRn (LHsExpr GhcRn)
@@ -1032,44 +1030,53 @@ add_void need_dummy_arg ty
| need_dummy_arg = mkVisFunTyMany unboxedUnitTy ty
| otherwise = ty
-tcPatToExpr :: [LocatedN Name] -> LPat GhcRn
- -> Either PatSynInvalidRhsReason (LHsExpr GhcRn)
+tcPatToExpr :: Name -> [LocatedN Name] -> LPat GhcRn -> TcM (LHsExpr GhcRn)
-- Given a /pattern/, return an /expression/ that builds a value
-- that matches the pattern. E.g. if the pattern is (Just [x]),
-- the expression is (Just [x]). They look the same, but the
-- input uses constructors from HsPat and the output uses constructors
-- from HsExpr.
--
--- Returns (Left r) if the pattern is not invertible, for reason r.
+-- Fails with TcRnPatSynInvalidRhs if the pattern is not invertible.
-- See Note [Builder for a bidirectional pattern synonym]
-tcPatToExpr args pat = go pat
+tcPatToExpr ps_name args pat = go pat
where
lhsVars = mkNameSet (map unLoc args)
+ invalidRhs :: PatSynInvalidRhsReason -> TcM a
+ invalidRhs why = setSrcSpan (getLocA pat) $
+ failWithTc $ TcRnPatSynInvalidRhs ps_name pat args why
+
-- Make a prefix con for prefix and infix patterns for simplicity
mkPrefixConExpr :: LocatedN (WithUserRdr Name)
-> [LPat GhcRn]
- -> Either PatSynInvalidRhsReason (HsExpr GhcRn)
- mkPrefixConExpr lcon@(L loc _) pats
- = do { exprs <- mapM go pats
+ -> TcM (HsExpr GhcRn)
+ mkPrefixConExpr lcon@(L loc con_name) pats
+ = do { con_like <- tcLookupConLike con_name
+ ; let tvbs = conLikeUserTyVarBinders con_like
+ ; (_ty_pats, val_pats) <- zipPatsBndrs pats tvbs
+ -- Type arguments _ty_pats are discarded, just like the SigPat's type.
+ -- See Note [Discarding types in the builder expression]
+ ; let ty_exprs = [wildCardTyArg | tvb <- tvbs, isVisibleForAllTyBinder tvb]
+ -- Placeholders `_` for the discarded required type arguments.
+ ; val_exprs <- mapM go val_pats
; let con = L (l2l loc) (HsVar noExtField lcon)
- ; return (unLoc $ mkHsApps con exprs)
- }
+ ; return (unLoc $ mkHsApps con (ty_exprs ++ val_exprs)) }
mkRecordConExpr :: LocatedN (WithUserRdr Name)
-> HsRecFields GhcRn (LPat GhcRn)
- -> Either PatSynInvalidRhsReason (HsExpr GhcRn)
+ -> TcM (HsExpr GhcRn)
mkRecordConExpr con (HsRecFields x fields dd)
= do { exprFields <- mapM go' fields
; return (RecordCon noExtField con (HsRecFields x exprFields dd)) }
- go' :: LHsRecField GhcRn (LPat GhcRn) -> Either PatSynInvalidRhsReason (LHsRecField GhcRn (LHsExpr GhcRn))
+ go' :: LHsRecField GhcRn (LPat GhcRn) -> TcM (LHsRecField GhcRn (LHsExpr GhcRn))
go' (L l rf) = L l <$> traverse go rf
- go :: LPat GhcRn -> Either PatSynInvalidRhsReason (LHsExpr GhcRn)
+ go :: LPat GhcRn -> TcM (LHsExpr GhcRn)
go (L loc p) = L loc <$> go1 p
- go1 :: Pat GhcRn -> Either PatSynInvalidRhsReason (HsExpr GhcRn)
+ go1 :: Pat GhcRn -> TcM (HsExpr GhcRn)
go1 (ConPat NoExtField con info)
= case info of
PrefixCon _ ps -> mkPrefixConExpr con ps
@@ -1077,13 +1084,13 @@ tcPatToExpr args pat = go pat
RecCon _ fields -> mkRecordConExpr con fields
go1 (SigPat _ pat _) = go1 (unLoc pat)
- -- See Note [Type signatures and the builder expression]
+ -- See Note [Discarding types in the builder expression]
go1 (VarPat _ (L l var))
| var `elemNameSet` lhsVars
= return $ mkHsVar (L l var)
| otherwise
- = Left (PatSynUnboundVar var)
+ = invalidRhs (PatSynUnboundVar var)
go1 (ParPat _ pat) = fmap (HsPar noExtField) (go pat)
go1 (ListPat _ pats)
= do { exprs <- mapM go pats
@@ -1104,10 +1111,7 @@ tcPatToExpr args pat = go pat
| otherwise = return $ HsOverLit noExtField n
go1 (SplicePat (HsUntypedSpliceTop _ pat) _) = go1 pat
go1 (SplicePat (HsUntypedSpliceNested _) _) = panic "tcPatToExpr: invalid nested splice"
- go1 (EmbTyPat _ tp) = return $ HsEmbTy noExtField (hstp_to_hswc tp)
- where hstp_to_hswc :: HsTyPat GhcRn -> LHsWcType GhcRn
- hstp_to_hswc (HsTP { hstp_ext = HsTPRn { hstp_nwcs = wcs }, hstp_body = hs_ty })
- = HsWC { hswc_ext = wcs, hswc_body = hs_ty }
+ go1 (EmbTyPat _ _tp) = panic "tcPatToExpr: invalid type pattern"
go1 (InvisPat _ _tp) = panic "tcPatToExpr: invalid invisible pattern"
go1 (XPat (HsPatExpanded _ pat))= go1 pat
@@ -1131,7 +1135,11 @@ tcPatToExpr args pat = go pat
go1 p@(NPlusKPat {}) = notInvertible p
go1 p@(OrPat {}) = notInvertible p
- notInvertible p = Left (PatSynNotInvertible p)
+ notInvertible p = invalidRhs (PatSynNotInvertible p)
+
+-- See Note [Discarding types in the builder expression]
+wildCardTyArg :: LHsExpr GhcRn
+wildCardTyArg = wrapGenSpan (HsHole (HoleVar (wrapGenSpan unnamedHoleRdrName)))
{- Note [Builder for a bidirectional pattern synonym]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1203,9 +1211,17 @@ one could write a nonsensical function like
or
g (K (Just True) False) = ...
-Note [Type signatures and the builder expression]
+Note [Discarding types in the builder expression]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The RHS of an implicitly bidirectional pattern synonym may mention types in
+three ways: a pattern signature (p :: ty), an invisible type argument (@ty),
+and a required type argument (type ty). Required type arguments may be obscured
+by the omission of the `type` keyword.
+
+A type may /bind/ variables when it occurs in a pattern, and those binders have
+no counterpart in the builder's scope, so tcPatToExpr must not carry them over.
Consider
+
pattern L x = Left x :: Either [a] [b]
In tc{Infer/Check}PatSynDecl we will check that the pattern has the
@@ -1223,6 +1239,71 @@ latter; #9867.) No, the job of the signature is done, so when
converting the pattern to an expression (for the builder RHS) we
simply discard the signature.
+The same reasoning applies to the other two forms, but the way we discard
+them differs. Which form an argument takes is not apparent from the pattern
+alone: in
+
+ pattern P x = MkT a x
+
+'a' looks like an ordinary variable pattern, and only the TyVarBinders in
+MkT's type say that it stands in a required type argument position. We get
+those binders from the typechecked ConLike, via conLikeUserTyVarBinders, so
+mkPrefixConExpr must look the constructor up before it can walk the arguments.
+It then lines them up against the binders with zipPatsBndrs, as described
+in Note [Zipping ConPat arguments with TyVarBinders] in GHC.Tc.Gen.Pat.
+
+Each argument is then treated accordingly:
+
+* Invisible type arguments (@ty) are dropped from the argument list
+ altogether. Given
+
+ pattern Q x = MkT @a x
+
+ the builder is $bQ x = MkT x. Dropping is safe because the argument
+ instantiates either a universal, which the expected type of the builder
+ already pins down, or an existential, which cannot take a concrete type
+ in a pattern anyway. Improper handling of type arguments led to #27440.
+
+* Required type arguments cannot be dropped, as that would change the syntactic
+ arity of the application. Instead, we replace them with wildcards `_`, the
+ equivalent of @_ for invisible type arguments. Given
+
+ pattern R x = MkT (type a) x
+ pattern P x = MkT a x
+
+ the builders are $bR x = MkT _ x and $bP x = MkT _ x, and the wildcard is
+ solved from the expected type. Retaining the type would mention a binder that
+ is not in scope in the builder (#27583).
+
+This reasoning holds when the RHS is a data constructor. Two caveats:
+
+* With a helper pattern synonym we can construct an example where discarding
+ the type argument rejects an otherwise valid program:
+
+ pattern Q :: forall a. Show a => Int -> S -- 'a' is ambiguous
+ pattern P n = Q @Bool n -- rejected: $bP n = Q n, ambiguous 'a'
+
+ The example introduces an ambiguous type variable occurring in a class
+ constraint. Such a variable serves no purpose, so we do not expect to
+ encounter this problem in practice. The workaround is to declare P
+ explicitly bidirectional and write the builder by hand.
+
+* A required type argument that binds a variable yields a suboptimal error
+ message: we fail to solve the wildcard, where we would rather report that 'a'
+ is not bound on the LHS.
+
+ data S where MkS :: forall a -> Show a => Int -> S
+ pattern P n = MkS a n -- $bP n = MkS _ n, ambiguous wildcard
+
+ Mind you, the program is rejected either way: it is not possible to bind 'a' on
+ the LHS until pattern synonyms support RequiredTypeArguments (#23704) or
+ TypeAbstractions (#27642).
+
+We will have to revisit this design once we do add support for those extensions
+in pattern synonym declarations (#23704, #27642). When type variables can be
+bound on the LHS, the builder has a scope for them, and discarding every type in
+the RHS is no longer the obvious thing to do.
+
Note [Record PatSyn Desugaring]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It is important that prov_theta comes before req_theta as this ordering is used
=====================================
testsuite/tests/patsyn/should_compile/T27440a.hs
=====================================
@@ -0,0 +1,12 @@
+{-# LANGUAGE DataKinds, PatternSynonyms, TypeAbstractions #-}
+module T27440a where
+
+import Data.Kind (Type)
+
+newtype Lit = Lit { litName :: String }
+
+type LitOfValue :: Bool -> Type
+newtype LitOfValue v = LitOfValue { underlyingLit :: Lit }
+
+pattern FalseLit :: Lit -> LitOfValue False
+pattern FalseLit a = LitOfValue @False a
=====================================
testsuite/tests/patsyn/should_compile/T27440b.hs
=====================================
@@ -0,0 +1,7 @@
+{-# LANGUAGE PatternSynonyms, TypeAbstractions #-}
+module T27440b where
+
+data T a = MkT a
+
+pattern P :: a -> T a
+pattern P x = MkT @a x
=====================================
testsuite/tests/patsyn/should_compile/T27440c.hs
=====================================
@@ -0,0 +1,7 @@
+{-# LANGUAGE ExistentialQuantification, PatternSynonyms, TypeAbstractions #-}
+module T27440c where
+
+data S = forall a. Show a => MkS a
+
+pattern P :: () => Show a => a -> S
+pattern P x = MkS @a x
=====================================
testsuite/tests/patsyn/should_compile/all.T
=====================================
@@ -90,3 +90,7 @@ test('T23038', normal, compile_fail, [''])
test('T22328', normal, compile, [''])
test('T26331', normal, compile, [''])
test('T26331a', normal, compile, [''])
+
+test('T27440a', normal, compile, [''])
+test('T27440b', normal, compile, [''])
+test('T27440c', normal, compile, [''])
=====================================
testsuite/tests/patsyn/should_fail/T27440d.hs
=====================================
@@ -0,0 +1,7 @@
+{-# LANGUAGE ExistentialQuantification, PatternSynonyms, TypeAbstractions #-}
+module T27440d where
+
+data S = forall a. Show a => MkS a
+
+pattern P :: Show a => a -> S
+pattern P x = MkS @a x
=====================================
testsuite/tests/patsyn/should_fail/T27440d.stderr
=====================================
@@ -0,0 +1,12 @@
+T27440d.hs:7:22: error: [GHC-25897]
+ • Couldn't match expected type ‘a’ with actual type ‘a1’
+ ‘a1’ is a rigid type variable bound by
+ a pattern with constructor: MkS :: forall a. Show a => a -> S,
+ in a pattern synonym declaration
+ at T27440d.hs:7:15-22
+ ‘a’ is a rigid type variable bound by
+ the signature for pattern synonym ‘P’
+ at T27440d.hs:6:14-29
+ • In the declaration for pattern synonym ‘P’
+ • Relevant bindings include x :: a1 (bound at T27440d.hs:7:22)
+
=====================================
testsuite/tests/patsyn/should_fail/all.T
=====================================
@@ -55,3 +55,5 @@ test('patsyn_where_fail1', normal, compile_fail, [''])
test('patsyn_where_fail2', normal, compile_fail, [''])
test('patsyn_where_fail3', normal, compile_fail, [''])
test('patsyn_where_fail4', normal, compile_fail, [''])
+
+test('T27440d', normal, compile_fail, [''])
=====================================
testsuite/tests/vdq-rta/should_compile/T27583a.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583a where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: T a
+pattern P = MkT (type a)
=====================================
testsuite/tests/vdq-rta/should_compile/T27583b.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583b where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: T a
+pattern P = MkT a
=====================================
testsuite/tests/vdq-rta/should_compile/T27583c.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583c where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: T Int
+pattern P = MkT (type Int)
=====================================
testsuite/tests/vdq-rta/should_compile/T27583d.hs
=====================================
@@ -0,0 +1,21 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583d where
+
+data T a where
+ MkT :: forall a -> a -> T a
+
+-- A required type argument is checked against the signature, with or without
+-- the 'type' herald. The variable/wildcard patterns x, _, (type x), (type _)
+-- can't mismatch the signature. Concrete types are in T27583e and T27583f.
+
+pattern P1 :: Int -> T Int
+pattern P1 n = MkT x n
+
+pattern P2 :: Int -> T Int
+pattern P2 n = MkT (type x) n
+
+pattern P3 :: Int -> T Int
+pattern P3 n = MkT _ n
+
+pattern P4 :: Int -> T Int
+pattern P4 n = MkT (type _) n
=====================================
testsuite/tests/vdq-rta/should_compile/T27583e.hs
=====================================
@@ -0,0 +1,21 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583e where
+
+data T a where
+ MkT :: forall a -> a -> T a
+
+-- A required type argument is checked against the signature, with or without
+-- the 'type' herald. Here the type written in that position agrees with the
+-- signature. T27583f is the same four patterns with a type that does not.
+
+pattern P1 :: Int -> T Int
+pattern P1 n = MkT Int n
+
+pattern P2 :: Int -> T Int
+pattern P2 n = MkT (type Int) n
+
+pattern P3 :: Maybe Int -> T (Maybe Int)
+pattern P3 n = MkT (Maybe Int) n
+
+pattern P4 :: Maybe Int -> T (Maybe Int)
+pattern P4 n = MkT (type (Maybe Int)) n
=====================================
testsuite/tests/vdq-rta/should_compile/T27583g.hs
=====================================
@@ -0,0 +1,14 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms, TypeAbstractions #-}
+module T27583g where
+
+data T a b c where
+ MkT :: forall a. forall b c -> a -> T a b c
+
+-- Check all argument variants in a constructor pattern at once: the invisible
+-- type argument `@a`, the required type argument `type b` with the herald, the
+-- required type argument `c` without the herald, and the value argument `x`.
+--
+-- The resulting builder is $bP x = MkT _ _ x.
+
+pattern P :: x -> T x y z
+pattern P x = MkT @a (type b) c x
=====================================
testsuite/tests/vdq-rta/should_compile/all.T
=====================================
@@ -39,3 +39,10 @@ test('T23738_th', req_th, compile, [''])
test('T24159_viewpat', normal, compile, [''])
test('T24159_type_syntax', normal, compile, [''])
test('T24159_th_type_syntax', req_th, compile, [''])
+
+test('T27583a', normal, compile, [''])
+test('T27583b', normal, compile, [''])
+test('T27583c', normal, compile, [''])
+test('T27583d', normal, compile, [''])
+test('T27583e', normal, compile, [''])
+test('T27583g', normal, compile, [''])
=====================================
testsuite/tests/vdq-rta/should_fail/T27440e.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms, TypeAbstractions #-}
+module T27440e where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: a -> T a
+pattern P x = MkT @a x
=====================================
testsuite/tests/vdq-rta/should_fail/T27440e.stderr
=====================================
@@ -0,0 +1,5 @@
+T27440e.hs:8:19: error: [GHC-88754]
+ • Ill-formed type pattern: @a
+ • In the pattern: MkT @a x
+ In the declaration for pattern synonym ‘P’
+
=====================================
testsuite/tests/vdq-rta/should_fail/T27583f.hs
=====================================
@@ -0,0 +1,21 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+module T27583f where
+
+data T a where
+ MkT :: forall a -> a -> T a
+
+-- A required type argument is checked against the signature, with or without
+-- the 'type' herald. Here the type written in that position does not agree
+-- with the signature. T27583e is the same four patterns with a type that does.
+
+pattern P1 :: Int -> T Int
+pattern P1 n = MkT Bool n
+
+pattern P2 :: Int -> T Int
+pattern P2 n = MkT (type Bool) n
+
+pattern P3 :: Maybe Int -> T (Maybe Int)
+pattern P3 n = MkT (Maybe Bool) n
+
+pattern P4 :: Maybe Int -> T (Maybe Int)
+pattern P4 n = MkT (type (Maybe Bool)) n
=====================================
testsuite/tests/vdq-rta/should_fail/T27583f.stderr
=====================================
@@ -0,0 +1,24 @@
+T27583f.hs:12:16: error: [GHC-83865]
+ • Couldn't match expected type ‘Int’ with actual type ‘Bool’
+ • In the pattern: MkT Bool n
+ In the declaration for pattern synonym ‘P1’
+
+T27583f.hs:15:16: error: [GHC-83865]
+ • Couldn't match expected type ‘Int’ with actual type ‘Bool’
+ • In the pattern: MkT (type Bool) n
+ In the declaration for pattern synonym ‘P2’
+
+T27583f.hs:18:16: error: [GHC-83865]
+ • Couldn't match type ‘Bool’ with ‘Int’
+ Expected: Maybe Int
+ Actual: Maybe Bool
+ • In the pattern: MkT (Maybe Bool) n
+ In the declaration for pattern synonym ‘P3’
+
+T27583f.hs:21:16: error: [GHC-83865]
+ • Couldn't match type ‘Bool’ with ‘Int’
+ Expected: Maybe Int
+ Actual: Maybe Bool
+ • In the pattern: MkT (type (Maybe Bool)) n
+ In the declaration for pattern synonym ‘P4’
+
=====================================
testsuite/tests/vdq-rta/should_fail/all.T
=====================================
@@ -32,3 +32,6 @@ test('T24159_type_syntax_tc_fail', normal, compile_fail, [''])
test('T24159_type_syntax_th_fail', normal, ghci_script, ['T24159_type_syntax_th_fail.script'])
test('T25127_fail_th_quote', normal, compile_fail, [''])
test('T25127_fail_arity', normal, compile_fail, [''])
+
+test('T27440e', normal, compile_fail, [''])
+test('T27583f', normal, compile_fail, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/4f98510802423dcd98fa62af7799718…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/4f98510802423dcd98fa62af7799718…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0