[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 4 commits: testsuite: Show baseline sample count and range in perf failures
by Marge Bot (@marge-bot) 20 Aug '26
by Marge Bot (@marge-bot) 20 Aug '26
20 Aug '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
1ffe999f by Simon Jakobi at 2026-08-20T08:07:25-04:00
testsuite: Show baseline sample count and range in perf failures
A perf baseline is the mean of all samples recorded for a commit, and
it prints as a single number, hiding how far the samples spread. When
the spread is wide, this can indicate an unstable metric that isn't
actually useful as a signal for the perf tests.
For example, in #27602, T27336's peak_megabytes_allocated baseline
showed as 757 when the underlying samples were 605 and 909.
When the baseline is averaged from more than one sample, say so in the
failure output: the one-line stat-failure reason shows the sample
range, and the detail block lists the raw samples. Single-sample
baselines print exactly as before.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
3827f24e by Simon Jakobi at 2026-08-20T08:07:25-04:00
testsuite: Fold Baseline into CommitMetric
A Baseline was just a CommitMetric plus the commit it came from, built
by copying fields across. Since get_commit_metric already knows that
commit, record it on CommitMetric itself and drop Baseline. This also
collapses both branches of find_baseline into plain returns.
Assisted-by: Claude Fable 5
- - - - -
ed4d3273 by Simon Jakobi at 2026-08-20T08:07:26-04:00
ci: Clarify comment on pushing perf notes after failures
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
8bb8a88e by Alan Zimmerman at 2026-08-20T08:07:26-04:00
EPA: Remove LocatedBC / SrcSpanBF
The custom annotations are now in the BooleanFormula TTG extension
points, so LBooleanFormula can now use the standard LocatedA.
- - - - -
12 changed files:
- .gitlab/ci.sh
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- testsuite/driver/perf_notes.py
- testsuite/driver/testglobals.py
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Types.hs
Changes:
=====================================
.gitlab/ci.sh
=====================================
@@ -1120,9 +1120,10 @@ case ${1:-help} in
setup) setup && cleanup_submodules ;;
configure) time_it "configure" configure ;;
build_hadrian) time_it "build" build_hadrian ;;
- # N.B. Always push notes, even if the build fails. This is okay to do as the
- # testsuite driver doesn't record notes for tests that fail due to
- # correctness.
+ # N.B. Always push notes, even if the build fails. Metrics from runs failing
+ # a perf stat check are deliberately recorded too — discarding them would
+ # bias the baseline towards whichever sample came first. Only correctness
+ # failures record nothing.
test_hadrian)
fetch_perf_notes
res=0
=====================================
compiler/GHC/Data/BooleanFormula.hs
=====================================
@@ -25,7 +25,7 @@ import GHC.Types.Unique
import GHC.Types.Unique.Set
import GHC.Types.SrcLoc (unLoc)
import GHC.Utils.Outputable
-import GHC.Parser.Annotation ( SrcSpanAnnBF )
+import GHC.Parser.Annotation ( SrcSpanAnnA, EpToken(..) )
import GHC.Hs.Extension (GhcPass (..), OutputableBndrId)
import Language.Haskell.Syntax.Extension (Anno, LIdP, IdP,
noExtField, NoExtField, DataConCantHappen,
@@ -38,12 +38,12 @@ import Language.Haskell.Syntax.BooleanFormula
-- Boolean formula type and smart constructors
----------------------------------------------------------------------
-type instance Anno (BooleanFormula (GhcPass p)) = SrcSpanAnnBF
+type instance Anno (BooleanFormula (GhcPass p)) = SrcSpanAnnA
type instance XBFVar (GhcPass _) = NoExtField
type instance XBFAnd (GhcPass _) = NoExtField
type instance XBFOr (GhcPass _) = NoExtField
-type instance XBFParens (GhcPass _) = NoExtField
+type instance XBFParens (GhcPass _) = (EpToken "(", EpToken ")")
type instance XXBooleanFormula (GhcPass _) = DataConCantHappen
instance BooleanFormulaDefault (GhcPass p) where
=====================================
compiler/GHC/Hs/Dump.hs
=====================================
@@ -97,7 +97,6 @@ showAstData bs ba a0 = blankLine $$ showAstData' a0
`ext2Q` located
`extQ` srcSpanAnnA
`extQ` srcSpanAnnN
- `extQ` srcSpanAnnBF
where generic :: Data a => a -> SDoc
generic t = parens $ text (showConstr (toConstr t))
@@ -396,10 +395,6 @@ showAstData bs ba a0 = blankLine $$ showAstData' a0
srcSpanAnnN :: EpAnn NameAnn -> SDoc
srcSpanAnnN = locatedAnn'' (text "SrcSpanAnnN")
- srcSpanAnnBF :: EpAnn AnnBooleanFormula -> SDoc
- srcSpanAnnBF = locatedAnn'' (text "SrcSpanAnnBF")
-
-
locatedAnn'' :: forall a. (Typeable a, Data a)
=> SDoc -> EpAnn a -> SDoc
locatedAnn'' tag ss = parens $
=====================================
compiler/GHC/Iface/Ext/Ast.hs
=====================================
@@ -2081,7 +2081,7 @@ instance ToHie PendingRnSplice where
toHie (PendingRnSplice _ e) = toHie e
instance (HiePass p, Data (IdGhcP p))
- => ToHie (GenLocated SrcSpanAnnBF (BooleanFormula (GhcPass p))) where
+ => ToHie (GenLocated SrcSpanAnnA (BooleanFormula (GhcPass p))) where
toHie (L span form) = concatM $ makeNode form (locA span) : case form of
Var _ a ->
[ toHie $ C Use a
=====================================
compiler/GHC/Iface/Syntax.hs
=====================================
@@ -1330,7 +1330,7 @@ pprIfaceDecl ss decl@(IfaceClass { ifName = clas
fromIfaceBooleanFormula (IfVar nm ) = Var noExtField $ noLocA . mkUnboundName . mkVarOccFS . ifLclNameFS $ nm
fromIfaceBooleanFormula (IfAnd bfs ) = And noExtField $ map (noLocA . fromIfaceBooleanFormula) bfs
fromIfaceBooleanFormula (IfOr bfs ) = Or noExtField $ map (noLocA . fromIfaceBooleanFormula) bfs
- fromIfaceBooleanFormula (IfParens bf) = Parens noExtField $ (noLocA . fromIfaceBooleanFormula) bf
+ fromIfaceBooleanFormula (IfParens bf) = Parens noAnn $ (noLocA . fromIfaceBooleanFormula) bf
-- See Note [Suppressing binder signatures] in GHC.Iface.Type
=====================================
compiler/GHC/IfaceToCore.hs
=====================================
@@ -121,7 +121,7 @@ import GHC.Types.Tickish
import GHC.Types.TyThing
import GHC.Types.Error
-import GHC.Parser.Annotation (noLocA)
+import GHC.Parser.Annotation (noLocA, noAnn)
import GHC.Fingerprint
@@ -885,7 +885,7 @@ tc_iface_decl _parent ignore_prags
tc_boolean_formula :: IfaceBooleanFormula -> IfL (BooleanFormula GhcRn)
tc_boolean_formula (IfAnd ibfs ) = BF.And NoExtField . map noLocA <$> traverse tc_boolean_formula ibfs
tc_boolean_formula (IfOr ibfs ) = BF.Or NoExtField . map noLocA <$> traverse tc_boolean_formula ibfs
- tc_boolean_formula (IfParens ibf) = BF.Parens NoExtField . noLocA <$> tc_boolean_formula ibf
+ tc_boolean_formula (IfParens ibf) = BF.Parens noAnn . noLocA <$> tc_boolean_formula ibf
tc_boolean_formula (IfVar nm ) = BF.Var NoExtField . noLocA <$> (lookupIfaceTop . mkVarOccFS . ifLclNameFS $ nm)
mk_sc_doc pred = text "Superclass" <+> ppr pred
=====================================
compiler/GHC/Parser.y
=====================================
@@ -3820,7 +3820,7 @@ name_boolformula_opt :: { LBooleanFormula GhcPs }
name_boolformula :: { LBooleanFormula GhcPs }
: name_boolformula_and { $1 }
| name_boolformula_and '|' name_boolformula
- {% do { h <- addTrailingVbarBF $1 (epTok $2)
+ {% do { h <- addTrailingVbarA $1 (epTok $2)
; return (sLLa $1 $> (Or noExtField [h,$3])) } }
name_boolformula_and :: { LBooleanFormula GhcPs }
@@ -3830,12 +3830,11 @@ name_boolformula_and :: { LBooleanFormula GhcPs }
name_boolformula_and_list :: { NonEmpty (LBooleanFormula GhcPs) }
: name_boolformula_atom { NE.singleton $1 }
| name_boolformula_atom ',' name_boolformula_and_list
- {% do { h <- addTrailingCommaBF $1 (epTok $2)
+ {% do { h <- addTrailingCommaA $1 (epTok $2)
; return (h NE.<| $3) } }
name_boolformula_atom :: { LBooleanFormula GhcPs }
- : '(' name_boolformula ')' {% amsr (sLL $1 $> (Parens noExtField $2))
- (AnnBooleanFormula (epTok $1) (epTok $3) []) }
+ : '(' name_boolformula ')' {% amsA' (sLL $1 $> (Parens (epTok $1, epTok $3) $2)) }
| name_var { sL1a $1 (Var noExtField $1) }
namelist :: { Located [LocatedN RdrName] }
@@ -4794,20 +4793,6 @@ addTrailingAnnA (L anns a) tok ta = do
-- -------------------------------------
-addTrailingVbarBF :: MonadP m => LocatedBF a -> EpToken "|" -> m (LocatedBF a)
-addTrailingVbarBF la tok = addTrailingAnnBF la (AddVbarAnn tok)
-
-addTrailingCommaBF :: MonadP m => LocatedBF a -> EpToken "," -> m (LocatedBF a)
-addTrailingCommaBF la tok = addTrailingAnnBF la (AddCommaAnn tok)
-
-addTrailingAnnBF :: MonadP m => LocatedBF a -> TrailingAnn -> m (LocatedBF a)
-addTrailingAnnBF (L anns a) ta = do
- !cs <- getCommentsFor (locA anns)
- let anns' = addTrailingAnnToBF ta cs anns
- return (L anns' a)
-
--- -------------------------------------
-
-- Mostly use to add AnnComma, special case it to NOP if adding a zero-width annotation
addTrailingCommaN :: MonadP m => LocatedN a -> SrcSpan -> m (LocatedN a)
addTrailingCommaN (L anns a) span = do
=====================================
compiler/GHC/Parser/Annotation.hs
=====================================
@@ -28,23 +28,19 @@ module GHC.Parser.Annotation (
-- ** Annotations in 'GenLocated'
LocatedA, LocatedN, LocatedAn,
- LocatedBF,
SrcSpanAnnA, SrcSpanAnnN,
- SrcSpanAnnBF,
-- ** Annotation data types used in 'GenLocated'
AnnList(..), AnnListBrackets(..),
AnnParen(..),
AnnCType(..),AnnWarningTxt(..),AnnOverlap(..),AnnAnnDecl(..),AnnPragSCC(..),
- AnnBooleanFormula(..),
NameAnn(..), NameAdornment(..),
NoEpAnns(..),
-- ** Trailing annotations in lists
TrailingAnn(..), ta_location,
addTrailingAnnToA, addTrailingCommaToN,
- addTrailingAnnToBF,
noTrailingN,
-- ** Utilities for converting between different 'GenLocated' when
@@ -430,8 +426,6 @@ emptyComments = EpaComments []
type LocatedA = GenLocated SrcSpanAnnA
type LocatedN = GenLocated SrcSpanAnnN
-type LocatedBF = GenLocated SrcSpanAnnBF
-
-- | Annotation for items appearing in a list. They can have one or
-- more trailing punctuations items, such as commas or semicolons.
type SrcSpanAnnA = EpAnn [TrailingAnn]
@@ -440,8 +434,6 @@ type SrcSpanAnnA = EpAnn [TrailingAnn]
-- on the context, such as backticks.
type SrcSpanAnnN = EpAnn NameAnn
-type SrcSpanAnnBF = EpAnn AnnBooleanFormula
-
-- | General representation of a 'GenLocated' type carrying a
-- parameterised annotation type.
type LocatedAn an = GenLocated (EpAnn an)
@@ -551,17 +543,6 @@ data AnnParen
| AnnParensHash (EpToken "(#") (EpToken "#)") -- ^ '(#', '#)'
deriving Data
--- ---------------------------------------------------------------------
--- | Exact print annotation for the 'BooleanFormula' data type.
-
-data AnnBooleanFormula
- = AnnBooleanFormula {
- abf_open :: (EpToken "("), -- ^ opening parenthesis.
- abf_close :: (EpToken ")"), -- ^ closing parenthesis.
- abf_trailing :: ![TrailingAnn] -- ^ items appearing after the
- -- item, such as '|', ','
- } deriving (Data,Eq)
-
-- ---------------------------------------------------------------------
-- Annotations for names
-- ---------------------------------------------------------------------
@@ -669,14 +650,6 @@ data AnnPragSCC
-- ---------------------------------------------------------------------
-addTrailingAnnToBF :: TrailingAnn -> EpAnnComments
- -> EpAnn AnnBooleanFormula -> EpAnn AnnBooleanFormula
-addTrailingAnnToBF t cs n = n { anns = addTrailing (anns n)
- , comments = comments n <> cs }
- where
- -- See Note [list append in addTrailing*]
- addTrailing n = n { abf_trailing = abf_trailing n ++ [t]}
-
-- | Helper function used in the parser to add a 'TrailingAnn' items
-- to an existing annotation.
addTrailingAnnToA :: TrailingAnn -> EpAnnComments
@@ -1030,9 +1003,6 @@ instance (NoAnn ann) => NoAnn (EpAnn ann) where
instance NoAnn NoEpAnns where
noAnn = NoEpAnns
-instance NoAnn AnnBooleanFormula where
- noAnn = AnnBooleanFormula noAnn noAnn []
-
instance NoAnn AnnList where
noAnn = AnnList Nothing ListNone noAnn
=====================================
testsuite/driver/perf_notes.py
=====================================
@@ -83,9 +83,13 @@ PerfStat = NamedTuple('PerfStat', [('test_env', TestEnv),
('metric', MetricName),
('value', float)])
-# A baseline recovered form stored metrics.
-Baseline = NamedTuple('Baseline', [('perfStat', PerfStat),
- ('commit', GitHash)])
+# A test's metric recovered from a commit's git note: the raw sample values
+# recorded there, and a PerfStat whose value is their mean. Serves as the
+# baseline when comparing a test run against an earlier commit.
+class CommitMetric(NamedTuple):
+ perfStat: PerfStat
+ commit: GitHash
+ samples: List[float]
# The type of exceptions which are thrown when computing the current stat value
# fails.
@@ -460,10 +464,10 @@ def get_allowed_changes(baseline_ref: Optional[GitRef]) -> Dict[TestName, List[A
else:
return get_allowed_perf_changes()
-# Cache of baseline values. This is a dict of dicts indexed on:
-# (useCiNamespace, commit) -> (test_env, test, metric, way) -> baseline
-# (bool , str ) -> (str , str , str , str) -> float
-_commit_metric_cache = {} # type: ignore
+# Cache of commit metrics.
+_commit_metric_cache: Dict[Tuple[NoteNamespace, GitHash],
+ Dict[Tuple[TestEnv, TestName, MetricName, WayName],
+ CommitMetric]] = {}
# Get the baseline of a test at a given commit. This is the expected value
# *before* the commit is applied (i.e. on the parent commit).
@@ -477,7 +481,7 @@ _commit_metric_cache = {} # type: ignore
# instead when looking for ci results)
# metric: str - test metric
# way: str - test way
-# returns: the Baseline or None if no metric was found within
+# returns: the baseline CommitMetric or None if no metric was found within
# BaselineSearchDepth commits and since the last expected change
# (ignoring any expected change in the given commit).
def baseline_metric(commit: GitHash,
@@ -486,7 +490,7 @@ def baseline_metric(commit: GitHash,
metric: MetricName,
way: WayName,
baseline_ref: Optional[GitRef]
- ) -> Optional[Baseline]:
+ ) -> Optional[CommitMetric]:
# For performance reasons (in order to avoid calling commit_hash), we assert
# commit is already a commit hash.
assert is_commit_hash(commit)
@@ -502,20 +506,16 @@ def baseline_metric(commit: GitHash,
# Searches through previous commits trying local then ci for each commit in.
def find_baseline(namespace: NoteNamespace,
test_env: TestEnv
- ) -> Optional[Baseline]:
+ ) -> Optional[CommitMetric]:
if baseline_commit is not None:
- current_metric = get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
- if current_metric is not None:
- return Baseline(current_metric, baseline_commit)
- else:
- return None
+ return get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
for depth, current_commit in list(enumerate(commit_hashes)):
if current_commit == commit: continue
# Check for a metric on this commit.
current_metric = get_commit_metric(namespace, current_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric, current_commit)
+ return current_metric
# Stop if there is an expected change at this commit. In that case
# metrics on ancestor commits will not be a valid baseline.
@@ -527,7 +527,7 @@ def baseline_metric(commit: GitHash,
# Test environment to use when comparing against CI namespace
ci_test_env = best_fit_ci_test_env()
- baseline = find_baseline(LocalNamespace, test_env) # type: Optional[Baseline]
+ baseline = find_baseline(LocalNamespace, test_env) # type: Optional[CommitMetric]
if baseline is None and ci_test_env is not None:
baseline = find_baseline(CiNamespace, ci_test_env)
@@ -545,23 +545,23 @@ def get_commit_metric_value_str_or_none(gitNoteRef,
result = get_commit_metric(gitNoteRef, commit, test_env, name, metric, way)
if result is None:
return None
- return str(result.value)
+ return str(result.perfStat.value)
-# gets the average commit metric from git notes.
+# gets the commit metric (average and raw samples) from git notes.
# gitNoteRef: git notes ref space e.g. "perf" or "ci/perf"
# ref: git commit
# test_env: test environment
# name: test name
# metric: test metric
# way: test way
-# returns: PerfStat | None if stats don't exist for the given input
+# returns: CommitMetric | None if stats don't exist for the given input
def get_commit_metric(gitNoteRef,
ref: Union[GitRef, GitHash],
test_env: TestEnv,
name: TestName,
metric: MetricName,
way: WayName
- ) -> Optional[PerfStat]:
+ ) -> Optional[CommitMetric]:
global _commit_metric_cache
assert test_env != None
commit = commit_hash(ref)
@@ -573,9 +573,9 @@ def get_commit_metric(gitNoteRef,
return _commit_metric_cache[cacheKeyA].get(cacheKeyB)
# Cache miss.
- # Calculate baselines from the current commit's git note.
+ # Calculate metrics from the current commit's git note.
# Note that the git note may contain data for other tests. All tests'
- # baselines will be collected and cached for future use.
+ # metrics will be collected and cached for future use.
allCommitMetrics = get_perf_stats(ref, gitNoteRef)
# Collect recorded values by cacheKeyB.
@@ -586,22 +586,32 @@ def get_commit_metric(gitNoteRef,
currentValues = values_by_cache_key_b.setdefault(currentCacheKey, [])
currentValues.append(float(perfStat.value))
- # Calculate and baseline (average of values) by cacheKeyB.
- baseline_by_cache_key_b = {}
+ # Calculate the metric (average of values, plus the values themselves)
+ # by cacheKeyB.
+ metric_by_cache_key_b = {}
for currentCacheKey, currentValues in values_by_cache_key_b.items():
- baseline_by_cache_key_b[currentCacheKey] = PerfStat( \
- currentCacheKey[0],
- currentCacheKey[1],
- currentCacheKey[3],
- currentCacheKey[2],
- sum(currentValues) / len(currentValues))
-
- # Save baselines to the cache.
- _commit_metric_cache[cacheKeyA] = baseline_by_cache_key_b
- return baseline_by_cache_key_b.get(cacheKeyB)
+ metric_by_cache_key_b[currentCacheKey] = CommitMetric(
+ PerfStat(
+ currentCacheKey[0],
+ currentCacheKey[1],
+ currentCacheKey[3],
+ currentCacheKey[2],
+ sum(currentValues) / len(currentValues)),
+ commit,
+ currentValues)
+
+ # Save metrics to the cache.
+ _commit_metric_cache[cacheKeyA] = metric_by_cache_key_b
+ return metric_by_cache_key_b.get(cacheKeyB)
+
+def format_sample(s: float) -> str:
+ return str(int(s)) if s == int(s) else str(s)
+
+def format_samples(samples: List[float]) -> str:
+ return ', '.join(format_sample(s) for s in samples)
def check_stats_change(actual: PerfStat,
- baseline: Baseline,
+ baseline: CommitMetric,
acceptance_window: MetricAcceptanceWindow,
allowed_perf_changes: Dict[TestName, List[AllowedPerfChange]] = {},
force_print = False
@@ -611,8 +621,8 @@ def check_stats_change(actual: PerfStat,
Parameters:
actual: the PerfStat with actual value
- baseline: the expected Baseline value (this should generally be derived
- from baseline_metric())
+ baseline: the CommitMetric to compare against (this should generally be
+ derived from baseline_metric())
acceptance_window: allowed deviation of the actual value from the expected
value.
allowed_perf_changes: allowed changes in stats. This is a dictionary as
@@ -654,9 +664,17 @@ def check_stats_change(actual: PerfStat,
' baseline @ %s' % baseline.commit
print(actual.metric, error + ':')
dev = 100.0 if expected_val == 0 else round(((float(actual.value) * 100) / int(expected_val)) - 100, 1)
+ # Show the sample spread so unreliable baselines become visible (#27602).
+ if len(baseline.samples) > 1:
+ samples_note = ('; baseline is mean of %d samples spanning %s..%s'
+ % (len(baseline.samples),
+ format_sample(min(baseline.samples)),
+ format_sample(max(baseline.samples))))
+ else:
+ samples_note = ''
change_line = (f'{actual.metric} {change.value} from {baseline.perfStat.test_env} '
f'baseline @ {baseline.commit[:7]}: {expected_val} -> {actual.value} '
- f'({dev:+g}%, allowed {acceptance_window.describe()})')
+ f'({dev:+g}%, allowed {acceptance_window.describe()}{samples_note})')
result = failBecause('stat ' + change_line, tag='stat')
if not change_allowed or force_print:
@@ -666,6 +684,10 @@ def check_stats_change(actual: PerfStat,
print(descr, str(val).rjust(length), extra)
display(' Expected ' + full_name + ' ' + actual.metric + ':', expected_val, acceptance_window.describe())
+ if len(baseline.samples) > 1:
+ display(' Samples ' + full_name + ' ' + actual.metric + ':',
+ len(baseline.samples),
+ '(' + format_samples(baseline.samples) + ')')
display(' Lower bound ' + full_name + ' ' + actual.metric + ':', lowerBound, '')
display(' Upper bound ' + full_name + ' ' + actual.metric + ':', upperBound, '')
display(' Actual ' + full_name + ' ' + actual.metric + ':', actual.value, '')
@@ -866,7 +888,7 @@ def main() -> None:
# HEAD~2 21234 21234
# HEAD~3 20000 20000
def strMetric(x):
- return '{:.2f}'.format(x.value) if x != None else ""
+ return '{:.2f}'.format(x.perfStat.value) if x != None else ""
# Data is in column major format, so transpose and pass to print_table.
T = TypeVar('T')
def transpose(xss: List[List[T]]) -> List[List[T]]:
=====================================
testsuite/driver/testglobals.py
=====================================
@@ -4,7 +4,7 @@
from my_typing import *
from pathlib import Path
-from perf_notes import MetricChange, PerfStat, Baseline, GitRef
+from perf_notes import MetricChange, PerfStat, CommitMetric, GitRef
from datetime import datetime
# -----------------------------------------------------------------------------
@@ -312,7 +312,7 @@ class TestResult:
PerfMetric = NamedTuple('PerfMetric',
[('change', MetricChange),
('stat', PerfStat),
- ('baseline', Optional[Baseline]) ])
+ ('baseline', Optional[CommitMetric]) ])
class TestRun:
def __init__(self) -> None:
=====================================
utils/check-exact/ExactPrint.hs
=====================================
@@ -353,10 +353,6 @@ instance HasTrailing (EpToken "{", EpToken "}") where
trailing _ = []
setTrailing a _ = a
-instance HasTrailing (AnnBooleanFormula) where
- trailing bf = abf_trailing bf
- setTrailing a ts = a { abf_trailing = ts }
-
-- ---------------------------------------------------------------------
fromAnn' :: (HasEntry a) => a -> Entry
@@ -2731,9 +2727,11 @@ instance ExactPrint (BF.BooleanFormula GhcPs) where
exact (BF.And e ls) = do
ls' <- mapM markAnnotated ls
return (BF.And e ls')
- exact (BF.Parens e x) = do
+ exact (BF.Parens (o,c) x) = do
+ o' <- markEpToken o
x' <- markAnnotated x
- return (BF.Parens e x')
+ c' <- markEpToken c
+ return (BF.Parens (o',c') x')
-- ---------------------------------------------------------------------
@@ -4474,17 +4472,6 @@ instance ExactPrint [LocatedA (StmtLR GhcPs GhcPs (LocatedA (HsCmd GhcPs)))] whe
stmts' <- markAnnotated stmts
return stmts'
-instance ExactPrint (LocatedBF (BF.BooleanFormula GhcPs)) where
- getAnnotationEntry = entryFromLocatedA
- setAnnotationAnchor = setAnchorAn
- exact (L an bf) = do
- debugM $ "LocatedCB [LBooleanFormula"
- let (AnnBooleanFormula op cp ta) = anns an
- op' <- markEpToken op
- bf' <- markAnnotated bf
- cp' <- markEpToken cp
- return (L (an {anns = AnnBooleanFormula op' cp' ta}) bf')
-
instance ExactPrint [Located HsDocStringChunk] where
getAnnotationEntry _ = NoEntryVal
setAnnotationAnchor a _ _ _ = a
=====================================
utils/haddock/haddock-api/src/Haddock/Types.hs
=====================================
@@ -834,7 +834,7 @@ type instance Anno (HsDecl DocNameI) = SrcSpanAnnA
type instance Anno (FamilyResultSig DocNameI) = EpAnn NoEpAnns
type instance Anno (HsOuterTyVarBndrs Specificity DocNameI) = SrcSpanAnnA
type instance Anno (HsSigType DocNameI) = SrcSpanAnnA
-type instance Anno (BooleanFormula DocNameI) = SrcSpanAnnBF
+type instance Anno (BooleanFormula DocNameI) = SrcSpanAnnA
type instance Anno (OverlapMode DocNameI) = SrcSpanAnnA
type instance Anno (CType DocNameI) = SrcSpanAnnA
type instance Anno (Header DocNameI) = SrcSpanAnnA
@@ -1041,7 +1041,7 @@ type instance XXHsContextDetails DocNameI = DataConCantHappen
type instance XBFVar DocNameI = NoExtField
type instance XBFAnd DocNameI = NoExtField
type instance XBFOr DocNameI = NoExtField
-type instance XBFParens DocNameI = NoExtField
+type instance XBFParens DocNameI = (EpToken "(", EpToken ")")
type instance XXBooleanFormula DocNameI = DataConCantHappen
-----------------------------------------------------------------------------
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e5bba7555255766786a65148a61f5f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e5bba7555255766786a65148a61f5f…
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] 11 commits: EPA some tests for layout. WIP
by Alan Zimmerman (@alanz) 19 Aug '26
by Alan Zimmerman (@alanz) 19 Aug '26
19 Aug '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
4f559864 by Alan Zimmerman at 2026-08-19T19:26:35+01:00
EPA some tests for layout. WIP
- - - - -
2d8be72c by Alan Zimmerman at 2026-08-19T19:26:35+01:00
Exactprint layout scope plan. Do not commit
- - - - -
2543b267 by Alan Zimmerman at 2026-08-19T21:29:45+01:00
EPA: tidy up a bit. Combine somewhere else
- - - - -
ad343bd7 by Alan Zimmerman at 2026-08-19T22:08:50+01:00
EPA: Introduce LayoutFrame stacks in ExactPrint state
- - - - -
95da65b9 by Alan Zimmerman at 2026-08-19T22:10:10+01:00
Plan update DO NOT COMMIT
- - - - -
a7a02608 by Alan Zimmerman at 2026-08-19T22:10:16+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
- - - - -
0ba9d11b by Alan Zimmerman at 2026-08-19T22:10:16+01:00
EPA: HsList attempt WIP
- - - - -
0d6e519e by Alan Zimmerman at 2026-08-19T22:10:16+01:00
Enable ppr test for Haddock1. It currently fails
- - - - -
ed79f702 by Alan Zimmerman at 2026-08-19T22:10:16+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
1bb4b198 by Alan Zimmerman at 2026-08-19T22:10:16+01:00
EPA: Add an overview doc for exact printing
- - - - -
10be628d by Simon Peyton Jones at 2026-08-19T22:10:16+01:00
Added an intro section
- - - - -
44 changed files:
- ANNLIST-LAYOUT-PLAN.md
- + ExactPrint.md
- + LAYOUT-SCOPE-PLAN.md
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/DocString.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/ThToHs.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- ghc/GHCi/UI.hs
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.hs
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- + testsuite/tests/printer/Layout.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/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/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/57acfce1ee5cd3c01600c9ba6bc6f8…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/57acfce1ee5cd3c01600c9ba6bc6f8…
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/T16836-implicit-field-strictness] Add -Wimplicit-field-strictness (#16836)
by Simon Jakobi (@sjakobi) 19 Aug '26
by Simon Jakobi (@sjakobi) 19 Aug '26
19 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T16836-implicit-field-strictness at Glasgow Haskell Compiler / GHC
Commits:
1dfbf23a by Simon Jakobi at 2026-08-19T23:34:20+02:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (! or ~), so that users can insulate
themselves against changes to the strictness default, e.g. via
StrictData. It complements the LazyFieldAnnotations extension
(4762a8bf30f) from GHC proposal 752, which makes ~ annotations
available for this purpose.
One diagnostic is emitted per data declaration, grouped by
constructor. The hint deliberately names both ! and ~ without
picking one: the choice is the user's.
Newtypes and 'type data' declarations are exempt since strictness
annotations are rejected there.
Fixes #16836.
Assisted-by: Claude Fable 5
- - - - -
16 changed files:
- + changelog.d/implicit-field-strictness-warning
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- docs/users_guide/using-warnings.rst
- + testsuite/tests/warnings/should_compile/T16836a.hs
- + testsuite/tests/warnings/should_compile/T16836a.stderr
- + testsuite/tests/warnings/should_compile/T16836b.hs
- + testsuite/tests/warnings/should_compile/T16836c.hs
- + testsuite/tests/warnings/should_compile/T16836c.stderr
- testsuite/tests/warnings/should_compile/all.T
Changes:
=====================================
changelog.d/implicit-field-strictness-warning
=====================================
@@ -0,0 +1,10 @@
+section: compiler
+synopsis: Add `-Wimplicit-field-strictness`
+issues: #16836
+
+description: {
+ The new opt-in warning :ghc-flag:`-Wimplicit-field-strictness` reports
+ data constructor fields that lack an explicit strictness annotation
+ (``!`` or ``~``). Writing ``~`` requires
+ :extension:`LazyFieldAnnotations`.
+}
=====================================
compiler/GHC/Driver/Flags.hs
=====================================
@@ -1142,6 +1142,7 @@ data WarningFlag =
| Opt_WarnUnrecognisedModifiers -- ^ @since 10.0
| Opt_WarnSemaphoreOpenFailure -- Since 10.0.1
| Opt_WarnDefaultedCallStack -- ^ @since 10.2
+ | Opt_WarnImplicitFieldStrictness -- ^ @since 10.2
deriving (Eq, Ord, Show, Enum, Bounded)
-- | Return the names of a WarningFlag
@@ -1251,6 +1252,7 @@ warnFlagNames wflag = case wflag of
Opt_WarnTypeEqualityRequiresOperators -> "type-equality-requires-operators" :| []
Opt_WarnMissingRoleAnnotations -> "missing-role-annotations" :| []
Opt_WarnImplicitRhsQuantification -> "implicit-rhs-quantification" :| []
+ Opt_WarnImplicitFieldStrictness -> "implicit-field-strictness" :| []
Opt_WarnIncompleteExportWarnings -> "incomplete-export-warnings" :| []
Opt_WarnIncompleteRecordSelectors -> "incomplete-record-selectors" :| []
Opt_WarnBadlyLevelledTypes -> "badly-levelled-types" :| []
=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -2449,6 +2449,7 @@ wWarningFlagsDeps = [minBound..maxBound] >>= \x -> case x of
Opt_WarnUnrecognisedModifiers -> warnSpec x
Opt_WarnSemaphoreOpenFailure -> warnSpec x
Opt_WarnDefaultedCallStack -> warnSpec x
+ Opt_WarnImplicitFieldStrictness -> warnSpec x
warningGroupsDeps :: [(Deprecation, FlagSpec WarningGroup)]
warningGroupsDeps = map mk warningGroups
=====================================
compiler/GHC/Tc/Errors/Ppr.hs
=====================================
@@ -1384,6 +1384,24 @@ instance Diagnostic TcRnMessage where
hang (text "Missing role annotation" <> colon)
2 (text "type role" <+> ppr name <+> hsep (map ppr roles))
+ TcRnImplicitFieldStrictness _name _lazy_anns cons -> mkSimpleDecorated $
+ hang (text "Constructor fields without explicit strictness" <> colon)
+ 2 (vcat (map ppr_con cons))
+ where
+ ppr_con (con, fields) =
+ bullet <+> text "In" <+> quotes (ppr con) <> colon <+> ppr_fields fields
+ ppr_fields fields
+ | let names = concat [ns | ImplicitStrictnessRecField _ ns <- fields]
+ , not (null names)
+ = text "field" <> plural names <+> and_list (map (quotes . ppr) names)
+ | otherwise
+ = let poss = [i | ImplicitStrictnessPosField _ i <- fields]
+ in text "the" <+> and_list (map speakNth poss)
+ <+> text "field" <> plural poss
+ and_list [] = empty
+ and_list [x] = x
+ and_list xs = hsep (punctuate comma (init xs)) <+> text "and" <+> last xs
+
TcRnIllformedTypePattern p
-> mkSimpleDecorated $
hang (text "Ill-formed type pattern:") 2 (ppr p)
@@ -2693,6 +2711,8 @@ instance Diagnostic TcRnMessage where
-> ErrorWithoutFlag
TcRnMissingRoleAnnotation{}
-> WarningWithFlag Opt_WarnMissingRoleAnnotations
+ TcRnImplicitFieldStrictness{}
+ -> WarningWithFlag Opt_WarnImplicitFieldStrictness
TcRnIllegalInvisTyVarBndr{}
-> ErrorWithoutFlag
TcRnIllegalWildcardTyVarBndr{}
@@ -3428,6 +3448,12 @@ instance Diagnostic TcRnMessage where
-> noHints
TcRnMissingRoleAnnotation{}
-> noHints
+ TcRnImplicitFieldStrictness _ lazy_anns _
+ -> SuggestExplicitFieldStrictness
+ : [ useExtensionInOrderTo
+ (text "to allow" <+> quotes (char '~') <+> text "annotations")
+ LangExt.LazyFieldAnnotations
+ | not lazy_anns ]
TcRnIllegalInvisTyVarBndr{}
-> [suggestExtension LangExt.TypeAbstractions]
TcRnIllegalWildcardTyVarBndr{}
=====================================
compiler/GHC/Tc/Errors/Types.hs
=====================================
@@ -123,6 +123,7 @@ module GHC.Tc.Errors.Types (
, TypeSyntax(..)
, typeSyntaxExtension
, SuggestLinear(..)
+ , ImplicitStrictnessField(..)
-- * Errors for hs-boot and signature files
, BadBootDecls(..)
@@ -4235,6 +4236,24 @@ data TcRnMessage where
-}
TcRnMissingRoleAnnotation :: Name -> [Role] -> TcRnMessage
+
+ {-| TcRnImplicitFieldStrictness is a warning that occurs when a data
+ constructor field lacks an explicit strictness annotation (@!@ or @~@)
+
+ Controlled by flags:
+ - Wimplicit-field-strictness
+
+ Test cases:
+ T16836a, T16836b
+
+ -}
+ TcRnImplicitFieldStrictness
+ :: Name -- ^ the type constructor
+ -> Bool -- ^ whether @LazyFieldAnnotations@ is enabled
+ -> [(Name, [ImplicitStrictnessField])]
+ -- ^ per data constructor, the fields lacking annotations
+ -> TcRnMessage
+
{-| TcRnPatersonCondFailure is an error that occurs when an instance
declaration fails to conform to the Paterson conditions. Which particular condition
fails depends on the constructor of PatersonCondFailure
@@ -6399,6 +6418,14 @@ data PatSynInvalidRhsReason
| PatSynUnboundVar !Name
deriving (Generic)
+-- | A constructor field lacking an explicit strictness annotation, as
+-- reported by 'TcRnImplicitFieldStrictness'.
+data ImplicitStrictnessField
+ = -- | A record field group @x, y :: ty@ sharing one (absent) annotation
+ ImplicitStrictnessRecField SrcSpan [RdrName]
+ | -- | A positional argument (1-based index)
+ ImplicitStrictnessPosField SrcSpan Int
+
data BadFieldAnnotationReason where
{-| A lazy data type field annotation (~) was used without enabling the
extension LazyFieldAnnotations.
=====================================
compiler/GHC/Tc/TyCl.hs
=====================================
@@ -4023,8 +4023,39 @@ dataDeclChecks tc_name mctxt cons
; is_boot <- tcIsHsBootOrSig -- Are we compiling an hs-boot file?
; unless (not (null cons) || empty_data_decls || is_boot) $
addErrTc (TcRnEmptyDataDeclsDisabled tc_name)
+
+ ; warn_implicit_strictness <- woptM Opt_WarnImplicitFieldStrictness
+ ; when warn_implicit_strictness $ case cons of
+ DataTypeCons False data_cons
+ | let offenders = concatMap conImplicitStrictnessFields data_cons
+ , not (null offenders)
+ -> do { lazy_anns <- xoptM LangExt.LazyFieldAnnotations
+ ; setSrcSpan (getSrcSpan tc_name) $ addDiagnosticTc $
+ TcRnImplicitFieldStrictness tc_name lazy_anns offenders }
+ _ -> return ()
+
; return gadt_syntax }
+conImplicitStrictnessFields :: LConDecl GhcRn -> [(Name, [ImplicitStrictnessField])]
+conImplicitStrictnessFields (L _ con)
+ | null fields = []
+ | otherwise = [ (unLoc n, fields) | n <- getConNames con ]
+ where
+ fields = case con of
+ ConDeclH98 { con_args = PrefixCon _ args } -> pos_fields args
+ ConDeclH98 { con_args = InfixCon _ a1 a2 } -> pos_fields [a1, a2]
+ ConDeclH98 { con_args = RecCon _ (L _ flds) } -> rec_fields flds
+ ConDeclGADT { con_g_args = PrefixConGADT _ args } -> pos_fields args
+ ConDeclGADT { con_g_args = RecConGADT _ (L _ flds) } -> rec_fields flds
+
+ pos_fields args = [ ImplicitStrictnessPosField (getLocA (cdf_type f)) i
+ | (i, f) <- zip [1 :: Int ..] args
+ , NoSrcStrict <- [cdf_bang f] ]
+ rec_fields flds = [ ImplicitStrictnessRecField (getLocA (cdf_type spec))
+ [ rdr | L _ (FieldOcc rdr _) <- names ]
+ | L _ (HsConDeclRecField _ names spec) <- flds
+ , NoSrcStrict <- [cdf_bang spec] ]
+
-----------------------------------
data DataDeclInfo
=====================================
compiler/GHC/Types/Error/Codes.hs
=====================================
@@ -542,6 +542,7 @@ type family GhcDiagnosticCode c = n | n -> c where
GhcDiagnosticCode "TcRnNegativeNumTypeLiteral" = 93632
GhcDiagnosticCode "TcRnUnusedQuantifiedTypeVar" = 54180
GhcDiagnosticCode "TcRnMissingRoleAnnotation" = 65490
+ GhcDiagnosticCode "TcRnImplicitFieldStrictness" = 47032
GhcDiagnosticCode "TcRnUntickedPromotedThing" = 49957
GhcDiagnosticCode "TcRnIllegalBuiltinSyntax" = 39716
=====================================
compiler/GHC/Types/Hint.hs
=====================================
@@ -343,6 +343,14 @@ data GhcHint
-}
| SuggestAddStandaloneKindSignature Name
+ {-| Suggests to annotate each constructor field with explicit strictness
+ (@!@ or @~@), without picking one.
+
+ Triggered by: 'GHC.Tc.Errors.Types.TcRnImplicitFieldStrictness'
+ Test case(s): warnings/should_compile/T16836a
+ -}
+ | SuggestExplicitFieldStrictness
+
{-| Suggests the user to fill in the wildcard constraint to
disambiguate which constraint that is.
=====================================
compiler/GHC/Types/Hint/Ppr.hs
=====================================
@@ -185,6 +185,9 @@ instance Outputable GhcHint where
-> text "Use a standalone deriving declaration instead"
SuggestAddStandaloneKindSignature name
-> text "Add a standalone kind signature for" <+> quotes (ppr name)
+ SuggestExplicitFieldStrictness
+ -> text "Annotate each field with" <+> quotes (char '!')
+ <+> text "(strict) or" <+> quotes (char '~') <+> text "(lazy)"
SuggestFillInWildcardConstraint
-> text "Fill in the wildcard constraint yourself"
SuggestAppropriateTHTick ns
=====================================
docs/users_guide/using-warnings.rst
=====================================
@@ -2505,6 +2505,28 @@ of ``-W(no-)*``.
In other words the type-class role cannot be accidentally left
representational or phantom, which could affected the code correctness.
+.. ghc-flag:: -Wimplicit-field-strictness
+ :shortdesc: warn when constructor fields lack explicit strictness annotations
+ :type: dynamic
+ :reverse: -Wno-implicit-field-strictness
+ :category:
+
+ :since: 10.2.1
+ :default: off
+
+ .. index::
+ single: strictness annotations, missing
+
+ If you would like GHC to check that every data constructor field carries
+ an explicit strictness annotation — ``!`` (strict) or ``~`` (lazy) — use
+ the :ghc-flag:`-Wimplicit-field-strictness` option. It reports one warning
+ per data declaration, listing the unannotated fields of each constructor.
+ Writing ``~`` requires :extension:`LazyFieldAnnotations`.
+
+ The warning applies to ``data`` and ``data instance`` declarations,
+ including GADT syntax. Newtypes and ``type data`` declarations are exempt,
+ as strictness annotations are rejected there.
+
.. ghc-flag:: -Wimplicit-rhs-quantification
:shortdesc: warn when type variables on the RHS of a type synonym are implicitly quantified
:type: dynamic
=====================================
testsuite/tests/warnings/should_compile/T16836a.hs
=====================================
@@ -0,0 +1,37 @@
+{-# OPTIONS_GHC -Wimplicit-field-strictness #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+module T16836a where
+
+-- plain multi-constructor data
+-- warns for both constructors
+data T a = MkT a Bool
+ | MkT2 !Int a
+
+-- record with a shared field group
+-- warns for x, y and z; not for b
+data R = MkR { x, y :: Int, z :: Char, b :: !Bool }
+
+-- infix constructor
+-- warns for the first argument
+data I = Int :+: !Bool
+
+-- GADT syntax
+-- warns for the first argument
+data G a where
+ MkG :: Int -> !Bool -> G a
+
+-- GADT record syntax
+-- warns for gx
+data GR a where
+ MkGR :: { gx :: Int, gy :: !Bool } -> GR a
+
+-- data family instance
+-- warns
+data family F a
+data instance F Int = MkF Char
+
+-- fully annotated
+-- doesn't warn
+data S = MkS !Int !Bool
=====================================
testsuite/tests/warnings/should_compile/T16836a.stderr
=====================================
@@ -0,0 +1,55 @@
+T16836a.hs:9:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkT’: the first and second fields
+ • In ‘MkT2’: the second field
+ • In the data type declaration for ‘T’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
+T16836a.hs:14:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkR’: fields ‘x’, ‘y’ and ‘z’
+ • In the data type declaration for ‘R’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
+T16836a.hs:18:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘:+:’: the first field
+ • In the data type declaration for ‘I’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
+T16836a.hs:22:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkG’: the first field
+ • In the data type declaration for ‘G’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
+T16836a.hs:27:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkGR’: field ‘gx’
+ • In the data type declaration for ‘GR’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
+T16836a.hs:32:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkF’: the first field
+ • In the data family instance declaration for ‘F’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
=====================================
testsuite/tests/warnings/should_compile/T16836b.hs
=====================================
@@ -0,0 +1,25 @@
+{-# OPTIONS_GHC -Wimplicit-field-strictness #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeData #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE LazyFieldAnnotations #-}
+module T16836b where
+
+-- fully annotated declarations don't warn
+data T a = MkT ~a !Bool
+data R = MkR { x, y :: !Int, z :: ~Char }
+data G a where
+ MkG :: !Int -> ~Bool -> G a
+data family F a
+data instance F Int = MkF !Char
+
+-- newtypes can't have annotations; exempt
+newtype N = MkN Int
+
+-- 'type data' can't have annotations; exempt
+type data TD = MkTD Bool
+
+-- no fields, nothing to annotate
+data E
+data Nullary = A | B
=====================================
testsuite/tests/warnings/should_compile/T16836c.hs
=====================================
@@ -0,0 +1,6 @@
+{-# OPTIONS_GHC -Wimplicit-field-strictness #-}
+{-# LANGUAGE StrictData #-}
+module T16836c where
+
+-- unannotated fields warn under StrictData too
+data T a = MkT a !Bool ~Char
=====================================
testsuite/tests/warnings/should_compile/T16836c.stderr
=====================================
@@ -0,0 +1,6 @@
+T16836c.hs:6:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • Constructor fields without explicit strictness:
+ • In ‘MkT’: the first field
+ • In the data type declaration for ‘T’
+ Suggested fix: Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+
=====================================
testsuite/tests/warnings/should_compile/all.T
=====================================
@@ -91,3 +91,6 @@ test('T25901_imp_unused_3', [extra_files(['T25901_helper_3.hs'])], multimod_comp
test('T25901_imp_unused_4', normal, compile, ['-Wunused-imports'])
test('T25901_imp_dodgy_1', [extra_files(['T25901_helper_1.hs'])], multimod_compile, ['T25901_imp_dodgy_1', '-v0 -Wdodgy-imports'])
test('T25901_imp_dodgy_2', [extra_files(['T25901_helper_2.hs'])], multimod_compile, ['T25901_imp_dodgy_2', '-v0 -Wdodgy-imports'])
+test('T16836a', normal, compile, [''])
+test('T16836b', normal, compile, [''])
+test('T16836c', normal, compile, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1dfbf23afd2e0603e84cb889f906998…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1dfbf23afd2e0603e84cb889f906998…
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/sjakobi/T16836-implicit-field-strictness
by Simon Jakobi (@sjakobi) 19 Aug '26
by Simon Jakobi (@sjakobi) 19 Aug '26
19 Aug '26
Simon Jakobi pushed new branch wip/sjakobi/T16836-implicit-field-strictness at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/sjakobi/T16836-implicit-field…
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/jeltsch/ghc-9-14-building-base] Change tag `x86_64-windows` to `new-x86_64-windows`
by Wolfgang Jeltsch (@jeltsch) 19 Aug '26
by Wolfgang Jeltsch (@jeltsch) 19 Aug '26
19 Aug '26
Wolfgang Jeltsch pushed to branch wip/jeltsch/ghc-9-14-building-base at Glasgow Haskell Compiler / GHC
Commits:
bef3a4ff by Wolfgang Jeltsch at 2026-08-19T16:28:35+03:00
Change tag `x86_64-windows` to `new-x86_64-windows`
- - - - -
1 changed file:
- .gitlab-ci.yml
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -1193,7 +1193,7 @@ base-with-other-ghcs-windows:
dependencies: null
image: null
tags:
- - x86_64-windows
+ - new-x86_64-windows
script: bash .gitlab/base-ci.sh x86_64-unknown-mingw32 9.14.1
rules:
- *full-ci
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bef3a4ff0c12a0984a385b156009aff…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/bef3a4ff0c12a0984a385b156009aff…
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/dcoutts/capability-yield] 149 commits: Coercion optimisation: avoid double-Sym for InstCo
by Duncan Coutts (@dcoutts) 19 Aug '26
by Duncan Coutts (@dcoutts) 19 Aug '26
19 Aug '26
Duncan Coutts pushed to branch wip/dcoutts/capability-yield at Glasgow Haskell Compiler / GHC
Commits:
722236dd by sheaf at 2026-07-18T08:48:31-04:00
Coercion optimisation: avoid double-Sym for InstCo
Ticket #27374 pointed out an issue with GHC.Core.Coercion.Opt.optCoercion's
handling of InstCo: it contravened (LC2) in Note [The LiftingContext in optCoercion]
because it applied the ambient 'sym' to a coercion that was then added
to the lifting context substitution.
Fixes #27374
Co-authored-by: Simon Jakobi <simon.jakobi(a)gmail.com>
- - - - -
ff70fc75 by sheaf at 2026-07-18T08:48:31-04:00
Coercion optimisation: avoid exponential behaviour
The change to coercion optimisation of 'InstCo' in the previous commit
introduces exponential behaviour to the coercion optimiser. To avoid
this, this commit provides a way to push in 'Sym' of an already-optimised
coercion: GHC.Core.Coercion.Opt.mkDeepSymCo.
See Note [Pushing Sym without re-optimising] in GHC.Core.Coercion.Opt.
- - - - -
dfef27f0 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Move THREADED_RTS-conditional struct members to end of Capability
Accessing members of the Capability struct from CMM code rely on
accessor macros. (The macros are generated by deriveConstants).
These macros have a single definition. This means that the offsets of
all struct members must *not* vary based on THREADED_RTS vs
!THREADED_RTS. This requires that any struct members that are
conditional on THREADED_RTS must occur after the unconditional struct
members. Hence we move all the ones that are conditional on
THREADED_RTS to the end.
Add a deriveConstants entry for the iomgr member of the Capability
struct, which was the motivation for this change.
Add warning messages to help our future selves. Debugging this took me
a couple hours in gdb!
- - - - -
c254e022 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Make the IOManager API use CapIOManager rather than Capability
This makes the API somewhat more self-contained and more consistent.
Now the IOManager API and each of the backends takes just the I/O
manager structure. Previously we had a bit of a mixture, depending on
whether the function needed access to the Capability or just the
CapIOManager.
We still need access to the cap, so we introduce a back reference to
reach the capability, via iomgr->cap.
Convert all uses in select and poll backends, but not win32 ones.
Convert callers in the scheduler and elsewhere.
Also convert the three CMM primops that call IOManager APIs. They just
need to use Capability_iomgr(MyCapability()).
- - - - -
4f3d8f31 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Split posix/MIO.c out of posix/Signals.c
The MIO I/O manager was secretly living inside the Signals file.
Now it gets its own file, like any other self-respecting I/O manager.
- - - - -
52ce04a9 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Rationalise some scheduler run queue utilities
Move them all to the same place in the file.
Make some static that were used only internally.
Also remove a redundant assignment after calling truncateRunQueue that
is already done within truncateRunQueue.
- - - - -
75bbdebc by Duncan Coutts at 2026-07-18T08:49:12-04:00
Rename initIOManager{AfterFork} to {re}startIOManager
These are more accurate names, since these actions happen after
initialisation and are really about starting (or restarting) background
threads.
- - - - -
724c0517 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Free per-cap I/O managers during shutdown and forkProcess
Historically this was not strictly necessary. The select and win32
legacy I/O managers did not maintain any dynamically allocated
resources. The new poll one does (an auxillary table), and so this
should be freed.
After forkProcess, all threads get deleted. This includes threads
waiting on I/O or timers. So as of this patch, resetting the I/O
manager is just about tidying things up. For example, for the poll
I/O manager this will reset the size of the AIOP table (which
otherwise grows but never shrinks).
In future however the re-initialising will become neeecessary for
functionality, since some I/O managers will need to re-initialise
wakeup fds that are set CLOEXEC.
- - - - -
c007d122 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a TODO to the MIO I/O manager
The direction of travel is to make I/O managers per-capability and have
all their state live in the struct CapIOManager. The MIO I/O manager
however still has a number of global variables.
It's not obvious how handle these globals however.
- - - - -
b65ab7b3 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a FIXME note in the Poll I/O manager
- - - - -
daf2bd6f by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add missing updateRemembSetPushClosure in poll I/O manager
For the non-moving GC.
- - - - -
e33ca830 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Minor doc improvement to struct StgAsyncIOOp member outcome
Mention the enumeration names, as well as their numeric values. The rest
of the code uses the enum names.
- - - - -
4edd2579 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Minor doc improvements for StgTSOBlockInfo
Clarify that certain union members are used only by certain legacy
I/O managers. Hopefully we will be able to remove these at some point.
- - - - -
536bedbb by Duncan Coutts at 2026-07-18T08:49:12-04:00
Avoid exporting various win32-specific rts symbols
The BeginPrivate.h / EndPrivate.h scheme works perfectly well on
Windows, but all of the rts/win32/*.h files were not using it.
- - - - -
8139b5ac by Duncan Coutts at 2026-07-18T08:49:12-04:00
Remove wakeupIOManager, ioManagerWakeup and setIOManagerWakeupFd
We no longer need wakeupIOManager for the threaded RTS case, so we can
remove it and the bits only needed to support it. This includes the
pipe/eventfd fd shared between the RTS and the in-library I/O manager
used for waking up the I/O manager thread. The pipe/eventfd still
exists, but it no longer has to be communicated to the RTS, since the
RTS no longer needs to use it.
So we remove the RTS API export setIOManagerWakeupFd, and remove uses of
it within the I/O managers in ghc-internal.
- - - - -
74fe7c66 by Duncan Coutts at 2026-07-18T08:49:12-04:00
Add a new interruptIOManager API for the I/O managers
It will be used to interrupt awaitCompletedTimeoutsOrIO. Also update the
return type and docs for awaitCompletedTimeoutsOrIO to have it return
false when it gets interrupted, and have no useful post condition in
that case.
- - - - -
38792843 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for select I/O manager
Uses the FdWakup mechanism.
- - - - -
2f3b00aa by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for poll I/O manager
Uses the FdWakup mechanism.
A quirk we have to cope with is that we now need to poll one more fd --
the wakeup_fd_r -- but this fd has no corresponding entry in the
aiop_table. This is awkward since we have set up our aiop_poll_table to
be an auxilliary table with matching indicies.
The solution this patch uses (and described in the comments) is to have
two tables: struct pollfd *aiop_poll_table, *full_poll_table;
and to have the aiop_poll_table alias the tail of the full_poll_table.
The head entry in the full_poll_table is the extra fd. So we poll the
full_poll_table, while the aiop_poll_table still has matching indicies
with the aiop_table.
Hurrah for C aliasing rules.
- - - - -
cee50131 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Add interruptIOManager support for win32 legacy I/O manager
And remove unused related helper resetAbandonRequestWait. It is not
called because the event is created in auto-reset mode, so never needs
to be reset manually.
- - - - -
cf453143 by Duncan Coutts at 2026-07-18T08:49:13-04:00
Note lack of interruptIOManager support for WinIO I/O manager
Though there's a plausible design, we can't sanely test it at the moment
due to related WinIO bugs. Filed as issue #27403.
- - - - -
1b74a0ad by Duncan Coutts at 2026-07-18T08:49:13-04:00
Be more explicit about enum IOReadOrWrite values, and type within cmm
Belt and braces.
- - - - -
b388d093 by Brian McKenna at 2026-07-18T17:51:50-04:00
Ignore ticks in the pattern-match term oracle
The term-oracle in the pattern-match checker is keyed by a canonical
form of the scrutinee, computed by `makeDictsCoherent`. That canonical
form was tick-sensitive: two occurrences of an otherwise identical
expression that happened to carry different ticks were treated as
distinct values, breaking long-distance information.
This shows up in practice under `-finfo-table-map`, because the
desugarer wraps every record-selector use site in a `SourceNote`
carrying that site's span. For example:
data Box = Box { unBox :: Maybe Int }
f b = case unBox b of
Nothing -> 0
Just _ -> let Just x = unBox b in x
The two `unBox b` expressionss carry different SourceNote spans, the
pattern-match checker sees them as different, the long-distance
information from the outer `Just _` branch never reaches the
let-pattern, and `Just x = unBox b` is wrongly reported as
non-exhaustive.
We now strip all ticks in `makeDictsCoherent`. This is documented as
Wrinkle (UD1) of Note [Unique dictionaries in the TmOracle CoreMap].
Fixes #27314
- - - - -
c23e1acb by Mrjtjmn at 2026-07-18T17:52:45-04:00
Add explanations for unsolved Typeable constraints
This commit adds explanations for unsolved 'Typeable' constraints.
GHC will now provide additional explanations for an unsolved constraint
of the form 'Typeable ty', explain why GHC did not solve Typeable constraint.
e.g.:
- 'ty' is a polymorphic type (e.g. forall a. a -> a)
- 'ty' is a qualified type (e.g. Eq Int => Int)
- 'ty' is an unboxed sum type
- 'ty' is an unreduced type family application
- 'ty' whose kind is not typeable
Fixes #26532
- - - - -
cbef021e by Artem Pelenitsyn at 2026-07-19T07:49:55-04:00
ghc-internal: Lock.hs: fix typo and indentation
- - - - -
42918646 by Duncan Coutts at 2026-07-19T07:50:36-04:00
Fix failing test GcStaticPointers for non-moving GC
Minor mistake in asserting something before checking for that same
thing.
Specifically, Bdescr asserts HEAP_ALLOCED_GC, but Bdescr was being used
prior to a guard that checks HEAP_ALLOCED_GC. The solution is just to
move the use of Bdescr after the guard.
Thanks to Simon Jakobi for identifying the problem.
- - - - -
c2f6dcd4 by Sasha Bogicevic at 2026-07-20T10:31:56+02:00
Improve error messages for invalid record wildcards
Record wildcard hints are now shown in more contexts and include
constructor arity; matching with `..` on a fieldless constructor
now produces a dedicated error message.
Fixes #21101
- - - - -
4c02e76b by Duncan Coutts at 2026-07-21T10:37:21-04:00
Mark test T27105 as fragile, citing issue #27522
Scheduler fairness is fine, except when it isn't. And it isn't on CI
machines surprisingly often! See the issue for details.
- - - - -
43dd2b15 by Recursion Ninja at 2026-07-21T17:09:53-04:00
Resolving many TTG related orphan type-class instances
This is part a technical debt removal effort made possible now
that separating out the AST via TTG has come to a close.
As the AST in 'L.H.S' has been incrementally separated from the GHC internals,
there are many accumulated orphan instance of 'Binary', 'NFData', 'Outputable',
and 'Uniquable'. The orphan instance of data-types from within 'L.H.S' have had
their orphan instances moved to either:
1. The module which defines the data-type
2. The module which defines the type-class;
i.e. moving an orphan 'Binary' instance to 'GHC.Utils.Binary'
Orphan instances resolved (37):
| Data-type | Resolved instance(s) | Former orphan module(s) |
| -------------------- | -------------------------- | ------------------------- |
| Role | Binary, NFData, Outputable | GHC.Core.Coercion.Axiom |
| SrcStrictness | Binary, NFData, Outputable | GHC.Core.DataCon |
| SrcUnpackedness | Binary, NFData, Outputable | GHC.Core.DataCon |
| Fixity | Binary, Outputable | GHC.Hs.Basic |
| FixityDirection | Binary, Outputable | GHC.Hs.Basic |
| LexicalFixity | Outputable | GHC.Hs.Basic |
| CCallTarget | NFData | GHC.Hs.Decls.Foreign |
| CType | NFData | GHC.Hs.Decls.Foreign |
| Header | NFData | GHC.Hs.Decls.Foreign |
| OverlapMode | Binary, NFData | GHC.Hs.Decls.Overlap |
| WithHsDocIdentifiers | NFData, Outputable | GHC.Hs.Doc |
| HsDocString | NFData | GHC.Hs.DocString |
| HsDocStringChunk | Binary, Outputable | GHC.Hs.DocString |
| HsDocStringDecorator | Binary, Outputable | GHC.Hs.DocString |
| NamespaceSpecifier | Outputable | GHC.Hs.ImpExp |
| ForAllTyFlag | Binary, NFData, Outputable | GHC.Hs.Specificity |
| Specificity | Binary, NFData | GHC.Hs.Specificity |
| PromotionFlag | Binary, Outputable | GHC.Types.Basic |
| FieldLabelString | Outputable, Uniquable | GHC.Types.FieldLabel |
| InlinePragma | Binary | GHC.Types.InlinePragma |
-------------------------
Metric Decrease:
hard_hole_fits
-------------------------
Closes #21262, #27469
- - - - -
ab9ab895 by Cheng Shao at 2026-07-21T17:10:53-04:00
rts: always use StgInt to represent cost center id
Currently cost center id is modeled as `Int` and it should be `StgInt`
uniformly in the RTS, hence this patch. Fixes #27524.
- - - - -
94d8f83b by Cheng Shao at 2026-07-22T11:30:40-04:00
hadrian: clean up stale cabal package flags in the tree
This patch cleans up stale cabal package flags in the tree and related
hadrian/autoconf logic. Closes #27474.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
0bf1d8c9 by Sasha Bogicevic at 2026-07-22T11:31:21-04:00
parser: don't suggest ImportQualifiedPost when it is already enabled
-Wprepositive-qualified-module unconditionally attached a hint to
enable ImportQualifiedPost, even when the extension was already on
(as it is by default under GHC2021). Record the extension's state in
the PsWarnImportPreQualified diagnostic and drop the hint when it is
already enabled.
Fixes #27380
- - - - -
700a1dd1 by Simon Jakobi at 2026-07-23T11:21:20-04:00
ci: Reduce lint job setup costs
Avoid fetching unnecessary history and submodules for lightweight lint
jobs. Run changelog validation without Hadrian.
Because the lint-author job is now based on the .lint template directly,
we enhance it to allow Git to read from the runner-owned checkouts,
In the previously used .lint-params template, this permissions issue was
addressed via `chown`.
Closes #27521.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
26a44fb0 by ARATA Mizuki at 2026-07-23T11:22:10-04:00
testsuite: Fix memory issues of doublex2_* and simd010
doublex2_* had reads from uninitialized memory.
simd010 had out-of-bounds array access.
Fixes #27544
- - - - -
4d798b17 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Eliminate STM_AWOKEN
It was used as nullary closure for the block_info.closure in the case of
a thread being awoken after an STM transaction.
However, while it was written, it was never read, so contributed nothing
to the behaviour. Furthermore, in the only place it was set (in
tryWakeupThread) the why_blocked was immediately overwritten by the
NotBlocked status, and the block_info was updated accordingly (by
appendToRunQueue).
So it didn't even serve a purpose of clarifying an intermediate state,
there really was no such intermediate state.
Cleaning this up will allow the BlockedOnSTM case to follow the same
pattern as the other why_blocked cases that do not use the block_info,
and in turn this reduces the number of different categories.
- - - - -
e1cece79 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Document that eventlog thread stop code ThreadBlocked is no longer used
It has not been used since GHC 7.0.x (2011). In 7.2 all the BlockedOn*
codes were added, and these were and are used instead of ThreadBlocked.
- - - - -
795db115 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Add a proper mapping to eventlog external thread stop status
That is the mapping from rts-internal codes, to the coes used in the
status field in the eventlog EVENT_STOP_THREAD event.
See issue #9003 for what goes wrong when we mess this up. In that
ticket, people note that we should really not require the internal
tso->why_blocked codes to leak into the external eventlog thread stop
codes. The same principle applies to the StgThreadReturnCode.
This change properly separates them, and explicitly maps between them
using a pair of (compact, constant) tables. These tables are pretty
small (with no alignment constraints) and will soon shrink so it seems
a sensible tradeoff.
We also introduce and use proper EVENT_STOP_THREAD constants in the
event log format header. Previously there was not specification in the
code for these (only in the docs): the values were encoded into the
conversion code.
This will allow us to renumber the internal why_blockd codes without
breaking the eventlog output.
- - - - -
6f1c8efa by Duncan Coutts at 2026-07-23T17:26:18-04:00
Remove unused tso->block_info.wakeup member
Presumably it was used once, but not now.
- - - - -
740b88a9 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Document StgTSOBlockInfo to say what cases use what members
In principle, tso->why_blocked is the tag for the StgTSOBlockInfo union,
so we should be able to say for each union member the why_blocked cases
that use that member.
- - - - -
5b92eae2 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Add a tso->block_info.mvar member and use it
in preference to the generic block_info.closure union member, with
casts.
The plan is that when we know what case we're in (via tso->why_blocked)
then we can always access the correct union member, and so we will only
need to access block_info.closure for generic cases where we don't know
or don't care.
- - - - -
d931715f by Duncan Coutts at 2026-07-23T17:26:18-04:00
Add a tso->block_info.unused member and use it
in preference to the generic block_info.closure union member, with
casts.
The plan is that when we know what case we're in (via tso->why_blocked)
then we can always access the correct union member, and so we will only
need to access block_info.closure for generic cases where we don't know
or don't care.
- - - - -
47e28ebb by Duncan Coutts at 2026-07-23T17:26:18-04:00
Avoid storing to tso->block_info.closure
In one case we can use a specific union member (.prev) instead. In
several cases the stores were in fact redundant because of subsequent
overwrites.
In scavengeTSO we replace setting tso->block_info.closure to a valid
closure, with an assertion that the block_info.unused is already set to
END_TSO_QUEUE which is a valid (static) closure.
- - - - -
96e4749d by Duncan Coutts at 2026-07-23T17:26:18-04:00
Renumber the tso->why_blocked constants
We can do this now because we have separated the internal values from
the external ones used in the eventlog.
This lets us put them back into a deliberate order and consolodate some
gaps.
More importantly, it is a prepation for a slightly more sophisticated
encoding.
- - - - -
8f62661c by Duncan Coutts at 2026-07-23T17:26:18-04:00
Define constants for the existing stg_threadStatuszh return codes
The stg_threadStatuszh reuses the internal tso->why_blocked codes but
also extends them with a couple previously magic values. This is awkward
since we need to know what those magic values are so we don't
accidentally use those values to mean something else. By pulling a
definition up to where the why_blocked codes are defined we will be able
to avoid mistakenly assining those codes some meaning (or just changing
the BlockedThreadComplete, BlockedThreadKilled code if necessary).
- - - - -
42c69ae2 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Extend the tso->why_blocked encoding to indicate block_info closures
We use some bit tricks to cheaply and generically test if a
tso->why_blocked tag implies that the corresponding tso->block_info will
contain a non-trivial valid closure (i.e. not just block_info.unused set
to END_TSO_QUEUE).
In particular we arrange for most why_blocked values to naturally have a
distinguishing bit, but for the BlockedOn{Read,Write,Delay} cases, they
can come in either non-closure or closure forms. We allow an additional
bit to distinguish these cases. The non-closure forms are only from
legacy I/O managers: select and win32-legacy. So this extra bit
mechanism will be able to be retired once the legacy I/O managers are
themselves retired.
This means in a few places we need to untag the why_blocked value before
inspecting it, but in most places we do not.
- - - - -
7c64632b by Duncan Coutts at 2026-07-23T17:26:18-04:00
Use BlockInfoForceNonClosure in the select I/O manager
- - - - -
8fd7104a by Duncan Coutts at 2026-07-23T17:26:18-04:00
Use BlockInfoForceNonClosure in the win32-legacy I/O manager
for the BlockedOn{Read,Write} since these use the non-heap allocated
StgAsyncIOResult.
- - - - -
e0da603b by Duncan Coutts at 2026-07-23T17:26:18-04:00
Enforce the why_blocked and block_info rules in checkTSO
We now check the cases wher IsBlockInfoClosure should hold, the cases
that are supposed to use block_info.unused == END_TSO_QUEUE, and which
cases are allowed to use BlockInfoForceNonClosure.
This partially enforces the use of why_blocked as a tag for the
block_info union. We could be stricter and check for the correct
expected info table for the closure cases.
- - - - -
1dd0f381 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Use IsBlockInfoClosure to simplify several tests
In GC and generic traversal we need to know if we should look at the
block_info.closure or not. Now we can do just that using a cheap bit
test on the why_blocked tag.
This fixes issue 26717, where the problem was that some GC modes did not
know when to look at block_info.closure, because the poll I/O manager
uses a closure for BlockedOn{Read,Write} while the select I/O manager
uses a non-closure. Now this information is in the why_blocked tag
itself.
- - - - -
7a00ffbc by Duncan Coutts at 2026-07-23T17:26:18-04:00
Remove the now-unused scavengeTSOIOManager
The GC no longer has to delegate to the I/O manager, since it can use
IsBlockInfoClosure to decide things itself.
- - - - -
522a481f by Duncan Coutts at 2026-07-23T17:26:18-04:00
Remove duplicate assertion
- - - - -
0874d965 by Duncan Coutts at 2026-07-23T17:26:18-04:00
Follow atomic access rules more consistently for tso->why_blocked
The rule is this:
store block_info *before* why_blocked
store why_blocked using store release
load why_blocked using load acquire
load block_info *after* why_blocked
This is a an atomic store release / load acquire pair and (if the reads
are in a separate thread to the writes, and the read receives the value
stored) then this guarantees a full "happens before" relationship of
these stores and loads.
In some cases, we do not need a full load acquire, because we don't read
the block_info at all and so do not need any ordering. In this case we
just need an atomic relaxed load.
This was being followed in most places, but not all. If there's good
reason in any case that we don't need atomic access, then we should
document that in a comment. In the absence of that I think it's easier
to follow the rule everywhere.
- - - - -
8f0bdbe1 by Duncan Coutts at 2026-07-23T17:26:19-04:00
Add a changelog entry
- - - - -
4fdfe757 by Alan Zimmerman at 2026-07-23T17:27:06-04:00
EPA: Keep decls together in ClassDecl
Similar to 1718230f4d3d19d8c49c0e5d496cb0fb6f399528 for HsValBindsLR,
this commit updates ClassDecl so that it no longer splits out the
assorted `LHsDecl GhcPs` until the renamer.
It does this by inserting a type family (separate from the classic TTG one) for this.
So
data TyClDecl
...
| ClassDecl {
...
tcdDecls :: XClassDecls pass
with
type instance XClassDecls GhcPs = [LHsDecl GhcPs]
type instance XClassDecls GhcRn = ClassDeclX GhcRn
type instance XClassDecls GhcTc = ClassDeclX GhcTc
data ClassDeclX pass
= ClassDeclX { tcdSigs :: [LSig pass], -- ^ Methods' signatures
tcdMeths :: LHsBinds pass, -- ^ Default methods
tcdATs :: [LFamilyDecl pass], -- ^ Associated types;
tcdATDefs :: [LTyFamDefltDecl pass], -- ^ Associated type defaults
tcdDocs :: [LDocDecl pass] -- ^ Haddock docs
}
- - - - -
f586c885 by Simon Jakobi at 2026-07-24T18:05:00-04:00
ci: Use shallow submodule clones by default
Limit submodule clones to depth one to reduce CI checkout costs. Keep
fetching full submodule history for the submodule lint jobs, which
inspect commits across a range.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
306120d2 by Duncan Coutts at 2026-07-24T18:05:43-04:00
Fix flaky test T3994 on FreeBSD
On current FreeBSD versions, calling getpgid on a zombie process fails.
In T3994, if we're really unlucky with delays and scheduling then we can
end up in exactly that situation.
Just catch that specific exception and ignore it. It's rare, and not our
fault.
- - - - -
7b116a0b by Cheng Shao at 2026-07-24T18:06:24-04:00
ci: add missing workaround for docker permissions in lint jobs
Some lint jobs use ci-images with default user `ghc`, and the gitlab
ci docker executor requires the `sudo chown` workaround to fix
workspace directory permission issue. This patch adds the missing
workarounds for the lint jobs. Fixes #27554.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
815149f3 by Andrzej Rybczak at 2026-07-25T15:06:43+00:00
Add -Wdefaulted-callstack
Adds a new warning, -Wdefaulted-callstack, which warns when an implicit
CallStack parameter is defaulted to the empty stack. In particular, this
includes call sites where a function with a HasCallStack constraint is called
from a definition that does *not* provide one. At such call sites the call stack
is cut off and does not include the enclosing definition's callers, which can be
a source of surprise if the user wants complete call stacks.
Closes #27077.
- - - - -
f6f2343f by Zubin Duggal at 2026-07-25T17:40:51-04:00
UniqueDFM: alter should preserve insertion order
Before it always inserting new elements at the end.
This is problematic because instances get inserted into the map with
`alterF`, which can change ordering of how instances are printed
with `:info` depending on the order in which we consult interfaces
I expect `alter id k = id` and `alter (fmap f) k = adjust f k`. Moving keys to
the end breaks that (`adjust` already preserves position).
Fixes #27532
- - - - -
5d0ab71a by fendor at 2026-07-27T17:31:05-04:00
Introduce global unit database cache
As a first step for better sharing of `UnitInfo` across `UnitEnv`,
we introduce a new datatype called `ExternalUnitDatabases`.
It primarily serves as an in-memory representation of *all*
`UnitDatabase`s across `UnitEnv`. This means, if multiple `HomeUnitEnv`s
depend on the same database, one way or another, we make sure that we
don't parse from disk every time.
Instead, we store the in-memory representation in `ExternalUnitDatabases`.
`ExternalUnitDatabaseCache` is the equivalent of `ExternalUnitState` in
the `UnitEnv`. It is a mutable variable wrapping `ExternalUnitDatabases`.
The mutable `ExternalUnitDatabaseCache` is used in `initUnits` to make
sure we don't parse the same unit database multiple times.
Almost by accident, we change the semantics of `initUnits` to honour
modifications to `packageDBFlags`.
The inability to change `packageDBFlags` while also reusing the already
parsed `UnitDatabase`s was reported in #26423 as a bug.
Hence, we think this behaviour change is warranted and acceptable,
especially since it comes with a breaking change to the `initUnits` API.
Add regression test for #26423
Closes #26423
- - - - -
6cce494a by fendor at 2026-07-27T17:31:05-04:00
Introduce UnitIndex for global external unit caching
`UnitInfo`s have been observed to cause a lot of memory usage in #27500.
Especially with multiple home units, as the same (external) units are
processed from scratch, even though most of the time we end up with
exactly the same `UnitInfo`.
We introduce a `UnitEnv` global cache that allows us to store external
unit information that is used across all `HomeUnitEnv`s.
The most important change in this commit is the introduction of the `UnitIndex`.
It stores a global mapping of `UnitId` -> `UnitInfo`, and `initUnits`
always uses the cached `UnitInfo` entry to populate each
`HomeUnitEnv`'s `UnitState`.
This allows us to ensure the following property:
> Each `UnitInfo` should be alive exactly once in GHC.
All `UnitState`s should reference 'UnitInfo's stored in the 'UnitIndex'.
This ensured by calling 'initUnits' with the 'UnitIndex'.
In addition, the `ExternalUnitDatabases` may also hold a reference
to each on-disk representation of `UnitInfo`.
This means, we impose an hard upper bound on the number of `UnitInfo`s
alive in the GHC session:
> The number of alive `UnitInfo`s closure objects must be the
> sum of all loaded unit database times two.
We add performance regression tests that make sure the number of live
`UnitInfo` cannot exceed this threshold.
Closes #27500
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
mhu-perf
LinkableUsage02
-------------------------
These metrics increases are especially notable, as we are not even
sharing anything big but merely the global package database with 50
entries.
It shows how careful sharing of `UnitInfo` can improve memory usage.
We expect this to be much more notable when the whole cabal package
database is shared across multiple home units.
`LinkableUsage02` metric decreases on unreg and i386 platform, only.
---
Technical details
To share the `UnitInfo`s correctly, it is important that we extract
the `WireMap` into the `UnitIndex`. At the moment of writing, `WireMap`
must be globally the same for all `HomeUnitEnv`s.
This is important, as we could otherwise not cache the "fully-resolved"
`UnitInfo`, as we don't change the `UnitId` or `unitAbiHash` when
resolving wired-in units. Thus, there could be ambiguities, when the
`WireMap` is not the same for all `UnitState`s across the `UnitEnv`.
We consider a `UnitInfo` fully-resolved, if wired-in units have been
updated, the `UnitInfo` has been validated and variables in the unit
config, such as `${pkgroot}` have been resolved.
Updating the wired-in units requires the `WireMap` to be globally the
same.
- - - - -
f8e3bee9 by Zubin Duggal at 2026-07-27T17:31:49-04:00
testsuite: skip runtime stats tests on debugged compilers
Debugged flavours build the boot libraries without optimisation, so the
runtime numbers do not match the baselines.
- - - - -
1e326770 by Zubin Duggal at 2026-07-27T17:31:50-04:00
testsuite: mark #20706 tests fragile rather than broken
Whether the static linux linker issues manifest depends on the host
toolchain.
- - - - -
c0b13cbe by Zubin Duggal at 2026-07-27T17:31:50-04:00
testsuite: exclude libnuma from mostly-static
It needs static system libraries (libnuma.a) that many platforms do not
ship.
Fixes #26914
- - - - -
bee1913d by Alan Zimmerman at 2026-07-28T16:42:29-04:00
EPA: ClsInstDecl with decls as [LHsDecl GhcPs] in GhcPs
Similar to 4fdfe75731e01dad7d7fa474c2703d0d3965afb1, this commit
changes the as-parsed representation of class instance declarations to
[LHsDecl GhcPs], and only separates them by type from the renamer onward.
This also allows us to remove all the AnnSortKey machinery for exact
printing, as it is now no longer needed.
- - - - -
72c55eee by Cheng Shao at 2026-07-28T16:43:11-04:00
hadrian: implement and use writeFileAtomic to fix race condition
This patch implements `writeFileAtomic` in hadrian and change all
invocations of shake non-atomic `writeFile'` to use `writeFileAtomic`,
to avoid multiple hadrian concurrent invocations overwriting the same
in-tree generated file not in the build root directory. Fixes #27536.
Additional notes:
- `writeFileChanged`/`writeFileChangedBS` cannot be made atomic since
it involves reading the file's older version, so their uses are left
alone. It doesn't affect #27536 given their outputs are contained in
the build root directory.
- It's possible to shrink this patch by only making writes outside the
build root directory atomic. But I think it's not worth the effort
for fine grained distinction here, and atomic writes within the
build root directory should also improve robustness of a hadrian
build.
- In the longer term we do want to make a ghc build only generate
files within the build root directory, though that's a lot of work
and outside the scope of this particular bugfix.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
46d4f963 by Sylvain Henry at 2026-07-29T06:38:40-04:00
RTS: correctly mark slop bytes when shrinking large arrays (#19048)
Correctly mark slop bytes even when profiling is off so that heap census
doesn't traverse garbage-collected closures.
- - - - -
4762a8bf by Simon Jakobi at 2026-07-29T06:39:23-04:00
Add -XLazyFieldAnnotations (GHC proposal 752)
Unbundle the prefix `~` lazy field annotation syntax from StrictData. The
new LazyFieldAnnotations extension controls whether `~` is accepted on
constructor fields. StrictData (and Strict, transitively) imply the new
extension.
See https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0752-l….
Closes #24455.
Assisted-by: Claude Opus 4.8
- - - - -
0b6dcc84 by Simon Jakobi at 2026-07-29T06:40:04-04:00
testsuite: Relax T24471 residency tolerance
T24471 peak residency fluctuates enough on i386 to cause spurious
failures. Use the standard residency tolerance while retaining the
existing allocation threshold.
See https://gitlab.haskell.org/ghc/ghc/-/work_items/24471#note_682303.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
90e95b34 by Cheng Shao at 2026-07-29T06:40:45-04:00
compiler: fix missing top-level procedure labels in cmm dumps
This patch fixes missing top-level procedure labels in some
intermediate Cmm pass dumps. Fixes #27553.
Co-authored-by: Codex <codex(a)openai.com>
- - - - -
360a5946 by sheaf at 2026-07-29T06:41:35-04:00
Add some type-family-heavy performance tests
FamAppCachePerf stress-tests the performance of lookups in the
type family application cache.
T27336 is a minimisation extracted from the reported reproducer.
SimplCastPerf is a measure of coercion growth due to the simplifier
calling mkTransCo without re-optimising the result.
- - - - -
3ec9e2b9 by Mike Pilgrem at 2026-07-31T08:21:33-04:00
GHC Guide: Improve docs on response files
- - - - -
e5b2a1f7 by sheaf at 2026-07-31T08:22:23-04:00
Disable Core Lint for TcPlugin_RewritePerf
This is a compiler performance test, but the test source hard-coded
-dcore-lint, defeating the measurement.
-------------------------
Metric Decrease:
TcPlugin_RewritePerf
-------------------------
- - - - -
85b10c00 by Alan Zimmerman at 2026-07-31T22:09:47+01:00
EPA: Remove LocatedP from OverlapMode
We have
type LocatedP = GenLocated SrcSpanAnnP
type SrcSpanAnnP = EpAnn AnnPragma
As the first step in removing this in favour of LocatedA which only
captures location, comments and trailing annotations, we remove it
from OverlapMode
We do this by moving the AnnPragma into the TTG extension point
instead.
- - - - -
c9a34a00 by Viktor Dukhovni at 2026-08-02T04:34:17-04:00
Fix note typo
- - - - -
4f2a21f7 by Andreas Klebinger at 2026-08-02T22:46:46-04:00
Apply oneShot Monad trick to STG LintM
- - - - -
21e4b89d by Andreas Klebinger at 2026-08-02T22:46:46-04:00
stgLint: Use a single reader env for read only arguments.
- - - - -
d415f38a by Alan Zimmerman at 2026-08-02T22:47:27-04:00
EPA: Remove LocatedP from CType
The next step of removing use of LocatedP by moving
the AnnPragma for CType into its TTG extension point
instead.
- - - - -
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
- - - - -
3a552476 by Simon Jakobi at 2026-08-09T15:20:06-04:00
Word64Map: add compareSize
compareSize m c compares the size of a map to an Int, but unlike
compare (size m) c it stops traversing the map once the outcome is
determined.
Based on https://github.com/haskell/containers/pull/1139
Assisted-by: Claude Opus 5
- - - - -
6e2c99d8 by Simon Jakobi at 2026-08-09T15:20:06-04: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
- - - - -
a938ab12 by sheaf at 2026-08-09T15:20:48-04:00
Testsuite: don't measure max residency for T27336
We really care more about total allocations for this test, so this commit
removes the maximum residency measurement.
- - - - -
7d94bb78 by Alan Zimmerman at 2026-08-09T15:21:29-04:00
EPA: Remove LocatedE, replace with LocatedA
This gets rid of one more LocatedXXX occurrence
- - - - -
9df24b7e by sheaf at 2026-08-10T14:28:30-04:00
hie.yaml: use a polyglot shell/batch script
This commit merges hie-bios and hie-bios.bat into a single polyglot
script. This avoids Windows users having to manually update hie.yaml
in order to be able to use HLS.
- - - - -
7f75c588 by Alan Zimmerman at 2026-08-10T14:29:11-04:00
EPA: Remove type parameter from AnnList
This is a step towards cutting AnnList down to its core for formatting
lists only
- - - - -
e8d1a0d6 by Bernhard M. Wiedemann at 2026-08-10T21:31:23-04:00
driver: Link object files in a deterministic order
The object files handed to the linker come from the HomePackageTable,
which is ordered by the order in which modules finished compiling. With
-j1 that is the build plan order, with -jN it is whatever the scheduler
produced, so the same sources can link to different (but equivalent)
binaries.
The order reaches the output: .text and .rodata contributions are
concatenated in link order, so e.g. building the hdav executable of the
DAV package twice, once with -j1 and once with -j4, yields two binaries
that differ in ~100kB of section contents.
Sort the home modules by module before collecting their linkables,
guarded under `Opt_ObjectDeterminism` .
Fixes #27612
Signed-off-by: Bernhard M. Wiedemann <bwiedemann(a)suse.de>
- - - - -
556db2f3 by sheaf at 2026-08-10T21:32:06-04:00
Reduce SpecConstr threshold in GHC.Tc.Solver.Rewrite
As remarked in #27628, this module currently sits on a knife's edge: if
the body of 'simplifyArgsWorker' is made even a tiny bit smaller, then
SpecConstr suddenly kicks in and causes disastrous reboxing of the
LiftingContext argument.
To make this less likely to happen, this commit lowers the SpecConstr
threshold.
- - - - -
c77d88fc by sheaf at 2026-08-13T10:15:22-04:00
Allow rewriting in RuntimeReps for newtype ConPats
This commit implements PHASE 2 of the FixedRuntimeRep plan described in
Note [The Concrete mechanism] in GHC.Tc.Utils.Concrete for newtype
constructor patterns.
In short, GHC now accepts programs of the form
f (MkN x) = ...
in which the argument 'x' of the newtype constructor pattern 'MkN x'
has a representation that is not syntactically concrete, e.g. it can be
'Id IntRep' reducing to 'IntRep'. See T20363{,b,c} for examples.
There are two main parts to the implementation:
1. Typechecking, in GHC.Tc.Gen.Pat.tcDataConPat.
See Note [Typechecking newtype constructor patterns] in GHC.Tc.Gen.Pat.
2. Desugaring. We restructure the code for desugaring pattern matches
by allowing the scrutinised match variable to be casted. This allows
us to accumulate coercions and avoids creating binders at intermediate
types tha don't have a fixed RuntimeRep.
See the revamped Note [Match Ids] in GHC.HsToCore.Monad.
Fixes #20363
-------------------------
Metric Increase:
InstanceMatching
-------------------------
- - - - -
6ba9dd41 by Wolfgang Jeltsch at 2026-08-13T10:16:07-04:00
Improve the documentation of `--show-iface`
This change in particular gets rid of the claim that `--show-iface`
writes *the* contents of the interface file in question. It doesn’t do
that; it only writes those parts that are likely of interest to a human
reader.
- - - - -
4bd193fa by Sylvain Henry at 2026-08-13T10:17:00-04:00
RTS: fix LDV profiler's slop skipping (#27585)
processHeapForDead was the one heap scanner not updated for the slop
marker encoding introduced in #19048. It still assumed slop is zeroed:
while (p < bd->free && !*p) p++; // skip slop
so it stopped at the (StgWord)(-1) sentinel and passed it to
processHeapClosureForDead. IS_FORWARDING_PTR(-1) holds, hence a garbage
size was read out of LDVW and the scan ran off the block, tripping
ASSERT(p == bd->free) on a debug RTS and silently corrupting the census
otherwise.
The loop was hand-copied in four places, so factor it out into skipSlop
in ClosureMacros.h and use it in ProfHeap.c, Sanity.c, Printer.c and
LdvProfile.c.
Co-Authored-By: Claude Opus 5 (1M context) <noreply(a)anthropic.com>
- - - - -
1446bb03 by Zubin Duggal at 2026-08-13T10:17:48-04:00
hadrian: Fix links to remaining doc sites to not use the package hash for haddock links
In 07267f79d91169f474cacc8bcd38d76a6e97887d we changed hadrian to not include the package hash in the haddock
directory. This patch takes care of a few remaining links that were missed in that patch
Fixes #27671
- - - - -
257c3ed7 by Simon Jakobi at 2026-08-13T10:18:29-04:00
Testsuite: widen InstanceMatching acceptance window to 5% (#27673)
...in order to unblock CI.
Assisted-by: Claude Fable 5
- - - - -
e4cfaaa0 by Simon Peyton Jones at 2026-08-14T01:09:32+02:00
Major patch to re-engineer known-key names
This big patch implements the New Plan for known-key names,
described in #27013.
Read the big Note [Overview of known-key names] in GHC.Types.Name
Some things had to be reworked slightly to accomodate the new known-keys
design. A significant one was the generation of auxiliary KindRep
bindings, which was greatly simplified. Note [Grand plan for Typeable]
was updated accordingly. Another example: GHC.Internal.CString was
merged into GHC.Internal.Types.
Co-authored-by: Rodrigo Mesquita <rodrigo.m.mesquita(a)gmail.com>
The couple hundreds of hours spent here by Rodrigo were sponsored by Well-Typed
Metrics: compile_time/bytes allocated
-------------------------------------
Baseline
Test Metric value New value Change
------------------------------------------------------------------------------------------
MultiComponentModules100(normal) ghc/alloc 24,312,779,672 24,990,470,432 +2.8% BAD
MultiComponentModulesRecomp(normal) ghc/alloc 601,924,960 621,884,888 +3.3% BAD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,884,065,432 12,531,373,704 +5.4% BAD
MultiLayerModules(normal) ghc/alloc 3,861,537,072 3,706,919,512 -4.0% GOOD
T13701(normal) ghc/alloc 3,517,246,392 3,237,179,616 -8.0% GOOD
T13820(normal) ghc/alloc 28,961,056 29,663,208 +2.4% BAD
T14697(normal) ghc/alloc 472,044,184 443,550,048 -6.0% GOOD
T18140(normal) ghc/alloc 47,905,664 49,115,808 +2.5% BAD
T4801(normal) ghc/alloc 269,339,096 263,432,040 -2.2% GOOD
T783(normal) ghc/alloc 341,112,672 333,339,952 -2.3% GOOD
hard_hole_fits(normal) ghc/alloc 222,164,728 213,433,808 -3.9% GOOD
mhu-perf(normal) ghc/alloc 49,011,440 46,706,280 -4.7% GOOD
geo. mean +0.1%
minimum -8.0%
maximum +5.4%
All performance regressions were investigated in depth. The surviving
ones:
- MultiComponentModules100, MultiComponentModulesRecomp100,
MultiComponentModulesRecomp regresses because existing bugs that make
an additional implicit edge do too much redundant work: #27053 and #27461
- T13820, T18140, T10547, T13035 regress because we load an additional
interface and associated Names for GHC.Essentials.
-------------------------
Metric Decrease:
MultiLayerModules
T13379
T13701
T14697
T26989
T4801
T783
T9961
hard_hole_fits
mhu-perf
size_hello_artifact
size_hello_obj
size_hello_unicode
Metric Increase:
LinkableUsage01
LinkableUsage02
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
T10547
T13035
T13820
T18140
T18698a
T18698b
T20049
-------------------------
Bumps submodule binary
Closes #27013
- - - - -
61665e79 by sheaf at 2026-08-14T01:09:33+02:00
Allow GHC.Essentials to be hidden
This commit overhauls several aspects of the known entity handling,
in order to allow GHC.Essentials to be hidden without a proliferation
of special cases in the compiler.
The main contribution is to introduce the 'UnresolvedImport' datatype
which cleans up a lot of ad-hoc handling relating to 'ModSummary',
fixing #27603. This allows us to reduce duplication, e.g. by having
Backpack reuse 'mkUnresolvedImports' instead of replicating the
"add implicit imports" logic. It also makes it easier to avoid
undesirable edge cases (such as making sure that the Template Haskell
'reifyModule' function does not leak the implicit GHC.Essentials import).
In particular, the infamous 'findImportedModuleWithIsBoot' is now simply
'resolveImport', taking a single 'UnresolvedImport' and resolving it
to a 'FindResult' (usually a 'Module').
Other changes:
- Cache the result of looking up GHC.Essentials (in TcM and DsM
environments) to avoid redundant work.
This reduces allocations on LinkableUsage01 and hard_hole_fits.
- Properly look up known entities for StaticPointers like we do for
other known entities everywhere else. This allows e.g. modules in
ghc-internal to use -XStaticPointers.
- When using multiple home units, we are now careful to handle the
situation in which we may have multiple GHC.Essentials modules
around. See the new tests under 'driver/multipleHomeUnits'.
- - - - -
b19fcc1c by Vladislav Zavialov at 2026-08-14T06:26:11-04:00
Increase test coverage of diagnostics, batch 2
Add test cases for the previously untested diagnostics:
[GHC-26133] TcRnForeignImportPrimSafeAnn
[GHC-68444] SumAltArityExceeded
[GHC-63966] IllegalSumAlt
[GHC-23882] IllegalDeclaration
[GHC-60220] InvalidCCallImpent
[GHC-18816] RecGadtNoCons
[GHC-38140] GadtNoCons
[GHC-37056] InvalidTypeInstanceHeader
[GHC-78486] InvalidTyFamInstLHS
[GHC-39639] DefaultDataInstDecl
[GHC-78822] AssocDefaultNotAssoc
[GHC-43510] NotSimpleUnliftedType
[GHC-41843] IOResultExpected
[GHC-07641] AtLeastOneArgExpected
[GHC-52886] InvalidTopDecl
Remove unused error constructors:
[GHC-92057] ImportLookupAmbiguous
- - - - -
7b27f25a by Simon Jakobi at 2026-08-14T06:26:54-04:00
testsuite: Drop peak_megabytes_allocated from LinkableUsage tests
LinkableUsage01/02 collected all metrics with a 2% tolerance. For
peak_megabytes_allocated, whose granularity is 1 MB, that window is
under 0.7 MB at this test's ~34 MB peak, so any 1 MB step failed the
test (#27613, #27489). Drop that metric: max_bytes_used guards the
Linkable-retention property with byte granularity, at a tolerance
that still comfortably exceeds the noise observed in CI.
Assisted-by: Claude Fable 5
- - - - -
e5de423b by Simon Jakobi at 2026-08-14T06:26:54-04:00
testsuite: Don't truncate fractional baselines when computing bounds
RelativeMetricAcceptanceWindow.get_bounds truncated the baseline with
int() before applying the tolerance. Baselines can be fractional (they
are averaged over several measurements), so this skewed the acceptance
window downwards: in #27613, a baseline of 33.67 at 2% tolerance
yielded bounds (32, 34) instead of (32, 35), rejecting a measurement
that was within tolerance.
Assisted-by: Claude Fable 5
- - - - -
db959f83 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Expect length001 failure in nonmoving_thr_sanity
length001 relies on an optimization rule to avoid excessive stack use.
The nonmoving_thr_sanity way does not enable optimization, so classify
its stack overflow as an expected failure, as is already done for the
other unoptimized nonmoving ways.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
4f2b7d90 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Omit T22859 in nonmoving threaded ways
T22859 checks allocation-limit handlers with output that depends on
precise allocation behaviour. The nonmoving threaded ways change where
these limits are reached, just as the already-omitted LLVM ways do.
Omit these ways instead of treating their incidental output differences
as test failures.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
c4abddcb by Simon Jakobi at 2026-08-14T15:16:44-04:00
rts/js: Implement eq_thread, and test Eq/Ord ThreadId (#16761)
Since d1f3c63701, Eq ThreadId is implemented via the RTS function
eq_thread, but the JS RTS never provided it, so comparing ThreadIds
for equality on the JS backend crashed with
ReferenceError: h$eq_thread is not defined
Like the C implementation, h$eq_thread uses pointer equality: The JS
RTS has exactly one thread object per thread.
Since previously no test exercised eq_thread directly, this commit
adds a test covering equality, its stability across GC, and agreement
with Ord.
Assisted-by: Claude Fable 5
- - - - -
4a7defa1 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Make listThreads1 insensitive to the RTS's own threads
listThreads1 expected `listThreads` to return exactly [ThreadId 1]. That
holds only under a non-threaded RTS. Under a threaded RTS however there
are more threads present, so we change the test to simply check that
`myThreadId` is present in the list.
Assisted-by: Claude Opus 5
- - - - -
b757727a by Vladislav Zavialov at 2026-08-14T15:17:27-04:00
Fix tcLookupId panic with RequiredTypeArguments and PatternSynonyms (#27586)
The arguments declared on the left-hand side of a pattern synonym are looked up
as term variables bound by its right-hand side. Prior to this patch, that lookup
panicked with RequiredTypeArguments:
data T a where
MkT :: forall a -> T a
pattern P :: Int -> T Int
pattern P x = MkT x
On the RHS, `x` looks like a term argument, so the renamer binds it in the term
namespace. Only during type checking does it turn out to be a type variable, so
the lookup on the LHS finds an ATyVar rather than an ATcId. As the lookup was
done with tcLookupId, it resulted in a panic.
Now the arguments are looked up with tcLookupPatSynArg, which reports an illegal
term-level use of `x`, just as an ordinary function definition `f (MkT x) = x`
does.
Test cases: T27586a T27586b T27586c
Assisted-by: Claude Opus 5
- - - - -
c130188d by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
loopImports: Don't dup ms_uid in summary imports
We were writing the ms_unitid of the mod summary with every single
import of that module
That complicated the code (as though the UnitId in that list could ever
be something else) and also allocates unnecessarily per every mod
import. Very slight allocation decrease measured locally in a few tests:
(MultiComponentModulesRecomp: -0.06%; MultiComponentModulesRecomp100: -0.05%)
Purely a clean up.
- - - - -
c71166a8 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: make control flow simpler and cache correct
This refactor extracts the control flow of downsweep into a single
function `dfsBuild`, which takes care of iteratively expanding and
traversing all nodes of the in-construction module graph necessary to
build a full `ModuleGraph`.
There are three levels of caching going on, all of which are necessary
to make sure we don't do repeated work (notably, NEVER summarise the
same module twice).
1. `dfsBuild` accumulates the final module graph and never revisits the
same node of the module graph. Cache is keyed by the final
`ModuleGraph`s `NodeKey`s.
2. For Module A in home-unit u1, each import in the list of imports
needs to be *found* (call to `findImportedModuleWithIsBoot`): at this
point, we only have the `ModuleName` of the import, not the `Module`.
This *finding* is somewhat expensive, so we cache it as well
(`ImportsCache`). The cache key is the home-unit to which the module
belongs~[1], the import package qualifier, and the ModuleName.
[1] Different home-units will have different package flags, which means
potentially different `Module` resolution for the same `ModuleName`.
3. The most expensive operation we want to avoid is summarising a
`Module` into a `ModSummary`, which notably involves parsing the
module header from scratch.
The third cache, in essence, maps a `Module` to its `ModSummary`
(named `ModSummaryCache`). This cache upholds the invariant: we NEVER
summarise the same module twice. In practice, the cache key is the
Module's UnitId and the Source path; the reason is we need to
distinguish between `.hs` and `.hs-boot` files, as their summaries
will differ.
Note that (2) can't guarantee this alone: Two ModuleName imports in
separate units can (and likely do) map to the same `Module`.
Note that the previous implementation failed to achieve the
no-duplicate-work summarisation invariant, and we ended up doing a
quadratic amount of processing in scenarios like test
`MultiComponentModules100`.
See also Note [Downsweep Control Flow and Caching]
Fixes #27461
Perf changes:
MultiComponentModules(normal) ghc/alloc 2,097,389,264 1,992,186,736 -5.0% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,173,770 21,293,867,360 -12.4% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,761,394 498,543,984 -17.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,968,240 8,895,404,864 -25.2% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
ebc4047b by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
implicitRequirementsShallow can never reach HoleUnit
findImportedModule will never return `HoleUnit` for a `ModuleName`
(a `HoleUnit` can only be found as a signature instantiation, never as a
directly *imported* thing)
Therefore, we can drop `[ModuleName]` returned by
`implicitRequirementsShallow`, which makes many things dead code.
Namely, the call to `implicitRequirementsShallow` from
GHC.Driver.Downsweep which was a performance bottleneck (for doing lots
of duplicate work in findImportedModule) is now entirely gone.
Fixes #27053
In an MR with this patch and the downsweep refactor (previous commit), CI says:
MultiComponentModules(normal) ghc/alloc 2,097,396,728 1,943,662,304 -7.3% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,182,136 17,227,574,440 -29.1% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,769,518 449,973,656 -25.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,976,408 4,828,894,160 -59.4% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
85a6ab01 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: Cache negative results
When traversing a module graph structure, a uniquely identified node
should always expand to the same thing.
I don't see how visiting the same node which failed to be expanded a
first time would ever successfully expand the second time we try to
expand it (eg. when coming from a different edge to it -- it is still
the same node!). The node expansion is local, based just based on the
node itself, not on the path to get there.
Therefore, this patch removes the weird behavior and commentary of
`dfsBuild` wrt to `Nothing` not being cached and being potentially
expanded a second time around to something different, which was
misleading and, ultimately, incorrect.
Now, we have a `MGRes`, which is more explicit about a node being
Skipped just being a node that is ignored whenever it is found (and that
skip is cached) -- and we may want to do this due to failures or due to
just trying nodes which might not work on purpose, like hs-boots.
We uniformly cache positive and negative results and remove the
assumption that there might be an ordering in which the same node
visited at a later time might be expanded differently.
This makes it possible to traverse the module nodes in parallel without
a change in behavior, since there's no longer a hidden ordering
requirement.
- - - - -
56747c3f by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
Organize and clean-up GHC.Driver.Downsweep
Simply some cosmetic changes, moving definitions around to structure the
module better into its relevant sections
(In go (ns ++ ss), it's not a problem to use ++ because it's a good
producer and we won't have to append fully before processing the next
item in go)
- - - - -
12f64118 by Wolfgang Jeltsch at 2026-08-15T06:31:12-04:00
Add support for textual output of bytecode file content
- - - - -
a737df91 by Brandon Chinn at 2026-08-15T12:40:25-04:00
Add law in qualified strings doc
- - - - -
e3188581 by Zubin Duggal at 2026-08-15T12:41:06-04:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
- - - - -
ca9b0b22 by mangoiv at 2026-08-15T12:41:47-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
1aac7095 by sheaf at 2026-08-16T04:37:16-04:00
Avoid wasteful allocations in mkTyConAppCo
The idiom "traverse isReflCo_maybe" followed by "map fst" used in
'GHC.Core.Coercion.mkTyConAppCo' was allocating a lot of waste.
This commit uses 'GHC.Data.Unboxed.traverseMaybeUB' to avoid all these
unnecessary intermediate allocations.
In a quick microbenchmark for 'mkTyConAppCo', this change resulted in:
- refl case (all argument coercions are reflexive):
- -60% runtime
- -80% allocations
- non-refl case:
- from 0% to -12% runtime (depending on which argument is non-refl)
- from 0% to -70% allocations ( -- '' -- )
Fixes #27648
-------------------------
Metric Decrease:
FamAppCachePerf
SimplCastPerf
T12425
T15703
T26426
T3064
T9872a
T9872b
T9872b_defer
T9872c
T9872d
T5321Fun
T9020
T9630
TcPlugin_RewritePerf
Metric Increase:
LinkableUsage02
-------------------------
- - - - -
3915e982 by Alan Zimmerman at 2026-08-16T04:37:58-04:00
EPA: Remove al_trailing from AnnList
It was not being used
- - - - -
fed942ac by Andreas Klebinger at 2026-08-17T12:11:57-04:00
testsuite: Use sigkill in process009.
SIGHUB might have been blocked by a (grand)*parent of the test.
In such cases the test would fail as the python process would simply
exist gracefully instead of committing to a premature end.
By using SIGKILL we can rely on the signal not being blocked, fixing #27578
in the process.
- - - - -
574c875f by Andreas Klebinger at 2026-08-17T12:12:39-04:00
Simplify comparison in DFM.hs
Fixes #27669
- - - - -
d8f1a2a3 by Alan Zimmerman at 2026-08-17T12:13:18-04:00
TTG: Add extension points to BooleanFormula
They are currently unused, but will be used for exact print
annotations next, allowing us to get rid of LocatedBF / SrcSpanAnnBF
- - - - -
93a2b20f by Andreas Klebinger at 2026-08-18T04:31:04-04:00
Fix a number of incorrect module references:
Fix module reference in Note [DataCon wrappers are conlike].
Fix module reference in Note [Detailed InertCans Invariants].
Fix module reference in Note [GHC's data format representations].
Fix module reference in Note [Grand plan for static forms].
Fix module reference in Note [How tuples work].
Fix module reference in Note [Solved dictionaries].
Fix module reference in Note [Stacktraces from Info Table Provenance Entries (IPE based stack unwinding)].
Fix module reference in Note [The VarBndr type and its uses].
Found the incorrect references with a llm.
- - - - -
eb0dfb01 by Simon Jakobi at 2026-08-18T04:31:44-04:00
ci: Run stack-hadrian-build only in full-ci pipelines
The job exists to catch changes that break hadrian/build-stack (#18726),
but nothing in the pipeline depends on it, and it can only break when
hadrian's dependencies change. Restricting it to full-ci (like
hadrian-multi) still covers marge-bot merge batches, so such breakage
cannot reach master unnoticed, while ordinary validate pipelines skip
the job.
Assisted-by: Claude Fable 5
- - - - -
16395c29 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Minor doc & comment improvements to releaseCapability_
- - - - -
c6845679 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Remove redundant USED_IF_THREADS attribute on Capability utilities
- - - - -
48248b50 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Move several Capability utils from Schedule.{c,h} to Capability.{c,h}
They probably should have been there all along. This means all the
pending_sync functionality is within Capability.{c,h}. We only expose
pending_sync for the purpose of inline header functions.
- - - - -
74218f4f by Duncan Coutts at 2026-08-19T14:02:27+01:00
Shuffle the pending sync type declarations for better readability
Move them together into the section with the related functions that use
them.
Also drop the legacy use of the C 'volatile' modifier on the
pending_sync variable. We use C atomics for such access, not volatile.
- - - - -
bc177702 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Rename returning task queue helpers
Follows a naming convention elsewhere. It also gives us suitable names
to distinguish appending vs prepending to the queue, and we're about to
add a prepend operation.
- - - - -
9ce54abe by Duncan Coutts at 2026-08-19T14:02:27+01:00
Add a prepend operation for the returning task queue
With a pending sync (e.g. for GC), we really want to be able to
prioritise the task waiting on the sync over all other returning tasks.
To do that we will need to prepend to the queue rather than append.
- - - - -
206cb6cf by Duncan Coutts at 2026-08-19T14:02:27+01:00
Split waitForSomeCapability out of waitForCapability and adjust callers
Previously waitForCapability had a general interface covering serveral
situations, but it's more useful and easier to understand with two more
specialised functions.
Previously waitForCapability had an in/out Capability parameter: if the
cap was non-null then it would wait to acquire that specific capability
(though in this case it had to be the capability the task was already
associated with), or if it was null then it would pick a suitable
capability, associate the task with it and wait for that capability.
So overall, this required and in/out parameter and suggested that the
capability returned could be different from the one passed in. That was
true if the task was associated with no capability, but false if it was.
This complicated the call sites where we know that the task does have an
associated capability, as it implied the capability could change when in
fact it cannot.
So we now split it: waitForSomeCapability handles this general case
where the task may or may not have an associated capability, and it
returns that new capability. And the waitForCapability now assumes that
the task is associated with a capability and does not need to return
anything. Neither function needs a Capability *cap argument any more.
This simplifies several call sites.
- - - - -
b1629d95 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Update stale comments related to waitFor[Some]Capability
In particular waitForCapability cannot change the capability.
Update a comment about grabbing a new Task in performGC_. This comment was
accurate in 2006, but it is no longer accurate since newBoundTask does not
in fact produce a new Task (typically). The right thing now is to talk about
InCalls, not tasks. That said, the code is still correct since
newBoundTask will push a new incall, but also deal with the general case
of an OS thread that does not yet have an associated Task.
- - - - -
468fc504 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Introduce waitForCapability_ with additional priority arg
Split waitForCapability into a wrapper with the existing type and a
worker with an extra argument.
The new high_priority argument controls whether the task is appended or
prepended to the returing task queue. The default, used by the
waitForCapability wrapper, is false, meaning append to the end of the
queue. This gives fairness.
The high_priority==true case will be used in the subsequent commit.
- - - - -
c6000166 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Make acquireAllCapabilities use high_priority on waitForCapability_
As discussed in issue #27473, a sync of all capabilities is something
that needs to happen promptly (but often doesn't).
One source of delay is that acquireAllCapabilities using
waitForCapability would put the task trying to acquire each capability
at the _end_ of the returning task queue. This gave every other returing
task a full timeslice to run. Meanwhile, several other capabilities are
blocked waiting for the sync to complete, leading to a loss of
throughput.
We use the new high_priority arg to waitForCapability_ to ensure that
the requesting task is put on the front of the returing task queue. This
will ensure that releaseCapability_ will prioritise giving the
capability to the task requesting the sync.
- - - - -
66f3354b by Duncan Coutts at 2026-08-19T14:02:27+01:00
In releaseCapability_ make the pending_sync case self-contained
Previously the pending_sync case had to be checked _after_ the returning
tasks case, since one of the possibilities (indeed the more likely
possibility) is that the task calling waitForCapability will have
enqueued itself as a returning task.
Now we make the pending_sync case self-contained. We note in a comment
the two possibilities: either the task calling waitForCapability has
enqueued itself already and is waiting, or it's not got there yet. We
can handle the first case by giving the capability to the task at the
head of the returning tasks queue, and the second case by leaving the
capability free.
Another way to look at this, is that we move a special case of handling
of the returning task case into the pending_sync case. That special case
being a returing task during a pending sync.
This makes the order of handling returning tasks vs pending sync
independent. This is good, because really they're in the wrong priority
order and we want to flip them around.
- - - - -
2dfea497 by Duncan Coutts at 2026-08-19T14:02:27+01:00
In releaseCapability_ prioritise pending sync over returning tasks
Fixes issue #27460
As explained in the issue, a pending sync (e.g. for GC) should be dealt
with promptly. Returning tasks are a lower priority.
Historically however we had to check returning tasks first, because the
synchronisation mechanism mixed up the task doing a sync with the tasks
returning from safe FFI calls. The task performing the sync was very
likely to be queued on the returning task list (and historically it was
at the _end_ of this list!).
We have now arranged that the task performing the sync is at the front
of the returning task list, and in the pending sync case we now check
the returning task list and run the first task from there if it there is
one.
This is by no means perfect, but it is better. See issue #27473 for a
more general issue of cleaning up the design of the pending sync.
- - - - -
a27a049b by Duncan Coutts at 2026-08-19T14:02:27+01:00
Split releaseCapability_ to hide the rarely-used bool parameter
releaseCapability_ had an extra bool param: always_wakeup. This is
rather a design wart. The reasons are now documented in comments.
Reduce the number of cases where we need to use always_wakeup == true.
There is now only one: prodCapability. That is because
releaseCapability_ does not know about signals (which is an example of
the design wart).
We can thus simplify most callers by splitting releaseCapability_ into a
worker and wrapper, where the wrapper does not have to have the
always_wakeup parameter. This simplifies all call sites except one.
This will also reduce churn at call site when we add a second (rarely
used) parameter in the subsequent commit.
- - - - -
3be9e467 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Extend releaseCapability__ with an extra wakeup_worker modifier
Document within releaseCapability__ the basic approach of looking for a
series of conditions in priority order and acting on them. Explain the
existing modifier within that understanding.
Then add a new modifier, wakeup_worker and explain it in similar terms.
What it does is skip two of the conditions in the priority list, with
the effect that we prioritise waking up a worker task over a returning
task or bound task.
This feature is not yet used in this commit, but it will be used as
part of a scheme to allow in-RTS I/O managers in the threaded RTS. This
scheme will make use of being able to start a background worker thread,
and that will use this feature to start it promptly.
Also correct the yieldCapability docs to cover all the conditions, and
in priority order for consistency.
- - - - -
5228a092 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Move enqueueWorker next to where it is used.
It's not general purpose at all. It's very specifically crafted to work
with it's only caller: yieldCapability. It does very suprising things
like releaseCapability_, release locks and terminate threads. This logic
would be much clearer if done within yieldCapability.
- - - - -
ff57aa40 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Move code out of enqueueWorker and into releaseCapability_
Instead of directly releasing locks and terminating tasks, have it
return whether the enqueue was successful or not. In the latter case,
releaseCapability_ itself will release locks and terminate the task.
This makes the logic of releaseCapability_ a lot clearer. Fiddling with
tasks is what releaseCapability_ does, so it's better not to try and
encapsulate this within a helper function.
- - - - -
9cfe2ee2 by Duncan Coutts at 2026-08-19T14:02:27+01:00
Clarify the logic and control flow in yieldCapability
yieldCapability is unfortunately a bit complicated. This change
restructures things slightly but should keep the behaviour the same.
Previously after calling releaseCapability_ we had a bunch of
alternatives, where in each branch we would use RELEASE_LOCK(cap->lock)
and do various things before/after the lock is released. This was a bit
hard to follow, or to extend (which we need to do).
So now we have unconditional acquire and release of the cap->lock, so
it's clear where that happens, with releaseCapability_ in between. Then
in between these steps we have the various other pre/post actions. Some
before releaseCapability_, some after while holing the lock, and some
after having released the lock.
We explain this structure in a longer comment, and refer back to the
structure from the code.
- - - - -
1242 changed files:
- .gitlab-ci.yml
- .gitlab/ci.sh
- + changelog.d/21101
- + changelog.d/27380
- + changelog.d/27532
- + changelog.d/27626
- + changelog.d/T20363
- + changelog.d/T26423
- + changelog.d/T26532
- + changelog.d/T26716
- + changelog.d/T27314.md
- + changelog.d/T27368-ppr-unreachable-cmm-blocks.md
- + changelog.d/T27374
- + changelog.d/T27440
- + changelog.d/T27455
- + changelog.d/T27557
- + changelog.d/T27583
- + changelog.d/T27586
- + changelog.d/T27589
- + changelog.d/T27639
- + changelog.d/downsweep-refactor
- + changelog.d/fix-cmm-dump-labels
- + changelog.d/fix-heap-census-large-arrays-19048
- + changelog.d/lazy-field-annotations
- + changelog.d/link-deterministic-order
- + changelog.d/refactor-known-names
- + changelog.d/show-byte-code
- + changelog.d/unit-index
- + changelog.d/warn-defaulted-callstack
- compiler/GHC.hs
- + compiler/GHC/Builtin.hs
- + compiler/GHC/Builtin/KnownKeys.hs
- + compiler/GHC/Builtin/KnownOccs.hs
- + compiler/GHC/Builtin/Modules.hs
- − compiler/GHC/Builtin/Names.hs
- − compiler/GHC/Builtin/Names/TH.hs
- compiler/GHC/Builtin/PrimOps.hs
- compiler/GHC/Builtin/PrimOps/Casts.hs
- compiler/GHC/Builtin/PrimOps/Ids.hs
- + compiler/GHC/Builtin/TH.hs
- compiler/GHC/Builtin/Uniques.hs
- compiler/GHC/Builtin/Uniques.hs-boot
- − compiler/GHC/Builtin/Utils.hs
- + compiler/GHC/Builtin/WiredIn/Ids.hs
- compiler/GHC/Builtin/Types/Prim.hs → compiler/GHC/Builtin/WiredIn/Prim.hs
- compiler/GHC/Builtin/Types/Literals.hs → compiler/GHC/Builtin/WiredIn/TypeLits.hs
- compiler/GHC/Builtin/Types.hs → compiler/GHC/Builtin/WiredIn/Types.hs
- compiler/GHC/Builtin/Types.hs-boot → compiler/GHC/Builtin/WiredIn/Types.hs-boot
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Serialize.hs
- + compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Cmm.hs
- compiler/GHC/Cmm/Pipeline.hs
- compiler/GHC/CmmToAsm/Format.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Class.hs
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Core/Coercion/Axiom.hs
- compiler/GHC/Core/Coercion/Opt.hs
- compiler/GHC/Core/DataCon.hs
- compiler/GHC/Core/FVs.hs
- compiler/GHC/Core/FamInstEnv.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Make.hs
- compiler/GHC/Core/Multiplicity.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/Opt/ConstantFold.hs
- compiler/GHC/Core/Opt/CprAnal.hs
- compiler/GHC/Core/Opt/DmdAnal.hs
- compiler/GHC/Core/Opt/LiberateCase.hs
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/SetLevels.hs
- compiler/GHC/Core/Opt/Simplify/Env.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Opt/SpecConstr.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/Ppr.hs
- compiler/GHC/Core/Predicate.hs
- compiler/GHC/Core/Rules.hs
- compiler/GHC/Core/SimpleOpt.hs
- compiler/GHC/Core/Subst.hs
- compiler/GHC/Core/TyCo/FVs.hs
- compiler/GHC/Core/TyCo/Rep.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Unfold.hs
- compiler/GHC/Core/Unify.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToIface.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Data/BooleanFormula.hs
- compiler/GHC/Data/Unboxed.hs
- 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/Driver/Backpack.hs
- compiler/GHC/Driver/Config/Tidy.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Env/KnotVars.hs
- compiler/GHC/Driver/Env/Types.hs
- compiler/GHC/Driver/Flags.hs
- compiler/GHC/Driver/GenerateCgIPEStub.hs
- compiler/GHC/Driver/Main/Hsc.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Make.hs
- compiler/GHC/Driver/MakeFile.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/Hs/Basic.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/ImpExp.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Lit.hs
- compiler/GHC/Hs/Pat.hs
- − compiler/GHC/Hs/Specificity.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Syn/Type.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Binds.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Foreign/C.hs
- compiler/GHC/HsToCore/Foreign/Call.hs
- compiler/GHC/HsToCore/Foreign/JavaScript.hs
- compiler/GHC/HsToCore/Foreign/Utils.hs
- compiler/GHC/HsToCore/Foreign/Wasm.hs
- compiler/GHC/HsToCore/ListComp.hs
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Match.hs-boot
- compiler/GHC/HsToCore/Match/Constructor.hs
- compiler/GHC/HsToCore/Match/Literal.hs
- compiler/GHC/HsToCore/Monad.hs
- compiler/GHC/HsToCore/Pmc/Check.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Pmc/Ppr.hs
- compiler/GHC/HsToCore/Pmc/Solver.hs
- compiler/GHC/HsToCore/Pmc/Solver/Types.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/HsToCore/Types.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/Iface/Binary.hs
- compiler/GHC/Iface/Env.hs
- − compiler/GHC/Iface/Env.hs-boot
- compiler/GHC/Iface/Errors/Ppr.hs
- compiler/GHC/Iface/Errors/Types.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Ext/Types.hs
- compiler/GHC/Iface/Load.hs
- compiler/GHC/Iface/Make.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Syntax.hs
- compiler/GHC/Iface/Tidy.hs
- compiler/GHC/Iface/Tidy/StaticPtrTable.hs
- compiler/GHC/Iface/Type.hs
- compiler/GHC/Iface/Warnings.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/Errors/Ppr.hs
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/Header.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Plugins.hs
- compiler/GHC/Rename/Env.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Lit.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Rename/Unbound.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Context.hs
- compiler/GHC/Runtime/Debugger.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Heap/Inspect.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Stg/BcPrep.hs
- compiler/GHC/Stg/Lint.hs
- compiler/GHC/Stg/Unarise.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/StgToCmm/Bind.hs
- compiler/GHC/StgToCmm/DataCon.hs
- compiler/GHC/StgToCmm/Env.hs
- compiler/GHC/StgToCmm/Foreign.hs
- compiler/GHC/StgToCmm/Lit.hs
- compiler/GHC/StgToCmm/Ticky.hs
- compiler/GHC/StgToJS/Apply.hs
- compiler/GHC/StgToJS/Arg.hs
- compiler/GHC/StgToJS/Expr.hs
- compiler/GHC/StgToJS/FFI.hs
- compiler/GHC/StgToJS/Linker/Utils.hs
- compiler/GHC/StgToJS/Utils.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/Deriv/Functor.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Deriv/Generics.hs
- compiler/GHC/Tc/Deriv/Infer.hs
- compiler/GHC/Tc/Deriv/Utils.hs
- compiler/GHC/Tc/Errors.hs
- compiler/GHC/Tc/Errors/Hole.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/Arrow.hs
- compiler/GHC/Tc/Gen/Bind.hs
- compiler/GHC/Tc/Gen/Default.hs
- compiler/GHC/Tc/Gen/Export.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Foreign.hs
- compiler/GHC/Tc/Gen/Head.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Sig.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Instance/Class.hs
- compiler/GHC/Tc/Instance/FunDeps.hs
- compiler/GHC/Tc/Instance/Typeable.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/Plugin.hs
- compiler/GHC/Tc/Solver.hs
- compiler/GHC/Tc/Solver/Default.hs
- compiler/GHC/Tc/Solver/Dict.hs
- compiler/GHC/Tc/Solver/FunDeps.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Solver/Rewrite.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Build.hs
- compiler/GHC/Tc/TyCl/Class.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/TyCl/Utils.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/Constraint.hs
- compiler/GHC/Tc/Types/Evidence.hs
- compiler/GHC/Tc/Types/LclEnv.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Tc/Utils/Concrete.hs
- compiler/GHC/Tc/Utils/Env.hs
- compiler/GHC/Tc/Utils/Instantiate.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Tc/Utils/TcMType.hs
- compiler/GHC/Tc/Utils/TcType.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Tc/Validity.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Arity.hs
- compiler/GHC/Types/Basic.hs
- compiler/GHC/Types/DefaultEnv.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Error/Codes.hs
- compiler/GHC/Types/FieldLabel.hs
- compiler/GHC/Types/Fixity.hs
- compiler/GHC/Types/ForeignCall.hs
- compiler/GHC/Types/GREInfo.hs
- compiler/GHC/Types/Hint.hs
- compiler/GHC/Types/Hint/Ppr.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/InlinePragma.hs
- compiler/GHC/Types/Literal.hs
- compiler/GHC/Types/Name.hs
- compiler/GHC/Types/Name/Cache.hs
- compiler/GHC/Types/Name/Ppr.hs
- compiler/GHC/Types/Name/Reader.hs
- compiler/GHC/Types/RepType.hs
- compiler/GHC/Types/TyThing.hs
- compiler/GHC/Types/Unique.hs
- compiler/GHC/Types/Unique/DFM.hs
- compiler/GHC/Types/Unique/FM.hs
- + compiler/GHC/Types/UnresolvedImport.hs
- compiler/GHC/Types/Var.hs
- compiler/GHC/Unit.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/External.hs
- + compiler/GHC/Unit/External/Database.hs
- + compiler/GHC/Unit/External/Index.hs
- + compiler/GHC/Unit/External/ModuleOrigin.hs
- + compiler/GHC/Unit/External/Providers.hs
- + compiler/GHC/Unit/External/Query.hs
- + compiler/GHC/Unit/External/Substitution.hs
- + compiler/GHC/Unit/External/Validate.hs
- + compiler/GHC/Unit/External/Visibility.hs
- + compiler/GHC/Unit/External/Wired.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/Finder/Types.hs
- compiler/GHC/Unit/Home/Graph.hs
- compiler/GHC/Unit/Info.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/ModSummary.hs
- compiler/GHC/Unit/Module/Warnings.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Unit/State.hs-boot
- compiler/GHC/Unit/Types.hs
- compiler/GHC/Utils/Binary.hs
- − compiler/GHC/Utils/Binary/Typeable.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/Binds.hs
- compiler/Language/Haskell/Syntax/BooleanFormula.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Decls/Foreign.hs
- compiler/Language/Haskell/Syntax/Doc.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/Language/Haskell/Syntax/ImpExp.hs
- compiler/Language/Haskell/Syntax/Specificity.hs
- compiler/ghc.cabal.in
- configure.ac
- distrib/configure.ac.in
- docs/index.html.in
- docs/users_guide/debugging.rst
- docs/users_guide/eventlog-formats.rst
- docs/users_guide/exts/qualified_strings.rst
- docs/users_guide/exts/strict.rst
- docs/users_guide/ghc_config.py.in
- docs/users_guide/separate_compilation.rst
- docs/users_guide/using-warnings.rst
- docs/users_guide/using.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/GHCi/UI.hs
- ghc/GHCi/UI/Monad.hs
- ghc/Main.hs
- hadrian/cfg/system.config.host.in
- hadrian/cfg/system.config.target.in
- − hadrian/hie-bios
- hadrian/hie-bios.bat
- hadrian/src/Hadrian/Builder/Ar.hs
- hadrian/src/Hadrian/Utilities.hs
- hadrian/src/Oracles/Flag.hs
- hadrian/src/Rules/BinaryDist.hs
- hadrian/src/Rules/CabalReinstall.hs
- hadrian/src/Rules/Documentation.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Rules/Library.hs
- hadrian/src/Rules/Nofib.hs
- hadrian/src/Rules/Program.hs
- hadrian/src/Rules/Register.hs
- hadrian/src/Rules/Rts.hs
- hadrian/src/Rules/SourceDist.hs
- hadrian/src/Rules/Test.hs
- hadrian/src/Rules/ToolArgs.hs
- hadrian/src/Settings/Packages.hs
- hie.yaml
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- libraries/base/src/Control/Applicative.hs
- libraries/base/src/Control/Concurrent.hs
- libraries/base/src/Control/Concurrent/Chan.hs
- libraries/base/src/Control/Concurrent/QSem.hs
- libraries/base/src/Control/Concurrent/QSemN.hs
- libraries/base/src/Data/Array/Byte.hs
- libraries/base/src/Data/Bifoldable.hs
- libraries/base/src/Data/Bifoldable1.hs
- libraries/base/src/Data/Bifunctor.hs
- libraries/base/src/Data/Bitraversable.hs
- libraries/base/src/Data/Bool.hs
- libraries/base/src/Data/Complex.hs
- libraries/base/src/Data/Data.hs
- libraries/base/src/Data/Enum.hs
- libraries/base/src/Data/Fixed.hs
- libraries/base/src/Data/Foldable1.hs
- libraries/base/src/Data/Functor/Classes.hs
- libraries/base/src/Data/Functor/Compose.hs
- libraries/base/src/Data/Functor/Contravariant.hs
- libraries/base/src/Data/Functor/Product.hs
- libraries/base/src/Data/Functor/Sum.hs
- libraries/base/src/Data/List.hs
- libraries/base/src/Data/List/NonEmpty.hs
- libraries/base/src/Data/List/NubOrdSet.hs
- libraries/base/src/Data/Semigroup.hs
- libraries/base/src/Data/Version.hs
- libraries/base/src/GHC/Base.hs
- libraries/base/src/GHC/ByteOrder.hs
- + libraries/base/src/GHC/Essentials.hs
- libraries/base/src/GHC/Exts.hs
- libraries/base/src/GHC/Fingerprint.hs
- libraries/base/src/GHC/RTS/Flags.hs
- libraries/base/src/GHC/ResponseFile.hs
- libraries/base/src/GHC/Stack.hs
- libraries/base/src/GHC/Stats.hs
- libraries/base/src/GHC/Weak/Finalize.hs
- libraries/base/src/Numeric.hs
- libraries/base/src/Prelude.hs
- libraries/base/src/System/CPUTime/Posix/ClockGetTime.hsc
- libraries/base/src/System/CPUTime/Posix/RUsage.hsc
- libraries/base/src/System/CPUTime/Posix/Times.hsc
- libraries/base/src/System/CPUTime/Unsupported.hs
- libraries/base/src/System/Console/GetOpt.hs
- libraries/base/src/System/Exit.hs
- libraries/base/src/System/IO.hs
- libraries/base/src/System/IO/OS.hs
- libraries/base/src/System/IO/Unsafe.hs
- libraries/base/src/System/Info.hs
- libraries/base/src/System/Timeout.hs
- libraries/base/src/Text/Printf.hs
- libraries/base/src/Text/Read.hs
- libraries/base/src/Text/Show/Functions.hs
- libraries/base/tests/all.T
- libraries/base/tests/listThreads1.hs
- libraries/base/tests/listThreads1.stdout
- libraries/base/tests/perf/ElemFusionUnknownList_O1.stderr
- libraries/base/tests/perf/ElemFusionUnknownList_O2.stderr
- libraries/binary
- libraries/ghc-experimental/src/Data/Sum/Experimental.hs
- libraries/ghc-experimental/src/Data/Tuple/Experimental.hs
- libraries/ghc-experimental/src/GHC/Profiling/Eras.hs
- libraries/ghc-experimental/src/Prelude/Experimental.hs
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingDisabled.hsc
- libraries/ghc-heap/GHC/Exts/Heap/FFIClosures_ProfilingEnabled.hsc
- libraries/ghc-internal/codepages/MakeTable.hs
- libraries/ghc-internal/ghc-internal.cabal.in
- libraries/ghc-internal/include/RtsIfaceSymbols.h
- libraries/ghc-internal/src/GHC/Internal/AllocationLimitHandler.hs
- libraries/ghc-internal/src/GHC/Internal/Arr.hs
- libraries/ghc-internal/src/GHC/Internal/ArrayArray.hs
- libraries/ghc-internal/src/GHC/Internal/Base.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/Backend.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/Backend/GMP.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/Backend/Native.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/BigNat.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/BigNat.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Bignum/Integer.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/Integer.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Bignum/Natural.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/Natural.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Bignum/Primitives.hs
- libraries/ghc-internal/src/GHC/Internal/Bignum/WordArray.hs
- libraries/ghc-internal/src/GHC/Internal/Bits.hs
- libraries/ghc-internal/src/GHC/Internal/ByteOrder.hs
- libraries/ghc-internal/src/GHC/Internal/CString.hs
- libraries/ghc-internal/src/GHC/Internal/Char.hs
- libraries/ghc-internal/src/GHC/Internal/Classes.hs
- libraries/ghc-internal/src/GHC/Internal/Classes/IP.hs
- libraries/ghc-internal/src/GHC/Internal/Clock.hsc
- libraries/ghc-internal/src/GHC/Internal/ClosureTypes.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/Bound.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/IO.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/POSIX.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/POSIX/Const.hsc
- libraries/ghc-internal/src/GHC/Internal/Conc/Signal.hs
- libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs
- libraries/ghc-internal/src/GHC/Internal/ConsoleHandler.hsc
- libraries/ghc-internal/src/GHC/Internal/Control/Arrow.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Category.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Concurrent/MVar.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Exception/Base.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fail.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/Fix.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/IO/Class.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Imp.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/ST/Lazy/Imp.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Monad/Zip.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Bits.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Coerce.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Data.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Dynamic.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Either.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Foldable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Function.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor/Const.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor/Utils.hs
- libraries/ghc-internal/src/GHC/Internal/Data/IORef.hs
- libraries/ghc-internal/src/GHC/Internal/Data/List.hs
- libraries/ghc-internal/src/GHC/Internal/Data/List/NonEmpty.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Maybe.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Monoid.hs
- libraries/ghc-internal/src/GHC/Internal/Data/NonEmpty.hs
- libraries/ghc-internal/src/GHC/Internal/Data/OldList.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Ord.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Proxy.hs
- libraries/ghc-internal/src/GHC/Internal/Data/STRef.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Semigroup/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Data/String.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Traversable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Tuple.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Type/Bool.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Type/Coercion.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Type/Equality.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Type/Ord.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Typeable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Typeable/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Unique.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Version.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Void.hs
- libraries/ghc-internal/src/GHC/Internal/Debug/Trace.hs
- libraries/ghc-internal/src/GHC/Internal/Desugar.hs
- libraries/ghc-internal/src/GHC/Internal/Encoding/UTF8.hs
- libraries/ghc-internal/src/GHC/Internal/Enum.hs
- libraries/ghc-internal/src/GHC/Internal/Enum.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Environment.hs
- libraries/ghc-internal/src/GHC/Internal/Err.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Arr.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Array.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Control.hs
- libraries/ghc-internal/src/GHC/Internal/Event/EPoll.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/IntTable.hs
- libraries/ghc-internal/src/GHC/Internal/Event/IntVar.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Internal/Types.hs
- libraries/ghc-internal/src/GHC/Internal/Event/KQueue.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/Manager.hs
- libraries/ghc-internal/src/GHC/Internal/Event/PSQ.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Poll.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/Thread.hs
- libraries/ghc-internal/src/GHC/Internal/Event/TimeOut.hs
- libraries/ghc-internal/src/GHC/Internal/Event/TimerManager.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Unique.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Windows.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/Windows/Clock.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Windows/ConsoleEvent.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/Windows/FFI.hsc
- libraries/ghc-internal/src/GHC/Internal/Event/Windows/ManagedThreadPool.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Windows/Thread.hs
- libraries/ghc-internal/src/GHC/Internal/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Exception/Context.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Context.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs-boot
- libraries/ghc-internal/src/GHC/Internal/ExecutionStack.hs
- libraries/ghc-internal/src/GHC/Internal/ExecutionStack/Internal.hsc
- libraries/ghc-internal/src/GHC/Internal/Exts.hs
- libraries/ghc-internal/src/GHC/Internal/Fingerprint.hs
- libraries/ghc-internal/src/GHC/Internal/Fingerprint/Type.hs
- libraries/ghc-internal/src/GHC/Internal/Float.hs
- libraries/ghc-internal/src/GHC/Internal/Float/ConversionUtils.hs
- libraries/ghc-internal/src/GHC/Internal/Float/RealFracMethods.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/C/ConstPtr.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/C/Error.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/C/String.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/C/String/Encoding.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/C/Types.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/ForeignPtr/Imp.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Marshal/Alloc.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Marshal/Array.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Marshal/Error.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Marshal/Pool.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Marshal/Utils.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Ptr.hs
- libraries/ghc-internal/src/GHC/Internal/Foreign/Storable.hs
- libraries/ghc-internal/src/GHC/Internal/ForeignPtr.hs
- libraries/ghc-internal/src/GHC/Internal/ForeignSrcLang.hs
- libraries/ghc-internal/src/GHC/Internal/Functor/ZipList.hs
- libraries/ghc-internal/src/GHC/Internal/GHCi.hs
- libraries/ghc-internal/src/GHC/Internal/GHCi/Helpers.hs
- libraries/ghc-internal/src/GHC/Internal/Generics.hs
- libraries/ghc-internal/src/GHC/Internal/Heap/Closures.hs
- libraries/ghc-internal/src/GHC/Internal/Heap/Constants.hsc
- libraries/ghc-internal/src/GHC/Internal/Heap/InfoTable.hsc
- libraries/ghc-internal/src/GHC/Internal/Heap/InfoTable/Types.hsc
- libraries/ghc-internal/src/GHC/Internal/Heap/InfoTableProf.hsc
- libraries/ghc-internal/src/GHC/Internal/Heap/ProfInfo/Types.hs
- libraries/ghc-internal/src/GHC/Internal/IO.hs
- libraries/ghc-internal/src/GHC/Internal/IO.hs-boot
- libraries/ghc-internal/src/GHC/Internal/IO/Buffer.hs
- libraries/ghc-internal/src/GHC/Internal/IO/BufferedIO.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Device.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/CodePage.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/CodePage/API.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/CodePage/Table.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/Failure.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/Iconv.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/Latin1.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/Types.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/UTF16.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/UTF32.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Encoding/UTF8.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs-boot
- libraries/ghc-internal/src/GHC/Internal/IO/FD.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/FD.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Internals.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock/Common.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock/Flock.hsc
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock/LinuxOFD.hsc
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock/NoOp.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Lock/Windows.hsc
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Text.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Types.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Types.hs-boot
- libraries/ghc-internal/src/GHC/Internal/IO/Handle/Windows.hs
- libraries/ghc-internal/src/GHC/Internal/IO/IOMode.hs
- libraries/ghc-internal/src/GHC/Internal/IO/SubSystem.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Unsafe.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Windows/Encoding.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Windows/Handle.hsc
- libraries/ghc-internal/src/GHC/Internal/IOArray.hs
- libraries/ghc-internal/src/GHC/Internal/IORef.hs
- libraries/ghc-internal/src/GHC/Internal/InfoProv.hs
- libraries/ghc-internal/src/GHC/Internal/InfoProv/Types.hsc
- libraries/ghc-internal/src/GHC/Internal/Int.hs
- libraries/ghc-internal/src/GHC/Internal/IsList.hs
- libraries/ghc-internal/src/GHC/Internal/Ix.hs
- libraries/ghc-internal/src/GHC/Internal/JS/Foreign/Callback.hs
- libraries/ghc-internal/src/GHC/Internal/JS/Prim.hs
- libraries/ghc-internal/src/GHC/Internal/LanguageExtensions.hs
- libraries/ghc-internal/src/GHC/Internal/Lexeme.hs
- libraries/ghc-internal/src/GHC/Internal/List.hs
- libraries/ghc-internal/src/GHC/Internal/MVar.hs
- libraries/ghc-internal/src/GHC/Internal/Magic.hs
- libraries/ghc-internal/src/GHC/Internal/Magic/Dict.hs
- libraries/ghc-internal/src/GHC/Internal/Maybe.hs
- libraries/ghc-internal/src/GHC/Internal/Num.hs
- libraries/ghc-internal/src/GHC/Internal/Num.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Numeric.hs
- libraries/ghc-internal/src/GHC/Internal/OverloadedLabels.hs
- libraries/ghc-internal/src/GHC/Internal/Pack.hs
- libraries/ghc-internal/src/GHC/Internal/Prim/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Prim/Ext.hs
- libraries/ghc-internal/src/GHC/Internal/Prim/Panic.hs
- libraries/ghc-internal/src/GHC/Internal/Prim/PtrEq.hs
- libraries/ghc-internal/src/GHC/Internal/Profiling.hs
- libraries/ghc-internal/src/GHC/Internal/Ptr.hs
- libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc
- libraries/ghc-internal/src/GHC/Internal/RTS/Flags/Test.hsc
- libraries/ghc-internal/src/GHC/Internal/Read.hs
- libraries/ghc-internal/src/GHC/Internal/Real.hs
- libraries/ghc-internal/src/GHC/Internal/Real.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Records.hs
- libraries/ghc-internal/src/GHC/Internal/ST.hs
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/ghc-internal/src/GHC/Internal/STRef.hs
- libraries/ghc-internal/src/GHC/Internal/Show.hs
- libraries/ghc-internal/src/GHC/Internal/Stable.hs
- libraries/ghc-internal/src/GHC/Internal/StableName.hs
- libraries/ghc-internal/src/GHC/Internal/Stack.hs
- libraries/ghc-internal/src/GHC/Internal/Stack.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Stack/Annotation.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/CCS.hsc
- libraries/ghc-internal/src/GHC/Internal/Stack/CloneStack.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Constants.hsc
- libraries/ghc-internal/src/GHC/Internal/Stack/ConstantsProf.hsc
- libraries/ghc-internal/src/GHC/Internal/Stack/Decode.hs
- libraries/ghc-internal/src/GHC/Internal/Stack/Types.hs
- libraries/ghc-internal/src/GHC/Internal/StaticPtr.hs
- libraries/ghc-internal/src/GHC/Internal/StaticPtr/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Stats.hsc
- libraries/ghc-internal/src/GHC/Internal/Storable.hs
- libraries/ghc-internal/src/GHC/Internal/System/Environment.hs
- libraries/ghc-internal/src/GHC/Internal/System/Environment/Blank.hsc
- libraries/ghc-internal/src/GHC/Internal/System/Environment/ExecutablePath.hsc
- libraries/ghc-internal/src/GHC/Internal/System/IO/Error.hs
- libraries/ghc-internal/src/GHC/Internal/System/Mem.hs
- libraries/ghc-internal/src/GHC/Internal/System/Posix/Internals.hs
- libraries/ghc-internal/src/GHC/Internal/System/Posix/Types.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Lib.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Lift.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Monad.hs
- libraries/ghc-internal/src/GHC/Internal/TH/Syntax.hs
- libraries/ghc-internal/src/GHC/Internal/Text/ParserCombinators/ReadP.hs
- libraries/ghc-internal/src/GHC/Internal/Text/ParserCombinators/ReadPrec.hs
- libraries/ghc-internal/src/GHC/Internal/Text/Read/Lex.hs
- libraries/ghc-internal/src/GHC/Internal/TopHandler.hs
- libraries/ghc-internal/src/GHC/Internal/Tuple.hs
- libraries/ghc-internal/src/GHC/Internal/Type/Reflection.hs
- libraries/ghc-internal/src/GHC/Internal/Type/Reflection/Unsafe.hs
- libraries/ghc-internal/src/GHC/Internal/TypeError.hs
- libraries/ghc-internal/src/GHC/Internal/TypeLits.hs
- libraries/ghc-internal/src/GHC/Internal/TypeLits/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/TypeNats.hs
- libraries/ghc-internal/src/GHC/Internal/TypeNats/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Types.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Bits.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Char/DerivedCoreProperties.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Char/UnicodeData/GeneralCategory.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Char/UnicodeData/SimpleLowerCaseMapping.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Char/UnicodeData/SimpleTitleCaseMapping.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Char/UnicodeData/SimpleUpperCaseMapping.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Version.hs
- libraries/ghc-internal/src/GHC/Internal/Unsafe/Coerce.hs
- libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Conc.hs
- libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Conc/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Exports.hs
- libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Imports.hs
- libraries/ghc-internal/src/GHC/Internal/Wasm/Prim/Types.hs
- libraries/ghc-internal/src/GHC/Internal/Weak.hs
- libraries/ghc-internal/src/GHC/Internal/Weak/Finalize.hs
- libraries/ghc-internal/src/GHC/Internal/Windows.hs
- libraries/ghc-internal/src/GHC/Internal/Word.hs
- libraries/ghc-internal/tools/ucd2haskell/exe/UCD2Haskell/ModuleGenerators.hs
- libraries/ghc-prim/Dummy.hs
- libraries/ghc-prim/ghc-prim.cabal
- libraries/template-haskell/Language/Haskell/TH/Lib.hs
- linters/lint-codes/LintCodes/Static.hs
- m4/fp_check_pthreads.m4
- rts/Apply.cmm
- rts/Capability.c
- rts/Capability.h
- rts/IOManager.c
- rts/IOManager.h
- rts/IOManagerInternals.h
- rts/LdvProfile.c
- rts/ZeroSlop.c → rts/MarkSlop.c
- rts/Messages.c
- rts/PrimOps.cmm
- rts/Printer.c
- rts/ProfHeap.c
- rts/Profiling.c
- rts/RaiseAsync.c
- rts/RaiseAsync.h
- rts/RtsAPI.c
- rts/RtsFlags.c
- rts/RtsStartup.c
- rts/RtsSymbols.c
- rts/STM.c
- rts/Schedule.c
- rts/Schedule.h
- rts/StgMiscClosures.cmm
- rts/ThreadPaused.c
- rts/Threads.c
- rts/Trace.c
- rts/Trace.h
- rts/TraverseHeap.c
- rts/eventlog/EventLog.c
- rts/eventlog/EventLog.h
- rts/include/Cmm.h
- rts/include/rts/Constants.h
- rts/include/rts/EventLogFormat.h
- rts/include/rts/IOInterface.h
- rts/include/rts/RtsToHsIface.h
- rts/include/rts/storage/ClosureMacros.h
- rts/include/rts/storage/Closures.h
- rts/include/rts/storage/TSO.h
- rts/include/stg/MiscClosures.h
- rts/js/thread.js
- rts/posix/FdWakeup.h
- + rts/posix/MIO.c
- + rts/posix/MIO.h
- rts/posix/Poll.c
- rts/posix/Poll.h
- rts/posix/Select.c
- rts/posix/Select.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/posix/Timeout.c
- rts/posix/Timeout.h
- rts/rts.cabal
- rts/sm/Compact.c
- rts/sm/NonMovingMark.c
- rts/sm/Sanity.c
- rts/sm/Scav.c
- rts/sm/Storage.c
- rts/win32/AsyncMIO.c
- rts/win32/AsyncMIO.h
- rts/win32/AsyncWinIO.h
- rts/win32/AwaitEvent.c
- rts/win32/AwaitEvent.h
- rts/win32/ConsoleHandler.h
- rts/win32/MIOManager.h
- rts/win32/ThrIOManager.h
- rts/win32/WorkQueue.h
- rts/win32/veh_excn.h
- testsuite/.gitignore
- testsuite/Makefile
- testsuite/driver/perf_notes.py
- testsuite/driver/runtests.py
- testsuite/driver/term_color.py
- testsuite/driver/testlib.py
- testsuite/driver/testutil.py
- testsuite/tests/ado/T13242a.stderr
- testsuite/tests/annotations/should_fail/annfail10.stderr
- testsuite/tests/backpack/cabal/bkpcabal07/Makefile
- testsuite/tests/backpack/should_compile/T20396.stderr
- testsuite/tests/backpack/should_fail/bkpfail17.stderr
- testsuite/tests/bytecode/TLinkable/all.T
- testsuite/tests/cabal/T12485/Makefile
- + testsuite/tests/cabal/T27013a/Makefile
- + testsuite/tests/cabal/T27013a/Setup.hs
- + testsuite/tests/cabal/T27013a/all.T
- + testsuite/tests/cabal/T27013a/composition.cabal
- + testsuite/tests/cabal/T27013a/src/Data/Composition.hs
- + testsuite/tests/cabal/T27013d/Composition.hs
- + testsuite/tests/cabal/T27013d/Makefile
- + testsuite/tests/cabal/T27013d/T27013d.stdout
- + testsuite/tests/cabal/T27013d/all.T
- testsuite/tests/callarity/unittest/CallArity1.hs
- 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/concurrent/should_run/T16761.hs
- + testsuite/tests/concurrent/should_run/T16761.stdout
- testsuite/tests/concurrent/should_run/T27105.hs
- testsuite/tests/concurrent/should_run/all.T
- testsuite/tests/corelint/LintEtaExpand.hs
- testsuite/tests/corelint/T21115b.stderr
- + testsuite/tests/corelint/T27374.hs
- testsuite/tests/corelint/all.T
- testsuite/tests/count-deps/CountDepsParser.stdout
- testsuite/tests/deSugar/should_compile/T13208.stdout
- testsuite/tests/deSugar/should_compile/T16615.stderr
- testsuite/tests/deSugar/should_compile/T2431.stderr
- + testsuite/tests/deSugar/should_run/LazyFieldAnnotationsSemantics.hs
- + testsuite/tests/deSugar/should_run/LazyFieldAnnotationsSemantics.stdout
- testsuite/tests/deSugar/should_run/all.T
- testsuite/tests/default/DefaultImportFail01.stderr
- testsuite/tests/default/DefaultImportFail02.stderr
- testsuite/tests/default/DefaultImportFail03.stderr
- testsuite/tests/default/DefaultImportFail04.stderr
- testsuite/tests/default/DefaultImportFail05.stderr
- testsuite/tests/default/DefaultImportFail07.stderr
- testsuite/tests/default/T25775.stderr
- testsuite/tests/deriving/should_compile/T14682.stderr
- testsuite/tests/deriving/should_compile/T20496.stderr
- testsuite/tests/diagnostic-codes/codes.stdout
- testsuite/tests/dmdanal/should_compile/T23398.stderr
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
- + testsuite/tests/driver/T26423/Hello.hs
- + testsuite/tests/driver/T26423/Makefile
- + testsuite/tests/driver/T26423/T26423.hs
- + testsuite/tests/driver/T26423/T26423.stderr
- + testsuite/tests/driver/T26423/T26423.stdout
- + testsuite/tests/driver/T26423/all.T
- + testsuite/tests/driver/T26423/test/Test.hs
- + testsuite/tests/driver/T26423/test/test.pkg
- + testsuite/tests/driver/T27013b/Makefile
- + testsuite/tests/driver/T27013b/T27013b.stdout
- + testsuite/tests/driver/T27013b/X.hs
- + testsuite/tests/driver/T27013b/all.T
- + testsuite/tests/driver/T27013c/Makefile
- + testsuite/tests/driver/T27013c/T27013c.stdout
- + testsuite/tests/driver/T27013c/X.hs
- + testsuite/tests/driver/T27013c/all.T
- + testsuite/tests/driver/T27013e/T27013e.hs
- + testsuite/tests/driver/T27013e/T27013e.stderr
- + testsuite/tests/driver/T27013e/all.T
- + testsuite/tests/driver/T27013f/T27013f.hs
- + testsuite/tests/driver/T27013f/T27013f.stderr
- + testsuite/tests/driver/T27013f/all.T
- + testsuite/tests/driver/T27013g/T27013g.hs
- + testsuite/tests/driver/T27013g/all.T
- + testsuite/tests/driver/T27013h/GHC/Essentials.hs
- + testsuite/tests/driver/T27013h/T27013h.stderr
- + testsuite/tests/driver/T27013h/all.T
- + testsuite/tests/driver/T27013h/unitT27013h
- + testsuite/tests/driver/T27013i/T27013i.hs
- + testsuite/tests/driver/T27013i/T27013i.stderr
- + testsuite/tests/driver/T27013i/all.T
- + testsuite/tests/driver/T27461/Main1.hs
- + testsuite/tests/driver/T27461/Main2.hs
- + testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461a.stderr
- + testsuite/tests/driver/T27461/T27461b.script
- + testsuite/tests/driver/T27461/T27461b.stderr
- + testsuite/tests/driver/T27461/T27461b.stdout
- + testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Bar.hs
- testsuite/tests/driver/T3007/A/Internal.hs
- testsuite/tests/driver/T3007/Makefile
- testsuite/tests/driver/T4437.hs
- + testsuite/tests/driver/TUnitInfo/Foo.hs
- + testsuite/tests/driver/TUnitInfo/Makefile
- + testsuite/tests/driver/TUnitInfo/all.T
- + testsuite/tests/driver/TUnitInfo/genMhu.sh
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-mhu.stdout
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-single.stdout
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space.hs
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space.stdout
- testsuite/tests/driver/all.T
- testsuite/tests/driver/make-prim/Makefile
- testsuite/tests/driver/mostly-static/Makefile
- testsuite/tests/driver/multipleHomeUnits/Makefile
- testsuite/tests/driver/multipleHomeUnits/all.T
- + testsuite/tests/driver/multipleHomeUnits/essentials-home/GHC/Essentials.hs
- + testsuite/tests/driver/multipleHomeUnits/essentials-order-base/B.hs
- + testsuite/tests/driver/multipleHomeUnits/essentials-order-user/U.hs
- + testsuite/tests/driver/multipleHomeUnits/essentials-user/M.hs
- testsuite/tests/driver/multipleHomeUnits/multipleHomeUnitsModuleVisibility.stderr
- + testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_essentials.stdout
- + testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_essentials_order.stderr
- + testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_essentials_recomp.stdout
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsHome
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsHomeHidden
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsOrderBase
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsOrderUser
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsUser
- + testsuite/tests/driver/multipleHomeUnits/unitEssentialsUserHidden
- testsuite/tests/driver/recomp24656/Makefile
- testsuite/tests/driver/recomp24656/recomp24656.stdout
- testsuite/tests/ffi/should_fail/all.T
- + testsuite/tests/ffi/should_fail/ccfail006.hs
- + testsuite/tests/ffi/should_fail/ccfail006.stderr
- + testsuite/tests/ffi/should_fail/ccfail007.hs
- + testsuite/tests/ffi/should_fail/ccfail007.stderr
- + testsuite/tests/ffi/should_fail/ccfail008.hs
- + testsuite/tests/ffi/should_fail/ccfail008.stderr
- + testsuite/tests/ffi/should_fail/ccfail009.hs
- + testsuite/tests/ffi/should_fail/ccfail009.stderr
- + testsuite/tests/ghc-api/EssentialsCoverage.hs
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/T8628.hs
- testsuite/tests/ghc-api/all.T
- testsuite/tests/ghc-api/downsweep/PartialDownsweep.hs
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/ghc-e/should_fail/T18441fail7.stderr
- testsuite/tests/ghc-e/should_run/ghc-e005.stderr
- testsuite/tests/ghci.debugger/scripts/break006.stderr
- testsuite/tests/ghci.debugger/scripts/print019.stderr
- testsuite/tests/ghci/T13786/all.T
- testsuite/tests/ghci/T16793/T16793.stdout
- testsuite/tests/ghci/T18060/T18060.stdout
- + testsuite/tests/ghci/T27532/Makefile
- + testsuite/tests/ghci/T27532/T27532.stdout
- + testsuite/tests/ghci/T27532/T27532j4.stdout
- + testsuite/tests/ghci/T27532/a.script
- + testsuite/tests/ghci/T27532/all.T
- + testsuite/tests/ghci/T27532/b.script
- + testsuite/tests/ghci/T27532/genT27532Modules
- testsuite/tests/ghci/linking/all.T
- testsuite/tests/ghci/linking/dyn/all.T
- testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout
- testsuite/tests/ghci/scripts/T4175.stdout
- testsuite/tests/ghci/scripts/T8469.stdout
- testsuite/tests/ghci/scripts/T8535.stdout
- testsuite/tests/ghci/scripts/T9881.stdout
- testsuite/tests/ghci/scripts/all.T
- testsuite/tests/ghci/scripts/ghci020.stdout
- testsuite/tests/ghci/scripts/ghci064.stdout
- testsuite/tests/ghci/should_run/T10145.stdout
- testsuite/tests/ghci/should_run/T18594.stdout
- testsuite/tests/haddock/haddock_examples/haddock.Test.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/hiefile/should_compile/T24493.stderr
- testsuite/tests/hiefile/should_run/T23120.stdout
- + testsuite/tests/hiefile/should_run/T25709.hs
- + testsuite/tests/hiefile/should_run/T25709.stdout
- testsuite/tests/hiefile/should_run/all.T
- testsuite/tests/iface/IfaceSharingIfaceType.hs
- testsuite/tests/iface/IfaceSharingName.hs
- testsuite/tests/indexed-types/should_fail/T12522a.stderr
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-prim-exports.stdout
- testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32
- testsuite/tests/interface-stability/template-haskell-exports.stdout
- testsuite/tests/javascript/Makefile
- testsuite/tests/javascript/T24495.hs
- + testsuite/tests/module/T27380.hs
- + testsuite/tests/module/T27380.stderr
- testsuite/tests/module/all.T
- testsuite/tests/module/mod184.stderr
- testsuite/tests/module/mod185.stderr
- testsuite/tests/numeric/should_compile/T14170.stdout
- testsuite/tests/numeric/should_compile/T14465.stdout
- testsuite/tests/numeric/should_compile/T23907.stderr
- testsuite/tests/numeric/should_compile/T7116.stdout
- testsuite/tests/overloadedlists/should_fail/overloadedlistsfail01.stderr
- testsuite/tests/package/T20010/all.T
- testsuite/tests/package/all.T
- 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/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail10.stderr
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail11.stderr
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail13.stderr
- testsuite/tests/parser/should_fail/RecordDotSyntaxFail8.stderr
- testsuite/tests/parser/should_fail/T16270h.hs
- testsuite/tests/partial-sigs/should_compile/ExtraConstraints3.stderr
- testsuite/tests/partial-sigs/should_fail/NamedWildcardsNotInMonotype.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/T26465.stderr
- + testsuite/tests/patsyn/should_fail/T27440d.hs
- + testsuite/tests/patsyn/should_fail/T27440d.stderr
- testsuite/tests/patsyn/should_fail/all.T
- + testsuite/tests/perf/compiler/FamAppCachePerf.hs
- + testsuite/tests/perf/compiler/SimplCastPerf.hs
- + testsuite/tests/perf/compiler/T27336.hs
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/should_run/ByteCodeAsm.hs
- testsuite/tests/plugins/all.T
- testsuite/tests/plugins/defaulting-plugin/DefaultInterference.hs
- testsuite/tests/plugins/defaulting-plugin/DefaultInvalid.hs
- testsuite/tests/plugins/defaulting-plugin/DefaultMultiParam.hs
- testsuite/tests/plugins/plugins09.stdout
- testsuite/tests/plugins/plugins10.stdout
- testsuite/tests/plugins/plugins11.stdout
- testsuite/tests/plugins/simple-plugin/Simple/ReplacePlugin.hs
- testsuite/tests/plugins/static-plugins.stdout
- + testsuite/tests/pmcheck/should_compile/T27314.hs
- testsuite/tests/pmcheck/should_compile/all.T
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/process/T3994.hs
- testsuite/tests/process/process009.hs
- testsuite/tests/process/process009.stdout
- testsuite/tests/profiling/should_run/callstack001.stdout
- testsuite/tests/profiling/should_run/callstack002.stderr
- testsuite/tests/profiling/should_run/callstack002.stdout
- testsuite/tests/rename/should_compile/T3103/Foreign/Ptr.hs
- testsuite/tests/rename/should_compile/T3103/GHC/Base.lhs
- testsuite/tests/rename/should_compile/T3103/GHC/Word.hs
- testsuite/tests/rename/should_compile/T3103/test.T
- + testsuite/tests/rename/should_fail/T21101.hs
- + testsuite/tests/rename/should_fail/T21101.stderr
- testsuite/tests/rename/should_fail/T9815.stderr
- testsuite/tests/rename/should_fail/T9815b.stderr
- testsuite/tests/rename/should_fail/T9815bghci.stderr
- testsuite/tests/rename/should_fail/T9815ghci.stderr
- testsuite/tests/rename/should_fail/all.T
- testsuite/tests/rep-poly/RepPolyRecordPattern.hs
- testsuite/tests/rep-poly/RepPolyRecordPattern.stderr
- testsuite/tests/rep-poly/RepPolyRecordUpdate.stderr
- testsuite/tests/rep-poly/T20113.stderr
- − testsuite/tests/rep-poly/T20363.stderr
- − testsuite/tests/rep-poly/T20363_show_co.hs
- − testsuite/tests/rep-poly/T20363_show_co.stderr
- − testsuite/tests/rep-poly/T20363b.stderr
- + testsuite/tests/rep-poly/T20363c.hs
- + testsuite/tests/rep-poly/T27639.hs
- testsuite/tests/rep-poly/all.T
- testsuite/tests/roles/should_compile/Roles1.stderr
- testsuite/tests/roles/should_compile/Roles13.stderr
- testsuite/tests/roles/should_compile/Roles14.stderr
- testsuite/tests/roles/should_compile/Roles2.stderr
- testsuite/tests/roles/should_compile/Roles3.stderr
- testsuite/tests/roles/should_compile/Roles4.stderr
- testsuite/tests/roles/should_compile/T8958.stderr
- + testsuite/tests/rts/T19048.hs
- + testsuite/tests/rts/T19048.stdout
- + testsuite/tests/rts/T27585.hs
- + testsuite/tests/rts/T27585.stdout
- testsuite/tests/rts/all.T
- testsuite/tests/rts/linker/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/show-bytecode/Example.hs
- + testsuite/tests/show-bytecode/Makefile
- + testsuite/tests/show-bytecode/all.T
- + testsuite/tests/show-bytecode/normalize
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout-javascript-unknown-ghcjs
- + testsuite/tests/show-bytecode/show-bytecode-hpc.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout-javascript-unknown-ghcjs
- testsuite/tests/simd/should_run/doublex2_arith.hs
- testsuite/tests/simd/should_run/doublex2_arith.stdout
- testsuite/tests/simd/should_run/doublex2_arith_baseline.hs
- testsuite/tests/simd/should_run/doublex2_arith_baseline.stdout
- testsuite/tests/simd/should_run/doublex2_fma.hs
- testsuite/tests/simd/should_run/doublex2_fma.stdout
- testsuite/tests/simd/should_run/simd010.hs
- testsuite/tests/simplCore/should_compile/OpaqueNoCastWW.stderr
- testsuite/tests/simplCore/should_compile/T13543.stderr
- testsuite/tests/simplCore/should_compile/T16038/T16038.stdout
- testsuite/tests/simplCore/should_compile/T24229a.stderr
- testsuite/tests/simplCore/should_compile/T24229b.stderr
- + 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/T3717.stderr
- testsuite/tests/simplCore/should_compile/T3772.stdout
- testsuite/tests/simplCore/should_compile/T4081.stderr
- testsuite/tests/simplCore/should_compile/T4908.stderr
- testsuite/tests/simplCore/should_compile/T4930.stderr
- testsuite/tests/simplCore/should_compile/T7360.stderr
- testsuite/tests/simplCore/should_compile/T8274.stdout
- testsuite/tests/simplCore/should_compile/T9400.stderr
- testsuite/tests/simplCore/should_compile/all.T
- testsuite/tests/simplCore/should_compile/noinline01.stderr
- testsuite/tests/simplCore/should_compile/par01.stderr
- testsuite/tests/simplCore/should_compile/rule2.stderr
- testsuite/tests/simplCore/should_compile/str-rules.hs
- testsuite/tests/splice-imports/SI35.hs
- testsuite/tests/tcplugins/ArgsPlugin.hs
- testsuite/tests/tcplugins/EmitWantedPlugin.hs
- testsuite/tests/tcplugins/RewritePlugin.hs
- testsuite/tests/tcplugins/T26395_Plugin.hs
- testsuite/tests/tcplugins/TcPlugin_RewritePerf.hs
- testsuite/tests/tcplugins/TcPlugin_RewritePerf.stderr
- testsuite/tests/tcplugins/TyFamPlugin.hs
- + testsuite/tests/th/AssocDefaultNotAssoc.hs
- + testsuite/tests/th/AssocDefaultNotAssoc.stderr
- testsuite/tests/th/T14741.hs
- + testsuite/tests/th/T20902.hs
- testsuite/tests/th/T21547.stderr
- testsuite/tests/th/T26568.stderr
- + testsuite/tests/th/T27013th.hs
- + testsuite/tests/th/TH_InvalidTopDecl.hs
- + testsuite/tests/th/TH_InvalidTopDecl.stderr
- testsuite/tests/th/TH_Roles2.stderr
- + testsuite/tests/th/TH_cvt_DefaultDataInstDecl.hs
- + testsuite/tests/th/TH_cvt_DefaultDataInstDecl.stderr
- + testsuite/tests/th/TH_cvt_GadtNoCons.hs
- + testsuite/tests/th/TH_cvt_GadtNoCons.stderr
- + testsuite/tests/th/TH_cvt_IllegalDeclaration.hs
- + testsuite/tests/th/TH_cvt_IllegalDeclaration.stderr
- + testsuite/tests/th/TH_cvt_IllegalSumAlt.hs
- + testsuite/tests/th/TH_cvt_IllegalSumAlt.stderr
- + testsuite/tests/th/TH_cvt_InvalidCCallImpent.hs
- + testsuite/tests/th/TH_cvt_InvalidCCallImpent.stderr
- + testsuite/tests/th/TH_cvt_InvalidTyFamInstLHS.hs
- + testsuite/tests/th/TH_cvt_InvalidTyFamInstLHS.stderr
- + testsuite/tests/th/TH_cvt_InvalidTypeInstanceHeader.hs
- + testsuite/tests/th/TH_cvt_InvalidTypeInstanceHeader.stderr
- + testsuite/tests/th/TH_cvt_RecGadtNoCons.hs
- + testsuite/tests/th/TH_cvt_RecGadtNoCons.stderr
- + testsuite/tests/th/TH_cvt_SumAltArityExceeded.hs
- + testsuite/tests/th/TH_cvt_SumAltArityExceeded.stderr
- + testsuite/tests/th/TH_pragmaSpecOld.hs
- + testsuite/tests/th/TH_pragmaSpecOld.stderr
- testsuite/tests/th/all.T
- + testsuite/tests/typecheck/should_compile/LazyFieldAnnotations.hs
- testsuite/tests/typecheck/should_compile/T13032.stderr
- testsuite/tests/typecheck/should_compile/T14273.stderr
- testsuite/tests/typecheck/should_compile/T18406b.stderr
- testsuite/tests/typecheck/should_compile/T18529.stderr
- + testsuite/tests/typecheck/should_compile/T27557.hs
- + testsuite/tests/typecheck/should_compile/WarnDefaultedCallStack.hs
- + testsuite/tests/typecheck/should_compile/WarnDefaultedCallStack.stderr
- testsuite/tests/typecheck/should_compile/all.T
- testsuite/tests/typecheck/should_compile/holes.stderr
- testsuite/tests/typecheck/should_compile/holes2.stderr
- testsuite/tests/typecheck/should_compile/holes3.stderr
- testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr
- testsuite/tests/typecheck/should_compile/valid_hole_fits.stderr
- testsuite/tests/typecheck/should_fail/LazyFieldsDisabled.stderr
- + testsuite/tests/typecheck/should_fail/LazyFieldsDisabledStrictData.hs
- + testsuite/tests/typecheck/should_fail/LazyFieldsDisabledStrictData.stderr
- testsuite/tests/typecheck/should_fail/T12921.stderr
- testsuite/tests/typecheck/should_fail/T14884.stderr
- testsuite/tests/typecheck/should_fail/T15067.stderr
- testsuite/tests/typecheck/should_fail/T15883b.stderr
- testsuite/tests/typecheck/should_fail/T15883c.stderr
- testsuite/tests/typecheck/should_fail/T15883d.stderr
- testsuite/tests/typecheck/should_fail/T21130.stderr
- + testsuite/tests/typecheck/should_fail/T26532.hs
- + testsuite/tests/typecheck/should_fail/T26532.stderr
- testsuite/tests/typecheck/should_fail/T3323.stderr
- testsuite/tests/typecheck/should_fail/T5095.stderr
- testsuite/tests/typecheck/should_fail/T5300.stderr
- testsuite/tests/typecheck/should_fail/T7279.stderr
- testsuite/tests/typecheck/should_fail/T9858b.stderr
- testsuite/tests/typecheck/should_fail/TcStaticPointersFail02.stderr
- testsuite/tests/typecheck/should_fail/TyAppPat_PatternBindingExistential.stderr
- testsuite/tests/typecheck/should_fail/all.T
- testsuite/tests/typecheck/should_fail/tcfail072.stderr
- testsuite/tests/typecheck/should_fail/tcfail097.stderr
- testsuite/tests/typecheck/should_fail/tcfail133.stderr
- testsuite/tests/typecheck/should_run/T22510.stdout
- testsuite/tests/unboxedsums/UbxSumLevPoly.hs
- testsuite/tests/unboxedsums/unboxedsums_unit_tests.hs
- + 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/T27586a.hs
- + testsuite/tests/vdq-rta/should_fail/T27586a.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586b.hs
- + testsuite/tests/vdq-rta/should_fail/T27586b.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586c.hs
- + testsuite/tests/vdq-rta/should_fail/T27586c.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
- testsuite/tests/warnings/should_compile/DerivingTypeable.stderr
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Transform.hs
- utils/check-exact/Utils.hs
- utils/check-ppr/Main.hs
- utils/deriveConstants/Main.hs
- utils/genprimopcode/Main.hs
- utils/haddock/haddock-api/src/Haddock.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Backends/LaTeX.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/GhcUtils.hs
- utils/haddock/haddock-api/src/Haddock/Interface.hs
- utils/haddock/haddock-api/src/Haddock/Interface/AttachInstances.hs
- utils/haddock/haddock-api/src/Haddock/Interface/Create.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/5ffc4c50023080a4bece34e5a2fd02…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5ffc4c50023080a4bece34e5a2fd02…
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/dcoutts/capability-yield] 15 commits: Move several Capability utils from Schedule.{c,h} to Capability.{c,h}
by Duncan Coutts (@dcoutts) 19 Aug '26
by Duncan Coutts (@dcoutts) 19 Aug '26
19 Aug '26
Duncan Coutts pushed to branch wip/dcoutts/capability-yield at Glasgow Haskell Compiler / GHC
Commits:
15bbf0ed by Duncan Coutts at 2026-08-19T13:43:20+01:00
Move several Capability utils from Schedule.{c,h} to Capability.{c,h}
They probably should have been there all along. This means all the
pending_sync functionality is within Capability.{c,h}. We only expose
pending_sync for the purpose of inline header functions.
- - - - -
d8b84119 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Shuffle the pending sync type declarations for better readability
Move them together into the section with the related functions that use
them.
Also drop the legacy use of the C 'volatile' modifier on the
pending_sync variable. We use C atomics for such access, not volatile.
- - - - -
adcf739e by Duncan Coutts at 2026-08-19T13:43:20+01:00
Rename returning task queue helpers
Follows a naming convention elsewhere. It also gives us suitable names
to distinguish appending vs prepending to the queue, and we're about to
add a prepend operation.
- - - - -
46ec6801 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Add a prepend operation for the returning task queue
With a pending sync (e.g. for GC), we really want to be able to
prioritise the task waiting on the sync over all other returning tasks.
To do that we will need to prepend to the queue rather than append.
- - - - -
5643d6fb by Duncan Coutts at 2026-08-19T13:43:20+01:00
Split waitForSomeCapability out of waitForCapability and adjust callers
Previously waitForCapability had a general interface covering serveral
situations, but it's more useful and easier to understand with two more
specialised functions.
Previously waitForCapability had an in/out Capability parameter: if the
cap was non-null then it would wait to acquire that specific capability
(though in this case it had to be the capability the task was already
associated with), or if it was null then it would pick a suitable
capability, associate the task with it and wait for that capability.
So overall, this required and in/out parameter and suggested that the
capability returned could be different from the one passed in. That was
true if the task was associated with no capability, but false if it was.
This complicated the call sites where we know that the task does have an
associated capability, as it implied the capability could change when in
fact it cannot.
So we now split it: waitForSomeCapability handles this general case
where the task may or may not have an associated capability, and it
returns that new capability. And the waitForCapability now assumes that
the task is associated with a capability and does not need to return
anything. Neither function needs a Capability *cap argument any more.
This simplifies several call sites.
- - - - -
0a6ae704 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Update stale comments related to waitFor[Some]Capability
In particular waitForCapability cannot change the capability.
Update a comment about grabbing a new Task in performGC_. This comment was
accurate in 2006, but it is no longer accurate since newBoundTask does not
in fact produce a new Task (typically). The right thing now is to talk about
InCalls, not tasks. That said, the code is still correct since
newBoundTask will push a new incall, but also deal with the general case
of an OS thread that does not yet have an associated Task.
- - - - -
47a69f63 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Introduce waitForCapability_ with additional priority arg
Split waitForCapability into a wrapper with the existing type and a
worker with an extra argument.
The new high_priority argument controls whether the task is appended or
prepended to the returing task queue. The default, used by the
waitForCapability wrapper, is false, meaning append to the end of the
queue. This gives fairness.
The high_priority==true case will be used in the subsequent commit.
- - - - -
f5041fa8 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Make acquireAllCapabilities use high_priority on waitForCapability_
As discussed in issue #27473, a sync of all capabilities is something
that needs to happen promptly (but often doesn't).
One source of delay is that acquireAllCapabilities using
waitForCapability would put the task trying to acquire each capability
at the _end_ of the returning task queue. This gave every other returing
task a full timeslice to run. Meanwhile, several other capabilities are
blocked waiting for the sync to complete, leading to a loss of
throughput.
We use the new high_priority arg to waitForCapability_ to ensure that
the requesting task is put on the front of the returing task queue. This
will ensure that releaseCapability_ will prioritise giving the
capability to the task requesting the sync.
- - - - -
7bda759f by Duncan Coutts at 2026-08-19T13:43:20+01:00
In releaseCapability_ make the pending_sync case self-contained
Previously the pending_sync case had to be checked _after_ the returning
tasks case, since one of the possibilities (indeed the more likely
possibility) is that the task calling waitForCapability will have
enqueued itself as a returning task.
Now we make the pending_sync case self-contained. We note in a comment
the two possibilities: either the task calling waitForCapability has
enqueued itself already and is waiting, or it's not got there yet. We
can handle the first case by giving the capability to the task at the
head of the returning tasks queue, and the second case by leaving the
capability free.
Another way to look at this, is that we move a special case of handling
of the returning task case into the pending_sync case. That special case
being a returing task during a pending sync.
This makes the order of handling returning tasks vs pending sync
independent. This is good, because really they're in the wrong priority
order and we want to flip them around.
- - - - -
c78e52b9 by Duncan Coutts at 2026-08-19T13:43:20+01:00
In releaseCapability_ prioritise pending sync over returning tasks
Fixes issue #27460
As explained in the issue, a pending sync (e.g. for GC) should be dealt
with promptly. Returning tasks are a lower priority.
Historically however we had to check returning tasks first, because the
synchronisation mechanism mixed up the task doing a sync with the tasks
returning from safe FFI calls. The task performing the sync was very
likely to be queued on the returning task list (and historically it was
at the _end_ of this list!).
We have now arranged that the task performing the sync is at the front
of the returning task list, and in the pending sync case we now check
the returning task list and run the first task from there if it there is
one.
This is by no means perfect, but it is better. See issue #27473 for a
more general issue of cleaning up the design of the pending sync.
- - - - -
ff821186 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Split releaseCapability_ to hide the rarely-used bool parameter
releaseCapability_ had an extra bool param: always_wakeup. This is
rather a design wart. The reasons are now documented in comments.
Reduce the number of cases where we need to use always_wakeup == true.
There is now only one: prodCapability. That is because
releaseCapability_ does not know about signals (which is an example of
the design wart).
We can thus simplify most callers by splitting releaseCapability_ into a
worker and wrapper, where the wrapper does not have to have the
always_wakeup parameter. This simplifies all call sites except one.
This will also reduce churn at call site when we add a second (rarely
used) parameter in the subsequent commit.
- - - - -
2eda7b95 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Extend releaseCapability__ with an extra wakeup_worker modifier
Document within releaseCapability__ the basic approach of looking for a
series of conditions in priority order and acting on them. Explain the
existing modifier within that understanding.
Then add a new modifier, wakeup_worker and explain it in similar terms.
What it does is skip two of the conditions in the priority list, with
the effect that we prioritise waking up a worker task over a returning
task or bound task.
This feature is not yet used in this commit, but it will be used as
part of a scheme to allow in-RTS I/O managers in the threaded RTS. This
scheme will make use of being able to start a background worker thread,
and that will use this feature to start it promptly.
Also correct the yieldCapability docs to cover all the conditions, and
in priority order for consistency.
- - - - -
b3246103 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Move enqueueWorker next to where it is used.
It's not general purpose at all. It's very specifically crafted to work
with it's only caller: yieldCapability. It does very suprising things
like releaseCapability_, release locks and terminate threads. This logic
would be much clearer if done within yieldCapability.
- - - - -
c0c6029d by Duncan Coutts at 2026-08-19T13:43:20+01:00
Move code out of enqueueWorker and into releaseCapability_
Instead of directly releasing locks and terminating tasks, have it
return whether the enqueue was successful or not. In the latter case,
releaseCapability_ itself will release locks and terminate the task.
This makes the logic of releaseCapability_ a lot clearer. Fiddling with
tasks is what releaseCapability_ does, so it's better not to try and
encapsulate this within a helper function.
- - - - -
5ffc4c50 by Duncan Coutts at 2026-08-19T13:43:20+01:00
Clarify the logic and control flow in yieldCapability
yieldCapability is unfortunately a bit complicated. This change
restructures things slightly but should keep the behaviour the same.
Previously after calling releaseCapability_ we had a bunch of
alternatives, where in each branch we would use RELEASE_LOCK(cap->lock)
and do various things before/after the lock is released. This was a bit
hard to follow, or to extend (which we need to do).
So now we have unconditional acquire and release of the cap->lock, so
it's clear where that happens, with releaseCapability_ in between. Then
in between these steps we have the various other pre/post actions. Some
before releaseCapability_, some after while holing the lock, and some
after having released the lock.
We explain this structure in a longer comment, and refer back to the
structure from the code.
- - - - -
6 changed files:
- rts/Capability.c
- rts/Capability.h
- rts/Messages.c
- rts/RtsAPI.c
- rts/Schedule.c
- rts/Schedule.h
Changes:
=====================================
rts/Capability.c
=====================================
@@ -58,11 +58,25 @@ Capability **capabilities;
// locking, so we don't do that.
static Capability *last_free_capability[MAX_NUMA_NODES];
+#if defined(THREADED_RTS)
/*
* Indicates that the RTS wants to synchronise all the Capabilities
* for some reason. All Capabilities should yieldCapability().
+ *
+ * This is an atomic variable, all accesses must use appropriate atomics.
+ */
+PendingSync *pending_sync = NULL;
+
+/*
+ * sync_finished_cond allows threads which do not own any capability (e.g. the
+ * concurrent mark thread) to participate in the sync protocol. In particular,
+ * if such a thread requests a sync while sync is already in progress it will
+ * block on sync_finished_cond, which will be signalled when the sync is
+ * finished (by releaseAllCapabilities).
*/
-PendingSync * volatile pending_sync = 0;
+static Condition sync_finished_cond;
+static Mutex sync_finished_mutex;
+#endif
// Number of logical NUMA nodes
uint32_t n_numa_nodes;
@@ -204,7 +218,7 @@ anySparks (void)
#if defined(THREADED_RTS)
STATIC_INLINE void
-newReturningTask (Capability *cap, Task *task)
+appendToReturningTaskQueue (Capability *cap, Task *task)
{
ASSERT_LOCK_HELD(&cap->lock);
ASSERT(task->next == NULL);
@@ -222,8 +236,25 @@ newReturningTask (Capability *cap, Task *task)
ASSERT_RETURNING_TASKS(cap,task);
}
+STATIC_INLINE void
+prependToReturningTaskQueue (Capability *cap, Task *task)
+{
+ ASSERT_LOCK_HELD(&cap->lock);
+ ASSERT(task->next == NULL);
+ task->next = cap->returning_tasks_hd;
+ cap->returning_tasks_hd = task;
+ if (cap->returning_tasks_tl == NULL) {
+ cap->returning_tasks_tl = task;
+ }
+
+ // See Note [Data race in shouldYieldCapability] in Schedule.c.
+ RELAXED_ADD(&cap->n_returning_tasks, 1);
+
+ ASSERT_RETURNING_TASKS(cap,task);
+}
+
STATIC_INLINE Task *
-popReturningTask (Capability *cap)
+popReturningTaskQueue (Capability *cap)
{
ASSERT_LOCK_HELD(&cap->lock);
Task *task;
@@ -349,6 +380,11 @@ initCapability (Capability *cap, uint32_t i)
* ------------------------------------------------------------------------- */
void initCapabilities (void)
{
+#if defined(THREADED_RTS)
+ initMutex(&sync_finished_mutex);
+ initCondition(&sync_finished_cond);
+#endif
+
/* Declare a couple capability sets representing the process and
clock domain. Each capability will get added to these capsets. */
traceCapsetCreate(CAPSET_OSPROCESS_DEFAULT, CapsetTypeOsProcess);
@@ -523,7 +559,7 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task)
#endif
/* ----------------------------------------------------------------------------
- * releaseCapability_
+ * releaseCapability and releaseCapability_
*
* This serves two purposes:
*
@@ -533,32 +569,60 @@ giveCapabilityToTask (Capability *cap USED_IF_DEBUG, Task *task)
*
* 2. There is no current task (cap->task == NULL), and thus the Capability
* is idle, and we want to wake up an idle Task to animate the Capability.
- * In this case set always_wakeup. See also prodCapability.
- *
- * Setting the always_wakeup parameter (almost) ensures that the capability is
- * not left idle: even if there is no known work to do, the capability will be
- * given to a worker task. There are two exceptions to this:
- * 1. if there is a pending sync then the capability is left idle, but in
- * anticipation of whichever task initiated the sync picking it up shortly.
- * 2. if the scheduler is shutting down and there are no threads on the run
- * queue and there are no spare workers then the capability is left idle.
- * It is not entirely clear if this corner case is intentional.
+ * See also prodCapability.
*
- * The caller must hold cap->lock and will still hold it after the call returns.
+ * Difference:
+ * - releaseCapability the caller /must not/ hold cap->lock.
+ * - releaseCapability_ the caller /must/ hold cap->lock.
*
* N.B. May need to take all_tasks_mutex, if it needs to start a new task.
*
* ------------------------------------------------------------------------- */
#if defined(THREADED_RTS)
-void
-releaseCapability_ (Capability* cap,
- bool always_wakeup)
+static void releaseCapability__ (Capability* cap,
+ bool always_wakeup,
+ bool wakeup_worker);
+
+void releaseCapability (Capability* cap)
+{
+ ACQUIRE_LOCK(&cap->lock);
+ releaseCapability__(cap, false /*always_wakeup*/,
+ false /*wakeup_worker*/);
+ RELEASE_LOCK(&cap->lock);
+}
+
+void releaseCapability_ (Capability* cap)
+{
+ releaseCapability__(cap, false /*always_wakeup*/,
+ false /*wakeup_worker*/);
+}
+
+/* The fact that we need an always_wakeup parameter for releaseCapability__ is
+ * a design wart. The Capability layer knows about most but not all sources of
+ * work for a capability. The always_wakeup parameter is there to account for
+ * the ones it does /not/ know about (which is I/O manager stuff: I/O, timers
+ * & signals).
+ *
+ * There are two sane designs:
+ * 1. the Capability layer knows nothing about the sources of work that the
+ * scheduler might want to do
+ * 2. the Capability layer knows *everything* about the sources of work.
+ *
+ * In neither sane design would we need this parameter. In the first design we
+ * would know externally if we should be waking or releasing a task and would
+ * instruct accordingly (probably by splitting releaseCapability to cover the
+ * two cases). In the second design, it would simply know about all the sources
+ * and so again there would be no need.
+ */
+
+static void releaseCapability__ (Capability* cap,
+ bool always_wakeup,
+ bool wakeup_worker)
{
{
Task *task = cap->running_task;
- ASSERT(task || always_wakeup);
// To cover purpose 2 above, we allow the cap->running_task to be
// NULL, to handle cases where a thread (that is not itself a Task)
// needs to wake up an idle task for the capability.
@@ -572,36 +636,101 @@ releaseCapability_ (Capability* cap,
// Remove the current Task owning the Capability (if any, see purpose 2).
RELAXED_STORE(&cap->running_task, NULL);
- // Check to see whether a worker thread can be given
- // the go-ahead to return the result of an external call..
- if (cap->n_returning_tasks != 0) {
- giveCapabilityToTask(cap,cap->returning_tasks_hd);
- // The Task pops itself from the queue (see waitForCapability())
- return;
- }
+ // We now look for a task to give the capability to, or otherwise we leave
+ // the capability free.
+ //
+ // We take one of these guarded actions, in priority order:
+ //
+ // 1. If there's a pending synchronisation of all capabilities (e.g. GC),
+ // then give the capability to the task performing the sync.
+ // 2. If there's a task returning (e.g. from safe FFI) on this capability,
+ // then give the capability to the first such task.
+ // 3. If the next runnable thread on this capability is a bound thread,
+ // then give the capability to the corresponding bound task.
+ // 4. If there are no spare worker tasks for this capability,
+ // then start one and give the capability to the new task.
+ // 5. If there is some work to do on this capability (e.g. runnable thread),
+ // then give the capability to a worker task.
+ // 6. Otherwise leave the capability free/idle.
+ //
+ // There are two modifiers to this priority list:
+ //
+ // * Setting always_wakeup modifies the case 5 predicate to be always true.
+ // This has the effect of giving the cap to a worker task, rather than
+ // leaving the cap idle, even if there is no obvious work to do.
+ //
+ // * Setting wakeup_worker skips cases 2 & 3. This prioritises waking a
+ // worker over returning tasks or bound tasks. It is also usually used in
+ // combination with always_wakeup.
+
- // If there is a pending sync, then we should just leave the Capability
- // free. The thread trying to sync will be about to call
- // waitForCapability().
+ // Guarded action 1:
+ // If there's a pending synchronisation of all capabilities (e.g. GC),
+ // then give the capability to the task performing the sync.
+ //
+ // If there is a pending sync, the task that requested the sync will
+ // subsequently use acquireAllCapabilities to place itself on the (front of
+ // the) returning_task list (of all capabilities). We will then be in one
+ // of two cases:
+ //
+ // 1. the task that requested the pending sync has already put itself onto
+ // the returning_tasks list; or
+ // 2. the task that requested the pending sync has not yet put itself onto
+ // the returning_tasks list. This is unlikely but possible depending on
+ // how the race is resolved.
//
- // Note: this is *after* we check for a returning task above,
- // because the task attempting to acquire all the capabilities may
- // be currently in waitForCapability() waiting for this
- // capability, in which case simply setting it as free would not
- // wake up the waiting task.
+ // The cap->lock is used by both waitForCapability and releaseCapability_
+ // to ensure we are definitely in one of the two cases above, and not some
+ // hideous mish-mash.
+ //
+ // In the first case we can give the capability to that task. It is highly
+ // likely that the task has prepended itself to the returning task queue,
+ // so we can give the capability to the task at the head of the returning
+ // task queue. It is not a correctness issue however if another returning
+ // task gets run first (indeed this was the historical behaviour).
+ //
+ // Note that there can be false positives for this case: if any other
+ // returning task is queued on the returning tasks list. We will still
+ // incur delays if this occurs, scheduling those returning tasks. It is
+ // likely however that the task performing the sync gets to waiting before
+ // the task running the capability responds to the interrupt signal.
+ //
+ // In the second case we leave the capability free since the task trying to
+ // sync will be about to call waitForCapability().
//
// FIXME: this pending_sync approach is a poor design, hard to understand
// and subject to various unnecessary delays. See issues #27460 and #27473.
//
PendingSync *sync = SEQ_CST_LOAD(&pending_sync);
if (sync && (sync->type != SYNC_GC_PAR || sync->idle[cap->no])) {
- debugTrace(DEBUG_sched, "sync pending, freeing capability %d", cap->no);
+ if (cap->n_returning_tasks != 0) {
+ // The task doing the sync should have used waitForCapability_
+ // using high_priority, so it should be at the head of the queue:
+ debugTrace(DEBUG_sched, "sync pending, passing capability %d", cap->no);
+ giveCapabilityToTask(cap,cap->returning_tasks_hd);
+ // The Task pops itself from the queue (see waitForCapability())
+ } else {
+ debugTrace(DEBUG_sched, "sync pending, freeing capability %d", cap->no);
+ }
return;
}
- // If the next thread on the run queue is a bound thread,
- // give this Capability to the appropriate Task.
- if (!emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
+ // Skip guarded actions 2 & 3 if wakeup_worker. See the list of actions and
+ // modifiers above.
+
+ // Guarded action 2:
+ // If there's a task returning (e.g. from safe FFI) on this capability,
+ // then give the capability to the first such task.
+ if (!wakeup_worker && cap->n_returning_tasks != 0) {
+ giveCapabilityToTask(cap,cap->returning_tasks_hd);
+ // The Task pops itself from the queue (see waitForCapability())
+ return;
+ }
+
+ // Guarded action 3:
+ // If the next runnable thread on this capability is a bound thread,
+ // then give the capability to the bound thread's corresponding task.
+ if (!wakeup_worker && !emptyRunQueue(cap) && peekRunQueue(cap)->bound) {
// Make sure we're not about to try to wake ourselves up
// ASSERT(task != cap->run_queue_hd->bound);
// assertion is false: in schedule() we force a yield after
@@ -612,11 +741,13 @@ releaseCapability_ (Capability* cap,
return;
}
+ // Guarded action 4:
+ // If there are no spare worker tasks for this capability,
+ // then start one and give the capability to the new task.
if (!cap->spare_workers) {
- // Create a worker thread if we don't have one. If the system
- // is interrupted, we only create a worker task if there
- // are threads that need to be completed. If the system is
- // shutting down, we never create a new worker.
+ // If the system is interrupted, we only create a worker task if there
+ // are threads that need to be completed. If the system is shutting
+ // down, we never create a new worker.
if (getSchedState() < SCHED_SHUTTING_DOWN || !emptyRunQueue(cap)) {
debugTrace(DEBUG_sched,
"starting new worker on capability %d", cap->no);
@@ -625,8 +756,9 @@ releaseCapability_ (Capability* cap,
}
}
- // If we have an unbound thread on the run queue, or if there's
- // anything else to do, give the Capability to a worker thread.
+ // Guarded action 5:
+ // If there is some work to do on this capability (e.g. runnable thread),
+ // then give the capability to a worker task.
if (always_wakeup ||
!emptyRunQueue(cap) || !emptyInbox(cap) ||
(!cap->disabled && !emptySparkPoolCap(cap)) || globalWorkToDo()) {
@@ -637,59 +769,14 @@ releaseCapability_ (Capability* cap,
}
}
+ // Guarded action 6:
+ // Otherwise leave the capability free/idle.
#if defined(PROFILING)
cap->r.rCCCS = CCS_IDLE;
#endif
RELAXED_STORE(&last_free_capability[cap->node], cap);
debugTrace(DEBUG_sched, "freeing capability %d", cap->no);
}
-
-void
-releaseCapability (Capability* cap)
-{
- ACQUIRE_LOCK(&cap->lock);
- releaseCapability_(cap, false);
- RELEASE_LOCK(&cap->lock);
-}
-
-void
-releaseAndWakeupCapability (Capability* cap)
-{
- ACQUIRE_LOCK(&cap->lock);
- releaseCapability_(cap, true);
- RELEASE_LOCK(&cap->lock);
-}
-
-static void
-enqueueWorker (Capability* cap)
-{
- Task *task;
-
- task = cap->running_task;
-
- // If the Task is stopped, we shouldn't be yielding, we should
- // be just exiting.
- ASSERT(!task->stopped);
- ASSERT(task->worker);
-
- if (cap->n_spare_workers < MAX_SPARE_WORKERS)
- {
- task->next = cap->spare_workers;
- cap->spare_workers = task;
- cap->n_spare_workers++;
- }
- else
- {
- debugTrace(DEBUG_sched, "%d spare workers already, exiting",
- cap->n_spare_workers);
- releaseCapability_(cap,false);
- // hold the lock until after workerTaskStop; c.f. scheduleWorker()
- workerTaskStop(task);
- RELEASE_LOCK(&cap->lock);
- shutdownThread();
- }
-}
-
#endif
/*
@@ -822,7 +909,7 @@ static Capability * waitForReturnCapability (Task *task)
continue;
}
RELAXED_STORE(&cap->running_task, task);
- popReturningTask(cap);
+ popReturningTaskQueue(cap);
RELEASE_LOCK(&cap->lock);
break;
}
@@ -893,38 +980,65 @@ static Capability * find_capability_for_task(const Task * task)
#endif /* THREADED_RTS */
/* ----------------------------------------------------------------------------
- * waitForCapability (Capability **pCap, Task *task)
+ * Capability *waitForCapability (Task *task)
*
- * Purpose: when an OS thread returns from an external call,
- * it calls waitForCapability() (via Schedule.resumeThread())
- * to wait for permission to enter the RTS & communicate the
- * result of the external call back to the Haskell thread that
- * made it.
+ * Purpose: when an OS thread returns from an external call, it calls
+ * waitForCapability() (via Schedule.resumeThread()) to wait for
+ * permission to enter the RTS & communicate the result of the external
+ * call back to the Haskell thread that made it. The task must already
+ * be associated with a capability.
*
- * pCap is strictly an output.
+ * Capability *waitForSomeCapability (Task *task)
*
+ * Like waitForCapability but the task need not already be associated
+ * with a capability. If it is not associated, an appropriate one will
+ * be chosen. Used in rts_lock(), for calling into the RTS from outside.
* ------------------------------------------------------------------------- */
-void waitForCapability (Capability **pCap, Task *task)
-{
#if !defined(THREADED_RTS)
-
+Capability *waitForSomeCapability (Task *task)
+{
MainCapability.running_task = task;
task->cap = &MainCapability;
- *pCap = &MainCapability;
+ return &MainCapability;
+}
+
+void waitForCapability (Task *task)
+{
+ waitForSomeCapability(task);
+}
#else
- Capability *cap = *pCap;
+static void waitForCapability_ (Task *task,
+ bool high_priority);
+
+Capability *waitForSomeCapability (Task *task)
+{
+ Capability *cap = task->cap;
if (cap == NULL) {
cap = find_capability_for_task(task);
// record the Capability as the one this Task is now associated with.
task->cap = cap;
- } else {
- ASSERT(task->cap == cap);
}
+ waitForCapability_(task, false /*high_priority*/);
+
+ return task->cap;
+}
+
+void waitForCapability (Task *task)
+{
+ waitForCapability_(task, false /*high_priority*/);
+}
+
+static void waitForCapability_ (Task *task,
+ bool high_priority)
+{
+ Capability *cap = task->cap;
+ ASSERT(task->cap);
+
debugTrace(DEBUG_sched, "returning; I want capability %d", cap->no);
ACQUIRE_LOCK(&cap->lock);
@@ -933,7 +1047,11 @@ void waitForCapability (Capability **pCap, Task *task)
RELAXED_STORE(&cap->running_task, task);
RELEASE_LOCK(&cap->lock);
} else {
- newReturningTask(cap,task);
+ if (high_priority) {
+ prependToReturningTaskQueue(cap,task);
+ } else {
+ appendToReturningTaskQueue(cap,task);
+ }
RELEASE_LOCK(&cap->lock);
cap = waitForReturnCapability(task);
}
@@ -945,10 +1063,8 @@ void waitForCapability (Capability **pCap, Task *task)
ASSERT_FULL_CAPABILITY_INVARIANTS(cap, task);
debugTrace(DEBUG_sched, "resuming capability %d", cap->no);
-
- *pCap = cap;
-#endif
}
+#endif
/* ----------------------------------------------------------------------------
* yieldCapability
@@ -957,12 +1073,6 @@ void waitForCapability (Capability **pCap, Task *task)
* when either we know that the Capability should be given to another Task, or
* there is nothing to do right now. One of the following is true:
*
- * - The current Task is a worker, and there's a bound thread at the head of
- * the run queue (or vice versa)
- *
- * - The run queue is empty. We'll be woken up again when there's work to
- * do.
- *
* - Another Task is trying to do parallel GC (pending_sync == SYNC_GC_PAR).
* We should become a GC worker for a while.
*
@@ -970,12 +1080,21 @@ void waitForCapability (Capability **pCap, Task *task)
* SYNC_GC_PAR), either to do a sequential GC, forkProcess, or
* setNumCapabilities. We should give up the Capability temporarily.
*
+ * - There is a Task returning from a safe FFI call.
+ *
+ * - The current Task is a worker, and there's a bound thread at the head of
+ * the run queue (or vice versa)
+ *
+ * - There is no work to do (empty run queue, inbox etc). We'll be woken up
+ * again when there's work to do.
+ *
* When yieldCapability returns *pCap will have been updated to the new
* capability held by the caller.
*
* ------------------------------------------------------------------------- */
#if defined(THREADED_RTS)
+static bool tryEnqueueWorker (Capability* cap);
/* See Note [GC livelock] in Schedule.c for why we have gcAllowed
and return the bool */
@@ -1030,28 +1149,61 @@ yieldCapability
// We must now release the capability and wait to be woken up again.
task->wakeup = false;
+ // What happens next is a bit complicated. It has the following outline:
+ //
+ // 1. take the cap->lock
+ // 2. "various stuff part A", pre-releaseCapability_ holding cap->lock
+ // 3. release the capability
+ // 4. "various stuff part B", post-releaseCapability_ holding cap->lock
+ // 5. release the cap->lock
+ // 6. "various stuff part C", post release cap->lock
+ //
+ // Much of the "various stuff" is also conditional, which complicates
+ // matters further. To try and maintain clarity we use the following
+ // variables in the conditions for the in-between steps.
+ //
+ bool terminate_worker = false;
+ bool task_is_worker = isWorker(task);
+ bool task_is_bound = isBoundTask(task);
+
+ // Step 1: take the cap->lock
ACQUIRE_LOCK(&cap->lock);
- // If this is a worker thread, put it on the spare_workers queue
- if (isWorker(task)) {
- enqueueWorker(cap);
+ // Step 2: "various stuff part A", pre-releaseCapability_ holding cap->lock
+ if (task_is_worker) {
+ // If this is a worker thread, try to put it on the spare_workers
+ // queue or if it is surplus then we will terminate it.
+ terminate_worker = !tryEnqueueWorker(cap);
}
- releaseCapability_(cap, false);
+ // Step 3: release the capability
+ releaseCapability_(cap);
- if (isWorker(task) || isBoundTask(task)) {
- RELEASE_LOCK(&cap->lock);
- cap = waitForWorkerCapability(task);
- } else {
+ // Step 4: "various stuff part B", post-releaseCapability_ holding cap->lock
+ if (terminate_worker) {
+ // hold the lock until after workerTaskStop; c.f. scheduleWorker()
+ workerTaskStop(task);
+ } else if (!task_is_worker && !task_is_bound) {
// Not a worker Task, or a bound Task. The only way we can be woken up
// again is to put ourselves on the returning_tasks queue, so that's
- // what we do. We still hold cap->lock at this point
- // The Task waiting for this Capability does not have it
- // yet, so we can be sure to be woken up later. (see #10545)
- newReturningTask(cap,task);
- RELEASE_LOCK(&cap->lock);
+ // what we do. We still hold cap->lock at this point. The Task waiting
+ // for this Capability does not have it yet, so we can be sure to be
+ // woken up later. (see #10545)
+ appendToReturningTaskQueue(cap,task);
+ }
+
+ // Step 5: release the cap->lock
+ RELEASE_LOCK(&cap->lock);
+
+ // Step 6. "various stuff part C", post release cap->lock
+ if (terminate_worker) {
+ shutdownThread();
+ } else if (task_is_worker || task_is_bound) {
+ cap = waitForWorkerCapability(task);
+ } else {
cap = waitForReturnCapability(task);
}
+ // End of step 6.
debugTrace(DEBUG_sched, "resuming capability %d", cap->no);
ASSERT(cap->running_task == task);
@@ -1067,8 +1219,226 @@ yieldCapability
return false;
}
+// Returns true if it could enqueue, and false if the worker is surplus to
+// requirements and should be terminated.
+static bool tryEnqueueWorker (Capability* cap)
+{
+ Task *task = cap->running_task;
+
+ // If the Task is stopped, we shouldn't be yielding, we should
+ // be just exiting.
+ ASSERT(!task->stopped);
+ ASSERT(task->worker);
+ ASSERT_LOCK_HELD(&cap->lock);
+
+ if (cap->n_spare_workers < MAX_SPARE_WORKERS)
+ {
+ task->next = cap->spare_workers;
+ cap->spare_workers = task;
+ cap->n_spare_workers++;
+ return true;
+ }
+ else
+ {
+ debugTrace(DEBUG_sched, "%d spare workers already, exiting",
+ cap->n_spare_workers);
+ return false;
+ }
+}
+
#endif /* THREADED_RTS */
+
+/* -----------------------------------------------------------------------------
+ * stopAllCapabilities()
+ *
+ * Stop all Haskell execution. This is used when we need to make some global
+ * change to the system, such as altering the number of capabilities, or
+ * forking.
+ *
+ * pCap may be NULL in the event that the caller doesn't yet own a capability.
+ *
+ * To resume after stopAllCapabilities(), use releaseAllCapabilities().
+ * -------------------------------------------------------------------------- */
+
+#if defined(THREADED_RTS)
+void stopAllCapabilities
+ ( Capability **pCap // [in/out] This thread's task's owned capability.
+ // pCap may be NULL if no capability is owned.
+ // Else *pCap != NULL
+ // On return, set to the task's newly owned
+ // capability (task->cap). Though, the Task will
+ // technically own all capabilities.
+ , Task *task // [in] This thread's task.
+ )
+{
+ stopAllCapabilitiesWith(pCap, task, SYNC_OTHER);
+}
+
+void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type)
+{
+ bool was_syncing;
+ SyncType prev_sync_type;
+
+ PendingSync sync = {
+ .type = sync_type,
+ .idle = NULL,
+ .task = task
+ };
+
+ do {
+ was_syncing = requestSync(pCap, task, &sync, &prev_sync_type);
+ } while (was_syncing);
+
+ acquireAllCapabilities(pCap ? *pCap : NULL, task);
+
+ resetSync();
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * requestSync()
+ *
+ * Commence a synchronisation between all capabilities. Normally not called
+ * directly, instead use stopAllCapabilities(). This is used by the GC, which
+ * has some special synchronisation requirements.
+ *
+ * Note that this can be called in two ways:
+ *
+ * - where *pcap points to a capability owned by the caller: in this case
+ * *prev_sync_type will reflect the in-progress sync type on return, if one
+ * *was found
+ *
+ * - where pcap == NULL: in this case the caller doesn't hold a capability.
+ * we only return whether or not a pending sync was found and prev_sync_type
+ * is unchanged.
+ *
+ * Returns:
+ * false if we successfully got a sync
+ * true if there was another sync request in progress,
+ * and we yielded to it. The value returned is the
+ * type of the other sync request.
+ * -------------------------------------------------------------------------- */
+
+#if defined(THREADED_RTS)
+bool requestSync
+ ( Capability **pcap // [in/out] This thread's task's owned capability.
+ // May change if there is an existing sync (true is returned).
+ // Precondition:
+ // pcap may be NULL
+ // *pcap != NULL
+ , Task *task // [in] This thread's task.
+ , PendingSync *new_sync // [in] The new requested sync.
+ , SyncType *prev_sync_type // [out] Only set if there is an existing sync (true is returned).
+ )
+{
+ PendingSync *sync;
+
+ sync = (PendingSync*)cas((StgVolatilePtr)&pending_sync,
+ (StgWord)NULL,
+ (StgWord)new_sync);
+
+ if (sync != NULL)
+ {
+ // sync is valid until we have called yieldCapability().
+ // After the sync is completed, we cannot read that struct any
+ // more because it has been freed.
+ *prev_sync_type = sync->type;
+ if (pcap == NULL) {
+ // The caller does not hold a capability (e.g. may be a concurrent
+ // mark thread). Consequently we must wait until the pending sync is
+ // finished before proceeding to ensure we don't loop.
+ // TODO: Don't busy-wait
+ ACQUIRE_LOCK(&sync_finished_mutex);
+ while (pending_sync) {
+ waitCondition(&sync_finished_cond, &sync_finished_mutex);
+ }
+ RELEASE_LOCK(&sync_finished_mutex);
+ } else {
+ do {
+ debugTrace(DEBUG_sched, "someone else is trying to sync (%d)...",
+ sync->type);
+ ASSERT(*pcap);
+ yieldCapability(pcap,task,true);
+ sync = SEQ_CST_LOAD(&pending_sync);
+ } while (sync != NULL);
+ }
+
+ // NOTE: task->cap might have changed now
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void resetSync (void)
+{
+ RELAXED_STORE(&pending_sync, 0);
+ signalCondition(&sync_finished_cond);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * acquireAllCapabilities()
+ *
+ * Grab all the capabilities except the one we already hold (cap may be NULL if
+ * the caller does not currently hold a capability). Used when synchronising
+ * before a single-threaded GC (SYNC_SEQ_GC), and before a fork (SYNC_OTHER).
+ *
+ * Only call this after requestSync(), otherwise a deadlock might
+ * ensue if another thread is trying to synchronise.
+ * -------------------------------------------------------------------------- */
+
+#if defined(THREADED_RTS)
+void acquireAllCapabilities(Capability *cap, Task *task)
+{
+ Capability *tmpcap = NULL;
+ uint32_t i;
+
+ ASSERT(SEQ_CST_LOAD(&pending_sync) != NULL);
+ for (i=0; i < getNumCapabilities(); i++) {
+ debugTrace(DEBUG_sched, "grabbing all the capabilities (%d/%d)",
+ i, getNumCapabilities());
+ tmpcap = getCapability(i);
+ if (tmpcap != cap) {
+ task->cap = tmpcap;
+ waitForCapability_(task, true /*high_priority*/);
+
+ // Note that waitForCapability only waits for the capability
+ // the task is associated with. There's no task migration here.
+ ASSERT(task->cap == tmpcap);
+ }
+ }
+ ASSERT(tmpcap != NULL);
+ task->cap = cap == NULL ? tmpcap : cap;
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * releaseAllCapabilities()
+ *
+ * Assuming this thread holds all the capabilities, release them all (except for
+ * the one passed in as keep_cap, if non-NULL).
+ * -------------------------------------------------------------------------- */
+
+#if defined(THREADED_RTS)
+void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task)
+{
+ uint32_t i;
+ ASSERT( task != NULL);
+ for (i = 0; i < n; i++) {
+ Capability *tmpcap = getCapability(i);
+ if (keep_cap != tmpcap) {
+ task->cap = tmpcap;
+ releaseCapability(tmpcap);
+ }
+ }
+ task->cap = keep_cap;
+}
+#endif
+
/*
* Note [migrated bound threads]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1117,7 +1487,14 @@ prodCapability (Capability *cap)
{
ACQUIRE_LOCK(&cap->lock);
if (!cap->running_task) {
- releaseCapability_(cap,true);
+ /* We have to use always_wakeup here because when prodCapability is
+ * used for ctl-c, releaseCapability__ does not know about pending
+ * signals (or the I/O managers generally) as one of the set of
+ * conditions to look for when deciding if a Task should be woken up
+ * to run the Capability. This is a bit of a design wart.
+ */
+ releaseCapability__(cap, true /*always_wakeup*/,
+ false /*wakeup_worker*/);
}
RELEASE_LOCK(&cap->lock);
}
@@ -1238,7 +1615,7 @@ shutdownCapability (Capability *cap USED_IF_THREADS,
if (!emptyRunQueue(cap) || cap->spare_workers) {
debugTrace(DEBUG_sched,
"runnable threads or workers still alive, yielding");
- releaseCapability_(cap,false); // this will wake up a worker
+ releaseCapability_(cap); // this will wake up a worker
RELEASE_LOCK(&cap->lock);
yieldThread();
continue;
=====================================
rts/Capability.h
=====================================
@@ -254,16 +254,13 @@ void moreCapabilities (uint32_t from, uint32_t to);
// ASSUMES: cap->running_task is the current Task.
//
#if defined(THREADED_RTS)
-void releaseCapability (Capability* cap);
-void releaseAndWakeupCapability (Capability* cap);
-void releaseCapability_ (Capability* cap, bool always_wakeup);
+void releaseCapability (Capability* cap);
+void releaseCapability_ (Capability* cap);
// assumes cap->lock is held
#else
// releaseCapability() is empty in non-threaded RTS
INLINE_HEADER void releaseCapability (Capability* cap STG_UNUSED) {};
-INLINE_HEADER void releaseAndWakeupCapability (Capability* cap STG_UNUSED) {};
-INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED,
- bool always_wakeup STG_UNUSED) {};
+INLINE_HEADER void releaseCapability_ (Capability* cap STG_UNUSED) {};
#endif
// declared in rts/include/rts/Threads.h:
@@ -283,46 +280,22 @@ INLINE_HEADER Capability *getCapability(uint32_t i)
return RELAXED_LOAD(&capabilities[i]);
}
+// Acquire the task's associated capability, waiting as necessary.
//
-// Types of global synchronisation
-//
-typedef enum {
- SYNC_OTHER,
- SYNC_GC_SEQ,
- SYNC_GC_PAR,
- SYNC_FLUSH_UPD_REM_SET,
- SYNC_FLUSH_EVENT_LOG
-} SyncType;
-
-//
-// Details about a global synchronisation
-//
-typedef struct {
- SyncType type; // The kind of synchronisation
- bool *idle; // Array of size n_capabilities. idle[i] is true
- // if capability i will be idle during this GC
- // cycle. Only available when doing GC (when
- // type is SYNC_GC_*).
- Task *task; // The Task performing the sync
-} PendingSync;
-
-//
-// Indicates that the RTS wants to synchronise all the Capabilities
-// for some reason. All Capabilities should stop and return to the
-// scheduler.
+// Note this acquires the specific capability the task is already associated
+// with (i.e. task->cap must be set). Alternatively, use waitForSomeCapability
+// to acquire some appropriate choice of capability.
//
-extern PendingSync * volatile pending_sync;
+void waitForCapability (Task *task);
-// Acquires a capability at a return point. If *cap is non-NULL, then
-// this is taken as a preference for the Capability we wish to
-// acquire.
+// If the task is not already associated with a capability, select some
+// reasonable choice of capability and associate the task with the chosen
+// capability (i.e. the task->cap is set to the new choice). Then acquire the
+// chosen capability, waiting as necessary.
//
-// OS threads waiting in this function get priority over those waiting
-// in waitForCapability().
+// Returns the now-acquired cap that was newly associated with the task.
//
-// On return, *cap is non-NULL, and points to the Capability acquired.
-//
-void waitForCapability (Capability **cap/*in/out*/, Task *task);
+Capability *waitForSomeCapability (Task *task);
EXTERN_INLINE void recordMutableCap (const StgClosure *p, Capability *cap,
uint32_t gen);
@@ -391,6 +364,50 @@ INLINE_HEADER void contextSwitchCapability(Capability *cap, bool immediately);
void interruptAllCapabilities(void);
INLINE_HEADER void interruptCapability(Capability *cap);
+#if defined(THREADED_RTS)
+//
+// Types of global synchronisation
+//
+typedef enum {
+ SYNC_OTHER,
+ SYNC_GC_SEQ,
+ SYNC_GC_PAR,
+ SYNC_FLUSH_UPD_REM_SET,
+ SYNC_FLUSH_EVENT_LOG
+} SyncType;
+
+void stopAllCapabilities (Capability **pCap, Task *task);
+void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type);
+void acquireAllCapabilities(Capability *cap, Task *task);
+void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task);
+
+//
+// Details about a global synchronisation
+//
+typedef struct {
+ SyncType type; // The kind of synchronisation
+ bool *idle; // Array of size n_capabilities. idle[i] is true
+ // if capability i will be idle during this GC
+ // cycle. Only available when doing GC (when
+ // type is SYNC_GC_*).
+ Task *task; // The Task performing the sync
+} PendingSync;
+
+// Used by scheduler for GC. Other use cases should prefer stopAllCapabilities.
+bool requestSync (Capability **pcap,
+ Task *task,
+ PendingSync *new_sync,
+ SyncType *prev_sync_type);
+void resetSync (void);
+
+// Is there a current pending sync? This is true after a successful requestSync
+// and before the sync completes.
+INLINE_HEADER bool anyPendingSync(void);
+
+// Private, use anyPendingSync() accessor.
+extern PendingSync *pending_sync;
+#endif
+
// Free all capabilities
void freeCapabilities (void);
@@ -516,6 +533,10 @@ INLINE_HEADER bool emptyInbox(Capability *cap)
RELAXED_LOAD(&cap->putMVars) == NULL);
}
+INLINE_HEADER bool anyPendingSync(void)
+{
+ return RELAXED_LOAD(&pending_sync);
+}
#endif
#include "EndPrivate.h"
=====================================
rts/Messages.c
=====================================
@@ -49,11 +49,7 @@ void sendMessage(Capability *from_cap, Capability *to_cap, Message *msg)
recordClosureMutated(from_cap,(StgClosure*)msg);
if (to_cap->running_task == NULL) {
- /* Precond for releaseCapability_ is: running_task || always_wakeup.
- * We have running_task == NULL, hence we must use always_wakeup. This
- * is ok since the inbox is now non-empty, so we wake a task anyway.
- */
- releaseCapability_(to_cap, true /*always_wakeup*/);
+ releaseCapability_(to_cap);
} else {
interruptCapability(to_cap);
}
=====================================
rts/RtsAPI.c
=====================================
@@ -654,8 +654,7 @@ rts_lock (void)
}
#endif
- cap = NULL;
- waitForCapability(&cap, task);
+ cap = waitForSomeCapability(task);
if (task->incall->prev_stack == NULL) {
// This is a new outermost call from C into Haskell land.
@@ -692,7 +691,7 @@ rts_unlock (Capability *cap)
// random point in the future, which causes problems for
// freeTaskManager().
ACQUIRE_LOCK(&cap->lock);
- releaseCapability_(cap,false);
+ releaseCapability_(cap);
// Finally, we can release the Task to the free list.
exitMyTask();
=====================================
rts/Schedule.c
=====================================
@@ -112,18 +112,6 @@ Mutex sched_mutex;
#define FORKPROCESS_PRIMOP_SUPPORTED
#endif
-/*
- * sync_finished_cond allows threads which do not own any capability (e.g. the
- * concurrent mark thread) to participate in the sync protocol. In particular,
- * if such a thread requests a sync while sync is already in progress it will
- * block on sync_finished_cond, which will be signalled when the sync is
- * finished (by releaseAllCapabilities).
- */
-#if defined(THREADED_RTS)
-static Condition sync_finished_cond;
-static Mutex sync_finished_mutex;
-#endif
-
/* -----------------------------------------------------------------------------
* static function prototypes
@@ -141,9 +129,6 @@ static void scheduleFindWork (Capability **pcap);
static void scheduleYield (Capability **pcap, Task *task);
#endif
#if defined(THREADED_RTS)
-static bool requestSync (Capability **pcap, Task *task,
- PendingSync *sync_type, SyncType *prev_sync_type);
-static void acquireAllCapabilities(Capability *cap, Task *task);
static void startWorkerTasks (uint32_t from USED_IF_THREADS,
uint32_t to USED_IF_THREADS);
#endif
@@ -676,7 +661,7 @@ shouldYieldCapability (Capability *cap, Task *task, bool didGcLast)
// n_returning_tasks. However, since this is an approximate predicate we can
// use a RELAXED ordering.
- return ((RELAXED_LOAD(&pending_sync) && !didGcLast) ||
+ return ((anyPendingSync() && !didGcLast) ||
RELAXED_LOAD(&cap->n_returning_tasks) != 0 ||
(!emptyRunQueue(cap) && (task->incall->tso == NULL
? peekRunQueue(cap)->bound != NULL
@@ -875,13 +860,9 @@ schedulePushWork(Capability *cap USED_IF_THREADS,
// release the capabilities
for (i = 0; i < n_free_caps; i++) {
task->cap = free_caps[i];
- if (sparkPoolSizeCap(cap) > 0) {
- // If we have sparks to steal, wake up a worker on the
- // capability, even if it has no threads to run.
- releaseAndWakeupCapability(free_caps[i]);
- } else {
- releaseCapability(free_caps[i]);
- }
+ // If there are sparks available, this will wake up a Task to run
+ // the Capability, even if it has no threads to run.
+ releaseCapability(free_caps[i]);
}
}
task->cap = cap; // reset to point to our Capability.
@@ -1412,194 +1393,6 @@ scheduleNeedHeapProfile( bool ready_to_gc )
}
}
-/* -----------------------------------------------------------------------------
- * stopAllCapabilities()
- *
- * Stop all Haskell execution. This is used when we need to make some global
- * change to the system, such as altering the number of capabilities, or
- * forking.
- *
- * pCap may be NULL in the event that the caller doesn't yet own a capability.
- *
- * To resume after stopAllCapabilities(), use releaseAllCapabilities().
- * -------------------------------------------------------------------------- */
-
-#if defined(THREADED_RTS)
-void stopAllCapabilities
- ( Capability **pCap // [in/out] This thread's task's owned capability.
- // pCap may be NULL if no capability is owned.
- // Else *pCap != NULL
- // On return, set to the task's newly owned
- // capability (task->cap). Though, the Task will
- // technically own all capabilities.
- , Task *task // [in] This thread's task.
- )
-{
- stopAllCapabilitiesWith(pCap, task, SYNC_OTHER);
-}
-
-void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type)
-{
- bool was_syncing;
- SyncType prev_sync_type;
-
- PendingSync sync = {
- .type = sync_type,
- .idle = NULL,
- .task = task
- };
-
- do {
- was_syncing = requestSync(pCap, task, &sync, &prev_sync_type);
- } while (was_syncing);
-
- acquireAllCapabilities(pCap ? *pCap : NULL, task);
-
- RELAXED_STORE(&pending_sync, 0);
- signalCondition(&sync_finished_cond);
-}
-#endif
-
-/* -----------------------------------------------------------------------------
- * requestSync()
- *
- * Commence a synchronisation between all capabilities. Normally not called
- * directly, instead use stopAllCapabilities(). This is used by the GC, which
- * has some special synchronisation requirements.
- *
- * Note that this can be called in two ways:
- *
- * - where *pcap points to a capability owned by the caller: in this case
- * *prev_sync_type will reflect the in-progress sync type on return, if one
- * *was found
- *
- * - where pcap == NULL: in this case the caller doesn't hold a capability.
- * we only return whether or not a pending sync was found and prev_sync_type
- * is unchanged.
- *
- * Returns:
- * false if we successfully got a sync
- * true if there was another sync request in progress,
- * and we yielded to it. The value returned is the
- * type of the other sync request.
- * -------------------------------------------------------------------------- */
-
-#if defined(THREADED_RTS)
-static bool requestSync
- ( Capability **pcap // [in/out] This thread's task's owned capability.
- // May change if there is an existing sync (true is returned).
- // Precondition:
- // pcap may be NULL
- // *pcap != NULL
- , Task *task // [in] This thread's task.
- , PendingSync *new_sync // [in] The new requested sync.
- , SyncType *prev_sync_type // [out] Only set if there is an existing sync (true is returned).
- )
-{
- PendingSync *sync;
-
- sync = (PendingSync*)cas((StgVolatilePtr)&pending_sync,
- (StgWord)NULL,
- (StgWord)new_sync);
-
- if (sync != NULL)
- {
- // sync is valid until we have called yieldCapability().
- // After the sync is completed, we cannot read that struct any
- // more because it has been freed.
- *prev_sync_type = sync->type;
- if (pcap == NULL) {
- // The caller does not hold a capability (e.g. may be a concurrent
- // mark thread). Consequently we must wait until the pending sync is
- // finished before proceeding to ensure we don't loop.
- // TODO: Don't busy-wait
- ACQUIRE_LOCK(&sync_finished_mutex);
- while (pending_sync) {
- waitCondition(&sync_finished_cond, &sync_finished_mutex);
- }
- RELEASE_LOCK(&sync_finished_mutex);
- } else {
- do {
- debugTrace(DEBUG_sched, "someone else is trying to sync (%d)...",
- sync->type);
- ASSERT(*pcap);
- yieldCapability(pcap,task,true);
- sync = SEQ_CST_LOAD(&pending_sync);
- } while (sync != NULL);
- }
-
- // NOTE: task->cap might have changed now
- return true;
- }
- else
- {
- return false;
- }
-}
-#endif
-
-/* -----------------------------------------------------------------------------
- * acquireAllCapabilities()
- *
- * Grab all the capabilities except the one we already hold (cap may be NULL is
- * the caller does not currently hold a capability). Used when synchronising
- * before a single-threaded GC (SYNC_SEQ_GC), and before a fork (SYNC_OTHER).
- *
- * Only call this after requestSync(), otherwise a deadlock might
- * ensue if another thread is trying to synchronise.
- * -------------------------------------------------------------------------- */
-
-#if defined(THREADED_RTS)
-static void acquireAllCapabilities(Capability *cap, Task *task)
-{
- Capability *tmpcap;
- uint32_t i;
-
- ASSERT(SEQ_CST_LOAD(&pending_sync) != NULL);
- for (i=0; i < getNumCapabilities(); i++) {
- debugTrace(DEBUG_sched, "grabbing all the capabilities (%d/%d)",
- i, getNumCapabilities());
- tmpcap = getCapability(i);
- if (tmpcap != cap) {
- // we better hope this task doesn't get migrated to
- // another Capability while we're waiting for this one.
- // It won't, because load balancing happens while we have
- // all the Capabilities, but even so it's a slightly
- // unsavoury invariant.
- task->cap = tmpcap;
- waitForCapability(&tmpcap, task);
- if (tmpcap->no != i) {
- barf("acquireAllCapabilities: got the wrong capability");
- }
- }
- }
- task->cap = cap == NULL ? tmpcap : cap;
-}
-#endif
-
-/* -----------------------------------------------------------------------------
- * releaseAllCapabilities()
- *
- * Assuming this thread holds all the capabilities, release them all (except for
- * the one passed in as keep_cap, if non-NULL).
- * -------------------------------------------------------------------------- */
-
-#if defined(THREADED_RTS)
-void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task)
-{
- uint32_t i;
- ASSERT( task != NULL);
- for (i = 0; i < n; i++) {
- Capability *tmpcap = getCapability(i);
- if (keep_cap != tmpcap) {
- task->cap = tmpcap;
- releaseCapability(tmpcap);
- }
- }
- task->cap = keep_cap;
-}
-#endif
-
/* -----------------------------------------------------------------------------
* Perform a garbage collection if necessary
* -------------------------------------------------------------------------- */
@@ -1665,7 +1458,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS,
// GC, we synchronise all the running threads using requestSync().
//
// Other capabilities are prevented from running yet more Haskell threads if
- // pending_sync is set. Tested inside yieldCapability() and
+ // anyPendingSync() holds. Tested inside yieldCapability() and
// releaseCapability() in Capability.c
PendingSync sync = {
@@ -1802,7 +1595,7 @@ scheduleDoGC (Capability **pcap, Task *task USED_IF_THREADS,
if (i != cap->no && idle_cap[i]) {
Capability *tmpcap = getCapability(i);
task->cap = tmpcap;
- waitForCapability(&tmpcap, task);
+ waitForCapability(task);
n_idle_caps++;
}
}
@@ -1910,10 +1703,10 @@ delete_threads_and_gc:
};
#if defined(THREADED_RTS)
- // reset pending_sync *before* GC, so that when the GC threads
+ // reset the pending sync *before* GC, so that when the GC threads
// emerge they don't immediately re-enter the GC.
- RELAXED_STORE(&pending_sync, 0);
- signalCondition(&sync_finished_cond);
+ resetSync();
+
config.parallel = gc_type == SYNC_GC_PAR;
GarbageCollect(config, cap, idle_cap);
#else
@@ -2074,8 +1867,7 @@ forkProcess(HsStablePtr *entry
task = newBoundTask();
- cap = NULL;
- waitForCapability(&cap, task);
+ cap = waitForSomeCapability(task);
#if defined(THREADED_RTS)
stopAllCapabilities(&cap, task);
@@ -2132,7 +1924,7 @@ forkProcess(HsStablePtr *entry
#endif
for (i=0; i < n_capabilities; i++) {
- releaseCapability_(getCapability(i),false);
+ releaseCapability_(getCapability(i));
RELEASE_LOCK(&getCapability(i)->lock);
}
@@ -2534,7 +2326,7 @@ suspendThread (StgRegTable *reg, bool interruptible)
suspendTask(cap,task);
cap->in_haskell = false;
- releaseCapability_(cap,false);
+ releaseCapability_(cap);
RELEASE_LOCK(&cap->lock);
@@ -2567,10 +2359,7 @@ resumeThread (void *task_)
task->cap = cap;
// Wait for permission to re-enter the RTS with the result.
- waitForCapability(&cap,task);
- // we might be on a different capability now... but if so, our
- // entry on the suspended_ccalls list will also have been
- // migrated.
+ waitForCapability(task);
// Remove the thread from the suspended list
recoverSuspendedTask(cap,task);
@@ -2721,7 +2510,7 @@ void scheduleWorker (Capability *cap, Task *task)
// Capability has been shut down.
//
ACQUIRE_LOCK(&cap->lock);
- releaseCapability_(cap,false);
+ releaseCapability_(cap);
workerTaskStop(task);
RELEASE_LOCK(&cap->lock);
}
@@ -2767,8 +2556,6 @@ initScheduler(void)
* the scheduler. */
#if defined(THREADED_RTS)
initMutex(&sched_mutex);
- initMutex(&sync_finished_mutex);
- initCondition(&sync_finished_cond);
#endif
ACQUIRE_LOCK(&sched_mutex);
@@ -2804,8 +2591,7 @@ exitScheduler (bool wait_foreign USED_IF_THREADS)
// If we haven't killed all the threads yet, do it now.
if (getSchedState() < SCHED_SHUTTING_DOWN) {
setSchedState(SCHED_INTERRUPTING);
- Capability *cap = task->cap;
- waitForCapability(&cap,task);
+ Capability *cap = waitForSomeCapability(task);
scheduleDoGC(&cap,task,true,false,false,true);
ASSERT(task->incall->tso == NULL);
releaseCapability(cap);
@@ -2850,17 +2636,19 @@ freeScheduler( void )
static void
performGC_(bool force_major, bool nonconcurrent)
{
- Task *task;
- Capability *cap = NULL;
-
- // We must grab a new Task here, because the existing Task may be
- // associated with a particular Capability, and chained onto the
- // suspended_ccalls queue.
- task = newBoundTask();
+ // We must use a new InCall for our Task here, because the existing Task's
+ // InCall may be chained onto the suspended_ccalls queue.
+ //
+ // If we could guarante there's an existing Task then we could just use
+ // newInCall/endInCall. But it's not clear the calling thread necessarily
+ // has an existing Task, so we play it safe and use newBoundTask/exitMyTask.
+ // This does the same newInCall/endInCall when there is an initialised Task,
+ // but will also do the right thing if there's no existing Task.
+ Task *task = newBoundTask();
// TODO: do we need to traceTask*() here?
- waitForCapability(&cap,task);
+ Capability *cap = waitForSomeCapability(task);
scheduleDoGC(&cap,task,force_major,false,false,nonconcurrent);
releaseCapability(cap);
exitMyTask();
=====================================
rts/Schedule.h
=====================================
@@ -55,12 +55,6 @@ StgWord findAtomicallyFrameHelper (Capability *cap, StgTSO *tso);
/* Entry point for a new worker */
void scheduleWorker (Capability *cap, Task *task);
-#if defined(THREADED_RTS)
-void stopAllCapabilitiesWith (Capability **pCap, Task *task, SyncType sync_type);
-void stopAllCapabilities (Capability **pCap, Task *task);
-void releaseAllCapabilities(uint32_t n, Capability *keep_cap, Task *task);
-#endif
-
/* The state of the scheduler. This is used to control the sequence
* of events during shutdown. See Note [shutdown] in Schedule.c.
*/
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e96b51be68839907a468266afca45a…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/e96b51be68839907a468266afca45a…
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
19 Aug '26
Rodrigo Mesquita pushed new branch wip/romes/27501 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/romes/27501
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/romes/27514] Concurrent workers: determinism, avoid duplicated work
by sheaf (@sheaf) 19 Aug '26
by sheaf (@sheaf) 19 Aug '26
19 Aug '26
sheaf pushed to branch wip/romes/27514 at Glasgow Haskell Compiler / GHC
Commits:
04ba9450 by sheaf at 2026-08-19T12:12:07+02:00
Concurrent workers: determinism, avoid duplicated work
- - - - -
3 changed files:
- compiler/GHC/Driver/Concurrency.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Pipeline/LogQueue.hs
Changes:
=====================================
compiler/GHC/Driver/Concurrency.hs
=====================================
@@ -12,7 +12,8 @@ module GHC.Driver.Concurrency
-- * Concurrent worker scheduling
, ConcurrentWorkerEnv(..)
, mapConcurrentWorkers
- , concurrentTraversal_DF
+ , NodeExpander(..)
+ , concurrentTraversal
)
where
@@ -20,12 +21,16 @@ import GHC.Prelude
import GHC.Driver.MakeSem
import GHC.Driver.Pipeline.LogQueue
- ( LogQueueQueue, finishLogQueue, initLogQueue, logThread
- , newLogQueue, newLogQueueQueue, parLogAction )
+ ( LogQueue, LogQueueQueue, finishLogQueue, initLogQueue, logThread
+ , newLogQueue, newLogQueueQueue, parLogAction, printLogs )
import GHC.Utils.Logger
( Logger, makeThreadSafe, pushLogHook )
+import GHC.Utils.Misc
+ ( HasDebugCallStack )
+import GHC.Utils.Outputable
+ ( Outputable(..), text, (<+>) )
import GHC.Utils.Panic
- ( panic )
+ ( massertPpr, pprPanic )
import GHC.Utils.TmpFs
( TmpFs, forkTmpFsFrom, mergeTmpFsInto, withLocalTmpFS )
@@ -36,31 +41,30 @@ import System.Semaphore
import Control.Concurrent
( ThreadId, forkIOWithUnmask, killThread, myThreadId )
import Control.Concurrent.MVar
- ( MVar, newEmptyMVar, newMVar, putMVar, takeMVar )
+ ( MVar, newMVar, putMVar, takeMVar )
import GHC.Conc
( labelThread )
#else
import Control.Concurrent
( ThreadId, forkIOWithUnmask, killThread, myThreadId
- , newQSem, signalQSem, waitQSem, MVar, takeMVar, putMVar, newEmptyMVar )
-import Control.Monad
- ( unless )
+ , newQSem, signalQSem, waitQSem )
import qualified Control.Monad.Catch as MC
import GHC.Conc
( getNumCapabilities, getNumProcessors, labelThread, setNumCapabilities )
#endif
import Control.Concurrent.STM
- ( TVar, atomically, check, modifyTVar', newTVarIO, readTVar, writeTVar )
+ ( TVar, atomically, check, modifyTVar', newTVarIO, readTVar, readTVarIO
+ , writeTVar )
import Control.Exception
( AsyncException(ThreadKilled), SomeAsyncException, SomeException
- , finally, fromException, mask, mask_, onException
+ , catch, finally, fromException, mask, mask_, onException
, throwIO, try, uninterruptibleMask_ )
import Control.Monad
- ( replicateM )
+ ( unless )
import Data.Foldable
- ( for_ )
+ ( for_, traverse_ )
import Data.IORef
- ( IORef, atomicModifyIORef', modifyIORef', newIORef, readIORef, writeIORef )
+ ( IORef, atomicModifyIORef', modifyIORef', newIORef, readIORef )
import qualified Data.Map as Map
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
@@ -211,72 +215,163 @@ withWorkerLimit logger tmpfs report_semaphore_failure limit action
action conc parent_work_env
--------------------------------------------------------------------------------
--- * Scheduling concurrent workers
+-- * Monotone data structures
--------------------------------------------------------------------------------
--- | Internal scheduler abstraction with two capabilities:
+-- | A map that only ever grows, and whose entries are written at most once.
+newtype MonotoneMap k v = MonotoneMap ( IORef ( Map.Map k v ) )
+
+newMonotoneMap :: Map.Map k v -> IO ( MonotoneMap k v )
+newMonotoneMap initial = MonotoneMap <$> newIORef initial
+
+-- | The outcome of inserting into a 'MonotoneMap' or a 'MonotoneSet'.
+data InsertionResult
+ -- | The key was absent before the insertion.
+ = Inserted
+ -- | The key was already present; the container is unchanged.
+ | AlreadyPresent
+
+-- | Write an entry into a 'MonotoneMap' unless the key is already present.
+insertMonotoneMap :: Ord k => MonotoneMap k v -> k -> v -> IO InsertionResult
+insertMonotoneMap ( MonotoneMap ref ) k v =
+ atomicModifyIORef' ref \ m ->
+ case Map.insertLookupWithKey ( \ _ _ old -> old ) k v m of
+ ( Nothing , m' ) -> ( m', Inserted )
+ ( Just _ , _ ) -> ( m , AlreadyPresent )
+
+-- | Write a new entry into a 'MonotoneMap'.
--
--- - spawn a new worker thread
--- - wait for a worker thread to complete
-data Scheduler r = Scheduler
- { spawnWorker :: (ConcurrentWorkerEnv -> IO r) -> IO ()
- -- ^ Spawn one concurrent worker.
- --
- -- The worker does not hold a token of the concurrency semaphore: the
- -- worker action should use 'withConcurrency' around the work whose
- -- concurrency should be limited.
- , awaitWorker :: IO (Either SomeException r)
- -- ^ Wait for one worker to complete.
- --
- -- Will crash if there are no outstanding workers.
- }
+-- Panics if the entry is already present.
+insertMonotoneMap_new
+ :: ( HasDebugCallStack, Ord k, Outputable k )
+ => MonotoneMap k v -> k -> v -> IO ()
+insertMonotoneMap_new mm k v =
+ insertMonotoneMap mm k v >>= \case
+ Inserted -> pure ()
+ AlreadyPresent -> pprPanic "monotone map: duplicate key" $ ppr k
+
+-- | The contents of a monotone map.
+freezeMonotoneMap :: MonotoneMap k v -> IO ( Map.Map k v )
+freezeMonotoneMap ( MonotoneMap ref ) = readIORef ref
+
+-- | A set that only ever grows.
+newtype MonotoneSet k = MonotoneSet ( IORef ( Set.Set k ) )
+
+newMonotoneSet :: Set.Set k -> IO ( MonotoneSet k )
+newMonotoneSet initial = MonotoneSet <$> newIORef initial
+
+-- | Add an element, unless it is already present.
+insertMonotoneSet :: Ord k => MonotoneSet k -> k -> IO InsertionResult
+insertMonotoneSet ( MonotoneSet ref ) k =
+ atomicModifyIORef' ref \ s ->
+ if k `Set.member` s
+ then ( s , AlreadyPresent )
+ else ( Set.insert k s , Inserted )
+
+--------------------------------------------------------------------------------
+-- * Pools of concurrent workers
+--------------------------------------------------------------------------------
--- | Internal implementation of a concurrent worker scheduler.
+-- | The order in which logging should happen when using concurrent workers.
+data LogOrder
+ -- | Log as we go.
+ --
+ -- Only valid when workers are spawned in a deterministic order.
+ = LogAsWeGo
+ -- | Accumulate logs per worker. Once all work is done, sort the logs
+ -- before proceeding.
+ --
+ -- Used when workers may be spawned in a non-deterministic order.
+ | SortLogs
+
+-- | A pool of concurrent workers with a given worker key type, supporting two
+-- operations:
--
--- Usage of this function requires the following:
+-- - spawning a new worker,
+-- - waiting on all workers to finish.
--
--- - all spawn/await actions are performed by a single thread,
--- - we never wait for more workers than were spawned,
--- - no worker outlives 'run_schedule'.
-run_schedule
- :: forall r a
- . String
- -- ^ thread label for workers
+-- See Note [Deterministic concurrent workers].
+data WorkerPool worker_key =
+ WorkerPool
+ { spawnWorker :: worker_key -> ( ConcurrentWorkerEnv -> IO () ) -> IO ()
+ -- ^ Spawn one worker with the given worker key.
+ --
+ -- May be called from inside another worker.
+ --
+ -- The worker does not hold a token of the concurrency semaphore: the
+ -- worker action should use 'withConcurrency' around the work whose
+ -- concurrency should be limited.
+ --
+ -- An exception escaping the action stops further workers from being
+ -- spawned, and is rethrown by 'waitForWorkers'.
+ , waitForWorkers :: IO ()
+ -- ^ Wait until all workers are done, throwing an exception if any
+ -- worker failed (which exception is thrown is not deterministic).
+ }
+
+-- | Internal implementation of a pool of concurrent workers.
+run_pool
+ :: forall worker_key a
+ . ( HasDebugCallStack, Ord worker_key, Outputable worker_key )
+ => String -- ^ thread label for workers
+ -> LogOrder
-> Concurrency
-> ConcurrentWorkerEnv
- -> (Scheduler r -> IO a)
- -- ^ worker action
+ -> ( WorkerPool worker_key -> IO a )
-> IO a
-run_schedule worker_label conc parent_work_env withScheduler =
+run_pool worker_label log_order conc parent_work_env withPool =
case conc of
Serial -> do
- results_var <- newIORef Seq.empty
+ queued_var <- newIORef $ Seq.empty @( worker_key, ConcurrentWorkerEnv -> IO () )
+ logs_var <- newMonotoneMap $ Map.empty @worker_key @LogQueue
+
let
- spawnWorker :: (ConcurrentWorkerEnv -> IO r) -> IO ()
- spawnWorker action = do
- res <- try @SomeException $
+ spawnWorker :: worker_key -> ( ConcurrentWorkerEnv -> IO () ) -> IO ()
+ spawnWorker worker_key action =
+ modifyIORef' queued_var ( Seq.|> ( worker_key, action ) )
+
+ run_worker :: worker_key -> ( ConcurrentWorkerEnv -> IO () ) -> IO ()
+ run_worker worker_key action = case log_order of
+ LogAsWeGo ->
workerEnv_withLocalTmpFS parent_work_env action
- case res of
- Left e
- | Just _ <- fromException @SomeAsyncException e
- -> throwIO e
- _ -> modifyIORef' results_var (Seq.|> res)
-
- awaitWorker :: IO (Either SomeException r)
- awaitWorker =
- readIORef results_var >>= \case
- res Seq.:<| rest -> do
- writeIORef results_var rest
- pure res
- Seq.Empty ->
- panic "run_schedule: no outstanding job"
-
- withScheduler $ Scheduler { spawnWorker, awaitWorker }
+ SortLogs -> do
+ -- Use a log queue for consistency with the concurrent case.
+ lq <- newLogQueue
+ insertMonotoneMap_new logs_var worker_key lq
+ let
+ worker_work_env :: ConcurrentWorkerEnv
+ worker_work_env =
+ parent_work_env
+ { cwe_logger = pushLogHook ( const ( parLogAction lq ) )
+ ( cwe_logger parent_work_env ) }
+ workerEnv_withLocalTmpFS worker_work_env action
+ `finally` finishLogQueue lq
+
+ waitForWorkers :: IO ()
+ waitForWorkers = do
+ next <- atomicModifyIORef' queued_var \ queued ->
+ case queued of
+ work Seq.:<| rest -> ( rest , Just work )
+ Seq.Empty -> ( queued, Nothing )
+ case next of
+ Nothing -> pure ()
+ Just ( worker_key, action ) ->
+ run_worker worker_key action *> waitForWorkers
+
+ print_logs :: IO ()
+ print_logs = do
+ logs <- freezeMonotoneMap logs_var
+ for_ ( Map.elems logs ) $ printLogs ( cwe_logger parent_work_env )
+
+ withPool ( WorkerPool { spawnWorker, waitForWorkers } )
+ `finally` print_logs
Concurrent ( ConcurrencyEnv { ce_next_log_queue_id, ce_log_queue_queue } ) -> do
- worker_tids_var <- newTVarIO $ Set.empty @ThreadId
- all_results_vars_var <- newIORef $ Seq.empty @(MVar (Either SomeException r))
+ worker_tids_var <- newTVarIO $ Set.empty @ThreadId
+ failure_var <- newTVarIO $ Nothing @SomeException
+ logs_var <- newMonotoneMap $ Map.empty @worker_key @LogQueue
+ last_spawned_var <- newIORef $ Nothing @worker_key
let
wait_for_workers :: IO ()
@@ -291,92 +386,118 @@ run_schedule worker_label conc parent_work_env withScheduler =
for_ tids killThread
wait_for_workers
- awaitWorker :: IO (Either SomeException r)
- awaitWorker =
- readIORef all_results_vars_var >>= \case
- first_worker_res_var Seq.:<| rest -> do
- writeIORef all_results_vars_var rest
- -- block on the earliest-spawned outstanding worker
- takeMVar first_worker_res_var
- Seq.Empty ->
- panic "run_schedule: no outstanding job"
-
- spawnWorker :: (ConcurrentWorkerEnv -> IO r) -> IO ()
- spawnWorker action = mask_ do
-
- worker_res_var <- newEmptyMVar
-
- -- TmpFs
- lcl_tmpfs <- forkTmpFsFrom (cwe_tmpfs parent_work_env)
-
- -- LogQueue
- lq <- do
- job_id <- atomicModifyIORef' ce_next_log_queue_id \n -> (n + 1, n)
- lq <- newLogQueue job_id
- atomically $ initLogQueue ce_log_queue_queue lq
- pure lq
+ -- Record a worker failure, preventing any further work from starting.
+ record_failure :: SomeException -> IO ()
+ record_failure e =
+ atomically $ modifyTVar' failure_var \ failure ->
+ case failure of
+ Nothing -> Just e
+ Just {} -> failure
- let
+ waitForWorkers :: IO ()
+ waitForWorkers = do
+ wait_for_workers
+ traverse_ throwIO =<< readTVarIO failure_var
+
+ -- Create the log queue of a worker, ordering it according to the worker key.
+ new_worker_log_queue :: worker_key -> IO LogQueue
+ new_worker_log_queue worker_key = do
+ lq <- newLogQueue
+ case log_order of
+ LogAsWeGo -> do
+ last_spawned <-
+ atomicModifyIORef' last_spawned_var \ last_spawned ->
+ ( Just worker_key, last_spawned )
+ massertPpr ( all ( < worker_key ) last_spawned ) $
+ text "run_pool: LogAsWeGo workers spawned out of order:"
+ <+> ppr last_spawned <+> text "then" <+> ppr worker_key
+ job_id <- atomicModifyIORef' ce_next_log_queue_id \ n -> ( n + 1, n )
+ atomically $ initLogQueue ce_log_queue_queue job_id lq
+ SortLogs ->
+ insertMonotoneMap_new logs_var worker_key lq
+ pure lq
+
+ -- Hand the log queues over for printing, in worker key order.
+ release_queued_logs :: IO ()
+ release_queued_logs = do
+ queued <- freezeMonotoneMap logs_var
+ unless ( Map.null queued ) do
+ first_id <-
+ atomicModifyIORef' ce_next_log_queue_id \ n ->
+ ( n + Map.size queued, n )
+ atomically $
+ for_ ( zip [ first_id .. ] ( Map.elems queued ) ) \ ( job_id, lq ) ->
+ initLogQueue ce_log_queue_queue job_id lq
+
+ spawnWorker :: worker_key -> ( ConcurrentWorkerEnv -> IO () ) -> IO ()
+ spawnWorker worker_key action = mask_ do
+ failure <- readTVarIO failure_var
+ case failure of
+ -- A worker has failed: don't start any more work.
+ Just {} -> pure ()
+ Nothing -> do
+
+ -- TmpFs
+ lcl_tmpfs <- forkTmpFsFrom ( cwe_tmpfs parent_work_env )
+
+ -- LogQueue
+ lq <- new_worker_log_queue worker_key
- worker_work_env :: ConcurrentWorkerEnv
- worker_work_env =
- parent_work_env
- { cwe_tmpfs = lcl_tmpfs
- , cwe_logger = pushLogHook (const (parLogAction lq))
- (cwe_logger parent_work_env)
- }
-
- -- Run a worker action and record its result.
- run_worker_and_record :: IO r -> IO ()
- run_worker_and_record worker_action = do
- res <- try @SomeException worker_action
- case res of
- Left e
- -- Worker is being cancelled: don't record anything.
- | Just ThreadKilled <- fromException e
- -> pure ()
- _ -> putMVar worker_res_var res
-
- -- Record that a worker thread is done.
- mark_worker_done :: ThreadId -> IO ()
- mark_worker_done tid =
- uninterruptibleMask_ do
- -- Uninterruptible: the deletion below /must/ occur.
- -- An uninterruptible mask is OK as we only ever block for (GAP) below.
- mergeTmpFsInto lcl_tmpfs $ cwe_tmpfs parent_work_env
- finishLogQueue lq
- atomically do
- tids <- readTVar worker_tids_var
- check $ tid `Set.member` tids
- -- Ensure we never end up with a dead ThreadId in 'worker_tids_var'
- -- (if the worker thread finishes before the parent thread has
- -- the time to add its ThreadId to 'worker_tids_var').
-
- writeTVar worker_tids_var $ Set.delete tid tids
-
- run_worker :: (forall b. IO b -> IO b) -> IO ()
- run_worker unmask = do
- tid <- myThreadId
- labelThread tid worker_label
let
- worker_action :: IO r
- worker_action = unmask $ action worker_work_env
- run_worker_and_record worker_action `finally`
- mark_worker_done tid
-
- worker_tid <-
- forkIOWithUnmask run_worker
- `onException` finishLogQueue lq
- -- Very short (GAP) between forking the thread and recording its ThreadId.
- atomically $ modifyTVar' worker_tids_var $ Set.insert worker_tid
- modifyIORef' all_results_vars_var (Seq.|> worker_res_var)
-
- mask \ restore -> do
- result <- restore (withScheduler $ Scheduler { spawnWorker, awaitWorker })
- `onException` cancel_workers
- restore wait_for_workers `onException` cancel_workers
- pure result
+ worker_work_env :: ConcurrentWorkerEnv
+ worker_work_env =
+ parent_work_env
+ { cwe_tmpfs = lcl_tmpfs
+ , cwe_logger = pushLogHook ( const ( parLogAction lq ) )
+ ( cwe_logger parent_work_env )
+ }
+
+ -- Record that a worker thread is done.
+ mark_worker_done :: ThreadId -> IO ()
+ mark_worker_done tid =
+ uninterruptibleMask_ do
+ -- Uninterruptible: the deletion below /must/ occur.
+ -- An uninterruptible mask is OK as we only ever block for (GAP) below.
+ mergeTmpFsInto lcl_tmpfs $ cwe_tmpfs parent_work_env
+ finishLogQueue lq
+ atomically do
+ tids <- readTVar worker_tids_var
+ check $ tid `Set.member` tids
+ -- Ensure we never end up with a dead ThreadId in 'worker_tids_var'
+ -- (if the worker thread finishes before the parent thread has
+ -- the time to add its ThreadId to 'worker_tids_var').
+
+ writeTVar worker_tids_var $ Set.delete tid tids
+
+ handle_worker_exception :: SomeException -> IO ()
+ handle_worker_exception e
+ -- Worker is being cancelled: not a failure to report.
+ | Just ThreadKilled <- fromException e
+ = pure ()
+ | otherwise
+ = record_failure e
+
+ run_worker :: ( forall b. IO b -> IO b ) -> IO ()
+ run_worker unmask = do
+ tid <- myThreadId
+ labelThread tid worker_label
+ ( unmask ( action worker_work_env )
+ `catch` handle_worker_exception )
+ `finally` mark_worker_done tid
+
+ worker_tid <-
+ forkIOWithUnmask run_worker
+ `onException` finishLogQueue lq
+ -- Very short (GAP) between forking the thread and recording its ThreadId.
+ atomically $ modifyTVar' worker_tids_var $ Set.insert worker_tid
+
+ ( `finally` release_queued_logs ) $
+ mask \ restore -> do
+ result <- restore ( withPool $ WorkerPool { spawnWorker, waitForWorkers } )
+ `onException` cancel_workers
+ restore wait_for_workers `onException` cancel_workers
+ pure result
--------------------------------------------------------------------------------
-- * Derived scheduling functionality
@@ -387,7 +508,9 @@ run_schedule worker_label conc parent_work_env withScheduler =
-- Workers run to completion (no early abort); the first exception
-- (in input order) is rethrown at the end.
mapConcurrentWorkers
- :: String -- ^ thread label for workers
+ :: forall a b
+ . HasDebugCallStack
+ => String -- ^ thread label for workers
-> Concurrency
-> ConcurrentWorkerEnv
-> (ConcurrentWorkerEnv -> a -> IO b)
@@ -398,67 +521,111 @@ mapConcurrentWorkers
-> [a]
-> IO [b]
mapConcurrentWorkers worker_label conc work_env f xs =
- run_schedule worker_label conc work_env \ scheduler -> do
- for_ xs \ x -> spawnWorker scheduler \ worker_env -> f worker_env x
- results <- replicateM (length xs) (awaitWorker scheduler)
- either throwIO pure (sequence results)
+ -- LogAsWeGo: workers are keyed by their position in the input list and
+ -- spawned in that same order, so their output can be printed as it is produced.
+ run_pool worker_label LogAsWeGo conc work_env \ pool -> do
+ results <- newMonotoneMap $ Map.empty @Int @( Either SomeException b )
+ for_ ( zip [ 0 .. ] xs ) \ ( i, x ) ->
+ spawnWorker pool i \ worker_env -> do
+ res <- try @SomeException $ f worker_env x
+ case res of
+ Left e
+ -- Take care to avoid swallowing async exceptions.
+ | Just _ <- fromException @SomeAsyncException e
+ -> throwIO e
+ _ -> insertMonotoneMap_new results i res
+ waitForWorkers pool
+ all_results <- freezeMonotoneMap results
+ massertPpr ( Map.size all_results == length xs ) $
+ text "mapConcurrentWorkers: missing results"
+ either throwIO pure $ sequence $ Map.elems all_results
+
+-- | How to expand a node in a graph for 'concurrentTraversal'.
+data NodeExpander k n v =
+ NodeExpander
+ { nodeKey :: n -> k
+ -- ^ The identity of a node.
+ , expandNode :: ConcurrentWorkerEnv -> n -> IO ( v, [n] )
+ -- ^ Expand a node into its result and the children to visit next.
+ --
+ -- To guarantee determinism, the children must be a pure function of the
+ -- input, and IO effects must not observably depend on the order in
+ -- which nodes are expanded.
+ --
+ -- NB: workers do not hold semaphore tokens by default; use
+ -- 'withConcurrency' to acquire one
+ }
--- | Depth-first traversal with on-the-fly expansion of nodes.
+-- | Deterministically traverse a graph whose nodes are discovered as they are
+-- expanded.
--
--- Each expansion step is handled by a worker thread under the given
--- concurrency control.
---
--- Deterministic: expansions are consumed in the order the nodes were
--- discovered, so the traversal is a function of the node graph alone.
---
--- Fails fast: the first worker exception cancels the outstanding workers and
--- is rethrown.
-concurrentTraversal_DF
- :: forall k n r
- . Ord k
+-- Fails fast: once a worker throws an exception, no further work is started,
+-- and the exception is rethrown once the outstanding workers finish.
+concurrentTraversal
+ :: forall k n v
+ . ( HasDebugCallStack, Ord k, Outputable k )
=> String -- ^ thread label for workers
-> Concurrency
-> ConcurrentWorkerEnv
- -> Map.Map k r
+ -> NodeExpander k n v
+ -> Map.Map k v
-- ^ results known ahead of time (no expansion needed)
-> [n]
-- ^ root nodes
- -> (n -> k)
- -- ^ node key from node
- -> (ConcurrentWorkerEnv -> n -> IO (r, [n]))
- -- ^ worker action: expand a node into its result and the children to visit next
- --
- -- NB: workers do not hold semaphore tokens by default; use
- -- 'withConcurrency' to acquire one
- -> IO (Map.Map k r)
-concurrentTraversal_DF worker_label conc work_env base_map roots key expand =
- run_schedule worker_label conc work_env \ scheduler -> do
+ -> IO ( Map.Map k v )
+concurrentTraversal
+ worker_label conc work_env
+ ( NodeExpander { nodeKey, expandNode } )
+ base_map roots
+ =
+ -- SortLogs: nodes are discovered in an order that depends on the schedule,
+ -- so the workers' logs must be ordered before being printed.
+ run_pool worker_label SortLogs conc work_env \ pool -> do
+
+ -- The keys whose expansion has been started.
+ claims <- newMonotoneSet $ Map.keysSet base_map
+ results <- newMonotoneMap base_map
+
let
- expand_node :: n -> ConcurrentWorkerEnv -> IO (k, (r, [n]))
- expand_node node worker_env = do
- res <- expand worker_env node
- pure (key node, res)
-
- go
- :: Map.Map k r -- expanded nodes and their results
- -> Set.Set k -- nodes currently being expanded
- -> [n] -- discovered nodes, to expand next
- -> IO (Map.Map k r)
- go !visited !pending (node : worklist)
- | k `Set.member` pending || k `Map.member` visited
- = go visited pending worklist
- | otherwise
- = do spawnWorker scheduler (expand_node node)
- go visited (Set.insert k pending) worklist
+ discover :: n -> IO ()
+ discover node =
+ -- Claim the work for this node to avoid any other worker duplicating it.
+ insertMonotoneSet claims key >>= \case
+ AlreadyPresent -> pure ()
+ Inserted ->
+ spawnWorker pool key \ worker_env -> do
+ ( result, children ) <- expandNode worker_env node
+ insertMonotoneMap_new results key result
+ for_ children discover
where
- k = key node
- go visited pending []
- | Set.null pending
- = pure visited
- | otherwise
- = awaitWorker scheduler >>= \case
- Left e -> throwIO e
- Right (k, (result, children)) ->
- go (Map.insert k result visited) (Set.delete k pending) children
-
- go base_map Set.empty roots
+ key = nodeKey node
+
+ for_ roots discover
+ waitForWorkers pool
+ freezeMonotoneMap results
+
+{- Note [Deterministic concurrent workers]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To provide deterministic output when doing graph traversal (as in downsweep)
+despite using concurrent workers, we ensure that nothing can observe the order
+in which workers do their work:
+
+ 1. Any chunk of work is performed at most once: every worker atomically claims
+ ownership of the work it is going to do before it starts that work.
+
+ 2. Workers report results by writing to a 'MonotoneMap', whose entries are
+ written at most once. Other outputs (such as logging output) is accumulated
+ in a deterministic order and reported at the end.
+
+This scheme allows us to retain maximum concurrency: it allows new edges to be
+discovered by any worker and immediately processed.
+
+For this scheme to provide deterministic output, we require that:
+
+ * The expansion of a node is a pure function of the node.
+ * The work itself should not observably depend on when it was run.
+
+Failure is not deterministic: which worker's exception is reported depends on
+the schedule. When deterministic error messages are desired, the workers should
+return an error value instead (as 'mapConcurrentWorkers' does).
+-}
=====================================
compiler/GHC/Driver/Downsweep.hs
=====================================
@@ -174,9 +174,8 @@ incrementally constructing a ModuleGraph using the GHC API; See #27054). So
`downsweep` takes a `Maybe ModuleGraph` as one of its arguments.
Downsweep iteratively *expands* each so-called 'DownsweepNode' into a list of
-its dependencies, and recursively traverses all reachable nodes in a parallel
-non-det-depth-first order using 'parDfsBuild'. A 'DownsweepNode' is *expanded*
-by 'dsNodeExpand':
+its dependencies, and recursively traverses all reachable nodes concurrently
+using 'parBuild'. A 'DownsweepNode' is *expanded* by 'dsNodeExpand':
dsNodeExpand :: DownsweepNode -> DownsweepM (NodeRes (ModuleGraphNode, [DownsweepNode]))
@@ -591,7 +590,7 @@ loopModuleNodeInfos :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [ModuleNodeInf
loopUnits :: M.Map NodeKey (NodeRes ModuleGraphNode) -> UnitId -> [UnitId] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
loopInstantiations :: M.Map NodeKey (NodeRes ModuleGraphNode) -> [(UnitId, InstantiatedUnit)] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
loopFromInteractive :: M.Map NodeKey (NodeRes ModuleGraphNode) -> Module -> [InteractiveImport] -> DownsweepM (M.Map NodeKey (NodeRes ModuleGraphNode))
-loopDownsweepNodes base_map nodes = parDfsBuild (Just base_map) nodes dsNodeInfoKey dsNodeExpand
+loopDownsweepNodes base_map nodes = parBuild (Just base_map) nodes dsNodeInfoKey dsNodeExpand
loopModuleNodeInfos base_map = loopDownsweepNodes base_map . map DSMod
loopUnits base_map homud = loopDownsweepNodes base_map . map (DSUnit homud)
loopInstantiations base_map = loopDownsweepNodes base_map . map (uncurry DSInst)
@@ -1732,10 +1731,10 @@ getPreprocessedImports hsc_env src_fn mb_phase maybe_buf = do
return PreprocessedImports {..}
--------------------------------------------------------------------------------
--- * Generic traversal of iteratively-built graph: parDfsBuild
+-- * Generic traversal of iteratively-built graph: parBuild
--------------------------------------------------------------------------------
--- | The result of expanding a node in 'parDfsBuild'.
+-- | The result of expanding a node in 'parBuild'.
data NodeRes v
-- | Computed the node payload successfully
= NSuccess v
@@ -1748,20 +1747,18 @@ data NodeRes v
-- abort.
| NSkip
--- | In a parallel non-det-depth-first order, and starting from the given roots, traverse a
--- graph by iteratively expanding a node into a payload and a list of children
--- nodes to visit next.
+-- | Starting from the given roots, traverse a graph by iteratively expanding a
+-- node into a payload and a list of children nodes to visit next, expanding
+-- nodes concurrently.
--
--- A node is NEVER visited/expanded more than once, as long as the node key
--- @k@, computed from the node @n@, uniquely identifies that node.
---
--- The first argument @base_map@ is the starting set of already visited nodes
--- (these nodes won't be expanded again!).
+-- A node is NEVER visited/expanded more than once: nodes are identified by
+-- their key @k@, and the first node discovered under a key is the one that is
+-- expanded.
--
-- The result is a mapping from the key of every node transitively reachable
-- from the root nodes (inclusively) to the payload returned by expanding that
-- node. The result includes the previously visited nodes given in @base_map@,
--- s.t. @parDfsBuild base_map [] _ _ == base_map@.
+-- s.t. @parBuild base_map [] _ _ _ == base_map@.
--
-- The @expand@ function returns a 'NodeRes'. See the 'NodeRes' documentation
-- for more information about each result type.
@@ -1771,7 +1768,7 @@ data NodeRes v
-- See Note [Parallel Downsweep] for more information about how parallelism is
-- achieved, and See Note [Downsweep Control Flow and Caching] for information
-- about the various caches used.
-parDfsBuild :: forall k v n. Ord k
+parBuild :: forall k v n. (Ord k, Outputable k)
=> Maybe (Map.Map k (NodeRes v))
-- ^ Base map, existing results. We won't re-expand any of the nodes
-- already present in this map.
@@ -1785,22 +1782,26 @@ parDfsBuild :: forall k v n. Ord k
-> DownsweepM (Map.Map k (NodeRes v))
-- ^ The result accumulates the payload of expanding the root nodes
-- and all nodes transitively reachable from those roots.
-parDfsBuild base_map roots key expand = ReaderT $ \ds_env -> do
+parBuild base_map roots nodeKey expand = ReaderT $ \ds_env -> do
let
conc :: Concurrency
conc = ds_concurrency ds_env
- expand_node :: ConcurrentWorkerEnv -> n -> IO (NodeRes v, [n])
- expand_node worker_env node = do
+ expandNode :: ConcurrentWorkerEnv -> n -> IO (NodeRes v, [n])
+ expandNode worker_env node = do
result <- withConcurrency conc $
runDownsweepM (setDownsweepWorkerEnv worker_env ds_env) (expand node)
pure $ case result of
NSkip -> (NSkip, [])
NSuccess (val, new_work) -> (NSuccess val, new_work)
- concurrentTraversal_DF "downsweep_worker" conc (viewHscWorkerEnv $ ds_hsc_env ds_env)
- (fromMaybe mempty base_map) roots key expand_node
+ concurrentTraversal "downsweep_worker"
+ conc
+ (viewHscWorkerEnv $ ds_hsc_env ds_env)
+ (NodeExpander { nodeKey, expandNode })
+ (fromMaybe mempty base_map)
+ roots
setDownsweepWorkerEnv :: ConcurrentWorkerEnv -> DownsweepEnv -> DownsweepEnv
setDownsweepWorkerEnv work_env env =
@@ -1809,7 +1810,7 @@ setDownsweepWorkerEnv work_env env =
{-
Note [Downsweep Control Flow and Caching]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The control flow of downsweep is extracted into a single function `parDfsBuild`,
+The control flow of downsweep is extracted into a single function `parBuild`,
which takes care of iteratively expanding and traversing all nodes of the
in-construction module graph necessary to build a full `ModuleGraph` at the
end.
@@ -1818,7 +1819,7 @@ There are three levels of caching going on, all of which are necessary to make
sure we don't do repeated work (notably, we NEVER summarise the same module
twice).
-1. `parDfsBuild` accumulates the final module graph and never revisits the
+1. `parBuild` accumulates the final module graph and never revisits the
same node of the module graph. Cache is keyed by the final
`ModuleGraph`s `NodeKey`s.
@@ -1900,9 +1901,12 @@ things, and that processing can often be costly (e.g. see `expandModuleSummary`)
We leverage multiple threads in this traversal to expand more than one module
at once, respecting -j<N> to mean we never expand more than N modules at once.
-We use the concurrent scheduling abstraction from GHC.Driver.Concurrency
-('concurrentTraversal_DF'). Each time we discover a new node, a worker is
-spawned to expand it. After each expansion completes, the resulting children
-nodes are pushed onto the worklist. With -j1 no threads are involved: each
-expansion runs in sequence.
+We use the concurrent traversal abstraction from GHC.Driver.Concurrency
+('concurrentTraversal'). Each time we discover a new node, a worker is spawned
+to expand it; a worker spawns the workers for the children it discovers itself,
+so an expansion never waits on an unrelated one. With -j1 no threads are
+involved: each expansion runs in sequence.
+
+The traversal is deterministic despite the fact that the order in which workers
+are spawned is not: see Note [Deterministic concurrent workers] in GHC.Driver.Concurrency.
-}
=====================================
compiler/GHC/Driver/Pipeline/LogQueue.hs
=====================================
@@ -4,6 +4,7 @@ module GHC.Driver.Pipeline.LogQueue ( LogQueue(..)
, finishLogQueue
, writeLogQueue
, parLogAction
+ , printLogs
, LogQueueQueue(..)
, initLogQueue
@@ -25,19 +26,18 @@ import Control.Monad
-- LogQueue Abstraction
--- | Each module is given a unique 'LogQueue' to redirect compilation messages
--- to. A 'Nothing' value contains the result of compilation, and denotes the
--- end of the message queue.
-data LogQueue = LogQueue { logQueueId :: !Int
- , logQueueMessages :: !(IORef [Maybe (MessageClass, SrcSpan, SDoc, LogFlags)])
+-- | A buffer of compilation messages produced by one worker.
+--
+-- A 'Nothing' value denotes the end of the message queue.
+data LogQueue = LogQueue { logQueueMessages :: !(IORef [Maybe (MessageClass, SrcSpan, SDoc, LogFlags)])
, logQueueSemaphore :: !(MVar ())
}
-newLogQueue :: Int -> IO LogQueue
-newLogQueue n = do
+newLogQueue :: IO LogQueue
+newLogQueue = do
mqueue <- newIORef []
sem <- newMVar ()
- return (LogQueue n mqueue sem)
+ return (LogQueue mqueue sem)
finishLogQueue :: LogQueue -> IO ()
finishLogQueue lq = do
@@ -50,7 +50,7 @@ writeLogQueue lq msg = do
-- | Internal helper for writing log messages
writeLogQueueInternal :: LogQueue -> Maybe (MessageClass,SrcSpan,SDoc, LogFlags) -> IO ()
-writeLogQueueInternal (LogQueue _n ref sem) msg = do
+writeLogQueueInternal (LogQueue ref sem) msg = do
atomicModifyIORef' ref $ \msgs -> (msg:msgs,())
_ <- tryPutMVar sem ()
return ()
@@ -61,9 +61,11 @@ parLogAction :: LogQueue -> LogAction
parLogAction log_queue log_flags !msgClass !srcSpan !msg =
writeLogQueue log_queue (msgClass,srcSpan,msg, log_flags)
--- Print each message from the log_queue using the global logger
+-- | Print each message from the log queue using the given logger.
+--
+-- Blocks until the queue has been finished with 'finishLogQueue'.
printLogs :: Logger -> LogQueue -> IO ()
-printLogs !logger (LogQueue _n ref sem) = read_msgs
+printLogs !logger (LogQueue ref sem) = read_msgs
where read_msgs = do
takeMVar sem
msgs <- atomicModifyIORef' ref $ \xs -> ([], reverse xs)
@@ -84,11 +86,23 @@ data LogQueueQueue = LogQueueQueue Int (IM.IntMap LogQueue)
newLogQueueQueue :: LogQueueQueue
newLogQueueQueue = LogQueueQueue 1 IM.empty
-addToQueueQueue :: LogQueue -> LogQueueQueue -> LogQueueQueue
-addToQueueQueue lq (LogQueueQueue n im) = LogQueueQueue n (IM.insert (logQueueId lq) lq im)
-
-initLogQueue :: TVar LogQueueQueue -> LogQueue -> STM ()
-initLogQueue lqq lq = modifyTVar lqq (addToQueueQueue lq)
+addToQueueQueue
+ :: Int -- ^ 1-indexed position in which to add
+ -> LogQueue
+ -> LogQueueQueue
+ -> LogQueueQueue
+addToQueueQueue i lq (LogQueueQueue n im) = LogQueueQueue n (IM.insert i lq im)
+
+-- | Hand a log queue to the log thread, to be printed at the given position.
+--
+-- Positions must be contiguous: the log thread prints position @n@ only after
+-- every position below @n@ is done.
+initLogQueue
+ :: TVar LogQueueQueue
+ -> Int -- ^ position in the 'LogQueueQueue' in which to insert the 'LogQueue'
+ -> LogQueue
+ -> STM ()
+initLogQueue lqq i lq = modifyTVar lqq (addToQueueQueue i lq)
-- | Return all items in the queue in ascending order
allLogQueues :: LogQueueQueue -> [LogQueue]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/04ba945021e675d73807501cf132cec…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/04ba945021e675d73807501cf132cec…
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/27627] 5 commits: Add tests for absent fillers at dictionary types
by Zubin (@wz1000) 19 Aug '26
by Zubin (@wz1000) 19 Aug '26
19 Aug '26
Zubin pushed to branch wip/27627 at Glasgow Haskell Compiler / GHC
Commits:
bf5e0fc3 by Zubin Duggal at 2026-08-19T15:32:25+05:30
Add tests for absent fillers at dictionary types
T27627 a unary class whose superclass is a non-unary class
T27627a ...whose superclass is a Constraint-kinded type family
T27627b ...whose superclass is a quantified constraint
T27627c a unary class applied to itself, (UC (UC (TC a)))
T27627e a (forall b. P b) dictionary that loops
- - - - -
26bc5e84 by Zubin Duggal at 2026-08-19T15:32:58+05:30
An abstract TyCon may hide a unary class
A class declared in an hs-boot file is an AbstractTyCon inside the
module loop, and compiling the real declaration may reveal it to be a
UnaryClassTyCon.
- isTerminatingType returned True for such AbstractTyCons
- IfaceToCore set the unary flag to False in the DFunId
So we could end up speculating bottom dictionaries because inside a module
loop we see an UnaryClassTyCon as an AbstractTyCon
Use mayBeUnaryClassTyCon instead of isUnaryClassTyCon, which returns True for an
abstract TyCon.
Fixes #27704
- - - - -
abac352f by Zubin Duggal at 2026-08-19T15:32:58+05:30
Specialise: don't drop a dead arg that the stable unfolding uses
specHeader decides an argument is dead by calling isDeadBinder on a binder of
the /optimised RHS/, then applies the filler to the /stable unfolding/
template instead. The two may differ, so the argument can be dead in
the RHS and not in the template.
The specialised function's unfolding then has an absent filler, and any call
site that inlines it evaluates the error thunk.
Thread the template's binders through specHeader alongside the RHS
binders and make a filler only when the argument is dead in both.
See Note [Dead args and stable unfoldings].
Fixes #27703
- - - - -
312ca902 by Zubin Duggal at 2026-08-19T15:32:58+05:30
Never make an absent filler at a constraint type,
isDictTy doesn't catch constraints hidden behind unreduced type family applications
Example:
type family F a :: Constraint
type instance F W = TC W
a :: F W => Int -> Int -- (F W) argument is absent
Oops! Entered absent arg Arg: irred
Type: F W
also in the test T27627f
Use `ConstraintLike <- typeTypeOrConstraint arg_ty` instead???
- - - - -
e3618220 by Zubin Duggal at 2026-08-19T15:32:58+05:30
CorePrep: don't speculate a call across an hs-boot edge
We take care not to evaluate things that might be bottom, like a
looping dictionary group, but our analysis is defeated by boot files.
We only track recursion within a module, so two dictionaries that
depend on each other across a module loop each look non-recursive, and
we might speculate them.
Any recursion we cannot see must cross an hs-boot edge, so refuse to
speculate calls that cross one.
Fixes #27717
- - - - -
61 changed files:
- + changelog.d/27627
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/Predicate.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/IfaceToCore.hs
- + testsuite/tests/core-to-stg/T27627/Callee.hs
- + testsuite/tests/core-to-stg/T27627/Caller.hs
- + testsuite/tests/core-to-stg/T27627/Main.hs
- + testsuite/tests/core-to-stg/T27627/T27627.stdout
- + testsuite/tests/core-to-stg/T27627/all.T
- + testsuite/tests/core-to-stg/T27627a/Callee.hs
- + testsuite/tests/core-to-stg/T27627a/Caller.hs
- + testsuite/tests/core-to-stg/T27627a/Main.hs
- + testsuite/tests/core-to-stg/T27627a/T27627a.stdout
- + testsuite/tests/core-to-stg/T27627a/all.T
- + testsuite/tests/core-to-stg/T27627b/Callee.hs
- + testsuite/tests/core-to-stg/T27627b/Caller.hs
- + testsuite/tests/core-to-stg/T27627b/Main.hs
- + testsuite/tests/core-to-stg/T27627b/T27627b.stdout
- + testsuite/tests/core-to-stg/T27627b/all.T
- + testsuite/tests/core-to-stg/T27627c/Callee.hs
- + testsuite/tests/core-to-stg/T27627c/Caller.hs
- + testsuite/tests/core-to-stg/T27627c/Main.hs
- + testsuite/tests/core-to-stg/T27627c/T27627c.stdout
- + testsuite/tests/core-to-stg/T27627c/all.T
- + testsuite/tests/core-to-stg/T27627e.hs
- + testsuite/tests/core-to-stg/T27627e.stdout
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- testsuite/tests/core-to-stg/all.T
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/14f16c515fab92ca2896bddd286e4f…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/14f16c515fab92ca2896bddd286e4f…
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