15 Aug '26
Andreas Klebinger pushed new branch wip/andreask/cfg-fix at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/andreask/cfg-fix
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/marge_bot_batch_merge_job] 5 commits: Add law in qualified strings doc
by Marge Bot (@marge-bot) 15 Aug '26
by Marge Bot (@marge-bot) 15 Aug '26
15 Aug '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
a737df91 by Brandon Chinn at 2026-08-15T12:40:25-04:00
Add law in qualified strings doc
- - - - -
e3188581 by Zubin Duggal at 2026-08-15T12:41:06-04:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
- - - - -
ca9b0b22 by mangoiv at 2026-08-15T12:41:47-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
95d19638 by sheaf at 2026-08-15T13:14:05-04:00
Avoid wasteful allocations in mkTyConAppCo
The idiom "traverse isReflCo_maybe" followed by "map fst" used in
'GHC.Core.Coercion.mkTyConAppCo' was allocating a lot of waste.
This commit uses 'GHC.Data.Unboxed.traverseMaybeUB' to avoid all these
unnecessary intermediate allocations.
In a quick microbenchmark for 'mkTyConAppCo', this change resulted in:
- refl case (all argument coercions are reflexive):
- -60% runtime
- -80% allocations
- non-refl case:
- from 0% to -12% runtime (depending on which argument is non-refl)
- from 0% to -70% allocations ( -- '' -- )
Fixes #27648
-------------------------
Metric Decrease:
FamAppCachePerf
SimplCastPerf
T12425
T15703
T26426
T3064
T9872a
T9872b
T9872b_defer
T9872c
T9872d
T5321Fun
T9020
T9630
Metric Increase:
LinkableUsage02
-------------------------
- - - - -
8dd06495 by Alan Zimmerman at 2026-08-15T13:14:05-04:00
EPA: Remove al_trailing from AnnList
It was not being used
- - - - -
30 changed files:
- + changelog.d/27626
- compiler/GHC/Core/Coercion.hs
- compiler/GHC/Data/Unboxed.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Types/Demand.hs
- docs/users_guide/exts/qualified_strings.rst
- hadrian/hie-bios.bat
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
- testsuite/tests/ghc-api/exactprint/T22919.stderr
- testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.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.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T15279.stderr
- testsuite/tests/parser/should_compile/T20718.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- utils/check-exact/ExactPrint.hs
- utils/check-exact/Main.hs
- utils/check-exact/Transform.hs
Changes:
=====================================
changelog.d/27626
=====================================
@@ -0,0 +1,5 @@
+section: compiler
+synopsis: Fix a bug where an argument used only by a function's stable unfolding
+ could still be marked absent, resulting in a runtime crash.
+mrs: !16503
+issues: #27626
=====================================
compiler/GHC/Core/Coercion.hs
=====================================
@@ -147,6 +147,7 @@ import GHC.Types.Basic
import GHC.Types.Unique
import GHC.Data.FastString
import GHC.Data.Pair
+import GHC.Data.Unboxed (traverseMaybeUB)
import GHC.Types.SrcLoc
import GHC.Builtin.KnownKeys
import GHC.Builtin.WiredIn.Prim
@@ -166,6 +167,7 @@ import Data.Char( isDigit )
import qualified Data.Monoid as Monoid
import Data.List.NonEmpty ( NonEmpty (..) )
import Control.DeepSeq
+import GHC.Exts (inline)
{-
%************************************************************************
@@ -722,6 +724,19 @@ isReflexiveCo_maybe co
= Nothing
where (Pair ty1 ty2, r) = coercionKindRole co
+-- | Like @\\ cos -> map fst <$> traverse isReflCo_maybe cos@,
+-- but avoiding wasteful allocations (see #27648).
+reflCos_maybe :: [Coercion] -> Maybe [Type]
+reflCos_maybe =
+ traverseMaybeUB
+ ( fmap fst
+ . inline isReflCo_maybe
+ -- isReflCo_maybe must inline so that 'fmap fst' fuses and the whole
+ -- function inlines into 'traverseMaybeUB'
+ )
+{-# INLINE reflCos_maybe #-}
+ -- to avoid allocating the result's 'Just' constructor application
+
forAllCoKindCo :: TyCoVar -> KindMCoercion -> KindCoercion
-- Get the kind coercion from a ForAllCo
forAllCoKindCo _ (MCo co) = co
@@ -811,8 +826,8 @@ mkTyConAppCo r tc cos
| ExpandsSyn tv_co_prs rhs_ty leftover_cos <- expandSynTyCon_maybe tc cos
= mkAppCos (liftCoSubst r (mkLiftingContext tv_co_prs) rhs_ty) leftover_cos
- | Just tys_roles <- traverse isReflCo_maybe cos
- = mkReflCo r (mkTyConApp tc (map fst tys_roles))
+ | Just tys <- reflCos_maybe cos
+ = mkReflCo r $! mkTyConApp tc tys
-- See Note [Refl invariant]
| otherwise = TyConAppCo r tc cos
=====================================
compiler/GHC/Data/Unboxed.hs
=====================================
@@ -13,10 +13,11 @@
module GHC.Data.Unboxed (
MaybeUB(JustUB, NothingUB),
- fmapMaybeUB, fromMaybeUB, apMaybeUB, maybeUB
+ fmapMaybeUB, fromMaybeUB, apMaybeUB, maybeUB,
+ traverseMaybeUB
) where
-import GHC.Prelude hiding (Maybe(..), Either(..))
+import GHC.Prelude
-- | Like Maybe, but using unboxed sums.
--
@@ -54,3 +55,28 @@ fmapMaybeUB f (JustUB x) = JustUB $ f x
maybeUB :: b -> (a -> b) -> MaybeUB a -> b
maybeUB _def f (JustUB x) = f x
maybeUB def _f NothingUB = def
+
+toMaybe :: MaybeUB a -> Maybe a
+toMaybe NothingUB = Nothing
+toMaybe (JustUB a) = Just a
+
+-- | Like 'traverse' for the 'Maybe' applicative, but avoiding intermediate
+-- allocations.
+--
+-- The passed-in function must inline for this to help at all.
+traverseMaybeUB
+ :: forall a b
+ . (a -> Maybe b) -- ^ @INLINE@ function to map over the list
+ -> [a] -> Maybe [b]
+{-# INLINE traverseMaybeUB #-}
+ -- 'traverseMaybeUB' must inline so that the function it is passed can inline
+traverseMaybeUB f = \ xs -> toMaybe $ go xs
+ where
+ go :: [a] -> MaybeUB [b]
+ go [] = JustUB []
+ go (a:as)
+ | Just b <- f a -- f is assumed to inline
+ , JustUB bs <- go as
+ = JustUB (b:bs)
+ | otherwise
+ = NothingUB
=====================================
compiler/GHC/Parser.y
=====================================
@@ -1796,10 +1796,10 @@ cvars1 :: { [RecordPatSynField GhcPs] }
where_decls :: { LocatedA (OrdList (LHsDecl GhcPs), EpToken "where", AnnList) }
: 'where' '{' decls '}' {% amsA' (sLL $1 $> (thdOf3 $ unLoc $3,
epTok $1,
- AnnList (Just (fstOf3 $ unLoc $3)) (ListBraces (epTok $2) (epTok $4)) (sndOf3 $ unLoc $3) [])) }
+ AnnList (Just (fstOf3 $ unLoc $3)) (ListBraces (epTok $2) (epTok $4)) (sndOf3 $ unLoc $3))) }
| 'where' vocurly decls close {% amsA' (sLL $1 $3 (thdOf3 $ unLoc $3,
epTok $1,
- AnnList (Just (fstOf3 $ unLoc $3)) ListNone (sndOf3 $ unLoc $3) [])) }
+ AnnList (Just (fstOf3 $ unLoc $3)) ListNone (sndOf3 $ unLoc $3))) }
pattern_synonym_sig :: { LSig GhcPs }
: 'pattern' con_list '::' sigtype
@@ -1935,9 +1935,9 @@ decls :: { Located (EpaLocation, [EpToken ";"], OrdList (LHsDecl GhcPs)) }
| {- empty -} { noLoc (noAnn, [],nilOL) }
decllist :: { Located (AnnList,Located (OrdList (LHsDecl GhcPs))) }
- : '{' decls '}' { sLL $1 $> (AnnList (Just (fstOf3 $ unLoc $2)) (ListBraces (epTok $1) (epTok $3)) (sndOf3 $ unLoc $2) []
+ : '{' decls '}' { sLL $1 $> (AnnList (Just (fstOf3 $ unLoc $2)) (ListBraces (epTok $1) (epTok $3)) (sndOf3 $ unLoc $2)
,sL1 $2 $ thdOf3 $ unLoc $2) }
- | vocurly decls close { sL1 $2 (AnnList (Just (fstOf3 $ unLoc $2)) ListNone (sndOf3 $ unLoc $2) []
+ | vocurly decls close { sL1 $2 (AnnList (Just (fstOf3 $ unLoc $2)) ListNone (sndOf3 $ unLoc $2)
,sL1 $2 $ thdOf3 $ unLoc $2) }
-- Binding groups other than those of class and instance declarations
@@ -1945,16 +1945,16 @@ decllist :: { Located (AnnList,Located (OrdList (LHsDecl GhcPs))) }
binds :: { Located (HsLocalBinds GhcPs) }
-- May have implicit parameters
-- No type declarations
- : decllist {% do { let { (AnnList anc p s t, decls) = unLoc $1 }
+ : decllist {% do { let { (AnnList anc p s, decls) = unLoc $1 }
; val_binds <- cvBindGroup (unLoc $ decls)
; !cs <- getCommentsFor (gl $1)
- ; return (sL1 $1 $ HsValBinds (EpAnn (glR $1) (AnnList anc p s t) cs, NoEpTok) val_binds)} }
+ ; return (sL1 $1 $ HsValBinds (EpAnn (glR $1) (AnnList anc p s) cs, NoEpTok) val_binds)} }
| '{' dbinds '}' {% acs (comb3 $1 $2 $3) (\loc cs -> (L loc
- $ HsIPBinds (EpAnn (spanAsAnchor (comb3 $1 $2 $3)) (AnnList (Just$ glR $2) (ListBraces (epTok $1) (epTok $3)) [] []) cs, NoEpTok) (IPBinds noExtField (reverse $ unLoc $2)))) }
+ $ HsIPBinds (EpAnn (spanAsAnchor (comb3 $1 $2 $3)) (AnnList (Just$ glR $2) (ListBraces (epTok $1) (epTok $3)) []) cs, NoEpTok) (IPBinds noExtField (reverse $ unLoc $2)))) }
| vocurly dbinds close {% acs (gl $2) (\loc cs -> (L loc
- $ HsIPBinds (EpAnn (glR $1) (AnnList (Just $ glR $2) ListNone [] []) cs, NoEpTok) (IPBinds noExtField (reverse $ unLoc $2)))) }
+ $ HsIPBinds (EpAnn (glR $1) (AnnList (Just $ glR $2) ListNone []) cs, NoEpTok) (IPBinds noExtField (reverse $ unLoc $2)))) }
wherebinds :: { Maybe (Located (HsLocalBinds GhcPs, Maybe EpAnnComments )) }
@@ -3282,7 +3282,7 @@ aexp2 :: { ECP }
-- arrow notation extension
| '(|' aexp cmdargs '|)' {% runPV (unECP $2) >>= \ $2 ->
fmap ecpFromCmd $
- amsA' (sLL $1 $> $ HsCmdArrForm (AnnList (glRM $1) (ListBanana (epUniTok $1) (epUniTok $4)) [] []) $2 Prefix
+ amsA' (sLL $1 $> $ HsCmdArrForm (AnnList (glRM $1) (ListBanana (epUniTok $1) (epUniTok $4)) []) $2 Prefix
(reverse $3)) }
projection :: { Located (NonEmpty (LocatedAn NoEpAnns (DotFieldOcc GhcPs))) }
@@ -3414,9 +3414,9 @@ tup_tail :: { forall b. DisambECP b => PV [Either (EpAnn Bool) (LocatedA b)] }
-- Never empty.
list :: { forall b. DisambECP b => SrcSpan -> (EpaLocation, EpaLocation) -> PV (LocatedA b) }
: texp { \loc (ao,ac) -> unECP $1 >>= \ $1 ->
- mkHsExplicitListPV loc [$1] (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) [] []) }
+ mkHsExplicitListPV loc [$1] (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) []) }
| lexps { \loc (ao,ac) -> $1 >>= \ $1 ->
- mkHsExplicitListPV loc (reverse $1) (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) [] []) }
+ mkHsExplicitListPV loc (reverse $1) (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) []) }
| texp '..' { \loc (ao,ac) -> unECP $1 >>= \ $1 ->
amsA' (L loc $ ArithSeq (AnnArithSeq (EpTok ao) Nothing (epTok $2) (EpTok ac)) Nothing (From $1))
>>= ecpFromExp' }
@@ -3440,7 +3440,7 @@ list :: { forall b. DisambECP b => SrcSpan -> (EpaLocation, EpaLocation) -> PV (
{ \loc (ao,ac) ->
checkMonadComp >>= \ ctxt ->
unECP $1 >>= \ $1 -> do { t <- addTrailingVbarA $1 (epTok $2)
- ; amsA' (L loc $ mkHsCompAnns ctxt (unLoc $3) t (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) [] [], noAnn))
+ ; amsA' (L loc $ mkHsCompAnns ctxt (unLoc $3) t (AnnList Nothing (ListSquare (EpTok ao) (EpTok ac)) [], noAnn))
>>= ecpFromExp' } }
lexps :: { forall b. DisambECP b => PV [LocatedA b] }
@@ -3546,11 +3546,11 @@ guardquals1 :: { Located [LStmt GhcPs (LHsExpr GhcPs)] }
altslist(PATS) :: { forall b. DisambECP b => PV (LocatedA ([LMatch GhcPs (LocatedA b)], AnnList)) }
: '{' alts(PATS) '}' { $2 >>= \ $2 -> amsA'
(sLL $1 $> (reverse (snd $ unLoc $2),
- (AnnList (Just $ glR $2) (ListBraces (epTok $1) (epTok $3)) (fst $ unLoc $2) []))) }
+ (AnnList (Just $ glR $2) (ListBraces (epTok $1) (epTok $3)) (fst $ unLoc $2)))) }
| vocurly alts(PATS) close { $2 >>= \ $2 -> amsA'
(L (getLoc $2) (reverse (snd $ unLoc $2),
- (AnnList (Just $ glR $2) ListNone (fst $ unLoc $2) []))) }
- | '{' '}' { amsA' (sLL $1 $> ([], (AnnList Nothing (ListBraces (epTok $1) (epTok $2)) [] []))) }
+ (AnnList (Just $ glR $2) ListNone (fst $ unLoc $2)))) }
+ | '{' '}' { amsA' (sLL $1 $> ([], (AnnList Nothing (ListBraces (epTok $1) (epTok $2)) []))) }
| vocurly close { return $ noLocA ([], noAnn) }
alts(PATS) :: { forall b. DisambECP b => PV (Located ([EpToken ";"],[LMatch GhcPs (LocatedA b)])) }
@@ -4727,7 +4727,7 @@ commentsPA la@(L l a) = do
hsDoAnn :: EpToken "rec" -> (EpToken "{", [EpToken ";"], EpToken "}") -> LocatedAn t b -> (AnnList, EpToken "rec")
hsDoAnn rec (ob, semis, cb) (L ll _)
- = (AnnList (Just $ spanAsAnchor (locA ll)) (ListBraces ob cb) semis [], rec)
+ = (AnnList (Just $ spanAsAnchor (locA ll)) (ListBraces ob cb) semis, rec)
listAsAnchorM :: [LocatedAn t a] -> Maybe EpaLocation
listAsAnchorM [] = Nothing
=====================================
compiler/GHC/Parser/Annotation.hs
=====================================
@@ -43,7 +43,7 @@ module GHC.Parser.Annotation (
-- ** Trailing annotations in lists
TrailingAnn(..), ta_location,
- addTrailingAnnToA, addTrailingAnnToL, addTrailingCommaToN,
+ addTrailingAnnToA, addTrailingCommaToN,
addTrailingAnnToBF,
noTrailingN,
@@ -529,10 +529,7 @@ data AnnList
= AnnList {
al_anchor :: !(Maybe EpaLocation), -- ^ start point of a list having layout
al_brackets :: !AnnListBrackets,
- al_semis :: [EpToken ";"], -- decls
- al_trailing :: ![TrailingAnn] -- ^ items appearing after the
- -- list, such as '=>' for a
- -- context
+ al_semis :: [EpToken ";"] -- decls
} deriving (Data,Eq)
data AnnListBrackets
@@ -672,16 +669,6 @@ data AnnPragSCC
-- ---------------------------------------------------------------------
--- | Helper function used in the parser to add a 'TrailingAnn' items
--- to an existing annotation.
-addTrailingAnnToL :: TrailingAnn -> EpAnnComments
- -> EpAnn AnnList -> EpAnn AnnList
-addTrailingAnnToL t cs n = n { anns = addTrailing (anns n)
- , comments = comments n <> cs }
- where
- -- See Note [list append in addTrailing*]
- addTrailing n = n { al_trailing = al_trailing n ++ [t]}
-
addTrailingAnnToBF :: TrailingAnn -> EpAnnComments
-> EpAnn AnnBooleanFormula -> EpAnn AnnBooleanFormula
addTrailingAnnToBF t cs n = n { anns = addTrailing (anns n)
@@ -714,7 +701,7 @@ noTrailingN s = s { anns = (anns s) { nann_trailing = [] } }
{-
Note [list append in addTrailing*]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The addTrailingAnnToL, addTrailingAnnToA and addTrailingCommaToN
+The addTrailingAnnToA and addTrailingCommaToN
functions are used to add a separator for an item when it occurs in a
list. So they are used to capture a comma, vbar, semicolon and similar.
@@ -1047,7 +1034,7 @@ instance NoAnn AnnBooleanFormula where
noAnn = AnnBooleanFormula noAnn noAnn []
instance NoAnn AnnList where
- noAnn = AnnList Nothing ListNone noAnn []
+ noAnn = AnnList Nothing ListNone noAnn
instance NoAnn NameAnn where
noAnn = NameAnnTrailing []
@@ -1141,8 +1128,8 @@ instance Outputable NameAnn where
= text "NameAnnTrailing" <+> ppr t
instance Outputable AnnList where
- ppr (AnnList anc p s t)
- = text "AnnList" <+> ppr anc <+> ppr p <+> ppr s <+> ppr t
+ ppr (AnnList l p s)
+ = text "AnnList" <+> ppr l <+> ppr p <+> ppr s
instance Outputable AnnListBrackets where
ppr (ListParens o c) = text "ListParens" <+> ppr o <+> ppr c
=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -437,7 +437,7 @@ mkRoleAnnotDecl loc tycon roles anns
mkMDo :: (EpToken "{", [EpToken ";"], EpToken "}") -> HsDoFlavour -> LocatedA [ExprLStmt GhcPs] -> EpaLocation -> EpaLocation -> HsExpr GhcPs
mkMDo (ob, semis, cb) ctxt stmts tok loc
- = mkHsDoAnns ctxt stmts (AnnList (Just loc) (ListBraces ob cb) semis [], tok)
+ = mkHsDoAnns ctxt stmts (AnnList (Just loc) (ListBraces ob cb) semis, tok)
-- | Converts a list of 'LHsTyVarBndr's annotated with their 'Specificity' to
-- binders without annotations. Only accepts specified variables, and errors if
@@ -1963,7 +1963,7 @@ instance DisambECP (HsCmd GhcPs) where
return $ L (EpAnn (spanAsAnchor l) noAnn cs) (mkHsCmdIf c a b anns)
mkHsDoPV l (ob,semis,cb) Nothing stmts tok_loc anc = do
!cs <- getCommentsFor l
- return $ L (EpAnn (spanAsAnchor l) noAnn cs) (HsCmdDo (AnnList (Just anc) (ListBraces ob cb) semis [], tok_loc) stmts)
+ return $ L (EpAnn (spanAsAnchor l) noAnn cs) (HsCmdDo (AnnList (Just anc) (ListBraces ob cb) semis, tok_loc) stmts)
mkHsDoPV l _ (Just m) _ _ _ = addFatalError $ mkPlainErrorMsgEnvelope l $ PsErrQualifiedDoInCmd m
mkHsParPV l lpar c rpar = do
!cs <- getCommentsFor l
@@ -2062,7 +2062,7 @@ instance DisambECP (HsExpr GhcPs) where
return $ L (EpAnn (spanAsAnchor l) noAnn cs) (mkHsIf c a b anns)
mkHsDoPV l (ob,semis,cb) mod stmts loc_tok anc = do
!cs <- getCommentsFor l
- return $ L (EpAnn (spanAsAnchor l) noAnn cs) (HsDo (AnnList (Just anc) (ListBraces ob cb) semis [], loc_tok) (DoExpr mod) stmts)
+ return $ L (EpAnn (spanAsAnchor l) noAnn cs) (HsDo (AnnList (Just anc) (ListBraces ob cb) semis, loc_tok) (DoExpr mod) stmts)
mkHsParPV l lpar e rpar = do
!cs <- getCommentsFor l
return $ L (EpAnn (spanAsAnchor l) noAnn cs) (HsPar (lpar, rpar) e)
=====================================
compiler/GHC/Types/Demand.hs
=====================================
@@ -1849,12 +1849,14 @@ botDmdEnv = mkEmptyDmdEnv botDiv
exnDmdEnv :: DmdEnv
exnDmdEnv = mkEmptyDmdEnv exnDiv
+combineDmdEnv :: (Demand -> Demand -> Demand) -> DmdEnv -> DmdEnv -> DmdEnv
+combineDmdEnv f (DE fv1 d1) (DE fv2 d2)
+ -- See Note [Demand env Equality]
+ = DE (plusVarEnv_CD f fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2))
+ (lubDivergence d1 d2)
+
lubDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-lubDmdEnv (DE fv1 d1) (DE fv2 d2) = DE lub_fv lub_div
- where
- -- See Note [Demand env Equality]
- lub_fv = plusVarEnv_CD lubDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- lub_div = lubDivergence d1 d2
+lubDmdEnv = combineDmdEnv lubDmd
addVarDmdEnv :: DmdEnv -> Id -> Demand -> DmdEnv
addVarDmdEnv env@(DE fvs div) id dmd
@@ -1908,36 +1910,31 @@ instance Eq DmdType where
= ds1 == ds2 -- cheap checks first
&& env1 == env2
+-- | The smaller 'DmdType' is eta expanded using its 'defaultArgDmd'.
+-- See Note [Default demand on free variables and arguments].
+zipDmdType :: (Demand -> Demand -> Demand) -> DmdType -> DmdType -> DmdType
+zipDmdType f (DmdType fv1 ds1) (DmdType fv2 ds2)
+ = DmdType (combineDmdEnv f fv1 fv2) (go ds1 ds2)
+ where
+ def1, def2 :: Demand -- Default argument demands for eta expansion
+ def1 = defaultArgDmd (de_div fv1)
+ def2 = defaultArgDmd (de_div fv2)
+
+ -- If `ds1` is shorter than `ds2`, extend `ds1` with the appropriate
+ -- default demand `def1`; and similarly if `ds2` is shorter
+ go (d1:ds1') (d2:ds2') = f d1 d2 : go ds1' ds2'
+ go [] ds2' = map (def1 `f`) ds2'
+ go ds1' [] = map (`f` def2) ds1'
+
-- | Compute the least upper bound of two 'DmdType's elicited /by the same
-- incoming demand/!
lubDmdType :: DmdType -> DmdType -> DmdType
-lubDmdType d1 d2 = DmdType lub_fv lub_ds
- where
- n = max (dmdTypeDepth d1) (dmdTypeDepth d2)
- (DmdType fv1 ds1) = etaExpandDmdType n d1
- (DmdType fv2 ds2) = etaExpandDmdType n d2
- lub_ds = zipWithEqual lubDmd ds1 ds2
- lub_fv = lubDmdEnv fv1 fv2
+lubDmdType = zipDmdType lubDmd
-- | Combine two 'DmdType's for stable unfolding analysis.
-- See Note [Combining demands for stable unfoldings].
maxDmdType :: DmdType -> DmdType -> DmdType
-maxDmdType (DmdType fv1 ds1) (DmdType fv2 ds2)
- = DmdType combined_fv combined_ds
- where
- combined_fv = maxDmdEnv fv1 fv2
- combined_ds = go ds1 ds2
- -- If lists have different lengths, keep remaining ds1 (from RHS)
- go rhs [] = rhs
- go [] _ = []
- go (r:rhs) (u:unfs) = maxDmd r u : go rhs unfs
-
--- | See Note [Combining demands for stable unfoldings].
-maxDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-maxDmdEnv (DE fv1 d1) (DE fv2 d2) = DE combined_fv combined_div
- where
- combined_fv = plusVarEnv_CD maxDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- combined_div = lubDivergence d1 d2
+maxDmdType = zipDmdType maxDmd
discardArgDmds :: DmdType -> DmdEnv
discardArgDmds (DmdType fv _) = fv
=====================================
docs/users_guide/exts/qualified_strings.rst
=====================================
@@ -15,3 +15,10 @@ The ``-XQualifiedStrings`` extension allows string literals to be qualified usin
This allows more precise control over the ``fromString`` function than :extension:`OverloadedStrings`, which overloads all string literals for an entire module.
If :extension:`MultilineStrings` is also enabled, ``M."""test"""`` will also work, desugaring the multiline string first, then the qualified string literal.
+
+Laws
+~~~~
+
+If the following expression typechecks, it should hold:
+
+* ``Data.String.fromString "str" == M."str"``
=====================================
hadrian/hie-bios.bat
=====================================
=====================================
testsuite/tests/count-deps/CountDepsParser.stdout
=====================================
@@ -86,6 +86,7 @@ GHC.Data.Pair
GHC.Data.Strict
GHC.Data.StringBuffer
GHC.Data.TrieMap
+GHC.Data.Unboxed
GHC.Data.Word64Map
GHC.Data.Word64Map.Internal
GHC.Data.Word64Map.Lazy
=====================================
testsuite/tests/dmdanal/should_run/M2.hs
=====================================
@@ -0,0 +1,28 @@
+module M2 where
+
+-- f's stable unfolding template is the PAP `mk g`, which is not
+-- eta-expanded, so its DmdType has depth 0. The big lambda keeps the
+-- template large enough that f does not certainlyWillInline, so it is
+-- worker/wrappered.
+{-# INLINABLE [1] f #-}
+f :: Int -> Int -> Float
+f = mk (\y -> succ . succ . succ . succ . succ . succ . succ . succ . succ $ y)
+
+-- Arity 1: the `let` does real work, so mk is not eta-expanded past it.
+-- The returned function really forces x.
+{-# NOINLINE mk #-}
+mk :: (Int -> Int) -> Int -> Int -> Float
+mk t = let s = t 1 in \dummy x -> x `seq` fromIntegral (t (dummy + s))
+
+{-# INLINE mkFast #-}
+mkFast :: (Int -> Int) -> Int -> Int -> Float
+mkFast t = \dummy x -> fromIntegral (t dummy)
+
+-- Active only before phase 1: rewrites f's RHS early, but is dead by
+-- the time f's stable unfolding (activation [1]) is inlined at call
+-- sites.
+{-# RULES "mk" [~1] forall t. mk t = mkFast t #-}
+
+{-# NOINLINE g #-}
+g :: Int -> Int
+g x = x + 1
=====================================
testsuite/tests/dmdanal/should_run/T27626.hs
=====================================
@@ -0,0 +1,6 @@
+-- The raised threshold persuades GHC to inline the worker's stable
+-- unfolding in this small program; a larger program does that naturally.
+{-# OPTIONS_GHC -funfolding-use-threshold=400 #-}
+module Main where
+import M2 ( f )
+main = print (f 19 12)
=====================================
testsuite/tests/dmdanal/should_run/T27626.stdout
=====================================
@@ -0,0 +1 @@
+38.0
=====================================
testsuite/tests/dmdanal/should_run/all.T
=====================================
@@ -36,3 +36,4 @@ test('T23208', exit_code(1), multimod_compile_and_run, ['T23208_Lib', 'T23208'])
test('T25439', normal, compile_and_run, [''])
test('T26748', normal, compile_and_run, [''])
test('T26416', [extra_files(['M1.hs'])], multimod_compile_and_run, ['T26416','M1.hs'])
+test('T27626', [extra_files(['M2.hs'])], multimod_compile_and_run, ['T27626','M2.hs'])
=====================================
testsuite/tests/ghc-api/exactprint/T22919.stderr
=====================================
@@ -71,7 +71,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/ghc-api/exactprint/ZeroWidthSemi.stderr
=====================================
@@ -83,7 +83,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/module/mod185.stderr
=====================================
@@ -95,7 +95,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/DumpParsedAst.stderr
=====================================
@@ -2124,7 +2124,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
=====================================
@@ -91,7 +91,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -214,7 +213,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -294,7 +292,6 @@
(ListBraces
(NoEpTok)
(NoEpTok))
- []
[])
(EpaSpan { DumpParsedAstComments.hs:14:7-8 }))
(DoExpr
@@ -364,7 +361,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
=====================================
@@ -32,7 +32,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/DumpSemis.stderr
=====================================
@@ -259,7 +259,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -333,7 +332,6 @@
(ListBraces
(NoEpTok)
(NoEpTok))
- []
[])
(EpaSpan { DumpSemis.hs:10:7-8 }))
(DoExpr
@@ -373,8 +371,7 @@
,(EpTok
(EpaSpan { DumpSemis.hs:11:10 }))
,(EpTok
- (EpaSpan { DumpSemis.hs:11:11 }))]
- [])
+ (EpaSpan { DumpSemis.hs:11:11 }))])
(EpaSpan { DumpSemis.hs:11:3-4 }))
(DoExpr
(Nothing))
@@ -554,7 +551,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -631,8 +627,7 @@
[(EpTok
(EpaSpan { DumpSemis.hs:16:5 }))
,(EpTok
- (EpaSpan { DumpSemis.hs:16:8 }))]
- [])
+ (EpaSpan { DumpSemis.hs:16:8 }))])
(EpaSpan { DumpSemis.hs:15:7-8 }))
(DoExpr
(Nothing))
@@ -806,7 +801,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -883,8 +877,7 @@
[(EpTok
(EpaSpan { DumpSemis.hs:22:12 }))
,(EpTok
- (EpaSpan { DumpSemis.hs:22:13 }))]
- [])
+ (EpaSpan { DumpSemis.hs:22:13 }))])
(EpaSpan { DumpSemis.hs:22:7-8 }))
(DoExpr
(Nothing))
@@ -1015,7 +1008,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1120,7 +1112,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1227,7 +1218,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1718,7 +1708,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1844,7 +1833,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1935,8 +1923,7 @@
,(EpTok
(EpaSpan { DumpSemis.hs:34:17 }))
,(EpTok
- (EpaSpan { DumpSemis.hs:34:18 }))]
- [])
+ (EpaSpan { DumpSemis.hs:34:18 }))])
(EpaComments
[]))
(NoEpTok))
@@ -1971,7 +1958,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -2084,7 +2070,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -2209,7 +2194,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -2328,8 +2312,7 @@
,(EpTok
(EpaSpan { DumpSemis.hs:38:7 }))
,(EpTok
- (EpaSpan { DumpSemis.hs:38:8 }))]
- []))
+ (EpaSpan { DumpSemis.hs:38:8 }))]))
(L
(EpAnn
(EpaSpan { DumpSemis.hs:(38,4)-(44,4) })
=====================================
testsuite/tests/parser/should_compile/KindSigs.stderr
=====================================
@@ -964,7 +964,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1664,7 +1663,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/T15279.stderr
=====================================
@@ -154,7 +154,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/T20718.stderr
=====================================
@@ -105,7 +105,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/parser/should_compile/T20846.stderr
=====================================
@@ -95,7 +95,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
testsuite/tests/printer/Test20297.stdout
=====================================
@@ -71,7 +71,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -163,7 +162,6 @@
(Just
(EpaSpan { Test20297.hs:7:3-7 }))
(ListNone)
- []
[])
(EpaComments
[]))
@@ -203,7 +201,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -289,7 +286,6 @@
(Just
(EpaSpan { Test20297.hs:11:9-26 }))
(ListNone)
- []
[])
(EpaComments
[(L
@@ -327,7 +323,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -401,7 +396,6 @@
(ListBraces
(NoEpTok)
(NoEpTok))
- []
[])
(EpaSpan { Test20297.hs:11:19-20 }))
(DoExpr
@@ -517,7 +511,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -603,7 +596,6 @@
(Just
(EpaSpan { Test20297.ppr.hs:5:3-7 }))
(ListNone)
- []
[])
(EpaComments
[]))
@@ -637,7 +629,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -723,7 +714,6 @@
(Just
(EpaSpan { Test20297.ppr.hs:9:7-24 }))
(ListNone)
- []
[])
(EpaComments
[]))
@@ -755,7 +745,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -829,7 +818,6 @@
(ListBraces
(NoEpTok)
(NoEpTok))
- []
[])
(EpaSpan { Test20297.ppr.hs:9:17-18 }))
(DoExpr
=====================================
testsuite/tests/printer/Test24533.stdout
=====================================
@@ -521,7 +521,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
@@ -1093,7 +1092,6 @@
(AnnList
(Nothing)
(ListNone)
- []
[]))
(L
(EpAnn
=====================================
utils/check-exact/ExactPrint.hs
=====================================
@@ -281,8 +281,8 @@ instance HasTrailing EpAnnSumPat where
setTrailing a _ = a
instance HasTrailing AnnList where
- trailing a = al_trailing a
- setTrailing a ts = a { al_trailing = ts }
+ trailing _ = []
+ setTrailing a _ = a
instance HasTrailing [TrailingAnn] where
trailing a = a
=====================================
utils/check-exact/Main.hs
=====================================
@@ -524,8 +524,8 @@ changeLocalDecls libdir (L l p) = do
let oldDecls' = captureLineSpacing oldDecls
let (VbSig o:oldBinds) = map wrapValBind oldDecls'
o' = setEntryDP o (DifferentLine 2 0)
- let (EpAnn anc (AnnList (Just _) a b dd) cs) = van
- let van' = (EpAnn anc (AnnList (Just (EpaDelta noSrcSpan (DifferentLine 1 4) [])) a b dd) cs)
+ let (EpAnn anc (AnnList (Just _) a b) cs) = van
+ let van' = (EpAnn anc (AnnList (Just (EpaDelta noSrcSpan (DifferentLine 1 4) [])) a b) cs)
let binds' = (HsValBinds (van',w)
(ValBinds noExtField (VbSig sig':VbBind decl':VbSig o':oldBinds)))
return (L lm (Match an mln pats (GRHSs emptyComments rhs binds')))
@@ -552,7 +552,6 @@ changeLocalDecls2 libdir (L l p) = do
let anc2 = (EpaDelta noSrcSpan (DifferentLine 1 4) [])
let an = (EpAnn anc
(AnnList (Just anc2) ListNone
- []
[])
emptyComments, EpTok (EpaDelta noSrcSpan (SameLine 0) []))
let decls = [VbSig sig', VbBind decl']
=====================================
utils/check-exact/Transform.hs
=====================================
@@ -1096,7 +1096,7 @@ oldWhereAnnotation (EpAnn anc an cs, _w) ww _oldSpan = an'
-- TODO: when we set DP (0,0) for the HsValBinds EpEpaLocation,
-- change the AnnList anchor to have the correct DP too
where
- (AnnList ancl p s t) = an
+ (AnnList ancl p s) = an
w = case ww of
WithWhere -> EpTok (EpaDelta noSrcSpan (SameLine 0) [])
WithoutWhere -> NoEpTok
@@ -1105,7 +1105,7 @@ oldWhereAnnotation (EpAnn anc an cs, _w) ww _oldSpan = an'
WithWhere -> (anc, ancl)
WithoutWhere -> (anc, ancl)
an' = (EpAnn anc'
- (AnnList ancl' p s t)
+ (AnnList ancl' p s)
cs, w)
newWhereAnnotation :: WithWhere -> (EpAnn AnnList, EpToken "where")
@@ -1117,7 +1117,7 @@ newWhereAnnotation ww = (an, w)
WithWhere -> EpTok (EpaDelta noSrcSpan (SameLine 0) [])
WithoutWhere -> NoEpTok
an = EpAnn anc
- (AnnList (Just anc2) ListNone [] [])
+ (AnnList (Just anc2) ListNone [])
emptyComments
-- ---------------------------------------------------------------------
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/58e926238f79e66e48ffbde83bd347…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/58e926238f79e66e48ffbde83bd347…
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] hadrian: set the executable bit for hie-bios.bat
by Marge Bot (@marge-bot) 15 Aug '26
by Marge Bot (@marge-bot) 15 Aug '26
15 Aug '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
ca9b0b22 by mangoiv at 2026-08-15T12:41:47-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
1 changed file:
- hadrian/hie-bios.bat
Changes:
=====================================
hadrian/hie-bios.bat
=====================================
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ca9b0b22afa2e4b73e3c755823e2601…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ca9b0b22afa2e4b73e3c755823e2601…
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
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
e3188581 by Zubin Duggal at 2026-08-15T12:41:06-04:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
- - - - -
6 changed files:
- + changelog.d/27626
- compiler/GHC/Types/Demand.hs
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
Changes:
=====================================
changelog.d/27626
=====================================
@@ -0,0 +1,5 @@
+section: compiler
+synopsis: Fix a bug where an argument used only by a function's stable unfolding
+ could still be marked absent, resulting in a runtime crash.
+mrs: !16503
+issues: #27626
=====================================
compiler/GHC/Types/Demand.hs
=====================================
@@ -1849,12 +1849,14 @@ botDmdEnv = mkEmptyDmdEnv botDiv
exnDmdEnv :: DmdEnv
exnDmdEnv = mkEmptyDmdEnv exnDiv
+combineDmdEnv :: (Demand -> Demand -> Demand) -> DmdEnv -> DmdEnv -> DmdEnv
+combineDmdEnv f (DE fv1 d1) (DE fv2 d2)
+ -- See Note [Demand env Equality]
+ = DE (plusVarEnv_CD f fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2))
+ (lubDivergence d1 d2)
+
lubDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-lubDmdEnv (DE fv1 d1) (DE fv2 d2) = DE lub_fv lub_div
- where
- -- See Note [Demand env Equality]
- lub_fv = plusVarEnv_CD lubDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- lub_div = lubDivergence d1 d2
+lubDmdEnv = combineDmdEnv lubDmd
addVarDmdEnv :: DmdEnv -> Id -> Demand -> DmdEnv
addVarDmdEnv env@(DE fvs div) id dmd
@@ -1908,36 +1910,31 @@ instance Eq DmdType where
= ds1 == ds2 -- cheap checks first
&& env1 == env2
+-- | The smaller 'DmdType' is eta expanded using its 'defaultArgDmd'.
+-- See Note [Default demand on free variables and arguments].
+zipDmdType :: (Demand -> Demand -> Demand) -> DmdType -> DmdType -> DmdType
+zipDmdType f (DmdType fv1 ds1) (DmdType fv2 ds2)
+ = DmdType (combineDmdEnv f fv1 fv2) (go ds1 ds2)
+ where
+ def1, def2 :: Demand -- Default argument demands for eta expansion
+ def1 = defaultArgDmd (de_div fv1)
+ def2 = defaultArgDmd (de_div fv2)
+
+ -- If `ds1` is shorter than `ds2`, extend `ds1` with the appropriate
+ -- default demand `def1`; and similarly if `ds2` is shorter
+ go (d1:ds1') (d2:ds2') = f d1 d2 : go ds1' ds2'
+ go [] ds2' = map (def1 `f`) ds2'
+ go ds1' [] = map (`f` def2) ds1'
+
-- | Compute the least upper bound of two 'DmdType's elicited /by the same
-- incoming demand/!
lubDmdType :: DmdType -> DmdType -> DmdType
-lubDmdType d1 d2 = DmdType lub_fv lub_ds
- where
- n = max (dmdTypeDepth d1) (dmdTypeDepth d2)
- (DmdType fv1 ds1) = etaExpandDmdType n d1
- (DmdType fv2 ds2) = etaExpandDmdType n d2
- lub_ds = zipWithEqual lubDmd ds1 ds2
- lub_fv = lubDmdEnv fv1 fv2
+lubDmdType = zipDmdType lubDmd
-- | Combine two 'DmdType's for stable unfolding analysis.
-- See Note [Combining demands for stable unfoldings].
maxDmdType :: DmdType -> DmdType -> DmdType
-maxDmdType (DmdType fv1 ds1) (DmdType fv2 ds2)
- = DmdType combined_fv combined_ds
- where
- combined_fv = maxDmdEnv fv1 fv2
- combined_ds = go ds1 ds2
- -- If lists have different lengths, keep remaining ds1 (from RHS)
- go rhs [] = rhs
- go [] _ = []
- go (r:rhs) (u:unfs) = maxDmd r u : go rhs unfs
-
--- | See Note [Combining demands for stable unfoldings].
-maxDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-maxDmdEnv (DE fv1 d1) (DE fv2 d2) = DE combined_fv combined_div
- where
- combined_fv = plusVarEnv_CD maxDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- combined_div = lubDivergence d1 d2
+maxDmdType = zipDmdType maxDmd
discardArgDmds :: DmdType -> DmdEnv
discardArgDmds (DmdType fv _) = fv
=====================================
testsuite/tests/dmdanal/should_run/M2.hs
=====================================
@@ -0,0 +1,28 @@
+module M2 where
+
+-- f's stable unfolding template is the PAP `mk g`, which is not
+-- eta-expanded, so its DmdType has depth 0. The big lambda keeps the
+-- template large enough that f does not certainlyWillInline, so it is
+-- worker/wrappered.
+{-# INLINABLE [1] f #-}
+f :: Int -> Int -> Float
+f = mk (\y -> succ . succ . succ . succ . succ . succ . succ . succ . succ $ y)
+
+-- Arity 1: the `let` does real work, so mk is not eta-expanded past it.
+-- The returned function really forces x.
+{-# NOINLINE mk #-}
+mk :: (Int -> Int) -> Int -> Int -> Float
+mk t = let s = t 1 in \dummy x -> x `seq` fromIntegral (t (dummy + s))
+
+{-# INLINE mkFast #-}
+mkFast :: (Int -> Int) -> Int -> Int -> Float
+mkFast t = \dummy x -> fromIntegral (t dummy)
+
+-- Active only before phase 1: rewrites f's RHS early, but is dead by
+-- the time f's stable unfolding (activation [1]) is inlined at call
+-- sites.
+{-# RULES "mk" [~1] forall t. mk t = mkFast t #-}
+
+{-# NOINLINE g #-}
+g :: Int -> Int
+g x = x + 1
=====================================
testsuite/tests/dmdanal/should_run/T27626.hs
=====================================
@@ -0,0 +1,6 @@
+-- The raised threshold persuades GHC to inline the worker's stable
+-- unfolding in this small program; a larger program does that naturally.
+{-# OPTIONS_GHC -funfolding-use-threshold=400 #-}
+module Main where
+import M2 ( f )
+main = print (f 19 12)
=====================================
testsuite/tests/dmdanal/should_run/T27626.stdout
=====================================
@@ -0,0 +1 @@
+38.0
=====================================
testsuite/tests/dmdanal/should_run/all.T
=====================================
@@ -36,3 +36,4 @@ test('T23208', exit_code(1), multimod_compile_and_run, ['T23208_Lib', 'T23208'])
test('T25439', normal, compile_and_run, [''])
test('T26748', normal, compile_and_run, [''])
test('T26416', [extra_files(['M1.hs'])], multimod_compile_and_run, ['T26416','M1.hs'])
+test('T27626', [extra_files(['M2.hs'])], multimod_compile_and_run, ['T27626','M2.hs'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e31885816b7c597ad89b05da64b21c5…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/e31885816b7c597ad89b05da64b21c5…
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
15 Aug '26
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
a737df91 by Brandon Chinn at 2026-08-15T12:40:25-04:00
Add law in qualified strings doc
- - - - -
1 changed file:
- docs/users_guide/exts/qualified_strings.rst
Changes:
=====================================
docs/users_guide/exts/qualified_strings.rst
=====================================
@@ -15,3 +15,10 @@ The ``-XQualifiedStrings`` extension allows string literals to be qualified usin
This allows more precise control over the ``fromString`` function than :extension:`OverloadedStrings`, which overloads all string literals for an entire module.
If :extension:`MultilineStrings` is also enabled, ``M."""test"""`` will also work, desugaring the multiline string first, then the qualified string literal.
+
+Laws
+~~~~
+
+If the following expression typechecks, it should hold:
+
+* ``Data.String.fromString "str" == M."str"``
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a737df9167b5869b8943063b9a06e95…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/a737df9167b5869b8943063b9a06e95…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27602] 3 commits: testsuite: Show baseline sample count and spread in perf failures
by Simon Jakobi (@sjakobi) 15 Aug '26
by Simon Jakobi (@sjakobi) 15 Aug '26
15 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27602 at Glasgow Haskell Compiler / GHC
Commits:
41bfc813 by Simon Jakobi at 2026-08-15T17:37:37+02:00
testsuite: Show baseline sample count and spread in perf failures
A perf baseline is the mean of all samples recorded for a commit, so a
single outlier can silently corrupt it. Previously, the failure output
gave no hint about such outliers: the baseline printed as one number.
In #27602, T27336's peak_megabytes_allocated baseline showed as 757
when the underlying samples were 605 and 909.
When the baseline is averaged from more than one sample, say so in the
failure message and list the samples, both in the one-line stat-failure
reason and in the detail block. Single-sample baselines print exactly
as before.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
a8a5f938 by Simon Jakobi at 2026-08-15T17:37:37+02:00
ci: Clarify comment on pushing perf notes after failures
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
a9c078d6 by Simon Jakobi at 2026-08-15T17:37:37+02:00
DEMO: Tighten T3064 residency tolerance (do not merge)
The previous demo attempts (T4830, T9675) failed for two reasons:
MR pipelines pin the perf baseline to the merge-base with master
(CI_MERGE_REQUEST_DIFF_BASE_SHA), and that merge-base had only one
recorded sample per test env — multi-sample notes accumulate only on
master commits that were the tip when a nightly pipeline ran, since
nightly jobs share TEST_ENV with their regular counterparts.
This branch is now rebased onto b757727a786, the newest master commit
with nightly samples. Its T3064 max_bytes_used baseline on
aarch64-linux-deb13-validate is bimodal (14626304 vs 13329528, mean
\~13.98M), so a 1% window cannot contain either mode and the failure
shows a baseline averaged from genuinely disagreeing samples.
aarch64-darwin-validate and x86_64-linux-fedora43-release have two
near-identical samples each, demoing the agreeing-samples output if
they fail too.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
3 changed files:
- .gitlab/ci.sh
- testsuite/driver/perf_notes.py
- testsuite/tests/perf/compiler/all.T
Changes:
=====================================
.gitlab/ci.sh
=====================================
@@ -1120,9 +1120,10 @@ case ${1:-help} in
setup) setup && cleanup_submodules ;;
configure) time_it "configure" configure ;;
build_hadrian) time_it "build" build_hadrian ;;
- # N.B. Always push notes, even if the build fails. This is okay to do as the
- # testsuite driver doesn't record notes for tests that fail due to
- # correctness.
+ # N.B. Always push notes, even if the build fails. Metrics from runs failing
+ # a perf stat check are deliberately recorded too — discarding them would
+ # bias the baseline towards whichever sample came first. Only correctness
+ # failures record nothing.
test_hadrian)
fetch_perf_notes
res=0
=====================================
testsuite/driver/perf_notes.py
=====================================
@@ -84,8 +84,11 @@ PerfStat = NamedTuple('PerfStat', [('test_env', TestEnv),
('value', float)])
# A baseline recovered form stored metrics.
-Baseline = NamedTuple('Baseline', [('perfStat', PerfStat),
- ('commit', GitHash)])
+class Baseline(NamedTuple):
+ perfStat: PerfStat
+ commit: GitHash
+ # The raw samples the baseline value was averaged over.
+ samples: List[float] = []
# The type of exceptions which are thrown when computing the current stat value
# fails.
@@ -465,6 +468,10 @@ def get_allowed_changes(baseline_ref: Optional[GitRef]) -> Dict[TestName, List[A
# (bool , str ) -> (str , str , str , str) -> float
_commit_metric_cache = {} # type: ignore
+# Like _commit_metric_cache, but mapping to the list of raw sample values the
+# baseline was averaged over. Filled by get_commit_metric.
+_commit_samples_cache = {} # type: ignore
+
# Get the baseline of a test at a given commit. This is the expected value
# *before* the commit is applied (i.e. on the parent commit).
# This searches git notes from older commits for recorded metrics (locally and
@@ -506,7 +513,8 @@ def baseline_metric(commit: GitHash,
if baseline_commit is not None:
current_metric = get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric, baseline_commit)
+ return Baseline(current_metric, baseline_commit,
+ get_commit_samples(namespace, baseline_commit, test_env, name, metric, way))
else:
return None
@@ -515,7 +523,8 @@ def baseline_metric(commit: GitHash,
# Check for a metric on this commit.
current_metric = get_commit_metric(namespace, current_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric, current_commit)
+ return Baseline(current_metric, current_commit,
+ get_commit_samples(namespace, current_commit, test_env, name, metric, way))
# Stop if there is an expected change at this commit. In that case
# metrics on ancestor commits will not be a valid baseline.
@@ -598,8 +607,28 @@ def get_commit_metric(gitNoteRef,
# Save baselines to the cache.
_commit_metric_cache[cacheKeyA] = baseline_by_cache_key_b
+ _commit_samples_cache[cacheKeyA] = values_by_cache_key_b
return baseline_by_cache_key_b.get(cacheKeyB)
+# Get the raw sample values that get_commit_metric averages over. Uses the
+# cache filled by get_commit_metric, so no extra git calls after it has run.
+def get_commit_samples(gitNoteRef,
+ ref: Union[GitRef, GitHash],
+ test_env: TestEnv,
+ name: TestName,
+ metric: MetricName,
+ way: WayName
+ ) -> List[float]:
+ get_commit_metric(gitNoteRef, ref, test_env, name, metric, way)
+ cacheKeyA = (gitNoteRef, commit_hash(ref))
+ cacheKeyB = (test_env, name, metric, way)
+ return _commit_samples_cache.get(cacheKeyA, {}).get(cacheKeyB, [])
+
+# Metric samples are integral in practice; '%g' would render large byte
+# counts in truncated scientific notation.
+def format_samples(samples: List[float]) -> str:
+ return ', '.join(str(int(s)) if s == int(s) else str(s) for s in samples)
+
def check_stats_change(actual: PerfStat,
baseline: Baseline,
acceptance_window: MetricAcceptanceWindow,
@@ -654,9 +683,17 @@ def check_stats_change(actual: PerfStat,
' baseline @ %s' % baseline.commit
print(actual.metric, error + ':')
dev = 100.0 if expected_val == 0 else round(((float(actual.value) * 100) / int(expected_val)) - 100, 1)
+ # A multi-sample baseline is a mean; show the samples so outliers
+ # corrupting the baseline are visible (#27602).
+ if len(baseline.samples) > 1:
+ samples_note = ('; baseline is mean of %d samples: %s'
+ % (len(baseline.samples),
+ format_samples(baseline.samples)))
+ else:
+ samples_note = ''
change_line = (f'{actual.metric} {change.value} from {baseline.perfStat.test_env} '
f'baseline @ {baseline.commit[:7]}: {expected_val} -> {actual.value} '
- f'({dev:+g}%, allowed {acceptance_window.describe()})')
+ f'({dev:+g}%, allowed {acceptance_window.describe()}{samples_note})')
result = failBecause('stat ' + change_line, tag='stat')
if not change_allowed or force_print:
@@ -666,6 +703,10 @@ def check_stats_change(actual: PerfStat,
print(descr, str(val).rjust(length), extra)
display(' Expected ' + full_name + ' ' + actual.metric + ':', expected_val, acceptance_window.describe())
+ if len(baseline.samples) > 1:
+ display(' Samples ' + full_name + ' ' + actual.metric + ':',
+ len(baseline.samples),
+ '(' + format_samples(baseline.samples) + ')')
display(' Lower bound ' + full_name + ' ' + actual.metric + ':', lowerBound, '')
display(' Upper bound ' + full_name + ' ' + actual.metric + ':', upperBound, '')
display(' Actual ' + full_name + ' ' + actual.metric + ':', actual.value, '')
=====================================
testsuite/tests/perf/compiler/all.T
=====================================
@@ -48,7 +48,9 @@ test('T4801',
[''])
test('T3064',
- [collect_compiler_residency(20),
+ [# Demo-only: tightened from 20 so the bimodal max_bytes_used
+ # baseline on aarch64-linux-deb13-validate fails.
+ collect_compiler_residency(1),
collect_compiler_runtime(2),
only_ways(['normal']),
],
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2965e0a244131ceafd4982bd028581…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/2965e0a244131ceafd4982bd028581…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27602] 9 commits: testsuite: Expect length001 failure in nonmoving_thr_sanity
by Simon Jakobi (@sjakobi) 15 Aug '26
by Simon Jakobi (@sjakobi) 15 Aug '26
15 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27602 at Glasgow Haskell Compiler / GHC
Commits:
db959f83 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Expect length001 failure in nonmoving_thr_sanity
length001 relies on an optimization rule to avoid excessive stack use.
The nonmoving_thr_sanity way does not enable optimization, so classify
its stack overflow as an expected failure, as is already done for the
other unoptimized nonmoving ways.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
4f2b7d90 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Omit T22859 in nonmoving threaded ways
T22859 checks allocation-limit handlers with output that depends on
precise allocation behaviour. The nonmoving threaded ways change where
these limits are reached, just as the already-omitted LLVM ways do.
Omit these ways instead of treating their incidental output differences
as test failures.
Assisted-by: gpt-5.6-sol via Codex CLI
- - - - -
c4abddcb by Simon Jakobi at 2026-08-14T15:16:44-04:00
rts/js: Implement eq_thread, and test Eq/Ord ThreadId (#16761)
Since d1f3c63701, Eq ThreadId is implemented via the RTS function
eq_thread, but the JS RTS never provided it, so comparing ThreadIds
for equality on the JS backend crashed with
ReferenceError: h$eq_thread is not defined
Like the C implementation, h$eq_thread uses pointer equality: The JS
RTS has exactly one thread object per thread.
Since previously no test exercised eq_thread directly, this commit
adds a test covering equality, its stability across GC, and agreement
with Ord.
Assisted-by: Claude Fable 5
- - - - -
4a7defa1 by Simon Jakobi at 2026-08-14T15:16:44-04:00
testsuite: Make listThreads1 insensitive to the RTS's own threads
listThreads1 expected `listThreads` to return exactly [ThreadId 1]. That
holds only under a non-threaded RTS. Under a threaded RTS however there
are more threads present, so we change the test to simply check that
`myThreadId` is present in the list.
Assisted-by: Claude Opus 5
- - - - -
b757727a by Vladislav Zavialov at 2026-08-14T15:17:27-04:00
Fix tcLookupId panic with RequiredTypeArguments and PatternSynonyms (#27586)
The arguments declared on the left-hand side of a pattern synonym are looked up
as term variables bound by its right-hand side. Prior to this patch, that lookup
panicked with RequiredTypeArguments:
data T a where
MkT :: forall a -> T a
pattern P :: Int -> T Int
pattern P x = MkT x
On the RHS, `x` looks like a term argument, so the renamer binds it in the term
namespace. Only during type checking does it turn out to be a type variable, so
the lookup on the LHS finds an ATyVar rather than an ATcId. As the lookup was
done with tcLookupId, it resulted in a panic.
Now the arguments are looked up with tcLookupPatSynArg, which reports an illegal
term-level use of `x`, just as an ordinary function definition `f (MkT x) = x`
does.
Test cases: T27586a T27586b T27586c
Assisted-by: Claude Opus 5
- - - - -
30e97b01 by Simon Jakobi at 2026-08-15T17:08:49+02:00
testsuite: Show baseline sample count and spread in perf failures
A perf baseline is the mean of all samples recorded for a commit, so a
single outlier can silently corrupt it. Previously, the failure output
gave no hint about such outliers: the baseline printed as one number.
In #27602, T27336's peak_megabytes_allocated baseline showed as 757
when the underlying samples were 605 and 909.
When the baseline is averaged from more than one sample, say so in the
failure message and list the samples, both in the one-line stat-failure
reason and in the detail block. Single-sample baselines print exactly
as before.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
dab95c58 by Simon Jakobi at 2026-08-15T17:08:49+02:00
ci: Clarify comment on pushing perf notes after failures
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
748b9ae2 by Simon Jakobi at 2026-08-15T17:09:27+02:00
testsuite: Don't format baseline samples with '%g'
'%g' renders large byte counts (e.g. max_bytes_used samples) in
scientific notation truncated to 6 significant digits, hiding the
exact values the sample listing exists to expose. Print integral
samples as plain integers instead.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
2965e0a2 by Simon Jakobi at 2026-08-15T17:09:48+02:00
DEMO: Tighten T3064 residency tolerance (do not merge)
The previous demo attempts (T4830, T9675) failed for two reasons:
MR pipelines pin the perf baseline to the merge-base with master
(CI_MERGE_REQUEST_DIFF_BASE_SHA), and that merge-base had only one
recorded sample per test env — multi-sample notes accumulate only on
master commits that were the tip when a nightly pipeline ran, since
nightly jobs share TEST_ENV with their regular counterparts.
This branch is now rebased onto b757727a786, the newest master commit
with nightly samples. Its T3064 max_bytes_used baseline on
aarch64-linux-deb13-validate is bimodal (14626304 vs 13329528, mean
\~13.98M), so a 1% window cannot contain either mode and the failure
shows a baseline averaged from genuinely disagreeing samples.
aarch64-darwin-validate and x86_64-linux-fedora43-release have two
near-identical samples each, demoing the agreeing-samples output if
they fail too.
Context: #27602
Assisted-by: Claude Fable 5
- - - - -
20 changed files:
- .gitlab/ci.sh
- + changelog.d/T27586
- compiler/GHC/Tc/TyCl/PatSyn.hs
- libraries/base/tests/all.T
- libraries/base/tests/listThreads1.hs
- libraries/base/tests/listThreads1.stdout
- rts/js/thread.js
- testsuite/driver/perf_notes.py
- + testsuite/tests/concurrent/should_run/T16761.hs
- + testsuite/tests/concurrent/should_run/T16761.stdout
- testsuite/tests/concurrent/should_run/all.T
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/rts/all.T
- + testsuite/tests/vdq-rta/should_fail/T27586a.hs
- + testsuite/tests/vdq-rta/should_fail/T27586a.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586b.hs
- + testsuite/tests/vdq-rta/should_fail/T27586b.stderr
- + testsuite/tests/vdq-rta/should_fail/T27586c.hs
- + testsuite/tests/vdq-rta/should_fail/T27586c.stderr
- testsuite/tests/vdq-rta/should_fail/all.T
Changes:
=====================================
.gitlab/ci.sh
=====================================
@@ -1120,9 +1120,10 @@ case ${1:-help} in
setup) setup && cleanup_submodules ;;
configure) time_it "configure" configure ;;
build_hadrian) time_it "build" build_hadrian ;;
- # N.B. Always push notes, even if the build fails. This is okay to do as the
- # testsuite driver doesn't record notes for tests that fail due to
- # correctness.
+ # N.B. Always push notes, even if the build fails. Metrics from runs failing
+ # a perf stat check are deliberately recorded too — discarding them would
+ # bias the baseline towards whichever sample came first. Only correctness
+ # failures record nothing.
test_hadrian)
fetch_perf_notes
res=0
=====================================
changelog.d/T27586
=====================================
@@ -0,0 +1,9 @@
+section: compiler
+issues: #27586
+mrs: !16440
+synopsis:
+ Fix a panic on a required type argument in a pattern synonym RHS
+description:
+ An argument of a pattern synonym that is matched against a required type
+ argument in the right-hand side no longer causes a panic; it is reported as
+ an illegal term-level use of a type variable.
=====================================
compiler/GHC/Tc/TyCl/PatSyn.hs
=====================================
@@ -137,7 +137,7 @@ tcInferPatSynDecl (PSB { psb_id = lname@(L _ name), psb_args = details
; (tclvl, wanted, ((lpat', args), pat_ty))
<- pushLevelAndCaptureConstraints $
tcInferPat FRRPatSynArg PatSynCtx lpat $
- mapM tcLookupId arg_names
+ mapM tcLookupPatSynArg arg_names
; let (ex_tvs, prov_dicts) = tcCollectEx lpat'
@@ -472,7 +472,7 @@ tcCheckPatSynDecl psb@PSB{ psb_id = lname@(L _ name), psb_args = details
-- location to x's binding site in lpat, namely the 'x' in Just (x,True).
-- Else the error message location is wherever tcCheckPat finished,
-- namely the right-hand corner of the pattern
- do { arg_id <- tcLookupId arg_name
+ do { arg_id <- tcLookupPatSynArg arg_name
; wrap <- tcSubTypeSigma (OccurrenceOf (idName arg_id))
GenSigCtxt
(idType arg_id)
@@ -645,6 +645,19 @@ collectPatSynArgInfo details =
InfixCon _ name1 name2 -> (map unLoc [name1, name2], True)
RecCon _ names -> (map (unLoc . recordPatSynPatVar) names, False)
+-- | Look up the 'Id' bound by the pattern for a declared argument of a pattern
+-- synonym. With @RequiredTypeArguments@ the argument may turn out to be a type
+-- variable, e.g. @pattern P x = MkT x@ where the argument of @MkT@ is a required
+-- type argument; then we report an illegal term-level use of @x@ (#27586).
+tcLookupPatSynArg :: Name -> TcM Id
+tcLookupPatSynArg arg_name
+ = do { thing <- tcLookup arg_name
+ ; case thing of
+ ATcId { tct_id = id } -> return id
+ AGlobal (AnId id) -> return id
+ ATyVar {} -> failIllegalTyVar (noUserRdr arg_name)
+ _ -> pprPanic "tcLookupPatSynArg" (ppr arg_name) }
+
wrongNumberOfParmsErr :: Name -> Arity -> Arity -> TcM a
wrongNumberOfParmsErr name decl_arity missing
= failWithTc $ TcRnPatSynArityMismatch name decl_arity missing
=====================================
libraries/base/tests/all.T
=====================================
@@ -80,7 +80,7 @@ test('length001',
# excessive amounts of stack space. So we specifically set a low
# stack limit and mark it as failing under a few conditions.
[extra_run_opts('+RTS -K8m -RTS'),
- expect_fail_for(['normal', 'threaded1', 'llvm', 'nonmoving', 'nonmoving_thr', 'nonmoving_thr_ghc', 'ext-interp']),
+ expect_fail_for(['normal', 'threaded1', 'llvm', 'nonmoving', 'nonmoving_thr', 'nonmoving_thr_sanity', 'nonmoving_thr_ghc', 'ext-interp']),
# JS doesn't support stack limit so the test sometimes passes just fine. Therefore the test is
# marked as fragile.
when(js_arch(), fragile(22921))],
=====================================
libraries/base/tests/listThreads1.hs
=====================================
@@ -2,5 +2,10 @@ module Main where
import GHC.Conc.Sync
+-- Regression test for the JS backend's ListThreadsOp, which used to omit the
+-- running thread. Whatever other threads the RTS has is irrelevant here.
main :: IO ()
-main = listThreads >>= print
+main = do
+ tid <- myThreadId
+ ts <- listThreads
+ print (tid `elem` ts)
=====================================
libraries/base/tests/listThreads1.stdout
=====================================
@@ -1 +1 @@
-[ThreadId 1]
+True
=====================================
rts/js/thread.js
=====================================
@@ -110,6 +110,10 @@ function h$rts_getThreadId(t) { // returns a CULLong
RETURN_UBX_TUP2((t.tid / Math.pow(2,32))>>>0, (t.tid & 0xFFFFFFFF)>>>0);
}
+function h$eq_thread(t1,t2) {
+ return t1 === t2 ? 1 : 0;
+}
+
function h$cmp_thread(t1,t2) {
if(t1.tid < t2.tid) return -1;
if(t1.tid > t2.tid) return 1;
=====================================
testsuite/driver/perf_notes.py
=====================================
@@ -84,8 +84,11 @@ PerfStat = NamedTuple('PerfStat', [('test_env', TestEnv),
('value', float)])
# A baseline recovered form stored metrics.
-Baseline = NamedTuple('Baseline', [('perfStat', PerfStat),
- ('commit', GitHash)])
+class Baseline(NamedTuple):
+ perfStat: PerfStat
+ commit: GitHash
+ # The raw samples the baseline value was averaged over.
+ samples: List[float] = []
# The type of exceptions which are thrown when computing the current stat value
# fails.
@@ -465,6 +468,10 @@ def get_allowed_changes(baseline_ref: Optional[GitRef]) -> Dict[TestName, List[A
# (bool , str ) -> (str , str , str , str) -> float
_commit_metric_cache = {} # type: ignore
+# Like _commit_metric_cache, but mapping to the list of raw sample values the
+# baseline was averaged over. Filled by get_commit_metric.
+_commit_samples_cache = {} # type: ignore
+
# Get the baseline of a test at a given commit. This is the expected value
# *before* the commit is applied (i.e. on the parent commit).
# This searches git notes from older commits for recorded metrics (locally and
@@ -506,7 +513,8 @@ def baseline_metric(commit: GitHash,
if baseline_commit is not None:
current_metric = get_commit_metric(namespace, baseline_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric, baseline_commit)
+ return Baseline(current_metric, baseline_commit,
+ get_commit_samples(namespace, baseline_commit, test_env, name, metric, way))
else:
return None
@@ -515,7 +523,8 @@ def baseline_metric(commit: GitHash,
# Check for a metric on this commit.
current_metric = get_commit_metric(namespace, current_commit, test_env, name, metric, way)
if current_metric is not None:
- return Baseline(current_metric, current_commit)
+ return Baseline(current_metric, current_commit,
+ get_commit_samples(namespace, current_commit, test_env, name, metric, way))
# Stop if there is an expected change at this commit. In that case
# metrics on ancestor commits will not be a valid baseline.
@@ -598,8 +607,28 @@ def get_commit_metric(gitNoteRef,
# Save baselines to the cache.
_commit_metric_cache[cacheKeyA] = baseline_by_cache_key_b
+ _commit_samples_cache[cacheKeyA] = values_by_cache_key_b
return baseline_by_cache_key_b.get(cacheKeyB)
+# Get the raw sample values that get_commit_metric averages over. Uses the
+# cache filled by get_commit_metric, so no extra git calls after it has run.
+def get_commit_samples(gitNoteRef,
+ ref: Union[GitRef, GitHash],
+ test_env: TestEnv,
+ name: TestName,
+ metric: MetricName,
+ way: WayName
+ ) -> List[float]:
+ get_commit_metric(gitNoteRef, ref, test_env, name, metric, way)
+ cacheKeyA = (gitNoteRef, commit_hash(ref))
+ cacheKeyB = (test_env, name, metric, way)
+ return _commit_samples_cache.get(cacheKeyA, {}).get(cacheKeyB, [])
+
+# Metric samples are integral in practice; '%g' would render large byte
+# counts in truncated scientific notation.
+def format_samples(samples: List[float]) -> str:
+ return ', '.join(str(int(s)) if s == int(s) else str(s) for s in samples)
+
def check_stats_change(actual: PerfStat,
baseline: Baseline,
acceptance_window: MetricAcceptanceWindow,
@@ -654,9 +683,17 @@ def check_stats_change(actual: PerfStat,
' baseline @ %s' % baseline.commit
print(actual.metric, error + ':')
dev = 100.0 if expected_val == 0 else round(((float(actual.value) * 100) / int(expected_val)) - 100, 1)
+ # A multi-sample baseline is a mean; show the samples so outliers
+ # corrupting the baseline are visible (#27602).
+ if len(baseline.samples) > 1:
+ samples_note = ('; baseline is mean of %d samples: %s'
+ % (len(baseline.samples),
+ format_samples(baseline.samples)))
+ else:
+ samples_note = ''
change_line = (f'{actual.metric} {change.value} from {baseline.perfStat.test_env} '
f'baseline @ {baseline.commit[:7]}: {expected_val} -> {actual.value} '
- f'({dev:+g}%, allowed {acceptance_window.describe()})')
+ f'({dev:+g}%, allowed {acceptance_window.describe()}{samples_note})')
result = failBecause('stat ' + change_line, tag='stat')
if not change_allowed or force_print:
@@ -666,6 +703,10 @@ def check_stats_change(actual: PerfStat,
print(descr, str(val).rjust(length), extra)
display(' Expected ' + full_name + ' ' + actual.metric + ':', expected_val, acceptance_window.describe())
+ if len(baseline.samples) > 1:
+ display(' Samples ' + full_name + ' ' + actual.metric + ':',
+ len(baseline.samples),
+ '(' + format_samples(baseline.samples) + ')')
display(' Lower bound ' + full_name + ' ' + actual.metric + ':', lowerBound, '')
display(' Upper bound ' + full_name + ' ' + actual.metric + ':', upperBound, '')
display(' Actual ' + full_name + ' ' + actual.metric + ':', actual.value, '')
=====================================
testsuite/tests/concurrent/should_run/T16761.hs
=====================================
@@ -0,0 +1,25 @@
+-- Test that Eq ThreadId is based on thread identity (eq_thread),
+-- not on the numeric thread id, which may wrap around (#16761).
+module Main (main) where
+
+import Control.Concurrent
+import System.Mem (performGC)
+
+main :: IO ()
+main = do
+ t0 <- myThreadId
+ print (t0 == t0)
+
+ mv <- newEmptyMVar
+ _ <- forkIO (myThreadId >>= putMVar mv)
+ tChild <- takeMVar mv
+ print (t0 == tChild)
+ print (tChild == tChild)
+
+ -- Equality must be stable even after the GC moves the TSOs.
+ performGC
+ print (t0 == t0)
+
+ -- Ord must agree with Eq.
+ print (compare t0 tChild /= EQ)
+ print (compare t0 t0 == EQ)
=====================================
testsuite/tests/concurrent/should_run/T16761.stdout
=====================================
@@ -0,0 +1,6 @@
+True
+False
+True
+True
+True
+True
=====================================
testsuite/tests/concurrent/should_run/all.T
=====================================
@@ -310,6 +310,8 @@ test('hs_try_putmvar003',
# Check forkIO exception determinism under optimization
test('T13330', normal, compile_and_run, ['-O'])
+test('T16761', normal, compile_and_run, [''])
+
test('T26341', normal, compile_and_run, [''])
# Test EINTR for async I/O interrupted by an exception (#26341)
=====================================
testsuite/tests/perf/compiler/all.T
=====================================
@@ -48,7 +48,9 @@ test('T4801',
[''])
test('T3064',
- [collect_compiler_residency(20),
+ [# Demo-only: tightened from 20 so the bimodal max_bytes_used
+ # baseline on aarch64-linux-deb13-validate fails.
+ collect_compiler_residency(1),
collect_compiler_runtime(2),
only_ways(['normal']),
],
=====================================
testsuite/tests/rts/all.T
=====================================
@@ -679,7 +679,7 @@ test('T22859',
[js_skip,
# This test is vulnerable to changes in allocation behaviour, so we disable it in some ways
when(arch('wasm32'), skip),
- omit_ways(llvm_ways)],
+ omit_ways(llvm_ways + ['nonmoving_thr', 'nonmoving_thr_sanity'])],
compile_and_run, ['-with-rtsopts -A8K'])
# These tests need access to the internal RTS headers.
=====================================
testsuite/tests/vdq-rta/should_fail/T27586a.hs
=====================================
@@ -0,0 +1,9 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+
+module T27586a where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: Int -> T Int
+pattern P x = MkT x
=====================================
testsuite/tests/vdq-rta/should_fail/T27586a.stderr
=====================================
@@ -0,0 +1,5 @@
+T27586a.hs:9:19: error: [GHC-01928]
+ • Illegal term-level use of the type variable ‘x’
+ • bound at T27586a.hs:9:19
+ • In the declaration for pattern synonym ‘P’
+
=====================================
testsuite/tests/vdq-rta/should_fail/T27586b.hs
=====================================
@@ -0,0 +1,8 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+
+module T27586b where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P x = MkT x
=====================================
testsuite/tests/vdq-rta/should_fail/T27586b.stderr
=====================================
@@ -0,0 +1,5 @@
+T27586b.hs:8:15: error: [GHC-01928]
+ • Illegal term-level use of the type variable ‘x’
+ • bound at T27586b.hs:8:19
+ • In the declaration for pattern synonym ‘P’
+
=====================================
testsuite/tests/vdq-rta/should_fail/T27586c.hs
=====================================
@@ -0,0 +1,9 @@
+{-# LANGUAGE GADTs, RequiredTypeArguments, PatternSynonyms #-}
+
+module T27586c where
+
+data T a where
+ MkT :: forall a -> T a
+
+pattern P :: Int -> T Int
+pattern P x <- MkT x
=====================================
testsuite/tests/vdq-rta/should_fail/T27586c.stderr
=====================================
@@ -0,0 +1,5 @@
+T27586c.hs:9:20: error: [GHC-01928]
+ • Illegal term-level use of the type variable ‘x’
+ • bound at T27586c.hs:9:20
+ • In the declaration for pattern synonym ‘P’
+
=====================================
testsuite/tests/vdq-rta/should_fail/all.T
=====================================
@@ -35,3 +35,6 @@ test('T25127_fail_arity', normal, compile_fail, [''])
test('T27440e', normal, compile_fail, [''])
test('T27583f', normal, compile_fail, [''])
+test('T27586a', normal, compile_fail, [''])
+test('T27586b', normal, compile_fail, [''])
+test('T27586c', normal, compile_fail, [''])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a4ddab82385619a28b7fd6de1b925e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/a4ddab82385619a28b7fd6de1b925e…
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/marge_bot_batch_merge_job] 3 commits: Add law in qualified strings doc
by Marge Bot (@marge-bot) 15 Aug '26
by Marge Bot (@marge-bot) 15 Aug '26
15 Aug '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
d143b24d by Brandon Chinn at 2026-08-15T09:10:03-04:00
Add law in qualified strings doc
- - - - -
6f6ec8c1 by Zubin Duggal at 2026-08-15T09:10:05-04:00
DmdAnal: Fix maxDmdType
We need to eta expand the smaller DmdType using defaultArgDmd, like in lubDmdType.
Introduce zipDmdType as a common combinator to implement both maxDmdType and lubDmdType
uniformly.
fixes #27626
- - - - -
58e92623 by mangoiv at 2026-08-15T09:10:07-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
8 changed files:
- + changelog.d/27626
- compiler/GHC/Types/Demand.hs
- docs/users_guide/exts/qualified_strings.rst
- hadrian/hie-bios.bat
- + testsuite/tests/dmdanal/should_run/M2.hs
- + testsuite/tests/dmdanal/should_run/T27626.hs
- + testsuite/tests/dmdanal/should_run/T27626.stdout
- testsuite/tests/dmdanal/should_run/all.T
Changes:
=====================================
changelog.d/27626
=====================================
@@ -0,0 +1,5 @@
+section: compiler
+synopsis: Fix a bug where an argument used only by a function's stable unfolding
+ could still be marked absent, resulting in a runtime crash.
+mrs: !16503
+issues: #27626
=====================================
compiler/GHC/Types/Demand.hs
=====================================
@@ -1849,12 +1849,14 @@ botDmdEnv = mkEmptyDmdEnv botDiv
exnDmdEnv :: DmdEnv
exnDmdEnv = mkEmptyDmdEnv exnDiv
+combineDmdEnv :: (Demand -> Demand -> Demand) -> DmdEnv -> DmdEnv -> DmdEnv
+combineDmdEnv f (DE fv1 d1) (DE fv2 d2)
+ -- See Note [Demand env Equality]
+ = DE (plusVarEnv_CD f fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2))
+ (lubDivergence d1 d2)
+
lubDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-lubDmdEnv (DE fv1 d1) (DE fv2 d2) = DE lub_fv lub_div
- where
- -- See Note [Demand env Equality]
- lub_fv = plusVarEnv_CD lubDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- lub_div = lubDivergence d1 d2
+lubDmdEnv = combineDmdEnv lubDmd
addVarDmdEnv :: DmdEnv -> Id -> Demand -> DmdEnv
addVarDmdEnv env@(DE fvs div) id dmd
@@ -1908,36 +1910,31 @@ instance Eq DmdType where
= ds1 == ds2 -- cheap checks first
&& env1 == env2
+-- | The smaller 'DmdType' is eta expanded using its 'defaultArgDmd'.
+-- See Note [Default demand on free variables and arguments].
+zipDmdType :: (Demand -> Demand -> Demand) -> DmdType -> DmdType -> DmdType
+zipDmdType f (DmdType fv1 ds1) (DmdType fv2 ds2)
+ = DmdType (combineDmdEnv f fv1 fv2) (go ds1 ds2)
+ where
+ def1, def2 :: Demand -- Default argument demands for eta expansion
+ def1 = defaultArgDmd (de_div fv1)
+ def2 = defaultArgDmd (de_div fv2)
+
+ -- If `ds1` is shorter than `ds2`, extend `ds1` with the appropriate
+ -- default demand `def1`; and similarly if `ds2` is shorter
+ go (d1:ds1') (d2:ds2') = f d1 d2 : go ds1' ds2'
+ go [] ds2' = map (def1 `f`) ds2'
+ go ds1' [] = map (`f` def2) ds1'
+
-- | Compute the least upper bound of two 'DmdType's elicited /by the same
-- incoming demand/!
lubDmdType :: DmdType -> DmdType -> DmdType
-lubDmdType d1 d2 = DmdType lub_fv lub_ds
- where
- n = max (dmdTypeDepth d1) (dmdTypeDepth d2)
- (DmdType fv1 ds1) = etaExpandDmdType n d1
- (DmdType fv2 ds2) = etaExpandDmdType n d2
- lub_ds = zipWithEqual lubDmd ds1 ds2
- lub_fv = lubDmdEnv fv1 fv2
+lubDmdType = zipDmdType lubDmd
-- | Combine two 'DmdType's for stable unfolding analysis.
-- See Note [Combining demands for stable unfoldings].
maxDmdType :: DmdType -> DmdType -> DmdType
-maxDmdType (DmdType fv1 ds1) (DmdType fv2 ds2)
- = DmdType combined_fv combined_ds
- where
- combined_fv = maxDmdEnv fv1 fv2
- combined_ds = go ds1 ds2
- -- If lists have different lengths, keep remaining ds1 (from RHS)
- go rhs [] = rhs
- go [] _ = []
- go (r:rhs) (u:unfs) = maxDmd r u : go rhs unfs
-
--- | See Note [Combining demands for stable unfoldings].
-maxDmdEnv :: DmdEnv -> DmdEnv -> DmdEnv
-maxDmdEnv (DE fv1 d1) (DE fv2 d2) = DE combined_fv combined_div
- where
- combined_fv = plusVarEnv_CD maxDmd fv1 (defaultFvDmd d1) fv2 (defaultFvDmd d2)
- combined_div = lubDivergence d1 d2
+maxDmdType = zipDmdType maxDmd
discardArgDmds :: DmdType -> DmdEnv
discardArgDmds (DmdType fv _) = fv
=====================================
docs/users_guide/exts/qualified_strings.rst
=====================================
@@ -15,3 +15,10 @@ The ``-XQualifiedStrings`` extension allows string literals to be qualified usin
This allows more precise control over the ``fromString`` function than :extension:`OverloadedStrings`, which overloads all string literals for an entire module.
If :extension:`MultilineStrings` is also enabled, ``M."""test"""`` will also work, desugaring the multiline string first, then the qualified string literal.
+
+Laws
+~~~~
+
+If the following expression typechecks, it should hold:
+
+* ``Data.String.fromString "str" == M."str"``
=====================================
hadrian/hie-bios.bat
=====================================
=====================================
testsuite/tests/dmdanal/should_run/M2.hs
=====================================
@@ -0,0 +1,28 @@
+module M2 where
+
+-- f's stable unfolding template is the PAP `mk g`, which is not
+-- eta-expanded, so its DmdType has depth 0. The big lambda keeps the
+-- template large enough that f does not certainlyWillInline, so it is
+-- worker/wrappered.
+{-# INLINABLE [1] f #-}
+f :: Int -> Int -> Float
+f = mk (\y -> succ . succ . succ . succ . succ . succ . succ . succ . succ $ y)
+
+-- Arity 1: the `let` does real work, so mk is not eta-expanded past it.
+-- The returned function really forces x.
+{-# NOINLINE mk #-}
+mk :: (Int -> Int) -> Int -> Int -> Float
+mk t = let s = t 1 in \dummy x -> x `seq` fromIntegral (t (dummy + s))
+
+{-# INLINE mkFast #-}
+mkFast :: (Int -> Int) -> Int -> Int -> Float
+mkFast t = \dummy x -> fromIntegral (t dummy)
+
+-- Active only before phase 1: rewrites f's RHS early, but is dead by
+-- the time f's stable unfolding (activation [1]) is inlined at call
+-- sites.
+{-# RULES "mk" [~1] forall t. mk t = mkFast t #-}
+
+{-# NOINLINE g #-}
+g :: Int -> Int
+g x = x + 1
=====================================
testsuite/tests/dmdanal/should_run/T27626.hs
=====================================
@@ -0,0 +1,6 @@
+-- The raised threshold persuades GHC to inline the worker's stable
+-- unfolding in this small program; a larger program does that naturally.
+{-# OPTIONS_GHC -funfolding-use-threshold=400 #-}
+module Main where
+import M2 ( f )
+main = print (f 19 12)
=====================================
testsuite/tests/dmdanal/should_run/T27626.stdout
=====================================
@@ -0,0 +1 @@
+38.0
=====================================
testsuite/tests/dmdanal/should_run/all.T
=====================================
@@ -36,3 +36,4 @@ test('T23208', exit_code(1), multimod_compile_and_run, ['T23208_Lib', 'T23208'])
test('T25439', normal, compile_and_run, [''])
test('T26748', normal, compile_and_run, [''])
test('T26416', [extra_files(['M1.hs'])], multimod_compile_and_run, ['T26416','M1.hs'])
+test('T27626', [extra_files(['M2.hs'])], multimod_compile_and_run, ['T27626','M2.hs'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5006c9d2d5b08c5f0f800f8e805366…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5006c9d2d5b08c5f0f800f8e805366…
You're receiving this email because of your account on gitlab.haskell.org. Manage all notifications: https://gitlab.haskell.org/-/profile/notifications | Help: https://gitlab.haskell.org/help
1
0
[Git][ghc/ghc][wip/sjakobi/T27459] 2 commits: testsuite: Add determ025, an error-message determinism test
by Simon Jakobi (@sjakobi) 15 Aug '26
by Simon Jakobi (@sjakobi) 15 Aug '26
15 Aug '26
Simon Jakobi pushed to branch wip/sjakobi/T27459 at Glasgow Haskell Compiler / GHC
Commits:
b85976c9 by Simon Jakobi at 2026-08-15T13:27:20+02:00
testsuite: Add determ025, an error-message determinism test
All existing tests in testsuite/tests/determinism compare interface
files; none check that error messages are independent of the order of
Uniques. Add a test that compiles a module producing an ambiguity error
with two different unique supplies and compares the errors.
Context: #27459, where lazy unifier enumeration made instance lists in
error messages potentially unique-ordered.
Assisted-by: Claude Fable 5
- - - - -
480e2cdf by Simon Jakobi at 2026-08-15T14:20:28+02:00
Sort the out-of-scope instances in potential-instance lists
With -fprint-potential-instances, the "...plus N instances involving
out-of-scope types" list was printed in the enumeration order of the
instance environment, which the previous commit made
unique-dependent: the list's order varied both under
-dinitial-unique/-dunique-increment perturbation and from run to run
with --make -j. Sort it with fuzzyClsInstCmp, like the in-scope
lists.
Also extend determ025 to cover -fprint-potential-instances.
Assisted-by: Claude Fable 5
- - - - -
4 changed files:
- compiler/GHC/Tc/Errors/Ppr.hs
- + testsuite/tests/determinism/determ025/A.hs
- + testsuite/tests/determinism/determ025/Makefile
- + testsuite/tests/determinism/determ025/all.T
Changes:
=====================================
compiler/GHC/Tc/Errors/Ppr.hs
=====================================
@@ -4999,10 +4999,12 @@ potentials_msg_with_options
n_show_matches = 3
n_show_unifiers = 2
- (in_scope_matches, not_in_scope_matches) = partition inst_in_scope matches
- (in_scope_unifiers, not_in_scope_unifiers) = partition inst_in_scope unifiers
- sorted_matches = sortBy fuzzyClsInstCmp in_scope_matches
- sorted_unifiers = sortBy fuzzyClsInstCmp in_scope_unifiers
+ -- Sort before partitioning so that the out-of-scope lists are also
+ -- shown in a deterministic order (#27459).
+ (sorted_matches, not_in_scope_matches)
+ = partition inst_in_scope (sortBy fuzzyClsInstCmp matches)
+ (sorted_unifiers, not_in_scope_unifiers)
+ = partition inst_in_scope (sortBy fuzzyClsInstCmp unifiers)
(show_these_matches, show_these_unifiers)
| show_all_potentials = (sorted_matches, sorted_unifiers)
| otherwise = (take n_show_matches sorted_matches
=====================================
testsuite/tests/determinism/determ025/A.hs
=====================================
@@ -0,0 +1,13 @@
+module A where
+
+-- An ambiguity error whose message lists potential instances, both in-scope
+-- and (via the instance-only imports) involving out-of-scope types.
+
+import Data.Functor.Const ()
+import Data.Functor.Identity ()
+import Data.Monoid ()
+import Data.Proxy ()
+import Data.Ord ()
+
+v :: Int
+v = foldr (+) 0 (pure 21)
=====================================
testsuite/tests/determinism/determ025/Makefile
=====================================
@@ -0,0 +1,18 @@
+TOP=../../..
+include $(TOP)/mk/boilerplate.mk
+include $(TOP)/mk/test.mk
+
+# Check that error messages don't depend on the order of Uniques (#27459):
+# compile a module producing an ambiguity error with two unique supplies
+# and compare the errors.
+determ025:
+ $(RM) A.hi A.o
+ -'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -dinitial-unique=0 -dunique-increment=1 A.hs 2> A.err.normal
+ $(RM) A.hi A.o
+ -'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -dinitial-unique=16777215 -dunique-increment=-1 A.hs 2> A.err.reversed
+ diff A.err.normal A.err.reversed
+ $(RM) A.hi A.o
+ -'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -fprint-potential-instances -dinitial-unique=0 -dunique-increment=1 A.hs 2> A.err.all.normal
+ $(RM) A.hi A.o
+ -'$(TEST_HC)' $(TEST_HC_OPTS) -v0 -fprint-potential-instances -dinitial-unique=16777215 -dunique-increment=-1 A.hs 2> A.err.all.reversed
+ diff A.err.all.normal A.err.all.reversed
=====================================
testsuite/tests/determinism/determ025/all.T
=====================================
@@ -0,0 +1,3 @@
+test('determ025',
+ [extra_files(['A.hs'])],
+ makefile_test, ['determ025'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/62cc6594f5e31e20dc473c3d7e9f16…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/62cc6594f5e31e20dc473c3d7e9f16…
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/marge_bot_batch_merge_job] 7 commits: loopImports: Don't dup ms_uid in summary imports
by Marge Bot (@marge-bot) 15 Aug '26
by Marge Bot (@marge-bot) 15 Aug '26
15 Aug '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
c130188d by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
loopImports: Don't dup ms_uid in summary imports
We were writing the ms_unitid of the mod summary with every single
import of that module
That complicated the code (as though the UnitId in that list could ever
be something else) and also allocates unnecessarily per every mod
import. Very slight allocation decrease measured locally in a few tests:
(MultiComponentModulesRecomp: -0.06%; MultiComponentModulesRecomp100: -0.05%)
Purely a clean up.
- - - - -
c71166a8 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: make control flow simpler and cache correct
This refactor extracts the control flow of downsweep into a single
function `dfsBuild`, which takes care of iteratively expanding and
traversing all nodes of the in-construction module graph necessary to
build a full `ModuleGraph`.
There are three levels of caching going on, all of which are necessary
to make sure we don't do repeated work (notably, NEVER summarise the
same module twice).
1. `dfsBuild` accumulates the final module graph and never revisits the
same node of the module graph. Cache is keyed by the final
`ModuleGraph`s `NodeKey`s.
2. For Module A in home-unit u1, each import in the list of imports
needs to be *found* (call to `findImportedModuleWithIsBoot`): at this
point, we only have the `ModuleName` of the import, not the `Module`.
This *finding* is somewhat expensive, so we cache it as well
(`ImportsCache`). The cache key is the home-unit to which the module
belongs~[1], the import package qualifier, and the ModuleName.
[1] Different home-units will have different package flags, which means
potentially different `Module` resolution for the same `ModuleName`.
3. The most expensive operation we want to avoid is summarising a
`Module` into a `ModSummary`, which notably involves parsing the
module header from scratch.
The third cache, in essence, maps a `Module` to its `ModSummary`
(named `ModSummaryCache`). This cache upholds the invariant: we NEVER
summarise the same module twice. In practice, the cache key is the
Module's UnitId and the Source path; the reason is we need to
distinguish between `.hs` and `.hs-boot` files, as their summaries
will differ.
Note that (2) can't guarantee this alone: Two ModuleName imports in
separate units can (and likely do) map to the same `Module`.
Note that the previous implementation failed to achieve the
no-duplicate-work summarisation invariant, and we ended up doing a
quadratic amount of processing in scenarios like test
`MultiComponentModules100`.
See also Note [Downsweep Control Flow and Caching]
Fixes #27461
Perf changes:
MultiComponentModules(normal) ghc/alloc 2,097,389,264 1,992,186,736 -5.0% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,173,770 21,293,867,360 -12.4% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,761,394 498,543,984 -17.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,968,240 8,895,404,864 -25.2% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
ebc4047b by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
implicitRequirementsShallow can never reach HoleUnit
findImportedModule will never return `HoleUnit` for a `ModuleName`
(a `HoleUnit` can only be found as a signature instantiation, never as a
directly *imported* thing)
Therefore, we can drop `[ModuleName]` returned by
`implicitRequirementsShallow`, which makes many things dead code.
Namely, the call to `implicitRequirementsShallow` from
GHC.Driver.Downsweep which was a performance bottleneck (for doing lots
of duplicate work in findImportedModule) is now entirely gone.
Fixes #27053
In an MR with this patch and the downsweep refactor (previous commit), CI says:
MultiComponentModules(normal) ghc/alloc 2,097,396,728 1,943,662,304 -7.3% GOOD
MultiComponentModules100(normal) ghc/alloc 24,310,182,136 17,227,574,440 -29.1% GOOD
MultiComponentModulesRecomp(normal) ghc/alloc 602,769,518 449,973,656 -25.3% GOOD
MultiComponentModulesRecomp100(normal) ghc/alloc 11,885,976,408 4,828,894,160 -59.4% GOOD
-------------------------
Metric Decrease:
MultiComponentModules
MultiComponentModules100
MultiComponentModulesRecomp
MultiComponentModulesRecomp100
-------------------------
- - - - -
85a6ab01 by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
downsweep: Cache negative results
When traversing a module graph structure, a uniquely identified node
should always expand to the same thing.
I don't see how visiting the same node which failed to be expanded a
first time would ever successfully expand the second time we try to
expand it (eg. when coming from a different edge to it -- it is still
the same node!). The node expansion is local, based just based on the
node itself, not on the path to get there.
Therefore, this patch removes the weird behavior and commentary of
`dfsBuild` wrt to `Nothing` not being cached and being potentially
expanded a second time around to something different, which was
misleading and, ultimately, incorrect.
Now, we have a `MGRes`, which is more explicit about a node being
Skipped just being a node that is ignored whenever it is found (and that
skip is cached) -- and we may want to do this due to failures or due to
just trying nodes which might not work on purpose, like hs-boots.
We uniformly cache positive and negative results and remove the
assumption that there might be an ordering in which the same node
visited at a later time might be expanded differently.
This makes it possible to traverse the module nodes in parallel without
a change in behavior, since there's no longer a hidden ordering
requirement.
- - - - -
56747c3f by Rodrigo Mesquita at 2026-08-15T06:29:53-04:00
Organize and clean-up GHC.Driver.Downsweep
Simply some cosmetic changes, moving definitions around to structure the
module better into its relevant sections
(In go (ns ++ ss), it's not a problem to use ++ because it's a good
producer and we won't have to append fully before processing the next
item in go)
- - - - -
12f64118 by Wolfgang Jeltsch at 2026-08-15T06:31:12-04:00
Add support for textual output of bytecode file content
- - - - -
5006c9d2 by mangoiv at 2026-08-15T06:39:07-04:00
hadrian: set the executable bit for hie-bios.bat
- - - - -
37 changed files:
- + changelog.d/downsweep-refactor
- + changelog.d/show-byte-code
- compiler/GHC/ByteCode/Serialize.hs
- + compiler/GHC/ByteCode/Show.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Tc/Utils/Backpack.hs
- compiler/GHC/Unit/Env.hs
- compiler/ghc.cabal.in
- docs/users_guide/using.rst
- ghc/GHC/Driver/Session/Mode.hs
- ghc/Main.hs
- hadrian/hie-bios.bat
- + testsuite/tests/driver/T27461/Main1.hs
- + testsuite/tests/driver/T27461/Main2.hs
- + testsuite/tests/driver/T27461/Makefile
- + testsuite/tests/driver/T27461/T27461a.stderr
- + testsuite/tests/driver/T27461/T27461b.script
- + testsuite/tests/driver/T27461/T27461b.stderr
- + testsuite/tests/driver/T27461/T27461b.stdout
- + testsuite/tests/driver/T27461/all.T
- + testsuite/tests/driver/T27461/src/Bar.hs
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/InterfaceModuleGraph.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- + testsuite/tests/show-bytecode/Example.hs
- + testsuite/tests/show-bytecode/Makefile
- + testsuite/tests/show-bytecode/all.T
- + testsuite/tests/show-bytecode/normalize
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout
- + testsuite/tests/show-bytecode/show-bytecode-breakpoints.stdout-javascript-unknown-ghcjs
- + testsuite/tests/show-bytecode/show-bytecode-hpc.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout
- + testsuite/tests/show-bytecode/show-bytecode-vanilla.stdout-javascript-unknown-ghcjs
- testsuite/tests/splice-imports/SI35.hs
- utils/check-ppr/Main.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9cff2f4ddcdfb7320c70fd0fd89834…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/9cff2f4ddcdfb7320c70fd0fd89834…
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