[Git][ghc/ghc][master] simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
by Marge Bot (@marge-bot) 03 Sep '26
by Marge Bot (@marge-bot) 03 Sep '26
03 Sep '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
6a44d591 by mangoiv at 2026-09-03T13:46:22-04:00
simplifier: remove a bogus `assert` in `rebuild_app'` in `GHC.CoreToStg.Prep.cpeApp`.
Prior to this commit
commit 08bc245be70d95801bc1138804ed1de9474fbdc0
Author: sheaf <sam.derbyshire(a)gmail.com>
Date: Sat Feb 28 16:30:43 2026 +0100
Clean up join points, casts & ticks
This commit shores up the logic dealing with casts and ticks occurring
in between a join point binding and a jump
any `PlaceRuntime` ticks we observed were profiling ticks, even though that
isn't necessary.
The more liberal rules in the above commit allow e.g. breakpoint ticks
(which are valid PlaceRuntime ticks) to legitimately appear in an argument
position.
Fixes #27556
- - - - -
6 changed files:
- + changelog.d/27556
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Types/Tickish.hs
- + testsuite/tests/simplCore/should_compile/T27556.hs
- + testsuite/tests/simplCore/should_compile/T27556.script
- testsuite/tests/simplCore/should_compile/all.T
Changes:
=====================================
changelog.d/27556
=====================================
@@ -0,0 +1,5 @@
+section: compiler
+synopsis: Remove a too strict assert in coreprep; fixes a bug where debug builds
+ of GHC would fail to compile correctly optimized programs.
+mrs: !16558
+issues: #27556
=====================================
compiler/GHC/CoreToStg/Prep.hs
=====================================
@@ -1260,10 +1260,9 @@ cpeApp top_env expr
-- See Note [Ticks and mandatory eta expansion]
AITick tickish
- | tickishPlace tickish == PlaceRuntime
+ | PlaceRuntime <- tickishPlace tickish
, req_depth > 0
- -> assert (isProfTick tickish) $
- rebuild_app' env as fun' floats ss (tickish:rt_ticks) req_depth
+ -> rebuild_app' env as fun' floats ss (tickish:rt_ticks) req_depth
| otherwise
-- See [Floating Ticks in CorePrep]
-> rebuild_app' env as fun' (snocFloat floats (FloatTick tickish)) ss rt_ticks req_depth
=====================================
compiler/GHC/Types/Tickish.hs
=====================================
@@ -13,7 +13,6 @@ module GHC.Types.Tickish (
mkNoCount,
mkNoScope,
tickishIsCode,
- isProfTick,
TickishPlacement(..),
tickishPlace,
tickishContains,
@@ -545,10 +544,6 @@ tickishIsCode ProfNote{} = True
tickishIsCode Breakpoint{} = True
tickishIsCode HpcTick{} = True
-isProfTick :: GenTickish pass -> Bool
-isProfTick ProfNote{} = True
-isProfTick _ = False
-
-- | Governs the kind of expression that the tick gets placed on when
-- annotating for example using @mkTick@. If we find that we want to
-- put a tickish on an expression ruled out here, we try to float it
=====================================
testsuite/tests/simplCore/should_compile/T27556.hs
=====================================
@@ -0,0 +1,4 @@
+import Control.Exception
+
+main = mask $ \restore -> restore (pure ())
+
=====================================
testsuite/tests/simplCore/should_compile/T27556.script
=====================================
@@ -0,0 +1 @@
+:l T27556
=====================================
testsuite/tests/simplCore/should_compile/all.T
=====================================
@@ -611,3 +611,4 @@ test('T27296', [], makefile_test, ['T27296'])
test('T27296b', [], makefile_test, ['T27296b'])
test('T27589', [grep_errmsg(r'wombat')], compile, ['-O -ddump-simpl -dno-typeable-binds -dsuppress-uniques'])
test('T27590', [grep_errmsg(r'wombat')], compile, ['-O -ddump-simpl -dno-typeable-binds -dsuppress-uniques'])
+test('T27556', [only_ways('ghci'), extra_hc_opts('-O -fno-unoptimized-core-for-interpreter')], ghci_script, ['T27556.script'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6a44d591804d46bfd6469e26836ffa3…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/6a44d591804d46bfd6469e26836ffa3…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][master] Add -Wimplicit-field-strictness (#16836)
by Marge Bot (@marge-bot) 03 Sep '26
by Marge Bot (@marge-bot) 03 Sep '26
03 Sep '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
9e7739ab by Simon Jakobi at 2026-09-03T13:45:43-04:00
Add -Wimplicit-field-strictness (#16836)
This opt-in warning fires when a data constructor field lacks an
explicit strictness annotation (`!` or `~`). It complements the
LazyFieldAnnotations extension (4762a8bf30f) from GHC proposal 752,
which makes `~` annotations available for this purpose.
Deciding which fields to report requires their levity, so the check
runs after typechecking. To keep the noise down, the diagnostic is
emitted once per data declaration, grouped by constructor.
Closes #16836.
Assisted-by: Claude Fable 5
- - - - -
20 changed files:
- + changelog.d/implicit-field-strictness-warning
- changelog.d/lazy-field-annotations
- 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/exts/strict.rst
- 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/T16836d.hs
- + testsuite/tests/warnings/should_compile/T16836d.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
+mrs: !16555
+
+description: {
+ The new opt-in warning :ghc-flag:`-Wimplicit-field-strictness` reports
+ data constructor fields that lack an explicit strictness annotation
+ (``!`` or ``~``).
+}
=====================================
changelog.d/lazy-field-annotations
=====================================
@@ -11,4 +11,7 @@ description: {
continues to control the default strictness of unannotated fields.
See `GHC Proposal #752 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0752-l…>`_.
+
+ Also note the new opt-in :ghc-flag:`-Wimplicit-field-strictness` warning, which
+ reports fields lacking an explicit annotation.
}
=====================================
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,21 @@ instance Diagnostic TcRnMessage where
hang (text "Missing role annotation" <> colon)
2 (text "type role" <+> ppr name <+> hsep (map ppr roles))
+ TcRnImplicitFieldStrictness _lazy_anns cons -> mkSimpleDecorated $
+ hang (text "These constructor fields lack an explicit strictness annotation" <> colon)
+ 2 (vcat (map ppr_con (NE.toList cons)))
+ where
+ ppr_con (con, fields) =
+ bullet <+> text "In" <+> quotes (ppr con) <> colon <+> ppr_fields (NE.toList fields)
+ ppr_fields fields
+ | let names = [n | ImplicitStrictnessRecField n <- fields]
+ , not (null names)
+ = text "field" <> plural names <+> quotedListWithAnd (map ppr names)
+ | otherwise
+ = let poss = [i | ImplicitStrictnessPosField i <- fields]
+ in text "field" <> plural poss
+ <+> unquotedListWith (text "and") (map int poss)
+
TcRnIllformedTypePattern p
-> mkSimpleDecorated $
hang (text "Ill-formed type pattern:") 2 (ppr p)
@@ -2693,6 +2708,8 @@ instance Diagnostic TcRnMessage where
-> ErrorWithoutFlag
TcRnMissingRoleAnnotation{}
-> WarningWithFlag Opt_WarnMissingRoleAnnotations
+ TcRnImplicitFieldStrictness{}
+ -> WarningWithFlag Opt_WarnImplicitFieldStrictness
TcRnIllegalInvisTyVarBndr{}
-> ErrorWithoutFlag
TcRnIllegalWildcardTyVarBndr{}
@@ -3428,6 +3445,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,23 @@ 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, T16836c, T16836d
+
+ -}
+ TcRnImplicitFieldStrictness
+ :: Bool -- ^ whether @LazyFieldAnnotations@ is enabled
+ -> NonEmpty (Name, NonEmpty 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 +6417,14 @@ data PatSynInvalidRhsReason
| PatSynUnboundVar !Name
deriving (Generic)
+-- | A constructor field lacking an explicit strictness annotation, as
+-- reported by 'TcRnImplicitFieldStrictness'.
+data ImplicitStrictnessField
+ -- | A record field
+ = ImplicitStrictnessRecField FieldLabelString
+ -- | A positional argument (1-based index)
+ | ImplicitStrictnessPosField Int
+
data BadFieldAnnotationReason where
{-| A lazy data type field annotation (~) was used without enabling the
extension LazyFieldAnnotations.
=====================================
compiler/GHC/Tc/TyCl.hs
=====================================
@@ -5022,6 +5022,16 @@ checkValidTyCon tc
; mapM_ (checkValidDataCon dflags ex_ok tc) data_cons
; mapM_ (checkPartialRecordField data_cons) (tyConFieldLabels tc)
+ ; warn_implicit_strictness <- woptM Opt_WarnImplicitFieldStrictness
+ ; when (warn_implicit_strictness
+ && not (isNewTyCon tc)
+ && not (isTypeDataTyCon tc)) $
+ whenIsJust (NE.nonEmpty (mapMaybe conImplicitStrictnessFields data_cons)) $
+ \offenders ->
+ do { lazy_anns <- xoptM LangExt.LazyFieldAnnotations
+ ; addDiagnosticTc $
+ TcRnImplicitFieldStrictness lazy_anns offenders }
+
-- Check that fields with the same name share a type
; mapM_ check_fields groups }}
where
@@ -5072,6 +5082,29 @@ checkValidTyCon tc
res2 = dataConOrigResTy con2
fty2 = dataConFieldType con2 lbl
+-- | For a given data constructor, collect the fields to report for
+-- @-Wimplicit-field-strictness@.
+--
+-- Only fields whose type is known to be lifted are collected: unlifted
+-- fields are unconditionally strict, and annotating one with @!@ or
+-- @~@ would trigger @-Wredundant-strictness-flags@.
+conImplicitStrictnessFields :: DataCon -> Maybe (Name, NonEmpty ImplicitStrictnessField)
+conImplicitStrictnessFields con
+ | Just ne_fields <- NE.nonEmpty fields
+ = Just (dataConName con, ne_fields)
+ | otherwise
+ = Nothing
+ where
+ fld_refs = case dataConFieldLabels con of
+ [] -> map ImplicitStrictnessPosField [1..]
+ lbls -> map (ImplicitStrictnessRecField . flLabel) lbls
+ fields = [ ref
+ | (ref, arg_ty, HsSrcBang _ _ NoSrcStrict)
+ <- zip3 fld_refs
+ (map scaledThing (dataConOrigArgTys con))
+ (dataConSrcBangs con)
+ , typeLevity_maybe arg_ty == Just Lifted ]
+
checkPartialRecordField :: [DataCon] -> FieldLabel -> TcM ()
-- Checks the partial record field selector, and warns.
-- See Note [Checking partial record field]
=====================================
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 @~@).
+
+ 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/exts/strict.rst
=====================================
@@ -194,6 +194,9 @@ The ``~`` annotation must be written in prefix form::
See `GHC Proposal #229 <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0229-w…>`__
for the precise rules.
+See also :ghc-flag:`-Wimplicit-field-strictness`, which warns about
+fields lacking an explicit annotation.
+
.. _strict-data:
Strict-by-default data types
=====================================
docs/users_guide/using-warnings.rst
=====================================
@@ -2505,6 +2505,21 @@ 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
+
+ This warning reports data constructor fields that lack an explicit
+ strictness annotation (``!`` or ``~``).
+
.. 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘MkT’: fields 1 and 2
+ • In ‘MkT2’: field 2
+ • 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘:+:’: field 1
+ • 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘MkG’: field 1
+ • 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • 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:33:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘MkF’: field 1
+ • 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]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘MkT’: field 1
+ • In the data type declaration for ‘T’
+ Suggested fix: Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+
=====================================
testsuite/tests/warnings/should_compile/T16836d.hs
=====================================
@@ -0,0 +1,20 @@
+{-# OPTIONS_GHC -Wimplicit-field-strictness #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# LANGUAGE UnliftedDatatypes #-}
+module T16836d where
+
+import GHC.Exts
+
+-- unlifted fields can't be usefully annotated; exempt
+data P = MkP Int# (# Int, Int #)
+
+type UD :: UnliftedType
+data UD = MkUD
+
+-- a field of an unlifted data type is exempt too
+data Q = MkQ UD
+
+-- mixed constructor
+-- warns only for the lifted field 2
+data M = MkM Int# Int
=====================================
testsuite/tests/warnings/should_compile/T16836d.stderr
=====================================
@@ -0,0 +1,9 @@
+T16836d.hs:20:1: warning: [GHC-47032] [-Wimplicit-field-strictness]
+ • These constructor fields lack an explicit strictness annotation:
+ • In ‘MkM’: field 2
+ • In the data type declaration for ‘M’
+ Suggested fixes:
+ • Annotate each field with ‘!’ (strict) or ‘~’ (lazy)
+ • Use the ‘LazyFieldAnnotations’ extension (implied by ‘StrictData’)
+ to allow ‘~’ annotations
+
=====================================
testsuite/tests/warnings/should_compile/all.T
=====================================
@@ -91,3 +91,7 @@ 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, [''])
+test('T16836d', normal, compile, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/9e7739ab58eca17e87c4f949987a4e7…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/9e7739ab58eca17e87c4f949987a4e7…
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/multi-threaded-debugger] ThreadBreaksIsolationMode
by Rodrigo Mesquita (@alt-romes) 03 Sep '26
by Rodrigo Mesquita (@alt-romes) 03 Sep '26
03 Sep '26
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC
Commits:
9d82db82 by Rodrigo Mesquita at 2026-09-03T18:44:53+01:00
ThreadBreaksIsolationMode
- - - - -
4 changed files:
- compiler/GHC.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- ghc/GHCi/UI/Monad.hs
Changes:
=====================================
compiler/GHC.hs
=====================================
@@ -198,7 +198,7 @@ module GHC (
isStmt, hasImport, isImport, isDecl,
-- ** The debugger
- SingleStep(..),
+ SingleStep(..), ThreadBreaksIsolationMode(..),
Resume(..),
History(historyBreakpointId, historyEnclosingDecls),
GHC.getHistorySpan, getHistoryModule,
=====================================
compiler/GHC/Runtime/Eval.hs
=====================================
@@ -13,7 +13,7 @@ module GHC.Runtime.Eval (
Resume(..), History(..),
execStmt, execStmt', ExecOptions(..), execOptions, ExecResult(..), resumeExec,
runDecls, runDeclsWithLocation, runParsedDecls,
- parseImportDecl, SingleStep(..),
+ parseImportDecl, SingleStep(..), ThreadBreaksIsolationMode(..),
abandon, abandonAll,
getResumeContext,
getHistorySpan,
@@ -184,8 +184,8 @@ execOptions = ExecOptions
, execSourceFile = "<interactive>"
, execLineNumber = 1
, execWrap = EvalThis -- just run the statement, don't wrap it in anything
- , execIsolateBreaks = True
- -- by default we just care about breakpoints hit in this "main" thread
+ , execIsolateMode = SingleThreadedBreaks
+ -- by default we just care about breakpoints hit in this eval thread
}
-- | Run a statement in the current interactive context.
@@ -211,7 +211,7 @@ execStmt input exec_opts@ExecOptions{..} = do
-- doing preprocessing on the AST before execution, e.g. in GHCi (see
-- GHCi.UI.runStmt).
execStmt' :: GhcMonad m => GhciLStmt GhcPs -> String -> ExecOptions -> m ExecResult
-execStmt' stmt stmt_text opts@ExecOptions{..} = do
+execStmt' stmt stmt_text ExecOptions{..} = do
hsc_env <- getSession
let interp = hscInterp hsc_env
@@ -232,8 +232,8 @@ execStmt' stmt stmt_text opts@ExecOptions{..} = do
status <-
liftIO $ do
- let eval_opts = initEvalOpts idflags' (enableGhcStepMode execSingleStep)
- { isolateThreadBreaks = execIsolateThreadBreaks }
+ let eval_opts = (initEvalOpts idflags' (enableGhcStepMode execSingleStep))
+ { isolateThreadBreaks = enableIsolateThreadBreaks execIsolateMode }
evalStmt interp eval_opts (execWrap hval)
let ic = hsc_IC hsc_env
@@ -241,7 +241,7 @@ execStmt' stmt stmt_text opts@ExecOptions{..} = do
size = ghciHistSize idflags'
- handleRunStatus opts stmt_text bindings ids
+ handleRunStatus execSingleStep execIsolateMode stmt_text bindings ids
status (emptyHistory size)
runDecls :: GhcMonad m => String -> m [Name]
@@ -293,15 +293,15 @@ emptyHistory size = nilBL size
-- breakpoint if we don't care about that breakpoint (e.g. if using :steplocal
-- or :stepmodule, rather than :step, we only care about certain breakpoints).
handleRunStatus :: GhcMonad m
- => ExecOptions
+ => SingleStep
+ -> ThreadBreaksIsolationMode
-> String
-> ResumeBindings
-> [Id]
-> EvalStatus_ [ForeignHValue] [HValueRef]
-> BoundedList History
-> m ExecResult
-handleRunStatus ExecOptions{execSingleStep = step, execIsolateThreadBreaks}
- expr bindings final_ids status history0 = do
+handleRunStatus step isolateMode expr bindings final_ids status history0 = do
hsc_env <- getSession
let
interp = hscInterp hsc_env
@@ -395,22 +395,25 @@ handleRunStatus ExecOptions{execSingleStep = step, execIsolateThreadBreaks}
return (ExecBreak names (Just ibi))
else do
-- resume with the same step type
- let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
- { isolateThreadBreaks = execIsolateThreadBreaks }
+ let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
+ { isolateThreadBreaks = enableIsolateThreadBreaks isolateMode }
status <- liftIO $ GHCi.resumeStmt interp eval_opts resume_ctxt_fhv
history <- if not tracing then pure history0 else do
history1 <- liftIO $ mkHistory hug apStack_fhv ibi
let !history' = history1 `consBL` history0
-- history is strict, otherwise our BoundedList is pointless.
return history'
- handleRunStatus step expr bindings final_ids status history
+ handleRunStatus step isolateMode expr bindings final_ids status history
where
tracing | RunAndLogSteps <- step = True
| otherwise = False
-resumeExec :: GhcMonad m => SingleStep -> Maybe Int
+resumeExec :: GhcMonad m
+ => SingleStep
+ -> ThreadBreaksIsolationMode
+ -> Maybe Int
-> m ExecResult
-resumeExec step mbCnt
+resumeExec step isolateMode mbCnt
= do
hsc_env <- getSession
let ic = hsc_IC hsc_env
@@ -455,7 +458,8 @@ resumeExec step mbCnt
(Just brkpt, Just cnt) -> setupBreakpoint interp brkpt cnt
_ -> return ()
- let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
+ let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
+ { isolateThreadBreaks = enableIsolateThreadBreaks isolateMode }
status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
let prevHistoryLst = fromListBL 50 hist
hug = hsc_HUG hsc_env
@@ -466,7 +470,7 @@ resumeExec step mbCnt
hist1 <- liftIO (mkHistory hug apStack bi)
return $ hist1 `consBL` fromListBL 50 hist
| otherwise -> pure prevHistoryLst
- handleRunStatus step expr bindings final_ids status =<< hist'
+ handleRunStatus step isolateMode expr bindings final_ids status =<< hist'
setupBreakpoint :: GhcMonad m => Interp -> InternalBreakpointId -> Int -> m () -- #19157
setupBreakpoint interp ibi cnt = do
=====================================
compiler/GHC/Runtime/Eval/Types.hs
=====================================
@@ -10,6 +10,7 @@ module GHC.Runtime.Eval.Types (
Resume(..), ResumeBindings, IcGlobalRdrEnv(..),
History(..), ExecResult(..),
SingleStep(..), enableGhcStepMode, breakHere,
+ ThreadBreaksIsolationMode(..), enableIsolateThreadBreaks,
ExecOptions(..)
) where
@@ -36,7 +37,8 @@ data ExecOptions
, execSourceFile :: String -- ^ filename (for errors)
, execLineNumber :: Int -- ^ line number (for errors)
, execWrap :: ForeignHValue -> EvalExpr ForeignHValue
- , execIsolateThreadBreaks :: Bool -- ^ just like @'isolateThreadBreaks'@
+ , execIsolateMode :: ThreadBreaksIsolationMode
+ -- ^ like @'isolateThreadBreaks'@
}
-- | What kind of stepping are we doing?
@@ -65,6 +67,17 @@ data SingleStep
| ModuleStep
{ breakAt :: SrcSpan }
+-- | Just like @'isolateThreadBreaks'@, determining whether the
+-- execution/resuming execution of an expression should stop whenever any
+-- non-isolated thread hits a breakpoint, or only when the thread executing
+-- that expression specifically stops.
+data ThreadBreaksIsolationMode
+ -- | Stop exclusively on the breakpoints in this thread
+ = SingleThreadedBreaks
+ -- | Stop whenever any thread that is not running in 'SingleThreadedBreaks'
+ -- isolation mode hits a breakpoint
+ | MultiThreadedBreaks
+
-- | Whether this 'SingleStep' mode requires instructing the interpreter to
-- step at every breakpoint or after every return (see @'EvalStep'@).
enableGhcStepMode :: SingleStep -> EvalStep
@@ -73,6 +86,10 @@ enableGhcStepMode StepOut{} = EvalStepOut
-- for the remaining step modes we need to stop at every single breakpoint.
enableGhcStepMode _ = EvalStepSingle
+enableIsolateThreadBreaks :: ThreadBreaksIsolationMode -> Bool
+enableIsolateThreadBreaks SingleThreadedBreaks = True
+enableIsolateThreadBreaks MultiThreadedBreaks = False
+
-- | Given a 'SingleStep' mode, whether the breakpoint was explicitly active,
-- and the SrcSpan of a breakpoint we hit, return @True@ if we should stop at
-- this breakpoint.
=====================================
ghc/GHCi/UI/Monad.hs
=====================================
@@ -412,7 +412,7 @@ resume step mbIgnoreCnt = do
withProgName (progname st) $
withArgs (args st) $
reflectGHCi x $ do
- GHC.resumeExec step mbIgnoreCnt
+ GHC.resumeExec step GHC.SingleThreadedBreaks mbIgnoreCnt
-- --------------------------------------------------------------------------
-- timing & statistics
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/9d82db8234bbbbe11096988b3210d85…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/9d82db8234bbbbe11096988b3210d85…
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] 57 commits: Never make an absent filler for a constraint type
by Alan Zimmerman (@alanz) 03 Sep '26
by Alan Zimmerman (@alanz) 03 Sep '26
03 Sep '26
Alan Zimmerman pushed to branch wip/az/exactprint-annotation-rationalisation at Glasgow Haskell Compiler / GHC
Commits:
7bf546fc by Simon Peyton Jones at 2026-08-31T23:48:53-04:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
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
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
- - - - -
5f474953 by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
cd5c6bcc by Zubin Duggal at 2026-08-31T23:48:53-04:00
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 isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
- - - - -
abfc224a by Zubin Duggal at 2026-08-31T23:48:53-04:00
Specialise: don't replace dead args with absent fillers
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.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
- - - - -
1557fd1c by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
4117e5ae by Wolfgang Jeltsch at 2026-08-31T23:49:35-04:00
Incorporate the `rethrowSTM` reexport into the `stm` submodule
- - - - -
4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T3586
unsafeRead and unsafeWrite use 0-based index.
Looking at #3586, the expected output seems to be 2.8e8.
Addresses #27596
- - - - -
3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T21305
writeInt64Array# takes an index measured in units of Int64 elements.
Fixes #27596
- - - - -
44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00
Fix buglet in INLINE-arity calculation for pattern synonyms
This fixes #27744.
The buglet was accidentally introduced by
commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sat Aug 1 00:13:02 2026 +0100
Fix three bugs related to required type args and INLINE pragmas
I failed to find all the calls to `addInlinePragArity`!
- - - - -
df058f1d by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, although their
noise profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates the remaining users of 'all' (and of the 'all'
default) to explicit per-metric collection, ahead of removing 'all'
from the driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved
by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425), residency remains gated via max_bytes_used,
now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all. One is a simplifier-ticks blowup and
the other is stated entirely in allocation numbers, so the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric. They are already gated at 1% via
collect_compiler_runtime. The ghc/max gate had previously broken CI
spuriously (9fd11585eb widened it from 1% to 10% for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
72dd2432 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated
at a single tolerance, although their noise profiles are incompatible,
making such tests either flaky or toothless (#27653).
Closes #27653.
Assisted-by: Claude Fable 5
- - - - -
2228cb30 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Make the deviation argument of collect_stats mandatory
Almost every caller passes an explicit tolerance matched to the
metric's noise profile, and the silent 20% default is far slacker than
'bytes allocated' merits. Only two tests relied on it. They now state
their tolerance explicitly:
large-project gets 10%, in line with other large compile-time tests.
T9848 gets 2%: its metric is byte-for-byte deterministic across CI
jobs and platforms of a given test_env, has drifted only about 2.5%
since 2015, and the fusion failure it guards against would show up as
a roughly +30000% jump.
Assisted-by: Claude Fable 5
- - - - -
56291fc5 by Cheng Shao at 2026-09-02T07:18:45-04:00
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
- - - - -
20eb3f41 by Cheng Shao at 2026-09-02T07:18:45-04:00
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
- - - - -
6af3089f by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA Fix HsCmdDo exact print with comments
Exact printing of HsCmdDo was ignoring the location for the do
statements, and this is an annotation that can have comments in it.
Update it so we print the statements as a unit, including any
comments.
Also add the result of auditing that we capture comments in all needed
places, noting that the remaining Anno SrcSpan instances are benign.
- - - - -
8b9d68b9 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Remove ListBanana / ListParens from AnnListBrackets
- - - - -
7c7b211b by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Tidy up mkHsDoPv, take AnnList directly
And some extra stuff by accident.
The main goal is to slim down AnnListBrackets to just have braces or
none.
This makes it clear that it serves only for lists which may have layout,
and opens the route to use EpLayout instead of AnnListBrackets
- - - - -
d37fc354 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Add specific layout field to AnnList
- - - - -
ee573d5a by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use EpaLocation in EpVirtualBraces
reword:
EPA Use vocurly as basis for AnnListLayout
For the existing cases that populate AnnList.
This also shows up AnnList usages that can never capture layout.
- - - - -
41c9e563 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use vocurly as basis for AnnListLayout
For the existing cases that populate AnnList.
This also shows up AnnList usages that can never capture layout.
- - - - -
fda7a82b by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: AnnList clean up patch for empty where clause
- - - - -
ce84e6b0 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Add TTG fields to FamilyInfo
This commit is a simple add, with unused extension fields.
It lays the groundwork for using them for exact print annotations
- - - - -
318a9fa6 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
WIP annclassdecl
- - - - -
0317310d by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Update AnnClsInstDecl to contain AnnList
It was already separately tracking the '{', '}' and leading ';' values.
This aligns it with layout-introducing parser productions ('vocurly'),
so the layout processing can be handled uniformly
- - - - -
43bcf7d4 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Tidy up markAnnListA'
It takes an action which can modify the AnnList, but this
is never used. So remove it.
- - - - -
6eaf8db6 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use AnnList for GADT declarations
- - - - -
50e7d2b9 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use AnnList in AnnClassDecl
- - - - -
135354b3 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
MOVE EPA: Plan for Fixing AnnList Layout Properly
- - - - -
1e82f47c by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make local binds located
Use LHsLocalBinds instead of HsLocalBinds
This reverses an earlier change, and brings processing of local binds
into alignement with the exact printing principle that the LocatedA
contents provide the location and extent of the enclosed item,
together with any comments or context-specific trailing items like
commas or semi colons.
The internal TTG extension points only carry exact print annotations
related to printing the item within its bounds.
This change brings back the problem of EmptyLocalBinds, which by
definition cannot have a location. These get a noSrcSpan location,
which is ignored in the exact printing process.
- - - - -
34ddb367 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use AnnList in HsMultiIf
It introduces layout, capture it
- - - - -
2414cc87 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Use AnnList in DecBrl
- - - - -
f8ad7558 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Replace AnnListLayout / AnnListBrackets with EpLayout
- - - - -
1ca1c941 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
WIP: setLayoutBoth inside markAnnListA
Current problem is it is a global one-shot, and MG has one too
Check: is it needed?
if so, honouring EpLayout may be needed
- - - - -
d38e771d by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA some tests for layout. WIP
- - - - -
8adabcd9 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
Exactprint layout scope plan. Do not commit
- - - - -
55706ebf by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: tidy up a bit. Combine somewhere else
- - - - -
cf9a4129 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Introduce LayoutFrame stacks in ExactPrint state
- - - - -
5933f435 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
Align with ghc-exactprint
- - - - -
594bc847 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Fix AnnList leading semis when layout
When there is layout, anything printed triggers it, including leading
semis.
Add some test case examples too
- - - - -
8c07e9d7 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
Exactprint: tidy up layout capturing
- - - - -
21acf99c by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: add makeDelta test for Test20297
The original #20297 related to comment placement for exact printing
after running ExactPrint.makeDelta on ParsedSource. Add an additional
test that explicitly tests this.
- - - - -
20e10b1f by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make ValBinds LocatedA in HsLocalBindsLR
If we have items with an AnnList Annotation, as part of another, which
has different content, it must be LocatedA. This is a technical requirement
due to the way comment allocation happens during the makeDelta processing.
Comments are queued for printing, and as soon as any non-comment is to
be printed, the pending comments before that position are printed, and
in makeDelta processing these attached as preceding comments to the thing
just printed.
The means they end up captured inside the layout region of the AnnList item.
So for
foo = x
where -- comment
x = 3
the comment ends up as a preceding comment of `x = 3`, inside the
layout, and prints as
foo = x
where
-- comment
x = 3
- - - - -
ef214a84 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make IPBinds LocatedA
- - - - -
9b5725ee by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make ClsInstDecl decls LocatedA
- - - - -
9628e2d2 by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make ClassDecls decls LocatedA
- - - - -
a82f5c5b by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make ClosedTypeFamily equations Located
For FamilyDecl/ClosedTypeFamily
- - - - -
0bd5da1f by Alan Zimmerman at 2026-09-02T18:17:56+01:00
EPA: Make dd_cons located
- - - - -
b0cb7630 by Alan Zimmerman at 2026-09-02T21:19:02+01:00
EPA: Make HsMultiIf options located
- - - - -
903dfa3e by Alan Zimmerman at 2026-09-02T22:24:23+01:00
EPA: Make DecBrl contents LocatedA
- - - - -
ade7d2cc by Alan Zimmerman at 2026-09-02T22:42:34+01:00
Extend Test20297 for additional occurrences
- - - - -
7ae6dc91 by Alan Zimmerman at 2026-09-03T18:43:43+01:00
Plan update DO NOT COMMIT
- - - - -
24aa13d8 by Alan Zimmerman at 2026-09-03T18:43:43+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
- - - - -
f76ea0c3 by Alan Zimmerman at 2026-09-03T18:43:43+01:00
EPA: HsList attempt WIP
- - - - -
f8a70d9d by Alan Zimmerman at 2026-09-03T18:43:43+01:00
Enable ppr test for Haddock1. It currently fails
- - - - -
c91bc0b4 by Alan Zimmerman at 2026-09-03T18:43:43+01:00
WIP on removing NoEpAnn. Likely abandon
- - - - -
bb94450b by Alan Zimmerman at 2026-09-03T18:43:43+01:00
EPA: Add an overview doc for exact printing
- - - - -
cb9f848c by Simon Peyton Jones at 2026-09-03T18:43:43+01:00
Added an intro section
- - - - -
198 changed files:
- + ANNLIST-LAYOUT-PLAN.md
- + ExactPrint.md
- + LAYOUT-SCOPE-PLAN.md
- + changelog.d/27627
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- + changelog.d/T27744
- + changelog.d/llvm-23
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Main/Interactive.hs
- compiler/GHC/Hs.hs
- compiler/GHC/Hs/Binds.hs
- compiler/GHC/Hs/Decls.hs
- compiler/GHC/Hs/Doc.hs
- compiler/GHC/Hs/DocString.hs
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Extension/Pass.hs
- compiler/GHC/Hs/ImpExp.hs
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Stats.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Docs.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Expr.hs-boot
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Rename/Bind.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Names.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Rename/Utils.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Tc/Deriv.hs
- compiler/GHC/Tc/Deriv/Functor.hs
- compiler/GHC/Tc/Deriv/Generate.hs
- compiler/GHC/Tc/Gen/Arrow.hs
- compiler/GHC/Tc/Gen/Bind.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl.hs
- compiler/GHC/Tc/TyCl/Instance.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Env.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- compiler/Language/Haskell/Syntax.hs
- compiler/Language/Haskell/Syntax/Basic.hs
- compiler/Language/Haskell/Syntax/Binds.hs
- compiler/Language/Haskell/Syntax/Decls.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- configure.ac
- ghc/GHCi/UI.hs
- libraries/base/tests/all.T
- libraries/stm
- rts/ProfHeap.c
- rts/eventlog/EventLog.c
- rts/prim/atomic.c
- testsuite/driver/README.md
- testsuite/driver/testlib.py
- + 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/ffi/should_run/T21305.hs
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/Test20239.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T17544_kw.stderr
- testsuite/tests/haddock/should_compile_flag_haddock/T24221.stderr
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.hs
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T15323.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20718b.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/parser/should_compile/T23315/T23315.stderr
- + testsuite/tests/patsyn/should_compile/T27744.hs
- testsuite/tests/patsyn/should_compile/all.T
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/compiler/hard_hole_fits.stderr
- testsuite/tests/perf/compiler/large-project/all.T
- testsuite/tests/perf/should_run/T3586.hs
- testsuite/tests/perf/should_run/T3586.stdout
- testsuite/tests/perf/space_leaks/all.T
- testsuite/tests/printer/AnnotationNoListTuplePuns.stdout
- + testsuite/tests/printer/Layout.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/T18791.stderr
- + testsuite/tests/printer/Test20297.expected.hs
- testsuite/tests/printer/Test20297.hs
- testsuite/tests/printer/Test20297.stdout
- + testsuite/tests/printer/Test20297MD.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + 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
- 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/ghc-toolchain/src/GHC/Toolchain/Program.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/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/4c2274426f4f7bc1c2d51b1bf99ad6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/4c2274426f4f7bc1c2d51b1bf99ad6…
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/multi-threaded-debugger] 3 commits: pretty great progressssssssss
by Rodrigo Mesquita (@alt-romes) 03 Sep '26
by Rodrigo Mesquita (@alt-romes) 03 Sep '26
03 Sep '26
Rodrigo Mesquita pushed to branch wip/romes/multi-threaded-debugger at Glasgow Haskell Compiler / GHC
Commits:
15213c93 by Rodrigo Mesquita at 2026-09-03T17:38:12+01:00
pretty great progressssssssss
- - - - -
4fb598c0 by Rodrigo Mesquita at 2026-09-03T18:18:04+01:00
ExecOptions isolateThreadBreaks
- - - - -
3311ac0a by Rodrigo Mesquita at 2026-09-03T18:40:42+01:00
ThreadBreaksIsolationMode
- - - - -
5 changed files:
- compiler/GHC/Driver/Config.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Eval/Types.hs
- libraries/ghci/GHCi/Message.hs
- libraries/ghci/GHCi/Run.hs
Changes:
=====================================
compiler/GHC/Driver/Config.hs
=====================================
@@ -49,6 +49,9 @@ initEvalOpts dflags step =
, stepOut = stepOut
, breakOnException = gopt Opt_BreakOnException dflags
, breakOnError = gopt Opt_BreakOnError dflags
+ , isolateThreadBreaks = True
+ -- Must opt-in to listening to all thread's breakpoints.
+ -- (e.g. for running 'main' in a debugger)
}
where
(singleStep, stepOut) = case step of
=====================================
compiler/GHC/Runtime/Eval.hs
=====================================
@@ -184,6 +184,8 @@ execOptions = ExecOptions
, execSourceFile = "<interactive>"
, execLineNumber = 1
, execWrap = EvalThis -- just run the statement, don't wrap it in anything
+ , execIsolateMode = SingleThreadedBreaks
+ -- by default we just care about breakpoints hit in this eval thread
}
-- | Run a statement in the current interactive context.
@@ -230,7 +232,8 @@ execStmt' stmt stmt_text ExecOptions{..} = do
status <-
liftIO $ do
- let eval_opts = initEvalOpts idflags' (enableGhcStepMode execSingleStep)
+ let eval_opts = (initEvalOpts idflags' (enableGhcStepMode execSingleStep))
+ { isolateThreadBreaks = enableIsolateThreadBreaks execIsolateMode }
evalStmt interp eval_opts (execWrap hval)
let ic = hsc_IC hsc_env
@@ -238,7 +241,7 @@ execStmt' stmt stmt_text ExecOptions{..} = do
size = ghciHistSize idflags'
- handleRunStatus execSingleStep stmt_text bindings ids
+ handleRunStatus execSingleStep execIsolateMode stmt_text bindings ids
status (emptyHistory size)
runDecls :: GhcMonad m => String -> m [Name]
@@ -290,14 +293,15 @@ emptyHistory size = nilBL size
-- breakpoint if we don't care about that breakpoint (e.g. if using :steplocal
-- or :stepmodule, rather than :step, we only care about certain breakpoints).
handleRunStatus :: GhcMonad m
- => SingleStep -> String
+ => SingleStep
+ -> ThreadBreaksIsolationMode
+ -> String
-> ResumeBindings
-> [Id]
-> EvalStatus_ [ForeignHValue] [HValueRef]
-> BoundedList History
-> m ExecResult
-
-handleRunStatus step expr bindings final_ids status history0 = do
+handleRunStatus step isolateMode expr bindings final_ids status history0 = do
hsc_env <- getSession
let
interp = hscInterp hsc_env
@@ -391,21 +395,25 @@ handleRunStatus step expr bindings final_ids status history0 = do
return (ExecBreak names (Just ibi))
else do
-- resume with the same step type
- let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
+ let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
+ { isolateThreadBreaks = enableIsolateThreadBreaks isolateMode }
status <- liftIO $ GHCi.resumeStmt interp eval_opts resume_ctxt_fhv
history <- if not tracing then pure history0 else do
history1 <- liftIO $ mkHistory hug apStack_fhv ibi
let !history' = history1 `consBL` history0
-- history is strict, otherwise our BoundedList is pointless.
return history'
- handleRunStatus step expr bindings final_ids status history
+ handleRunStatus step isolateMode expr bindings final_ids status history
where
tracing | RunAndLogSteps <- step = True
| otherwise = False
-resumeExec :: GhcMonad m => SingleStep -> Maybe Int
+resumeExec :: GhcMonad m
+ => SingleStep
+ -> ThreadBreaksIsolationMode
+ -> Maybe Int
-> m ExecResult
-resumeExec step mbCnt
+resumeExec step isolateMode mbCnt
= do
hsc_env <- getSession
let ic = hsc_IC hsc_env
@@ -450,7 +458,8 @@ resumeExec step mbCnt
(Just brkpt, Just cnt) -> setupBreakpoint interp brkpt cnt
_ -> return ()
- let eval_opts = initEvalOpts dflags (enableGhcStepMode step)
+ let eval_opts = (initEvalOpts dflags (enableGhcStepMode step))
+ { isolateThreadBreaks = enableIsolateThreadBreaks isolateMode }
status <- liftIO $ GHCi.resumeStmt interp eval_opts fhv
let prevHistoryLst = fromListBL 50 hist
hug = hsc_HUG hsc_env
@@ -461,7 +470,7 @@ resumeExec step mbCnt
hist1 <- liftIO (mkHistory hug apStack bi)
return $ hist1 `consBL` fromListBL 50 hist
| otherwise -> pure prevHistoryLst
- handleRunStatus step expr bindings final_ids status =<< hist'
+ handleRunStatus step isolateMode expr bindings final_ids status =<< hist'
setupBreakpoint :: GhcMonad m => Interp -> InternalBreakpointId -> Int -> m () -- #19157
setupBreakpoint interp ibi cnt = do
=====================================
compiler/GHC/Runtime/Eval/Types.hs
=====================================
@@ -10,6 +10,7 @@ module GHC.Runtime.Eval.Types (
Resume(..), ResumeBindings, IcGlobalRdrEnv(..),
History(..), ExecResult(..),
SingleStep(..), enableGhcStepMode, breakHere,
+ ThreadBreaksIsolationMode(..), enableIsolateThreadBreaks,
ExecOptions(..)
) where
@@ -36,6 +37,8 @@ data ExecOptions
, execSourceFile :: String -- ^ filename (for errors)
, execLineNumber :: Int -- ^ line number (for errors)
, execWrap :: ForeignHValue -> EvalExpr ForeignHValue
+ , execIsolateMode :: ThreadBreaksIsolationMode
+ -- ^ like @'isolateThreadBreaks'@
}
-- | What kind of stepping are we doing?
@@ -64,6 +67,17 @@ data SingleStep
| ModuleStep
{ breakAt :: SrcSpan }
+-- | Just like @'isolateThreadBreaks'@, determining whether the
+-- execution/resuming execution of an expression should stop whenever any
+-- non-isolated thread hits a breakpoint, or only when the thread executing
+-- that expression specifically stops.
+data ThreadBreaksIsolationMode
+ -- | Stop exclusively on the breakpoints in this thread
+ = SingleThreadedBreaks
+ -- | Stop whenever any thread that is not running in 'SingleThreadedBreaks'
+ -- isolation mode hits a breakpoint
+ | MultiThreadedBreaks
+
-- | Whether this 'SingleStep' mode requires instructing the interpreter to
-- step at every breakpoint or after every return (see @'EvalStep'@).
enableGhcStepMode :: SingleStep -> EvalStep
@@ -72,6 +86,10 @@ enableGhcStepMode StepOut{} = EvalStepOut
-- for the remaining step modes we need to stop at every single breakpoint.
enableGhcStepMode _ = EvalStepSingle
+enableIsolateThreadBreaks :: ThreadBreaksIsolationMode -> Bool
+enableIsolateThreadBreaks SingleThreadedBreaks = True
+enableIsolateThreadBreaks MultiThreadedBreaks = False
+
-- | Given a 'SingleStep' mode, whether the breakpoint was explicitly active,
-- and the SrcSpan of a breakpoint we hit, return @True@ if we should stop at
-- this breakpoint.
=====================================
libraries/ghci/GHCi/Message.hs
=====================================
@@ -388,12 +388,38 @@ putTHMessage m = case m of
GetPackageRoot -> putWord8 25
AddDependentDirectory a -> putWord8 26 >> put a
-data EvalOpts = EvalOpts
+data EvalOpts
+ = EvalOpts
{ useSandboxThread :: Bool
, singleStep :: Bool
, stepOut :: Bool
, breakOnException :: Bool
, breakOnError :: Bool
+ , isolateThreadBreaks :: Bool
+ -- ^ If @isolateThreadBreaks == True@, any breakpoint hit by the thread
+ -- forked~[1] to execute this expression is only seen by a caller observing
+ -- this thread's result explicitly. That is, this thread's 'EvalStatus' can
+ -- be read by 'readThreadEvalStatus', but will be ignored by
+ -- 'readAnyThreadEvalBreak'.
+ --
+ -- Secondly, we only return the 'EvalStatus' of this thread specifically --
+ -- either a breakpoint was hit *in this thread*, or it has finished
+ -- evaluating the main expression and produced a result.
+ -- Notably, if any other thread in the interpreter hits a breakpoint, ignore it.
+ --
+ -- If @isolateThreadBreaks == False@, any breakpoint hit in *any* thread
+ -- (except isolated ones) will make this evaluation return the 'EvalBreak'
+ -- for that thread that stopped. This is typically what you want if debugging
+ -- a "main" program, since you care to know if any thread forked off of that
+ -- main program hits a breakpoint, not just the main thread specifically.
+ -- However, we will only return an 'EvalCompleted' for the main thread's
+ -- result. The results of other threads spawned by main are ignored.
+ --
+ -- In essence, when @False@, the result of evaluation is
+ -- @readAnyThreadEvalBreak `orElse` readThreadEvalStatus mainThreadId@.
+ --
+ -- [1] a thread is forked to execute the expression by default, unless
+ -- @-fno-ghci-sandbox@ which sets @useSandboxThread = False@
}
deriving (Generic, Show)
=====================================
libraries/ghci/GHCi/Run.hs
=====================================
@@ -157,6 +157,57 @@ notSupportedJS :: Message a -> b
notSupportedJS m = error ("Message not supported with the JavaScript interpreter: " ++ show m)
#endif
+-- Malloc and copy the bytes. We don't have any way to monitor the
+-- lifetime of this memory, so it just leaks.
+mkString :: ByteString -> IO (RemotePtr ())
+mkString bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do
+ ptr <- mallocBytes len
+ copyBytes ptr cstr len
+ return (castRemotePtr (toRemotePtr ptr))
+
+mkString0 :: ByteString -> IO (RemotePtr ())
+mkString0 bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do
+ ptr <- mallocBytes (len+1)
+ copyBytes ptr cstr len
+ pokeElemOff (ptr :: Ptr CChar) len 0
+ return (castRemotePtr (toRemotePtr ptr))
+
+mkCostCentres :: RemotePtr () -> [(BS.ShortByteString, BS.ShortByteString)] -> IO [RemotePtr CostCentre]
+#if defined(PROFILING)
+mkCostCentres mod ccs = do
+ let c_module = fromRemotePtr $ castRemotePtr mod
+ mapM (mk_one c_module) ccs
+ where
+ mk_one c_module (decl_path,srcspan) = do
+ c_name <- newCStringFromSBS decl_path
+ c_srcspan <- newCStringFromSBS srcspan
+ toRemotePtr <$> c_mkCostCentre c_name c_module c_srcspan
+
+foreign import ccall unsafe "mkCostCentre"
+ c_mkCostCentre :: Ptr CChar -> Ptr CChar -> Ptr CChar -> IO (Ptr CostCentre)
+#else
+mkCostCentres _ _ = return []
+#endif
+
+getIdValFromApStack :: HValue -> Int -> IO (Maybe HValue)
+getIdValFromApStack apStack (I# stackDepth) = do
+ case getApStackVal# apStack stackDepth of
+ (# ok, result #) ->
+ case ok of
+ 0# -> return Nothing -- AP_STACK not found
+ _ -> return (Just (unsafeCoerce# result))
+
+-- Exceptions can't be marshaled because they're dynamically typed, so
+-- everything becomes a String.
+tryEval :: IO a -> IO (EvalResult a)
+tryEval io = do
+ e <- try io
+ case e of
+ Left ex -> return (EvalException (toSerializableException ex))
+ Right a -> return (EvalSuccess a)
+
+--------------------------------------------------------------------------------
+
evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (EvalStatus [HValueRef])
evalStmt opts expr = do
io <- mkIO expr
@@ -189,6 +240,9 @@ evalStringToString r str = do
r <- (unsafeCoerce io :: String -> IO String) str
evaluate (force r)
+
+--------------------------------------------------------------------------------
+
-- | Process the Seq message to force a value. #2950
-- If during this processing a breakpoint is hit, return
-- an EvalBreak value in the EvalStatus to the UI process,
@@ -205,16 +259,7 @@ doSeq ref = clearEvalStatus <$> do
-- | Process a ResumeSeq message. Continue the :force processing #2950
-- after a breakpoint.
resumeSeq :: RemoteRef ThreadId -> IO (EvalStatus ())
-resumeSeq hvref = clearEvalStatus <$> do
- resumeThreadId <- localRef hvref
- withBreakAction evalOptsSeq (Just resumeThreadId) $ do
- ResumeContext{..} <- getThreadResumeContext resumeThreadId
- mask_ $ do
- putMVar resumeBreakMVar () -- this awakens the stopped thread...
- redirectInterrupts resumeThreadId $
- withShadowThread resumeThreadId $
- atomically $
- readThreadEvalStatus resumeThreadId
+resumeSeq hvref = clearEvalStatus <$> resumeStmt evalOptsSeq hvref
evalOptsSeq :: EvalOpts
evalOptsSeq = EvalOpts
@@ -223,8 +268,46 @@ evalOptsSeq = EvalOpts
, stepOut = False
, breakOnException = False
, breakOnError = False
+ , isolateThreadBreaks = True
}
+--------------------------------------------------------------------------------
+
+-- | When abandoning a computation we have to
+-- (a) kill the thread with an async exception, so that the
+-- computation itself is stopped, and
+-- (b) fill in the MVar. This step is necessary because any
+-- thunks that were under evaluation will now be updated
+-- with the partial computation, which still ends in takeMVar,
+-- so any attempt to evaluate one of these thunks will block
+-- unless we fill in the MVar.
+-- (c) wait for the thread to terminate by taking its status MVar. This
+-- step is necessary to prevent race conditions with
+-- -fbreak-on-exception (see #5975).
+-- See test break010.
+abandonStmt :: RemoteRef ThreadId -> IO ()
+abandonStmt hvref = do
+ tid <- localRef hvref
+ ResumeContext{..} <- getThreadResumeContext tid
+ killThread tid
+ withShadowThread tid $ do
+ putMVar resumeBreakMVar ()
+ _ <- atomically $
+ readThreadEvalStatus tid
+ return ()
+
+--------------------------------------------------------------------------------
+
+resumeStmt :: EvalOpts -> RemoteRef ThreadId -> IO (EvalStatus [HValueRef])
+resumeStmt opts rtid = do
+ resumeThreadId <- localRef rtid
+ ResumeContext{..} <- getThreadResumeContext resumeThreadId
+ withBreakAction opts resumeThreadId $ \waitForResult ->
+ mask_ $ do
+ putMVar resumeBreakMVar () -- this awakens the stopped thread...
+ redirectInterrupts resumeThreadId $
+ waitForResult
+
-- When running a computation, we redirect ^C exceptions to the running
-- thread. ToDo: we might want a way to continue even if the target
-- thread doesn't die when it receives the exception... "this thread
@@ -236,57 +319,138 @@ evalOptsSeq = EvalOpts
-- putMVar, otherwise deadlock ensues. (#1583, #1922, #1946)
sandboxIO :: EvalOpts -> IO [HValueRef] -> IO (EvalStatus [HValueRef])
-sandboxIO opts io = do
- -- We are running in uninterruptibleMask
- withBreakAction opts Nothing $ do
- let runIt = measureAlloc $ tryEval $ rethrow opts $ clearCCS io
- if useSandboxThread opts
- then do
- tid <- forkIO $ do
- tid <- myThreadId
- labelThread tid "GHCi sandbox"
- unsafeUnmask runIt >>= writeThreadEvalStatus tid
-
- redirectInterrupts tid $ unsafeUnmask $ atomically $
- (EvalPaused <$> readAnyThreadEvalBreak)
- `orElse` readThreadEvalStatus tid
- else
- -- GLUT on OS X needs to run on the main thread. If you
- -- try to use it from another thread then you just get a
- -- white rectangle rendered. For this, or anything else
- -- with such restrictions, you can turn the GHCi sandbox off
- -- and things will be run in the main thread.
- --
- -- BUT, note that the debugging features (breakpoints,
- -- tracing, etc.) need the expression to be running in a
- -- separate thread, so debugging is only enabled when
- -- using the sandbox.
- runIt
-
--- We want to turn ^C into a break when -fbreak-on-exception is on,
--- but it's an async exception and we only break for sync exceptions.
--- Idea: if we catch and re-throw it, then the re-throw will trigger
--- a break. Great - but we don't want to re-throw all exceptions, because
--- then we'll get a double break for ordinary sync exceptions (you'd have
--- to :continue twice, which looks strange). So if the exception is
--- not "Interrupted", we unset the exception flag before throwing.
+sandboxIO opts io
+ | useSandboxThread opts
+ = do
+ wait <- newEmptyMVar
+
+ tid <- forkIO $ do
+
+ takeMVar wait -- don't run anything in this thread before
+ -- `withBreakAction` has a chance to set up.
+
+ tid <- myThreadId
+ labelThread tid "GHCi sandbox"
+ unsafeUnmask runIt >>= writeThreadEvalStatus tid
+
+ -- We are running in uninterruptibleMask
+ withBreakAction opts tid $ \waitForResult -> do
+ putMVar wait ()
+ redirectInterrupts tid $ unsafeUnmask $
+ waitForResult
+
+ | otherwise
+ = do
+ -- GLUT on OS X needs to run on the main thread. If you
+ -- try to use it from another thread then you just get a
+ -- white rectangle rendered. For this, or anything else
+ -- with such restrictions, you can turn the GHCi sandbox off
+ -- and things will be run in the main thread.
+ --
+ -- BUT, note that the debugging features (breakpoints,
+ -- tracing, etc.) need the expression to be running in a
+ -- separate thread, so debugging is only enabled when
+ -- using the sandbox.
+ runIt
+ where
+ runIt = measureAlloc $ tryEval $ rethrow opts $ clearCCS io
+
+ -- We want to turn ^C into a break when -fbreak-on-exception is on,
+ -- but it's an async exception and we only break for sync exceptions.
+ -- Idea: if we catch and re-throw it, then the re-throw will trigger
+ -- a break. Great - but we don't want to re-throw all exceptions, because
+ -- then we'll get a double break for ordinary sync exceptions (you'd have
+ -- to :continue twice, which looks strange). So if the exception is
+ -- not "Interrupted", we unset the exception flag before throwing.
+ --
+ rethrow :: EvalOpts -> IO a -> IO a
+ rethrow EvalOpts{..} io =
+ catchNoPropagate io $ \(ExceptionWithContext cx se) -> do
+ -- If -fbreak-on-error, we break unconditionally,
+ -- but with care of not breaking twice
+ if breakOnError && not breakOnException
+ then poke exceptionFlag 1
+ else case fromException se of
+ -- If it is a "UserInterrupt" exception, we allow
+ -- a possible break by way of -fbreak-on-exception
+ Just UserInterrupt -> return ()
+ -- In any other case, we don't want to break
+ _ -> poke exceptionFlag 0
+ rethrowIO (ExceptionWithContext cx se)
+
+ measureAlloc :: IO (EvalResult a) -> IO (EvalStatus a)
+ measureAlloc io = do
+ setAllocationCounter 0 -- #16012
+ a <- io
+ ctr <- getAllocationCounter
+ let allocs = negate $ fromIntegral ctr
+ return (EvalComplete allocs a)
+
+-- | This function sets up the given thread for catching breakpoints, and
+-- resets everything when the computation has stopped running. It unfortunately
+-- still does some global things (namely, may set the global
+-- break-on-exceptions rts flag), but otherwise acts only on the given thread.
--
-rethrow :: EvalOpts -> IO a -> IO a
-rethrow EvalOpts{..} io =
- catchNoPropagate io $ \(ExceptionWithContext cx se) -> do
- -- If -fbreak-on-error, we break unconditionally,
- -- but with care of not breaking twice
- if breakOnError && not breakOnException
- then poke exceptionFlag 1
- else case fromException se of
- -- If it is a "UserInterrupt" exception, we allow
- -- a possible break by way of -fbreak-on-exception
- Just UserInterrupt -> return ()
- -- In any other case, we don't want to break
- _ -> poke exceptionFlag 0
- rethrowIO (ExceptionWithContext cx se)
-
+-- The continuation `IO` action receives an `IO` argument. That IO argument
+-- will block waiting for a result from the interpreter; when 'EvalOpts' sets
+-- @isolateThreadBreaks == True@, this result will come from the given thread
+-- exclusively (either Break or Complete), otherwise, the result might be a
+-- Break from any non-isolated thread or Complete from this thread specifically.
+--
+-- Note that 'EvalComplete' results must be written by the thread explicitly to the
+-- global thread eval status mapping using 'writeThreadEvalStatus'. 'EvalBreak'
+-- results are automatically written by a thread which hits a breakpoint.
+-- See e.g. 'sandboxIO' for an example of writing 'EvalComplete'.
--
+-- Typically, @isolatedThreadBreaks == False@ is used to make sure we stop
+-- whenever "main" or one of the threads spawned by "main" hit a breakpoint,
+-- rather than just "main" being observed. This should be used for the main
+-- debuggee execution of a debugger.
+withBreakAction :: EvalOpts -> ThreadId -> (IO (EvalStatus [HValueRef]) -> IO a) -> IO a
+withBreakAction opts tid@(ThreadId tid#) act
+ = bracket setBreakAction resetBreakAction (\_ -> act waitForResult)
+ where
+ setBreakAction = do
+ poke breakPointIOAction globalBreakStablePtr
+ -- TODO: This is poke thread unsafe, as one thread might be accessing this
+ -- global variable while this thread tries to overwrite it. We should
+ -- rather do it when the interpreter process/internal is initialized
+ -- somehow.
+
+ runIf breakOnException $ \_ -> poke exceptionFlag 1
+ -- Breaking on exceptions is not enabled by default, since it
+ -- might be a bit surprising. The exception flag is turned off
+ -- as soon as it is hit, or in resetBreakAction below.
+
+ runIf# singleStep rts_enableStopNextBreakpoint
+ runIf# stepOut rts_enableStopAfterReturn
+ runIf isolateThreadBreaks setShadowThread
+
+ resetBreakAction () = do
+ poke exceptionFlag 0
+ rts_disableStopAfterReturn tid#
+ rts_disableStopNextBreakpoint tid#
+ runIf isolateThreadBreaks unsetShadowThread
+
+ -- freeStablePtr stablePtr TODO: we never free the global stablePtr for
+ -- global action. Maybe we should have a de-init for clean up.
+ -- TODO: don't set it in `setBreakAction` either. Should be on
+ -- interpreter initialization.
+
+ runIf pred what = when (pred opts) (what tid)
+ runIf# pred what = when (pred opts) (what tid#)
+
+ waitForResult
+ | isolateThreadBreaks opts
+ = atomically $
+ readThreadEvalStatus tid
+
+ | otherwise
+ = atomically $
+ (EvalPaused <$> readAnyThreadEvalBreak)
+ `orElse`
+ readThreadEvalStatus tid
+
-- While we're waiting for the sandbox thread to return a result, if
-- the current thread receives an asynchronous exception we re-throw
-- it at the sandbox thread and continue to wait.
@@ -314,140 +478,6 @@ redirectInterrupts target wait = do
Nothing -> wait
Just target -> do throwTo target (e :: SomeException); wait
-measureAlloc :: IO (EvalResult a) -> IO (EvalStatus a)
-measureAlloc io = do
- setAllocationCounter 0 -- #16012
- a <- io
- ctr <- getAllocationCounter
- let allocs = negate $ fromIntegral ctr
- return (EvalComplete allocs a)
-
--- Exceptions can't be marshaled because they're dynamically typed, so
--- everything becomes a String.
-tryEval :: IO a -> IO (EvalResult a)
-tryEval io = do
- e <- try io
- case e of
- Left ex -> return (EvalException (toSerializableException ex))
- Right a -> return (EvalSuccess a)
-
--- This function sets up the interpreter for catching breakpoints, and
--- resets everything when the computation has stopped running. This
--- is a not-very-good way to ensure that only the interactive
--- evaluation should generate breakpoints.
-withBreakAction :: EvalOpts
- -> Maybe ThreadId -- ^ If resuming, the current threadId
- -> IO a -> IO a
-withBreakAction opts mtid act
- = bracket setBreakAction resetBreakAction (\_ -> act)
- where
- setBreakAction = do
- poke breakPointIOAction globalBreakStablePtr -- TODO: This is thread unsafe, as one thread might be accessing this global variable while this thread tries to overwrite it. We should rather do it when the interpreter process/internal is initialized somehow.
- when (breakOnException opts) $ poke exceptionFlag 1
- when (singleStep opts) $ do
- rts_enableStopNextBreakpointAll
- case mtid of
- Nothing -> pure ()
- Just (ThreadId tid) -> do
- rts_enableStopNextBreakpoint tid
- when (stepOut opts) $ do
- case mtid of
- Nothing -> pure ()
- Just (ThreadId tid) -> do
- rts_enableStopAfterReturn tid
- return ()
- -- Breaking on exceptions is not enabled by default, since it
- -- might be a bit surprising. The exception flag is turned off
- -- as soon as it is hit, or in resetBreakAction below.
-
- resetBreakAction {-stablePtr-}() = do
- -- poke breakPointIOAction noBreakStablePtr -- Never unset it.
- -- TODO: What about evaluating a thunk with breakpoints? when did we get "ignoring breakpoint..." before?
- poke exceptionFlag 0
- case mtid of
- Just (ThreadId tid) -> rts_disableStopAfterReturn tid
- _ -> pure ()
- case mtid of
- Nothing -> rts_disableStopNextBreakpointAll
- Just (ThreadId tid) -> do
- rts_disableStopNextBreakpoint tid
- -- freeStablePtr stablePtr
-
-resumeStmt
- :: EvalOpts -> RemoteRef ThreadId
- -> IO (EvalStatus [HValueRef])
-resumeStmt opts rtid = do
- resumeThreadId <- localRef rtid
- ResumeContext{..} <- getThreadResumeContext resumeThreadId
- withBreakAction opts (Just resumeThreadId) $
- mask_ $ do
- putMVar resumeBreakMVar () -- this awakens the stopped thread...
- redirectInterrupts resumeThreadId $
- withShadowThread resumeThreadId $
- atomically $ readThreadEvalStatus resumeThreadId
-
--- when abandoning a computation we have to
--- (a) kill the thread with an async exception, so that the
--- computation itself is stopped, and
--- (b) fill in the MVar. This step is necessary because any
--- thunks that were under evaluation will now be updated
--- with the partial computation, which still ends in takeMVar,
--- so any attempt to evaluate one of these thunks will block
--- unless we fill in the MVar.
--- (c) wait for the thread to terminate by taking its status MVar. This
--- step is necessary to prevent race conditions with
--- -fbreak-on-exception (see #5975).
--- See test break010.
-abandonStmt :: RemoteRef ThreadId -> IO ()
-abandonStmt hvref = do
- tid <- localRef hvref
- ResumeContext{..} <- getThreadResumeContext tid
- killThread tid
- putMVar resumeBreakMVar ()
- _ <- withShadowThread tid $ atomically $
- readThreadEvalStatus tid
- return ()
-
--- Malloc and copy the bytes. We don't have any way to monitor the
--- lifetime of this memory, so it just leaks.
-mkString :: ByteString -> IO (RemotePtr ())
-mkString bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do
- ptr <- mallocBytes len
- copyBytes ptr cstr len
- return (castRemotePtr (toRemotePtr ptr))
-
-mkString0 :: ByteString -> IO (RemotePtr ())
-mkString0 bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do
- ptr <- mallocBytes (len+1)
- copyBytes ptr cstr len
- pokeElemOff (ptr :: Ptr CChar) len 0
- return (castRemotePtr (toRemotePtr ptr))
-
-mkCostCentres :: RemotePtr () -> [(BS.ShortByteString, BS.ShortByteString)] -> IO [RemotePtr CostCentre]
-#if defined(PROFILING)
-mkCostCentres mod ccs = do
- let c_module = fromRemotePtr $ castRemotePtr mod
- mapM (mk_one c_module) ccs
- where
- mk_one c_module (decl_path,srcspan) = do
- c_name <- newCStringFromSBS decl_path
- c_srcspan <- newCStringFromSBS srcspan
- toRemotePtr <$> c_mkCostCentre c_name c_module c_srcspan
-
-foreign import ccall unsafe "mkCostCentre"
- c_mkCostCentre :: Ptr CChar -> Ptr CChar -> Ptr CChar -> IO (Ptr CostCentre)
-#else
-mkCostCentres _ _ = return []
-#endif
-
-getIdValFromApStack :: HValue -> Int -> IO (Maybe HValue)
-getIdValFromApStack apStack (I# stackDepth) = do
- case getApStackVal# apStack stackDepth of
- (# ok, result #) ->
- case ok of
- 0# -> return Nothing -- AP_STACK not found
- _ -> return (Just (unsafeCoerce# result))
-
clearEvalStatus :: EvalStatus a -> EvalStatus ()
clearEvalStatus = \case
EvalComplete w (EvalException se) -> EvalComplete w (EvalException se)
@@ -460,6 +490,15 @@ clearEvalStatus = \case
-- TODO: Move to independent module, and don't expose the global variables at all. move also the global break action maybe.
-- Maybe there could even be something in that module for setting the global
-- break action on start. maybe just an init function
+--
+-- TODO: Note which explains when things are evacuated from the map, why
+-- EvalSuccess's don't leak, what is the idea, why have MVar and TVar, why have
+-- shadowed threads. Must also explain whatever we come up with for Finalizers
+-- to make sure threads evacuate themselves out of the map. Explain in the note
+-- as well the status of GHCi multi-threaded, explaining it currently uses
+-- single-threaded
+--
+-- TODO: Write a blog post about multi-threaded debugging (mby with the contents of the note..)
globalBreakStablePtr :: StablePtr BreakpointCallback
globalBreakStablePtr = unsafePerformIO $ newStablePtr globalBreakAction
@@ -542,7 +581,7 @@ readThreadEvalStatus tid = do
readAnyThreadEvalBreak :: STM EvalBreak
readAnyThreadEvalBreak = do
dbgs <- readTVar globalDbgStatusVar
- -- try reading all mvars and retry if none yield a result
+ -- try reading any thread's 'EvalBreak' and retry if none have yielded one yet
foldr (\x next -> readStatus dbgs x `orElse` next) retry (Map.toList (threadEvalStatuses dbgs))
where
readStatus GlobalDbgStatus{..} (tid, _) -- don't look at dbg-shadowed threads
@@ -572,6 +611,10 @@ writeThreadEvalStatus tid new =
tid new threadEvalStatuses
, shadowedThreads }
+-- | Brackets 'setShadowThread'
+withShadowThread :: ThreadId -> IO a -> IO a
+withShadowThread tid = bracket_ (setShadowThread tid) (unsetShadowThread tid)
+
-- | Make sure that anyone watching for threads hitting breakpoints with
-- 'readAnyThreadEvalBreak' ignore the given thread if it ever hits a breakpoint.
--
@@ -579,23 +622,24 @@ writeThreadEvalStatus tid new =
-- 'readThreadEvalStatus' for that ThreadId, which is typically desired when an
-- expression to run should not be seen at all by the debugger (e.g. when
-- evaluating an expr to :force a variable)
-withShadowThread :: ThreadId -> IO a -> IO a
-withShadowThread tid = bracket_ setShadowThread unsetShadowThread
- where
- setShadowThread = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , shadowedThreads = Set.insert tid shadowedThreads
- }
- unsetShadowThread = atomically $ do
- GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
- writeTVar globalDbgStatusVar
- GlobalDbgStatus
- { threadEvalStatuses
- , shadowedThreads = Set.delete tid shadowedThreads
- }
+setShadowThread :: ThreadId -> IO ()
+setShadowThread tid = atomically $ do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses
+ , shadowedThreads = Set.insert tid shadowedThreads
+ }
+
+-- | Undoes 'setShadowThread'
+unsetShadowThread :: ThreadId -> IO ()
+unsetShadowThread tid = atomically $ do
+ GlobalDbgStatus{..} <- readTVar globalDbgStatusVar
+ writeTVar globalDbgStatusVar
+ GlobalDbgStatus
+ { threadEvalStatuses
+ , shadowedThreads = Set.delete tid shadowedThreads
+ }
-- | When we launch a new thread using the debugger, we map ... TODO ... all
-- threads launched by that main stmt may further spawn threads, and any of
@@ -615,8 +659,9 @@ globalDbgStatusVar = unsafePerformIO $ newTVarIO $ GlobalDbgStatus Map.empty Set
data GlobalDbgStatus = GlobalDbgStatus
{ threadEvalStatuses :: !(Map.Map ThreadId (EvalStatus [HValueRef]))
-- ^ Whenever a thread hits a breakpoint it inserts in this map its 'EvalBreak'.
- -- We can additionally observe the result of a thread by calling
- -- `writeThreadEvalStatus` at the end with its result, e.g. in 'sandboxIO'
+ -- We can additionally observe a thread's result by calling
+ -- `writeThreadEvalStatus tid (EvalComplete ...)` when the main expression
+ -- finishes evaluating, see e.g. 'sandboxIO'.
, shadowedThreads :: !(Set.Set ThreadId)
-- ^ Threads in this map are meant to be ignored by 'readAnyThreadEvalBreak'.
-- That is, even if one of the threads in this set hits a breakpoint, we
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9d6ecb8bf57353854a5444c8ea0d13…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9d6ecb8bf57353854a5444c8ea0d13…
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/improve-closure-property-check] Re-implement the home unit closure check
by Wolfgang Jeltsch (@jeltsch) 03 Sep '26
by Wolfgang Jeltsch (@jeltsch) 03 Sep '26
03 Sep '26
Wolfgang Jeltsch pushed to branch wip/jeltsch/improve-closure-property-check at Glasgow Haskell Compiler / GHC
Commits:
f20a6ade by Wolfgang Jeltsch at 2026-09-03T20:24:09+03:00
Re-implement the home unit closure check
Compared to the previous implementation, the new one has the following
advantages:
* It is correct.
- It distinguishes between units that have the same unit ID but
different ABI hashes.
- When `-hide-all-packages` is not used, it considers as home unit
dependencies also units that are made implicitly available
because they are in the package database.
* It seems to be faster in usual settings.
- In particular, it does not have a preparation phase in which it
merges the dependency information from all home units, so that
its running time does not grow linearly with the size of the
package database in normal Cabal scenarios.
* It is (hopefully) clearer.
* It is better documented.
- - - - -
7 changed files:
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/Unit/External/Index.hs
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/Makefile
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
Changes:
=====================================
compiler/GHC/Driver/Downsweep.hs
=====================================
@@ -6,6 +6,8 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE ViewPatterns #-}
+{-# OPTIONS_GHC -Wno-invalid-haddock #-}
+
-- | See Note [The ModuleGraph]
module GHC.Driver.Downsweep
( downsweep
@@ -54,7 +56,6 @@ import GHC.Runtime.Context
import Language.Haskell.Syntax.ImpExp
import GHC.Types.UnresolvedImport
-import GHC.Data.Graph.Directed
import GHC.Data.FastString
import GHC.Data.Maybe ( expectJust )
import qualified GHC.Data.Maybe as M
@@ -71,12 +72,14 @@ import GHC.Utils.Logger
import GHC.Utils.Fingerprint
import GHC.Utils.TmpFs
import GHC.Utils.Constants
+import GHC.Utils.Monad.State.Strict
import GHC.Types.Error
import GHC.Types.Target
import GHC.Types.SourceFile
import GHC.Types.SourceError
import GHC.Types.SrcLoc
+import GHC.Types.Unique.Set
import GHC.Types.Unique.Map
import GHC.Types.PkgQual
import GHC.Types.Basic
@@ -91,9 +94,12 @@ import GHC.Unit.Module.Graph
import GHC.Unit.Module.Deps
import qualified GHC.Unit.Home.Graph as HUG
import GHC.Unit.Module.Stage
+import GHC.Unit.External.Index (GlobalUnitKey, mkGlobalUnitKey)
import Data.Either ( partitionEithers, lefts )
+import Data.Map (Map)
import qualified Data.Map as Map
+import Data.Set (Set)
import qualified Data.Set as Set
import Control.Concurrent.MVar
@@ -101,7 +107,7 @@ import Control.Monad
import Control.Monad.Trans.Except ( ExceptT(..), runExceptT, throwE )
import qualified Control.Monad.Catch as MC
import Data.Maybe
-import Data.List (partition)
+import Data.List (sort, partition)
import Data.Time
import Data.List (unfoldr)
import Data.Bifunctor (first, bimap)
@@ -933,52 +939,172 @@ rootSummariesParallel n_jobs hsc_env diag_wrapper msg get_summary = do
-- * Check/validate properties and error out
--------------------------------------------------------------------------------
--- | This function checks then important property that if both p and q are home units
--- then any dependency of p, which transitively depends on q is also a home unit.
+-- | Checks whether the given 'UnitEnv' has the closure property.
--
--- See Note [Multiple Home Units], section 'Closure Property'.
-checkHomeUnitsClosed :: UnitEnv -> [DriverMessages]
-checkHomeUnitsClosed ue
- | Set.null bad_unit_ids = []
- | otherwise = [singleMessage $ mkPlainErrorMsgEnvelope rootLoc $ DriverHomePackagesNotClosed (Set.toList bad_unit_ids)]
+-- See the section “Closure Property” in @Note [Multiple Home Units]@ for the
+-- definition of the closure property an @Note [Home unit closure property
+-- check]@ below for a discussion of the algorithm used for this check, its
+-- justification, and a potential alternative.
+checkHomeUnitsClosed :: UnitEnv -> [DriverMessages]
+checkHomeUnitsClosed unit_env
+ | null offenders = []
+ | otherwise = [
+ singleMessage $
+ mkPlainErrorMsgEnvelope error_source_span $
+ DriverHomePackagesNotClosed (sort offenders)
+ ]
where
- home_id_set = HUG.allUnits $ ue_home_unit_graph ue
- bad_unit_ids = upwards_closure Set.\\ home_id_set {- Remove all home units reached, keep only bad nodes -}
- rootLoc = mkGeneralSrcSpan (fsLit "<command line>")
- downwards_closure :: Graph (Node UnitId UnitId)
- downwards_closure = graphFromEdgedVerticesUniq graphNodes
+ -- | The 'UnitId' and 'HomeUnitEnv' of each home unit.
+ home_unit_data :: [(UnitId, HomeUnitEnv)]
+ home_unit_data = HUG.unitEnv_assocs (ue_home_unit_graph unit_env)
+
+ -- | The 'UnitId's of all home units.
+ home_units :: UniqSet UnitId
+ home_units = mkUniqSet (map fst home_unit_data)
+
+ -- | All offending dependencies. A dependency of a unit /u/ on a unit /v/ is
+ -- offending exactly if /u/ is an external unit reachable from a home unit
+ -- and /v/ is a home unit. Each such dependency is represented in this list
+ -- by the pair of the 'UnitId' of /u/ and the 'UnitId' of /v/.
+ offenders :: [(UnitId, UnitId)]
+ offenders
+ = evalState (collect (map (homeUnitEnv_units . snd) home_unit_data)) $
+ Set.empty
+ where
- inverse_closure = graphReachability $ transposeG downwards_closure
+ -- | Collects offending dependencies.
+ collect :: [UnitState]
+ -- ^ The 'UnitState's of the home units from which to traverse
+ -- the dependency graph.
+ -> State (Set GlobalUnitKey) [(UnitId, UnitId)]
+ -- ^ A stateful computation that collects offending dependencies
+ -- that have not yet been found, using its state to keep track
+ -- of which units have already been considered as sources of
+ -- offending dependencies.
+ collect []
+ = pure []
+ collect (current_unit_state : remaining_unit_states)
+ = (++) <$> collect_for_home_unit
+ (unitInfoMap current_unit_state)
+ (map (toUnitId . fst) $ explicitUnits $ current_unit_state)
+ <*> collect remaining_unit_states
+ where
- upwards_closure = Set.fromList $ map node_key $ allReachableMany inverse_closure [DigraphNode uid uid [] | uid <- Set.toList home_id_set]
+ -- | Collects offending dependencies that are reachable from a particular
+ -- home unit.
+ collect_for_home_unit
+ :: UnitInfoMap
+ -- ^ The 'UnitInfoMap' of the home unit.
+ -> [UnitId]
+ -- ^ The 'UnitId's of the units from which to traverse the dependency
+ -- graph.
+ -> State (Set GlobalUnitKey) [(UnitId, UnitId)]
+ -- ^ A stateful computation that collects offending dependencies that
+ -- have not yet been found, using its state to keep track of which
+ -- units have already been considered as sources of offending
+ -- dependencies.
+ collect_for_home_unit _ []
+ = return []
+ collect_for_home_unit unit_info_map (current_unit : remaining_units) = do
+ let
+
+ -- | The 'UnitInfo' of the current unit.
+ unit_info :: UnitInfo
+ unit_info
+ = fromMaybe (pprPanic unit_not_found_msg (ppr current_unit)) $
+ lookupUniqMap unit_info_map current_unit
+ where
+
+ -- | The message that says that a unit was not found.
+ unit_not_found_msg :: String
+ unit_not_found_msg = "Unit not found during closure property check"
+
+ -- | A 'GlobalUnitKey' that identifies the current unit.
+ global_unit_key :: GlobalUnitKey
+ global_unit_key = mkGlobalUnitKey current_unit (unitAbiHash unit_info)
+
+ has_been_processed <- gets (Set.member global_unit_key)
+ if has_been_processed
+ then collect_for_home_unit unit_info_map remaining_units
+ else do
+ modify (Set.insert global_unit_key)
+ let
+
+ -- | The 'UnitId's of the units that the current unit depends on.
+ needed_units :: [UnitId]
+ needed_units = unitDepends unit_info
+
+ -- | The offending dependencies of the current unit.
+ current_offenders :: [(UnitId, UnitId)]
+ current_offenders
+ | current_unit `elementOfUniqSet` home_units
+ = []
+ | otherwise
+ = map ((,) current_unit) $
+ nonDetEltsUniqSet $
+ mkUniqSet needed_units `intersectUniqSets` home_units
+
+ remaining_offenders <- collect_for_home_unit unit_info_map $
+ needed_units ++ remaining_units
+ return $ current_offenders ++ remaining_offenders
+
+ -- | A fake source span used for reporting violations of the closure property.
+ error_source_span :: SrcSpan
+ error_source_span = mkGeneralSrcSpan (fsLit "<command line>")
- all_unit_direct_deps :: UniqMap UnitId (Set.Set UnitId)
- all_unit_direct_deps
- = HUG.unitEnv_foldWithKey go emptyUniqMap $ ue_home_unit_graph ue
- where
- go rest this this_uis =
- plusUniqMap_C Set.union
- (addToUniqMap_C Set.union external_depends this (Set.fromList $ this_deps))
- rest
- where
- external_depends = mapUniqMap (Set.fromList . unitDepends) (unitInfoMap this_units)
- this_units = homeUnitEnv_units this_uis
- this_deps = [ toUnitId unit | (unit,Just _) <- explicitUnits this_units]
-
- graphNodes :: [Node UnitId UnitId]
- graphNodes = go Set.empty home_id_set
- where
- go done todo
- = case Set.minView todo of
- Nothing -> []
- Just (uid, todo')
- | Set.member uid done -> go done todo'
- | otherwise -> case lookupUniqMap all_unit_direct_deps uid of
- Nothing -> pprPanic "uid not found" (ppr (uid, all_unit_direct_deps))
- Just depends ->
- let todo'' = (depends Set.\\ done) `Set.union` todo'
- in DigraphNode uid uid (Set.toList depends) : go (Set.insert uid done) todo''
+{-
+
+Note [Home unit closure property check]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Per the definition in Note [Multiple Home Units], a unit environment has the
+closure property exactly if there are no paths /h/₁ →* /e/ →* /h/₂ in the
+dependency graph, where /h/₁ and /h/₂ are home units and /e/ is an external
+unit. However, the algorithm used by 'checkHomeUnitsClosed' searches for
+so-called offending dependencies, which are dependencies /e/ → /h/₂ that are
+part of a path /h/₁ →* /e/ → /h/₂ in the dependency graph. To see that this is a
+viable approach, consider the following:
+
+ * A path /h/₁ →* /e/ → /h/₂ is also a path /h/₁ →* /e/ →* /h/₂.
+
+ * For each path /h/₁ →* /e/ →* /h/₂, there exists a path /h/₁ →* /e/′ → /h/₂′,
+ where /e/′ is an external unit and /h/₂′ is a home unit. Such a path can be
+ constructed by taking as /h/₂′ the first home unit on the path /e/ →* /h/₂
+ and as /e/′ the, necessarily external, unit preceding it.
+
+Concretely, the algorithm picks one home unit after the other, determines what
+units it directly depends on, and, starting from them, follows unit dependencies
+to search for offending dependencies. It does not follow dependencies that have
+been followed before, possibly when processing another home unit. To achieve
+this, the algorithm tracks, across home units, from which units it has already
+followed dependencies. For this tracking, it identifies each unit by a
+'GlobalUnitKey', which is a pair of a 'UnitId' and an ABI hash. Using only a
+'UnitId' would not work, because 'UnitId's are not always globally unique. Also
+using only an ABI hash is not an option, because an ABI hash is not necessarily
+an ABI hash: it can also be the string @"inline"@.
+
+The correctness of this algorithm rests on the, likely correct, assumption that,
+among the units mentioned in the 'UnitState' of a particular home unit, any unit
+can be uniquely identified by its 'UnitId' and thus 'UnitId' clashes can only
+occur across the 'UnitState's of different home units.
+
+An alternative approach to finding offending dependencies would be to follow
+dependencies starting from all units that /any/ home unit directly depends on
+instead of considering the different home units separately. A corresponding
+algorithm could in principle find the dependencies of a particular unit
+independently of any home unit by fetching the 'UnitInfo' of that unit from the
+'GlobalUnitInfoMap'. However, for such a lookup the algorithm would need not
+only the 'UnitId' but also the ABI hash of the unit in question. Therefore,
+whenever following a dependency of a unit /u/ on a unit /v/, it would have to
+determine the ABI hash of /v/, so that it could later look up /v/’s
+dependencies. The ABI hashes of all units that /u/ depends on should be
+available in the 'unitAbiDepends' field of /u/’s 'UnitInfo'. However, at the
+time of writing, 'unitAbiDepends' never contained anything other than the empty
+list during GHC test runs, which indicated that this alternative solution was
+impossible to realize.
+
+-}
--------------------------------------------------------------------------------
-- * Enable Code Gen for Template Haskell
@@ -1763,7 +1889,7 @@ data NodeRes v
--
-- See also Note [Downsweep Control Flow and Caching]
dfsBuild :: (Ord k, Monad m)
- => Maybe (Map.Map k (NodeRes v))
+ => Maybe (Map k (NodeRes v))
-- ^ Base map, existing results. We won't re-expand any of the nodes
-- already present in this map.
-> [n]
@@ -1773,7 +1899,7 @@ dfsBuild :: (Ord k, Monad m)
-> (n -> m (NodeRes (v,[n])))
-- ^ Expand this node into its payload result and into the list of
-- children nodes to visit next.
- -> m (Map.Map k (NodeRes v))
+ -> m (Map k (NodeRes v))
-- ^ The result accumulates the payload of expanding the root nodes
-- and all nodes transitively reachable from those roots.
dfsBuild base_map roots key expand = go roots (fromMaybe Map.empty base_map)
=====================================
compiler/GHC/Driver/Errors/Ppr.hs
=====================================
@@ -235,10 +235,17 @@ instance Diagnostic DriverMessage where
"but no output will be generated.") $$
(text "There is no module named" <+>
quotes (ppr mod_name) <> text "."))
- DriverHomePackagesNotClosed needed_unit_ids
- -> mkSimpleDecorated $ vcat ([text "Home units are not closed."
- , text "It is necessary to also load the following units:" ]
- ++ map (\uid -> text "-" <+> ppr uid) needed_unit_ids)
+ DriverHomePackagesNotClosed offending_dependencies
+ -> mkSimpleDecorated $
+ hang (text "Some units are not loaded but depend on loaded units.")
+ 4
+ (vcat (map pprDependency offending_dependencies))
+ where
+
+ pprDependency :: (UnitId, UnitId) -> SDoc
+ pprDependency (external_unit, home_unit)
+ = ppr external_unit <+> arrow <+> ppr home_unit
+
DriverInterfaceError reason -> diagnosticMessage (ifaceDiagnosticOpts opts) reason
DriverInconsistentDynFlags msg
=====================================
compiler/GHC/Driver/Errors/Types.hs
=====================================
@@ -369,7 +369,7 @@ data DriverMessage where
DriverRedirectedNoMain :: !ModuleName -> DriverMessage
- DriverHomePackagesNotClosed :: ![UnitId] -> DriverMessage
+ DriverHomePackagesNotClosed :: ![(UnitId, UnitId)] -> DriverMessage
DriverInterfaceError :: !IfaceMessage -> DriverMessage
=====================================
compiler/GHC/Unit/External/Index.hs
=====================================
@@ -308,6 +308,7 @@ data GlobalUnitKey =
GlobalUnitKey
!UnitId -- ^ Unit Id of the 'UnitInfo'
!UnitAbiHash -- ^ ABI hash of the 'UnitInfo'
+ deriving (Eq, Ord)
globalUnitKeyFromUnitInfo :: UnitInfo -> GlobalUnitKey
globalUnitKeyFromUnitInfo ui = mkGlobalUnitKey (unitId ui) (unitAbiHash ui)
=====================================
testsuite/tests/driver/multipleHomeUnits/mhu-closure/Makefile
=====================================
@@ -10,14 +10,21 @@ CONFIGURE=configure \
--ghc-options='$(TEST_HC_OPTS)' \
--package-db=../tmp.d
TEST_BUILD='$(TEST_HC)' $(TEST_HC_OPTS) -fhide-source-paths -fforce-recomp
+ONLY_BASE=-hide-all-packages -package base
mhu-closure: clean pkg-database
- $(TEST_BUILD) -unit @unitP
- $(TEST_BUILD) -unit @unitP -unit @unitQ
+ ! $(TEST_BUILD) -unit @unitP
+ ! $(TEST_BUILD) -unit @unitP -unit @unitQ
! $(TEST_BUILD) -unit @unitP -unit @unitR
! $(TEST_BUILD) -unit @unitP -unit @unitR1
$(TEST_BUILD) -unit @unitP -unit @unitQ -unit @unitR
- $(TEST_BUILD) -unit @unitP -unit @unitQ -unit @unitR1
+ ! $(TEST_BUILD) -unit @unitP -unit @unitQ -unit @unitR1
+ $(TEST_BUILD) $(ONLY_BASE) -unit @unitP
+ $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ
+ ! $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitR
+ ! $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitR1
+ $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ -unit @unitR
+ $(TEST_BUILD) $(ONLY_BASE) -unit @unitP -unit @unitQ -unit @unitR1
.PHONY: pkg-database
pkg-database:
=====================================
testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
=====================================
@@ -1,10 +1,28 @@
<command line>: error: [GHC-03271]
- Home units are not closed.
- It is necessary to also load the following units:
- - q-0.1.0.0
+ Some units are not loaded but depend on loaded units.
+ q-0.1.0.0 -> p-0.1.0.0
<command line>: error: [GHC-03271]
- Home units are not closed.
- It is necessary to also load the following units:
- - q-0.1.0.0
+ Some units are not loaded but depend on loaded units.
+ r-0.1.0.0 -> q-0.1.0.0
+
+<command line>: error: [GHC-03271]
+ Some units are not loaded but depend on loaded units.
+ q-0.1.0.0 -> p-0.1.0.0
+
+<command line>: error: [GHC-03271]
+ Some units are not loaded but depend on loaded units.
+ q-0.1.0.0 -> p-0.1.0.0
+
+<command line>: error: [GHC-03271]
+ Some units are not loaded but depend on loaded units.
+ r-0.1.0.0 -> q-0.1.0.0
+
+<command line>: error: [GHC-03271]
+ Some units are not loaded but depend on loaded units.
+ q-0.1.0.0 -> p-0.1.0.0
+
+<command line>: error: [GHC-03271]
+ Some units are not loaded but depend on loaded units.
+ q-0.1.0.0 -> p-0.1.0.0
=====================================
testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
=====================================
@@ -1,3 +1,6 @@
+[1 of 3] Compiling P[p-0.1.0.0]
+[2 of 3] Compiling Q[q-0.1.0.0]
+[3 of 3] Compiling R[r-0.1.0.0]
[1 of 1] Compiling P
[1 of 2] Compiling P[p-0.1.0.0]
[2 of 2] Compiling Q[q-0.1.0.0]
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f20a6ade8bccd29e2f298c51039cdd3…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f20a6ade8bccd29e2f298c51039cdd3…
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/mangoiv/9.12.5-rc4] Add tests for absent fillers at dictionary types
by Magnus (@MangoIV) 03 Sep '26
by Magnus (@MangoIV) 03 Sep '26
03 Sep '26
Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
feec438d by Zubin Duggal at 2026-09-03T16:10:51+02:00
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
(cherry picked from commit 5f474953d1880232b5e6c5741f08746e28cd25ab)
- - - - -
23 changed files:
- + 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/all.T
Changes:
=====================================
testsuite/tests/core-to-stg/T27627/Callee.hs
=====================================
@@ -0,0 +1,28 @@
+{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables #-}
+{-# OPTIONS_GHC -fno-worker-wrapper #-}
+module Callee where
+
+-- Not unary, so ($p1TC d) is not trivial and CorePrep binds it separately.
+class Eq a => TC a where
+ tcDummy :: a -> Int
+
+-- Unary: at runtime a (UC a) dictionary is the (TC a) dictionary it wraps.
+class TC a => UC a where {}
+
+instance TC Int where tcDummy _ = 0
+instance UC Int
+
+data Dict c where
+ Dict :: c => Dict c
+
+-- Ignores its argument, so the Dict below is absent-demanded.
+{-# NOINLINE discard #-}
+discard :: Dict c -> Int
+discard _ = 42
+
+-- Body compiles to discard (Dict @(Eq a) ($p1TC ($p1UC d)))
+-- The Dict is a value, so CorePrep floats the selection out and evaluates it
+-- at the head of b. -fno-worker-wrapper keeps the dictionary parameter.
+{-# NOINLINE b #-}
+b :: forall a. UC a => a -> Int
+b _ = discard (Dict :: Dict (Eq a))
=====================================
testsuite/tests/core-to-stg/T27627/Caller.hs
=====================================
@@ -0,0 +1,9 @@
+module Caller where
+
+import Callee
+
+-- b ignores its dictionary, so a's is absent. Worker/wrapper must not make a
+-- filler: b speculates a superclass selection out of it.
+{-# NOINLINE a #-}
+a :: UC t => t -> Int
+a x = b x + 1
=====================================
testsuite/tests/core-to-stg/T27627/Main.hs
=====================================
@@ -0,0 +1,4 @@
+module Main where
+import Caller
+main :: IO ()
+main = print (a (1 :: Int))
=====================================
testsuite/tests/core-to-stg/T27627/T27627.stdout
=====================================
@@ -0,0 +1 @@
+43
=====================================
testsuite/tests/core-to-stg/T27627/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27627',
+ [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
+ multimod_compile_and_run,
+ ['Main', '-O'])
=====================================
testsuite/tests/core-to-stg/T27627a/Callee.hs
=====================================
@@ -0,0 +1,32 @@
+{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables, TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses, FlexibleInstances #-}
+{-# OPTIONS_GHC -fno-worker-wrapper #-}
+module Callee where
+
+import Data.Kind (Constraint)
+
+-- Reduces to (TC a), so at runtime a (UC a) dictionary is a (TC a) dictionary.
+type family F a :: Constraint
+type instance F a = TC a
+
+-- Not unary.
+class Eq a => TC a where
+ tcDummy :: a -> Int
+
+-- Unary: one superclass field, the unreduced (F a).
+-- See (NBD1) in Note [NON-BOTTOM-DICTS invariant].
+class F a => UC a
+
+instance TC Int where tcDummy _ = 0
+instance UC Int
+
+data Dict c where
+ Dict :: c => Dict c
+
+{-# NOINLINE discard #-}
+discard :: Dict c -> Int
+discard _ = 42
+
+{-# NOINLINE b #-}
+b :: forall a. UC a => a -> Int
+b _ = discard (Dict :: Dict (Eq a))
=====================================
testsuite/tests/core-to-stg/T27627a/Caller.hs
=====================================
@@ -0,0 +1,9 @@
+module Caller where
+
+import Callee
+
+-- b ignores its dictionary, so a's is absent. Worker/wrapper must not make a
+-- filler: b speculates a superclass selection out of it.
+{-# NOINLINE a #-}
+a :: UC t => t -> Int
+a x = b x + 1
=====================================
testsuite/tests/core-to-stg/T27627a/Main.hs
=====================================
@@ -0,0 +1,6 @@
+module Main where
+
+import Caller
+
+main :: IO ()
+main = print (a (3 :: Int))
=====================================
testsuite/tests/core-to-stg/T27627a/T27627a.stdout
=====================================
@@ -0,0 +1 @@
+43
=====================================
testsuite/tests/core-to-stg/T27627a/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27627a',
+ [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
+ multimod_compile_and_run,
+ ['Main', '-O'])
=====================================
testsuite/tests/core-to-stg/T27627b/Callee.hs
=====================================
@@ -0,0 +1,28 @@
+{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables, QuantifiedConstraints #-}
+{-# LANGUAGE UndecidableInstances, FlexibleInstances, RankNTypes #-}
+{-# OPTIONS_GHC -fno-worker-wrapper #-}
+module Callee where
+
+-- Not unary, so a (TC a) dictionary terminates.
+class Eq a => TC a where
+ tcDummy :: a -> Int
+
+-- Unary: one superclass field, (forall a. TC (f a)).
+-- See (NBD1) in Note [NON-BOTTOM-DICTS invariant].
+class (forall a. TC (f a)) => UQ f
+
+newtype Id a = MkId a
+instance Eq (Id a) where _ == _ = True
+instance TC (Id a) where tcDummy _ = 0
+instance UQ Id
+
+data Dict c where
+ Dict :: c => Dict c
+
+{-# NOINLINE discard #-}
+discard :: Dict c -> Int
+discard _ = 42
+
+{-# NOINLINE b #-}
+b :: forall f. UQ f => f Int -> Int
+b _ = discard (Dict :: Dict (Eq (f Int)))
=====================================
testsuite/tests/core-to-stg/T27627b/Caller.hs
=====================================
@@ -0,0 +1,9 @@
+module Caller where
+
+import Callee
+
+-- b ignores its dictionary, so a's is absent. Worker/wrapper must not make a
+-- filler: b speculates a superclass selection out of it.
+{-# NOINLINE a #-}
+a :: UQ f => f Int -> Int
+a x = b x + 1
=====================================
testsuite/tests/core-to-stg/T27627b/Main.hs
=====================================
@@ -0,0 +1,7 @@
+module Main where
+
+import Callee
+import Caller
+
+main :: IO ()
+main = print (a (MkId 3 :: Id Int))
=====================================
testsuite/tests/core-to-stg/T27627b/T27627b.stdout
=====================================
@@ -0,0 +1 @@
+43
=====================================
testsuite/tests/core-to-stg/T27627b/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27627b',
+ [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
+ multimod_compile_and_run,
+ ['Main', '-O'])
=====================================
testsuite/tests/core-to-stg/T27627c/Callee.hs
=====================================
@@ -0,0 +1,25 @@
+{-# LANGUAGE GADTs, ScopedTypeVariables, UndecidableInstances, FlexibleInstances #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
+{-# OPTIONS_GHC -fno-worker-wrapper #-}
+module Callee where
+
+import Data.Kind (Constraint)
+
+class Eq a => TC a where
+ tcDummy :: a -> Int
+
+class c => UC (c :: Constraint)
+
+instance TC Int where tcDummy _ = 0
+instance c => UC c
+
+data Dict c where
+ Dict :: c => Dict c
+
+{-# NOINLINE discard #-}
+discard :: Dict c -> Int
+discard _ = 42
+
+{-# NOINLINE b #-}
+b :: forall a. UC (UC (TC a)) => a -> Int
+b _ = discard (Dict :: Dict (Eq a))
=====================================
testsuite/tests/core-to-stg/T27627c/Caller.hs
=====================================
@@ -0,0 +1,7 @@
+module Caller where
+
+import Callee
+
+{-# NOINLINE a #-}
+a :: UC (UC (TC t)) => t -> Int
+a x = b x + 1
=====================================
testsuite/tests/core-to-stg/T27627c/Main.hs
=====================================
@@ -0,0 +1,4 @@
+module Main where
+import Caller
+main :: IO ()
+main = print (a (1 :: Int))
=====================================
testsuite/tests/core-to-stg/T27627c/T27627c.stdout
=====================================
@@ -0,0 +1 @@
+43
=====================================
testsuite/tests/core-to-stg/T27627c/all.T
=====================================
@@ -0,0 +1,4 @@
+test('T27627c',
+ [extra_files(['Main.hs', 'Caller.hs', 'Callee.hs'])],
+ multimod_compile_and_run,
+ ['Main', '-O'])
=====================================
testsuite/tests/core-to-stg/T27627e.hs
=====================================
@@ -0,0 +1,23 @@
+{-# LANGUAGE QuantifiedConstraints, UndecidableInstances, FlexibleInstances,
+ UndecidableSuperClasses, FlexibleContexts, RankNTypes #-}
+-- T27627b's class shape, with a method and a recursive instance.
+module Main where
+
+class Eq a => TC a
+class (forall a. TC (f a)) => UQ f where { uqDummy :: f Int -> Int }
+
+newtype Id a = MkId a
+instance Eq (Id a) where _ == _ = True
+instance UQ f => TC (f a)
+instance UQ Id where uqDummy _ = 7
+
+{-# NOINLINE dead #-}
+dead :: TC a => a -> Int -> Int
+dead _ n = n + 1
+
+{-# NOINLINE useUQ #-}
+useUQ :: UQ f => f Int -> Int -> Int
+useUQ x n = dead x n
+
+main :: IO ()
+main = print (useUQ (MkId 3 :: Id Int) 41)
=====================================
testsuite/tests/core-to-stg/T27627e.stdout
=====================================
@@ -0,0 +1 @@
+42
=====================================
testsuite/tests/core-to-stg/all.T
=====================================
@@ -8,3 +8,4 @@ test('T24124', normal, compile, ['-O -ddump-stg-final -dno-typeable-binds -dsupp
test('T24334', normal, compile_and_run, ['-O'])
test('T24463', normal, compile, ['-O'])
test('T25924a', [ignore_stderr], compile_and_run, ['-O'])
+test('T27627e', normal, compile_and_run, ['-O0'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/feec438d628b953c892fdbcc35bc2c5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/feec438d628b953c892fdbcc35bc2c5…
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
Magnus pushed new branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/mangoiv/9.12.5-rc4
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/27323] 36 commits: Add Data.RealFloat and Infinity/NegInfinity/NaN pattern synonyms (#26961)
by Sasha Bogicevic (@Bogicevic) 03 Sep '26
by Sasha Bogicevic (@Bogicevic) 03 Sep '26
03 Sep '26
Sasha Bogicevic pushed to branch wip/27323 at Glasgow Haskell Compiler / GHC
Commits:
b5d29ab8 by Brandon Chinn at 2026-08-25T18:42:08-04:00
Add Data.RealFloat and Infinity/NegInfinity/NaN pattern synonyms (#26961)
- - - - -
e60eb3bc by Andreas Klebinger at 2026-08-25T18:42:59-04:00
rts linker: Fix pointer arithmetic issue in flushInstructionCacheRISCV64
We accidentally operated over `uint64_t*` when we should use `uint8_t`.
Fixes #27569
- - - - -
e9bbe8f9 by Andreas Klebinger at 2026-08-26T15:09:23-04:00
cmm dumps: Add machop width info with -dppr-debug for infix ops.
- - - - -
86e3a9d8 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
CmmLint: Check for unsupported MachOp widths
machOpArgReps now maps MachOp + Width to a list of supported
argument widths or Nothing if the given operation is not supported
at the given width.
This allows us to check for nonsensical combinations like FloatToInt
at Word16.
Similarly we now check that every address is actually wordwidth.
- - - - -
13781cca by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: The big subword truncation fix.
A set of slightly related fixes to arm subword handling:
Bitmask immediates:
Don't produce overflowing assembly literals.
There is still another bug here that causes us to miss some valid
literals but we will fix that later.
Improve subword truncation handling:
We now use a small set of helpers to truncate `Register` values rather
than truncating immediate `Reg` values which greatly simplifies the code
structure. This fixes a great many bugs to do with sign/zero extending subwords
or the lack thereof.
We now establish the invariant that subword values are zero-extended at
every site at which they come into "scope" of the ncg, and rely on the
invariant throughout rather than pessimistically inserting redundant
extensions in a hodgepodge manner at the use sites of these values.
This fixes at least the bugs described in issues #27533, #27430
#27537, #27538, #27539, and #27550. But likely more bugs yet not
found.
Subword ffi results:
Apply truncations when calling functions returning
subword values.
genCondJump:
Don't sign extend signed values in the input register as
it might map to a local variable, corrupting the value stored within.
Fix subword store/load instructions.:
We used to read those at 32bit width even for smaller values possibly
resulting in invalid memory access. Now we construct the suffix for
subword variants based on the instruction format for these.
- - - - -
d8fa5d7c by Andreas Klebinger at 2026-08-26T15:09:24-04:00
arm64 ncg: Fix MO_V_Broadcast for non-literals.
We now use OpReg instead of OpScalarAsVec as required since we broadcast a gp register.
Also adds a test. Fixes #27565.
- - - - -
94822c95 by Andreas Klebinger at 2026-08-26T15:09:24-04:00
Add some test cases covering bugs in the arm ncg.
* Test for #27430 (subword ffi results)
* #27537 - subword conversions
* #27538 - subwords used in conditional
* #27533 - single byte read
- - - - -
dd1ba88a by Andreas Klebinger at 2026-08-26T15:09:24-04:00
cmmLint: Lint against MO_FS_Truncate subword use.
- - - - -
fd22f71e by Zubin Duggal at 2026-08-26T15:10:20-04:00
ghc-internal: annotateSTM should use catchSTM# rather than catch#
A catch# frame inside a transaction breaks retry and async exception
delivery.
Fixes #27657
- - - - -
bb324171 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: refactor to reduce THREADED_RTS in MSG_UPD_TSO_FLAGS
- No behavior change in this commit (well, a small optimization here
makes us do less work if the target TSO owned by the curr. capability)
- Move all THREADED_RTS CPP needed into `updThreadFlag`
- Merge MSG_SET_TSO_FLAGS and MSG_UNSET_TSO_FLAGS into MSG_UPD_TSO_FLAGS
plus a `set` bool field in the MessageUpdTSOFlag struct
Towards #27729
- - - - -
ed99b7b7 by Rodrigo Mesquita at 2026-08-26T15:10:59-04:00
rts: Fix race condition in MSG_UPD_TSO_FLAGS execution
The code for processing the MSG_UPD_TSO_FLAGS message was not taking
into consideration that the TSO's owner might have moved in between that
capability receiving the message (since it was its previous owner) and
starting to process its inbox (a point at which it was no longer the
owner)
Added Note [TSO owner may change in between Msg being sent and received]
to explain this race and the pattern used to fix this, where we just
forward the message to the new owner.
Fixes #27729
- - - - -
cd653714 by Alan Zimmerman at 2026-08-26T15:11:49-04:00
EPA: Uses Parsers.parseModule for exactprint tests
Parsers.parseModule is the advertised way to parse for use for exact
printing in the ghc-exactprint library. This commit updates the GHC
exact print testing to use it.
This requires moving the comment balancing that was occurring
only in the test path into the advertising parser path, so it moves
from Transforms.hs to Utils.hs.
Also update the comment adding to honour trailing annotations
- - - - -
d1d01fa5 by Wolfgang Jeltsch at 2026-08-27T13:17:59+03:00
Add `rethrowSTM` and improve STM-related documentation
Adding `rethrowSTM` resolves #26758.
The implementation of `rethrowSTM` is completely analogous to the one of
`rethrowIO`.
The following is established for the documentation of `throwSTM` and
`catchSTM`:
* Both operations are directly described as analogs of their `IO`
counterparts.
* There is no reference to `throw` in the documentation of `throwSTM`,
because, although such a reference is great in the documentation of
`throwIO`, it is somewhat out of place in the documentation of
`throwSTM`.
* Instead of repeating part of `throwIO`’s documentation, the
documentation of `throwSTM` just recommends using `throwSTM` instead
of `throw` and references the corresponding arguments in the
documentation of `throwIO`.
- - - - -
06fde293 by fendor at 2026-08-28T06:06:44-04:00
GHCi: Fix order of `PackageDBFlag`s for interactive home unit
`PackageDBFlag`s are stored in reverse order of cli specification.
When sorting the `PackageDBFlag`s by longest common prefix, we need thus
to reverse the package db stacks before calculating the prefix.
We make sure to reverse the package db stack for the interactive home
unit to uphold that later specified package dbs overwrite earlier ones.
Resolved and adds regression test for #27640
- - - - -
024c4d04 by fendor at 2026-08-28T06:07:23-04:00
Reuse the UnitIndexCache after initialising multiple home units
- - - - -
55326fa0 by Alan Zimmerman at 2026-08-28T06:08:03-04:00
EPA: Some Haddock processing tweaks
These changes to the Haddock postprocessing should not change
behaviour, but just bring it more closely in line with the
original, changed at 44309cd377f
And add some haddock exactprint tests to show they work.
- - - - -
b3ddee95 by Andreas Klebinger at 2026-08-28T13:57:46-04:00
hadrian: Deprecate quickest flavour.
It was more of a trap for new users than actually beneficial so we
deprecate it and suggest quick+no_dynamic_libs to users instead.
- - - - -
a1d81390 by Andreas Klebinger at 2026-08-28T13:58:37-04:00
cmm: Always favour entry block during block deduplication.
We now always keep the first block in the CmmGraph. This way we avoid
the need to update the entry info table.
Failing to do so caused #27722.
Fixes #27722.
- - - - -
5bd65f00 by Andreas Klebinger at 2026-08-28T13:59:16-04:00
test: FamAppCachePerf - Only collect bytes allocated. Fixes 27747
- - - - -
ced53ce6 by mangoiv at 2026-08-29T07:15:24-04:00
nightlies: output yaml to file only
Previously we would just output the metadata to stdout
which risks that it's clobbered by incidental debugt output.
We now output to file only.
Fixes #27511
- - - - -
578bd185 by Andreas Klebinger at 2026-08-29T07:16:05-04:00
Specialise: Stop looping on recursive dictionaries in interestingDict
interestingDict now doesn't look through loopbreaker unfoldings.
Doing so would cause infinite loops on certain dictionaries.
Fixes #27705.
- - - - -
7bf546fc by Simon Peyton Jones at 2026-08-31T23:48:53-04:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
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
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
- - - - -
5f474953 by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
cd5c6bcc by Zubin Duggal at 2026-08-31T23:48:53-04:00
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 isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
- - - - -
abfc224a by Zubin Duggal at 2026-08-31T23:48:53-04:00
Specialise: don't replace dead args with absent fillers
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.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
- - - - -
1557fd1c by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
4117e5ae by Wolfgang Jeltsch at 2026-08-31T23:49:35-04:00
Incorporate the `rethrowSTM` reexport into the `stm` submodule
- - - - -
4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T3586
unsafeRead and unsafeWrite use 0-based index.
Looking at #3586, the expected output seems to be 2.8e8.
Addresses #27596
- - - - -
3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T21305
writeInt64Array# takes an index measured in units of Int64 elements.
Fixes #27596
- - - - -
44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00
Fix buglet in INLINE-arity calculation for pattern synonyms
This fixes #27744.
The buglet was accidentally introduced by
commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sat Aug 1 00:13:02 2026 +0100
Fix three bugs related to required type args and INLINE pragmas
I failed to find all the calls to `addInlinePragArity`!
- - - - -
df058f1d by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, although their
noise profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates the remaining users of 'all' (and of the 'all'
default) to explicit per-metric collection, ahead of removing 'all'
from the driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved
by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425), residency remains gated via max_bytes_used,
now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all. One is a simplifier-ticks blowup and
the other is stated entirely in allocation numbers, so the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric. They are already gated at 1% via
collect_compiler_runtime. The ghc/max gate had previously broken CI
spuriously (9fd11585eb widened it from 1% to 10% for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
72dd2432 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated
at a single tolerance, although their noise profiles are incompatible,
making such tests either flaky or toothless (#27653).
Closes #27653.
Assisted-by: Claude Fable 5
- - - - -
2228cb30 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Make the deviation argument of collect_stats mandatory
Almost every caller passes an explicit tolerance matched to the
metric's noise profile, and the silent 20% default is far slacker than
'bytes allocated' merits. Only two tests relied on it. They now state
their tolerance explicitly:
large-project gets 10%, in line with other large compile-time tests.
T9848 gets 2%: its metric is byte-for-byte deterministic across CI
jobs and platforms of a given test_env, has drifted only about 2.5%
since 2015, and the fusion failure it guards against would show up as
a roughly +30000% jump.
Assisted-by: Claude Fable 5
- - - - -
56291fc5 by Cheng Shao at 2026-09-02T07:18:45-04:00
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
- - - - -
20eb3f41 by Cheng Shao at 2026-09-02T07:18:45-04:00
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
- - - - -
c311fd90 by Sasha Bogicevic at 2026-09-03T15:59:20+02:00
Don't report -XStrict-generated bangs under -Wredundant-bang-patterns
With -XStrict, decideBangHood inserts bang patterns on binders. When
such a bang cannot force anything (e.g. on a binder of unlifted type),
-Wredundant-bang-patterns reported it, even though there is no bang in
the source to remove.
The desugarer now tracks whether it is desugaring compiler-generated
code, mirroring tcl_in_gen_code in the typechecker:
* decideBangHood places the bangs it inserts at a generatedSrcSpan.
* DsLclEnv gains a dsl_in_generated_code field, maintained solely by
putSrcSpanDs: a real span clears it, a generated span sets it.
* The pattern-match checker's desugaring (desugarLPat) pushes each
pattern's location, and on a bang in generated code emits a PmBang
with no SrcInfo: it is still divergence-checked (so inaccessible-RHS
warnings under -XStrict survive, #21761), but never reported as
redundant.
See Note [Desugaring -XStrict matches in Pmc] in GHC.HsToCore.Pmc.Desugar.
Making these bangs proper expanded patterns instead is tracked as #27677.
- - - - -
194 changed files:
- .gitlab-ci.yml
- .gitlab/rel_eng/mk-ghcup-metadata/mk_ghcup_metadata.py
- + changelog.d/27627
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- changelog.d/T27202
- + changelog.d/T27323
- + changelog.d/T27657
- + changelog.d/T27705
- + changelog.d/T27722-cbe-entry-block.md
- + changelog.d/T27744
- + changelog.d/arm_ncg_fixes_T27430
- + changelog.d/llvm-23
- + changelog.d/rethrow-stm
- changelog.d/unit-index
- compiler/GHC/Cmm/CommonBlockElim.hs
- compiler/GHC/Cmm/Expr.hs
- compiler/GHC/Cmm/Lint.hs
- compiler/GHC/Cmm/MachOp.hs
- compiler/GHC/Cmm/Parser.y
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/HsToCore/Monad.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Types.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Parser/PostProcess/Haddock.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- configure.ac
- ghc/GHCi/UI.hs
- hadrian/README.md
- hadrian/doc/cross-compile.md
- hadrian/doc/flavours.md
- hadrian/doc/make.md
- hadrian/doc/windows.md
- hadrian/hadrian.cabal
- hadrian/src/CommandLine.hs
- hadrian/src/Flavour.hs
- hadrian/src/Settings.hs
- − hadrian/src/Settings/Flavours/Quickest.hs
- libraries/base/base.cabal.in
- libraries/base/changelog.md
- + libraries/base/src/Data/RealFloat.hs
- libraries/base/src/GHC/Conc.hs
- libraries/base/tests/all.T
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- libraries/stm
- rts/CloneStack.c
- rts/Interpreter.c
- rts/Messages.c
- rts/ProfHeap.c
- rts/StgMiscClosures.cmm
- rts/Threads.c
- rts/Threads.h
- rts/eventlog/EventLog.c
- rts/include/rts/storage/Closures.h
- rts/include/stg/MiscClosures.h
- rts/linker/elf_reloc_riscv64.c
- rts/prim/atomic.c
- testsuite/driver/README.md
- testsuite/driver/testlib.py
- testsuite/tests/cmm/should_compile/Makefile
- + testsuite/tests/cmm/should_compile/T27368-ppr-debug.stderr
- − testsuite/tests/cmm/should_compile/T27368-ppr-debug.stdout
- testsuite/tests/cmm/should_compile/all.T
- + testsuite/tests/codeGen/should_run/T27430.hs
- + testsuite/tests/codeGen/should_run/T27430.stdout
- + testsuite/tests/codeGen/should_run/T27430_c.c
- + testsuite/tests/codeGen/should_run/T27533.hs
- + testsuite/tests/codeGen/should_run/T27533.stdout
- + testsuite/tests/codeGen/should_run/T27533_cmm.cmm
- + testsuite/tests/codeGen/should_run/T27537.hs
- + testsuite/tests/codeGen/should_run/T27537.stdout
- + testsuite/tests/codeGen/should_run/T27538.hs
- + testsuite/tests/codeGen/should_run/T27538.stdout
- testsuite/tests/codeGen/should_run/all.T
- + testsuite/tests/concurrent/should_run/T27657a.hs
- + testsuite/tests/concurrent/should_run/T27657a.stdout
- + testsuite/tests/concurrent/should_run/T27657b.hs
- + testsuite/tests/concurrent/should_run/T27657b.stdout
- testsuite/tests/concurrent/should_run/all.T
- + 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/ffi/should_run/T21305.hs
- + testsuite/tests/ghci/prog-mhu007/Makefile
- + testsuite/tests/ghci/prog-mhu007/a/A.hs
- + testsuite/tests/ghci/prog-mhu007/all.T
- + testsuite/tests/ghci/prog-mhu007/b/B.hs
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.script
- + testsuite/tests/ghci/prog-mhu007/prog-mhu007.stdout
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/Bar.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-bar/testpkg-bar.pkg
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/Foo.hs
- + testsuite/tests/ghci/prog-mhu007/testpkg-foo/testpkg-foo.pkg
- + testsuite/tests/ghci/prog-mhu007/unitA
- + testsuite/tests/ghci/prog-mhu007/unitB
- 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/patsyn/should_compile/T27744.hs
- testsuite/tests/patsyn/should_compile/all.T
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/compiler/large-project/all.T
- testsuite/tests/perf/should_run/T3586.hs
- testsuite/tests/perf/should_run/T3586.stdout
- testsuite/tests/perf/space_leaks/all.T
- + testsuite/tests/pmcheck/should_compile/T27323.hs
- testsuite/tests/pmcheck/should_compile/all.T
- + testsuite/tests/printer/Haddock1.hs
- testsuite/tests/printer/Makefile
- testsuite/tests/printer/all.T
- + testsuite/tests/simd/should_run/T27565.hs
- + testsuite/tests/simd/should_run/T27565.stdout
- testsuite/tests/simd/should_run/all.T
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + 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
- + testsuite/tests/simplCore/should_run/T27705.hs
- + testsuite/tests/simplCore/should_run/T27705.stdout
- + testsuite/tests/simplCore/should_run/T27705_Inst.hs
- testsuite/tests/simplCore/should_run/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/ghc-toolchain/src/GHC/Toolchain/Program.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Target.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f14b27ba9cd7043cb32c00ad0e522e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f14b27ba9cd7043cb32c00ad0e522e…
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/improve-closure-property-check] 22 commits: hadrian: Deprecate quickest flavour.
by Wolfgang Jeltsch (@jeltsch) 03 Sep '26
by Wolfgang Jeltsch (@jeltsch) 03 Sep '26
03 Sep '26
Wolfgang Jeltsch pushed to branch wip/jeltsch/improve-closure-property-check at Glasgow Haskell Compiler / GHC
Commits:
b3ddee95 by Andreas Klebinger at 2026-08-28T13:57:46-04:00
hadrian: Deprecate quickest flavour.
It was more of a trap for new users than actually beneficial so we
deprecate it and suggest quick+no_dynamic_libs to users instead.
- - - - -
a1d81390 by Andreas Klebinger at 2026-08-28T13:58:37-04:00
cmm: Always favour entry block during block deduplication.
We now always keep the first block in the CmmGraph. This way we avoid
the need to update the entry info table.
Failing to do so caused #27722.
Fixes #27722.
- - - - -
5bd65f00 by Andreas Klebinger at 2026-08-28T13:59:16-04:00
test: FamAppCachePerf - Only collect bytes allocated. Fixes 27747
- - - - -
ced53ce6 by mangoiv at 2026-08-29T07:15:24-04:00
nightlies: output yaml to file only
Previously we would just output the metadata to stdout
which risks that it's clobbered by incidental debugt output.
We now output to file only.
Fixes #27511
- - - - -
578bd185 by Andreas Klebinger at 2026-08-29T07:16:05-04:00
Specialise: Stop looping on recursive dictionaries in interestingDict
interestingDict now doesn't look through loopbreaker unfoldings.
Doing so would cause infinite loops on certain dictionaries.
Fixes #27705.
- - - - -
7bf546fc by Simon Peyton Jones at 2026-08-31T23:48:53-04:00
Never make an absent filler for a constraint type
mkAbsentFiller used isTerminatingType to decide, but that is not enough.
Consider
class Eq a => UC a where {}
let u :: UC Int -- UC Int is a "non-terminating type"
u = error "Absent"
let e :: Eq Int -- Eq Int is a "terminating type"
e = $p1UC u
We clearly must not make a filler for `e`, because we speculatively
evaluate it. But speculatively evaluating `e` forces `u`, so we must not
make one for `u` either.
Asking isDictTy instead is not enough either, because it does not catch a
constraint hidden behind an unreduced type family application:
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
So play safe and use isPredTy: never make an absent filler for any
constraint-kinded type.
Fixes #27627
- - - - -
5f474953 by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
cd5c6bcc by Zubin Duggal at 2026-08-31T23:48:53-04:00
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 isTerminatingTyCon, which returns False for an abstract TyCon.
The Bool in DFunId is now a cache for isTerminatingTyCon, set in
mkDFunIdDetails.
Fixes #27704
- - - - -
abfc224a by Zubin Duggal at 2026-08-31T23:48:53-04:00
Specialise: don't replace dead args with absent fillers
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.
Dropping dead args in the specialiser is rarely worth it, to quote Simon,
"The later worker/wrapper pass will pick up the dead arg later if it is really dead. Keeps the specialiser simpler."
So instead of trying to check if the arg really is dead in the stable unfolding,
just drop the logic for dropping dead args in the specialiser altogeher.
Fixes #27703
- - - - -
1557fd1c by Zubin Duggal at 2026-08-31T23:48:53-04:00
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
- - - - -
4117e5ae by Wolfgang Jeltsch at 2026-08-31T23:49:35-04:00
Incorporate the `rethrowSTM` reexport into the `stm` submodule
- - - - -
4bfbf5c8 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T3586
unsafeRead and unsafeWrite use 0-based index.
Looking at #3586, the expected output seems to be 2.8e8.
Addresses #27596
- - - - -
3f9db6d4 by ARATA Mizuki at 2026-09-01T18:55:08-04:00
testsuite: Fix out-of-bounds access in T21305
writeInt64Array# takes an index measured in units of Int64 elements.
Fixes #27596
- - - - -
44d7788f by Simon Peyton Jones at 2026-09-01T18:55:53-04:00
Fix buglet in INLINE-arity calculation for pattern synonyms
This fixes #27744.
The buglet was accidentally introduced by
commit 3a0f9a51c1dacc474c7fd128082edd8bf4081256
Author: Simon Peyton Jones <simon.peytonjones(a)gmail.com>
Date: Sat Aug 1 00:13:02 2026 +0100
Fix three bugs related to required type args and INLINE pragmas
I failed to find all the calls to `addInlinePragArity`!
- - - - -
df058f1d by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Migrate perf tests off collect_compiler_stats('all')
The 'all' metric argument applies a single tolerance to bytes
allocated, max_bytes_used and peak_megabytes_allocated, although their
noise profiles are incompatible (#27653): allocations are nearly
deterministic, residency needs 10-20%, and peak is quantized to 1 MB.
Any single tolerance is too tight for one metric or too slack for
another. This migrates the remaining users of 'all' (and of the 'all'
default) to explicit per-metric collection, ahead of removing 'all'
from the driver.
peak_megabytes_allocated is dropped everywhere: its 1 MB granularity
makes tight relative windows meaningless (#27613), and it is sensitive
to GC timing. In #27489 it drifted by -5.3% while max_bytes_used moved
by less than 0.1%. Where a test guards a memory property,
max_bytes_used covers it at byte granularity.
Where the motivating ticket was about compile-time memory
(T11545, T15304, T26425), residency remains gated via max_bytes_used,
now with a residency-appropriate tolerance.
max_bytes_used is dropped where residency was only ever an accident of
'all':
* T15630, T15630a, T20261: the underlying tickets (#15630, #20261)
contain no memory data at all. One is a simplifier-ticks blowup and
the other is stated entirely in allocation numbers, so the 20%
window never had teeth.
* T21839c: #21839's measurements show residency essentially flat
(+0.16%) while allocations moved +7%, so allocations are the
discriminating metric. They are already gated at 1% via
collect_compiler_runtime. The ghc/max gate had previously broken CI
spuriously (9fd11585eb widened it from 1% to 10% for that reason).
Allocation tolerances are tightened to the testsuite's conventional 2%
where 'all' previously left them at 10-20%.
Assisted-by: Claude Fable 5
- - - - -
72dd2432 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Remove the 'all' metric argument of collect_stats
'all' gated bytes allocated, max_bytes_used and peak_megabytes_allocated
at a single tolerance, although their noise profiles are incompatible,
making such tests either flaky or toothless (#27653).
Closes #27653.
Assisted-by: Claude Fable 5
- - - - -
2228cb30 by Simon Jakobi at 2026-09-02T07:18:06-04:00
testsuite: Make the deviation argument of collect_stats mandatory
Almost every caller passes an explicit tolerance matched to the
metric's noise profile, and the silent 20% default is far slacker than
'bytes allocated' merits. Only two tests relied on it. They now state
their tolerance explicitly:
large-project gets 10%, in line with other large compile-time tests.
T9848 gets 2%: its metric is byte-for-byte deterministic across CI
jobs and platforms of a given test_env, has drifted only about 2.5%
since 2015, and the fusion failure it guards against would show up as
a roughly +30000% jump.
Assisted-by: Claude Fable 5
- - - - -
56291fc5 by Cheng Shao at 2026-09-02T07:18:45-04:00
autoconf/ghc-toolchain: bump llvm upper bound to support llvm 23
This commit bumps llvm upper bound to support llvm 23.
- - - - -
20eb3f41 by Cheng Shao at 2026-09-02T07:18:45-04:00
rts: fix compilation issues with clang 23
clang 23 has broadened `-Wall`/`-Wextra` ranges, exposing some minor
issues in the rts when building with validate flavours:
- Unused locals
- `#pragma GCC diagnostic pop` mismatch
This commit fixes those.
- - - - -
0595408a by Wolfgang Jeltsch at 2026-09-02T17:45:34+03:00
Improve the `mhu-closure` makefile
- - - - -
8cf1453f by Wolfgang Jeltsch at 2026-09-03T16:27:27+03:00
Improve the definition of the home unit closure property
- - - - -
c7713cd3 by Wolfgang Jeltsch at 2026-09-03T16:52:03+03:00
Re-implement the home unit closure check
Compared to the previous implementation, the new one has the following
advantages:
* It is correct.
- It distinguishes between units that have the same unit ID but
different ABI hashes.
- When `-hide-all-packages` is not used, it considers as home unit
dependencies also units that are made implicitly available
because they are in the package database.
* It seems to be faster in usual settings.
- In particular, it does not have a preparation phase in which it
merges the dependency information from all home units, so that
its running time does not grow linearly with the size of the
package database in normal Cabal scenarios.
* It is (hopefully) clearer.
* It is better documented.
- - - - -
120 changed files:
- .gitlab-ci.yml
- .gitlab/rel_eng/mk-ghcup-metadata/mk_ghcup_metadata.py
- + changelog.d/27627
- + changelog.d/27703
- + changelog.d/27704
- + changelog.d/27717
- + changelog.d/T27705
- + changelog.d/T27722-cbe-entry-block.md
- + changelog.d/T27744
- + changelog.d/llvm-23
- compiler/GHC/Cmm/CommonBlockElim.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Errors/Ppr.hs
- compiler/GHC/Driver/Errors/Types.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Types/Demand.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- compiler/GHC/Types/Literal.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/External/Index.hs
- configure.ac
- hadrian/README.md
- hadrian/doc/cross-compile.md
- hadrian/doc/flavours.md
- hadrian/doc/make.md
- hadrian/doc/windows.md
- hadrian/hadrian.cabal
- hadrian/src/CommandLine.hs
- hadrian/src/Flavour.hs
- hadrian/src/Settings.hs
- − hadrian/src/Settings/Flavours/Quickest.hs
- libraries/base/tests/all.T
- libraries/stm
- rts/ProfHeap.c
- rts/eventlog/EventLog.c
- rts/prim/atomic.c
- testsuite/driver/README.md
- testsuite/driver/testlib.py
- + 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/driver/multipleHomeUnits/mhu-closure/Makefile
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stderr
- testsuite/tests/driver/multipleHomeUnits/mhu-closure/mhu-closure.stdout
- testsuite/tests/ffi/should_run/T21305.hs
- + testsuite/tests/patsyn/should_compile/T27744.hs
- testsuite/tests/patsyn/should_compile/all.T
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/perf/compiler/large-project/all.T
- testsuite/tests/perf/should_run/T3586.hs
- testsuite/tests/perf/should_run/T3586.stdout
- testsuite/tests/perf/space_leaks/all.T
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + 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
- + testsuite/tests/simplCore/should_run/T27705.hs
- + testsuite/tests/simplCore/should_run/T27705.stdout
- + testsuite/tests/simplCore/should_run/T27705_Inst.hs
- testsuite/tests/simplCore/should_run/all.T
- utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Target.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0d8dbbf4a75a120430eefc411e39a6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/0d8dbbf4a75a120430eefc411e39a6…
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