[Git][ghc/ghc][wip/int-index/epa-parens] EPA: Use AnnParen for tuples and sums
by Vladislav Zavialov (@int-index) 01 Apr '26
by Vladislav Zavialov (@int-index) 01 Apr '26
01 Apr '26
Vladislav Zavialov pushed to branch wip/int-index/epa-parens at Glasgow Haskell Compiler / GHC
Commits:
ef641c07 by Vladislav Zavialov at 2026-04-01T06:59:09+03:00
EPA: Use AnnParen for tuples and sums
Summary of changes
* Do not use AnnParen in XListTy, replace it with EpToken "[" and "]"
* Specialise AnnParen to tuple/sums by dropping the AnnParensSquare
and keeping only AnnParens and AnnParensHash
* Use AnnParen in XExplicitTuple
* Use AnnParen in XExplicitTupleTy
* Use AnnParen in XTuplePat
* Use AnnParen in XExplicitSum (via AnnExplicitSum)
* Use AnnParen in XSumPat (via EpAnnSumPat)
This is a refactoring with no user-facing changes.
- - - - -
13 changed files:
- compiler/GHC/Hs/Dump.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/PostProcess.hs
- testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T20452.stderr
- utils/check-exact/ExactPrint.hs
Changes:
=====================================
compiler/GHC/Hs/Dump.hs
=====================================
@@ -221,7 +221,6 @@ showAstData bs ba a0 = blankLine $$ showAstData' a0
NoBlankEpAnnotations -> parens (case ap of
(AnnParens o c) -> text "AnnParens" $$ vcat [showAstData' o, showAstData' c]
(AnnParensHash o c) -> text "AnnParensHash" $$ vcat [showAstData' o, showAstData' c]
- (AnnParensSquare o c) -> text "AnnParensSquare" $$ vcat [showAstData' o, showAstData' c]
)
annClassDecl :: AnnClassDecl -> SDoc
=====================================
compiler/GHC/Hs/Expr.hs
=====================================
@@ -263,7 +263,7 @@ type instance XPar GhcPs = (EpToken "(", EpToken ")")
type instance XPar GhcRn = NoExtField
type instance XPar GhcTc = NoExtField
-type instance XExplicitTuple GhcPs = (EpaLocation, EpaLocation)
+type instance XExplicitTuple GhcPs = AnnParen
type instance XExplicitTuple GhcRn = NoExtField
type instance XExplicitTuple GhcTc = NoExtField
@@ -556,14 +556,13 @@ mkHsVarWithUserRdr rdr n = HsVar noExtField $
data AnnExplicitSum
= AnnExplicitSum {
- aesOpen :: EpaLocation,
+ aesParens :: AnnParen,
aesBarsBefore :: [EpToken "|"],
- aesBarsAfter :: [EpToken "|"],
- aesClose :: EpaLocation
+ aesBarsAfter :: [EpToken "|"]
} deriving Data
instance NoAnn AnnExplicitSum where
- noAnn = AnnExplicitSum noAnn noAnn noAnn noAnn
+ noAnn = AnnExplicitSum noAnn noAnn noAnn
data AnnFieldLabel
= AnnFieldLabel {
=====================================
compiler/GHC/Hs/Pat.hs
=====================================
@@ -113,7 +113,7 @@ type instance XListPat GhcRn = NoExtField
type instance XListPat GhcTc = Type
-- List element type, for use in hsPatType.
-type instance XTuplePat GhcPs = (EpaLocation, EpaLocation)
+type instance XTuplePat GhcPs = AnnParen
type instance XTuplePat GhcRn = NoExtField
type instance XTuplePat GhcTc = [Type]
@@ -260,13 +260,13 @@ discarded inside tcMatchPats, where we know if visible pattern retained or erase
-- API Annotations types
data EpAnnSumPat = EpAnnSumPat
- { sumPatParens :: (EpaLocation, EpaLocation)
+ { sumPatParens :: AnnParen
, sumPatVbarsBefore :: [EpToken "|"]
, sumPatVbarsAfter :: [EpToken "|"]
} deriving Data
instance NoAnn EpAnnSumPat where
- noAnn = EpAnnSumPat (noAnn, noAnn) [] []
+ noAnn = EpAnnSumPat noAnn [] []
-- ---------------------------------------------------------------------
=====================================
compiler/GHC/Hs/Type.hs
=====================================
@@ -430,7 +430,7 @@ type instance XQualTy (GhcPass _) = NoExtField
type instance XTyVar (GhcPass _) = EpToken "'"
type instance XAppTy (GhcPass _) = NoExtField
type instance XFunTy (GhcPass _) = NoExtField
-type instance XListTy (GhcPass _) = AnnParen
+type instance XListTy (GhcPass _) = (EpToken "[", EpToken "]")
type instance XTupleTy (GhcPass _) = AnnParen
type instance XSumTy (GhcPass _) = AnnParen
type instance XOpTy (GhcPass _) = NoExtField
@@ -455,7 +455,7 @@ type instance XExplicitListTy GhcPs = (EpToken "'", EpToken "[", EpToken "]")
type instance XExplicitListTy GhcRn = NoExtField
type instance XExplicitListTy GhcTc = Kind
-type instance XExplicitTupleTy GhcPs = (EpToken "'", EpToken "(", EpToken ")")
+type instance XExplicitTupleTy GhcPs = (EpToken "'", AnnParen)
type instance XExplicitTupleTy GhcRn = NoExtField
type instance XExplicitTupleTy GhcTc = [Kind]
=====================================
compiler/GHC/Parser.y
=====================================
@@ -2370,14 +2370,14 @@ atype :: { LHsType GhcPs }
| '(' ktype ')' {% amsA' (sLL $1 $> $ HsParTy (epTok $1, epTok $3) $2) }
-- see Note [Promotion] for the followings
| SIMPLEQUOTE '(' ')' {% do { requireLTPuns PEP_QuoteDisambiguation $1 $>
- ; amsA' (sLL $1 $> $ HsExplicitTupleTy (epTok $1,epTok $2,epTok $3) IsPromoted []) }}
+ ; amsA' (sLL $1 $> $ HsExplicitTupleTy (epTok $1, AnnParens (epTok $2) (epTok $3)) IsPromoted []) }}
| SIMPLEQUOTE gen_qcon {% amsA' (sLL $1 $> $ HsTyVar (epTok $1) IsPromoted $2) }
| SIMPLEQUOTE sysdcon_nolist {% do { requireLTPuns PEP_QuoteDisambiguation $1 (reLoc $>)
; amsA' (sLL $1 $> $ HsTyVar (epTok $1) IsPromoted (L (getLoc $2) $ nameRdrName (dataConName (unLoc $2)))) }}
| SIMPLEQUOTE '(' ktype ',' comma_types1 ')'
{% do { requireLTPuns PEP_QuoteDisambiguation $1 $>
; h <- addTrailingCommaA $3 (epTok $4)
- ; amsA' (sLL $1 $> $ HsExplicitTupleTy (epTok $1,epTok $2,epTok $6) IsPromoted (h : $5)) }}
+ ; amsA' (sLL $1 $> $ HsExplicitTupleTy (epTok $1, AnnParens (epTok $2) (epTok $6)) IsPromoted (h : $5)) }}
| '[' ']' {% withCombinedComments $1 $> (mkListSyntaxTy0 (epTok $1) (epTok $2)) }
| SIMPLEQUOTE '[' comma_types0 ']' {% do { requireLTPuns PEP_QuoteDisambiguation $1 $>
; amsA' (sLL $1 $> $ HsExplicitListTy (epTok $1, epTok $2, epTok $4) IsPromoted $3) }}
@@ -3185,7 +3185,7 @@ aexp2 :: { ECP }
| '(' tup_exprs ')' { ECP $
$2 >>= \ $2 ->
mkSumOrTuplePV (noAnnSrcSpan $ comb2 $1 $>) Boxed $2
- (glR $1,glR $3)}
+ (AnnParens (epTok $1) (epTok $3))}
| '(' orpats(exp2) ')' {% do
{ pat <- hintOrPats (sL1a $2 (OrPat NoExtField (unLoc $2)))
@@ -3201,11 +3201,11 @@ aexp2 :: { ECP }
| '(#' texp '#)' { ECP $
unECP $2 >>= \ $2 ->
mkSumOrTuplePV (noAnnSrcSpan $ comb2 $1 $>) Unboxed (Tuple [Right $2])
- (glR $1,glR $3) }
+ (AnnParensHash (epTok $1) (epTok $3)) }
| '(#' tup_exprs '#)' { ECP $
$2 >>= \ $2 ->
mkSumOrTuplePV (noAnnSrcSpan $ comb2 $1 $>) Unboxed $2
- (glR $1,glR $3) }
+ (AnnParensHash (epTok $1) (epTok $3)) }
| '[' list ']' { ECP $ $2 (comb2 $1 $>) (glR $1,glR $3) }
| '_' { ECP $ mkHsWildCardPV (getLoc $1) }
=====================================
compiler/GHC/Parser/Annotation.hs
=====================================
@@ -552,12 +552,11 @@ data AnnListBrackets
-- Annotations for parenthesised elements, such as tuples, lists
-- ---------------------------------------------------------------------
--- | exact print annotation for an item having surrounding "brackets", such as
--- tuples or lists
+-- | exact print annotation for an item having parentheses, with or without
+-- the hash symbol, e.g. tuples, unboxed tuples, unboxed sums
data AnnParen
= AnnParens (EpToken "(") (EpToken ")") -- ^ '(', ')'
| AnnParensHash (EpToken "(#") (EpToken "#)") -- ^ '(#', '#)'
- | AnnParensSquare (EpToken "[") (EpToken "]") -- ^ '[', ']'
deriving Data
-- ---------------------------------------------------------------------
@@ -1219,7 +1218,6 @@ instance (Outputable e)
instance Outputable AnnParen where
ppr (AnnParens o c) = text "AnnParens" <+> ppr o <+> ppr c
ppr (AnnParensHash o c) = text "AnnParensHash" <+> ppr o <+> ppr c
- ppr (AnnParensSquare o c) = text "AnnParensSquare" <+> ppr o <+> ppr c
instance Outputable AnnListItem where
ppr (AnnListItem ts) = text "AnnListItem" <+> ppr ts
=====================================
compiler/GHC/Parser/PostProcess.hs
=====================================
@@ -1223,17 +1223,11 @@ checkContext orig_t@(L (EpAnn l _ cs) _orig_t) =
-- With NoListTuplePuns, contexts are parsed as data constructors, which causes failure
-- downstream.
-- This converts them just like when they are parsed as types in the punned case.
- check (oparens,cparens,cs) (L _l (HsExplicitTupleTy (q,o,c) _ ts))
- = punsAllowed >>= \case
- True -> unprocessed
- False -> do
- let
- (op, cp) = case q of
- EpTok ql -> ([EpTok ql], [c])
- _ -> ([o], [c])
- mkCTuple (oparens ++ op, cp ++ cparens, cs) ts
+ check (oparens,cparens,cs) (L _l (HsExplicitTupleTy (_, AnnParens o c) NotPromoted ts))
+ = mkCTuple (oparens ++ [o], c : cparens, cs) ts
+
check (opi,cpi,csi) (L _lp1 (HsParTy (o,c) ty))
- -- to be sure HsParTy doesn't get into the way
+ -- to be sure HsParTy doesn't get in the way
= check (o:opi, c:cpi, csi) ty
-- No need for anns, returning original
@@ -1264,11 +1258,10 @@ checkContextExpr orig_expr@(L (EpAnn l _ cs) _) =
where
check :: ([EpToken "("],[EpToken ")"],EpAnnComments)
-> LHsExpr GhcPs -> PV (LocatedC [LHsExpr GhcPs])
- check (oparens,cparens,cs) (L _ (ExplicitTuple (ap_open, ap_close) tup_args boxity))
+ check (oparens,cparens,cs) (L _ (ExplicitTuple (AnnParens open_tok close_tok) tup_args Boxed))
-- Neither unboxed tuples (#e1,e2#) nor tuple sections (e1,,e2,) can be a context
- | isBoxed boxity
- , Just es <- tupArgsPresent_maybe tup_args
- = mkCTuple (oparens ++ [EpTok ap_open], EpTok ap_close : cparens, cs) es
+ | Just es <- tupArgsPresent_maybe tup_args
+ = mkCTuple (oparens ++ [open_tok], close_tok : cparens, cs) es
check (opi, cpi, csi) (L _ (HsPar (open_tok, close_tok) expr))
= check (opi ++ [open_tok], close_tok : cpi, csi) expr
check (oparens,cparens,cs) (L _ (HsVar _ (L (EpAnn _ (NameAnnOnly (NameParens open closed) []) _) name)))
@@ -1841,7 +1834,7 @@ class (b ~ (Body b) GhcPs, AnnoBody b) => DisambECP b where
mkHsBangPatPV :: SrcSpan -> LocatedA b -> EpToken "!" -> PV (LocatedA b)
-- | Disambiguate tuple sections and unboxed sums
mkSumOrTuplePV
- :: SrcSpanAnnA -> Boxity -> SumOrTuple b -> (EpaLocation, EpaLocation) -> PV (LocatedA b)
+ :: SrcSpanAnnA -> Boxity -> SumOrTuple b -> AnnParen -> PV (LocatedA b)
-- | Disambiguate "type t" (embedded type)
mkHsEmbTyPV :: SrcSpan -> EpToken "type" -> LHsType GhcPs -> PV (LocatedA b)
-- | Validate infixexp LHS to reject unwanted {-# SCC ... #-} pragmas
@@ -3663,7 +3656,7 @@ hintBangPat span e = do
addError $ mkPlainErrorMsgEnvelope span $ PsErrIllegalBangPattern e
mkSumOrTupleExpr :: SrcSpanAnnA -> Boxity -> SumOrTuple (HsExpr GhcPs)
- -> (EpaLocation, EpaLocation)
+ -> AnnParen
-> PV (LHsExpr GhcPs)
-- Tuple
@@ -3678,15 +3671,15 @@ mkSumOrTupleExpr l@(EpAnn anc an csIn) boxity (Tuple es) anns = do
-- Sum
-- mkSumOrTupleExpr l Unboxed (Sum alt arity e) =
-- return $ L l (ExplicitSum noExtField alt arity e)
-mkSumOrTupleExpr l@(EpAnn anc anIn csIn) Unboxed (Sum alt arity e barsp barsa) (o, c) = do
- let an = AnnExplicitSum o barsp barsa c
+mkSumOrTupleExpr l@(EpAnn anc anIn csIn) Unboxed (Sum alt arity e barsp barsa) anns = do
+ let an = AnnExplicitSum anns barsp barsa
!cs <- getCommentsFor (locA l)
return $ L (EpAnn anc anIn (csIn Semi.<> cs)) (ExplicitSum an alt arity e)
mkSumOrTupleExpr l Boxed a@Sum{} _ =
addFatalError $ mkPlainErrorMsgEnvelope (locA l) $ PsErrUnsupportedBoxedSumExpr a
mkSumOrTuplePat
- :: SrcSpanAnnA -> Boxity -> SumOrTuple (PatBuilder GhcPs) -> (EpaLocation, EpaLocation)
+ :: SrcSpanAnnA -> Boxity -> SumOrTuple (PatBuilder GhcPs) -> AnnParen
-> PV (LocatedA (PatBuilder GhcPs))
-- Tuple
@@ -3842,7 +3835,7 @@ mkTupleSyntaxTy parOpen args parClose =
HsExplicitTupleTy annsKeyword NotPromoted args
annParen = AnnParens parOpen parClose
- annsKeyword = (NoEpTok, parOpen, parClose)
+ annsKeyword = (NoEpTok, annParen)
-- | Decide whether to parse tuple con syntax @(,)@ in a type as a
-- type or data constructor, based on the extension @ListTuplePuns@.
@@ -3894,7 +3887,7 @@ mkListSyntaxTy1 brkOpen t brkClose =
HsExplicitListTy annsKeyword NotPromoted [t]
annsKeyword = (NoEpTok, brkOpen, brkClose)
- annParen = AnnParensSquare brkOpen brkClose
+ annParen = (brkOpen, brkClose)
parseError :: HsExpr GhcPs
parseError = HsHole HoleError
=====================================
testsuite/tests/ghc-api/T25121_status.stdout
=====================================
@@ -18,8 +18,8 @@ X(ExplicitList) mismatch
>>> AnnList ()
<<< ((EpToken "'"),(EpToken "["),(EpToken "]"))
X(ExplicitTuple) mismatch
- >>> ((EpaLocation' [GenLocated (EpaLocation' NoComments) EpaComment]),(EpaLocation' [GenLocated (EpaLocation' NoComments) EpaComment]))
- <<< ((EpToken "'"),(EpToken "("),(EpToken ")"))
+ >>> AnnParen
+ <<< ((EpToken "'"),AnnParen)
X(Hole) mismatch
>>> HoleKind
<<< EpToken "_"
=====================================
testsuite/tests/parser/should_compile/DumpParsedAst.stderr
=====================================
@@ -265,7 +265,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpParsedAst.hs:9:16 }))
(EpTok
@@ -647,7 +647,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpParsedAst.hs:10:27 }))
(EpTok
=====================================
testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
=====================================
@@ -596,7 +596,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpRenamedAst.hs:12:27 }))
(EpTok
@@ -701,7 +701,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpRenamedAst.hs:11:16 }))
(EpTok
@@ -1896,7 +1896,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpRenamedAst.hs:31:12 }))
(EpTok
@@ -1961,7 +1961,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { DumpRenamedAst.hs:32:10 }))
(EpTok
=====================================
testsuite/tests/parser/should_compile/KindSigs.stderr
=====================================
@@ -728,7 +728,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { KindSigs.hs:19:12 }))
(EpTok
@@ -1417,13 +1417,14 @@
(EpaComments
[]))
(HsExplicitTupleTy
- ((,,)
+ ((,)
(EpTok
(EpaSpan { KindSigs.hs:28:16 }))
- (EpTok
- (EpaSpan { KindSigs.hs:28:17 }))
- (EpTok
- (EpaSpan { KindSigs.hs:28:44 })))
+ (AnnParens
+ (EpTok
+ (EpaSpan { KindSigs.hs:28:17 }))
+ (EpTok
+ (EpaSpan { KindSigs.hs:28:44 }))))
(IsPromoted)
[(L
(EpAnn
@@ -1501,7 +1502,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { KindSigs.hs:28:34 }))
(EpTok
=====================================
testsuite/tests/parser/should_compile/T20452.stderr
=====================================
@@ -453,7 +453,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { T20452.hs:8:57 }))
(EpTok
@@ -699,7 +699,7 @@
(EpaComments
[]))
(HsListTy
- (AnnParensSquare
+ ((,)
(EpTok
(EpaSpan { T20452.hs:9:57 }))
(EpTok
=====================================
utils/check-exact/ExactPrint.hs
=====================================
@@ -857,9 +857,6 @@ markParenO (AnnParens o c) = do
markParenO (AnnParensHash o c) = do
o' <- markEpToken o
return (AnnParensHash o' c)
-markParenO (AnnParensSquare o c) = do
- o' <- markEpToken o
- return (AnnParensSquare o' c)
markParenC :: (Monad m, Monoid w) => AnnParen -> EP w m AnnParen
markParenC (AnnParens o c) = do
@@ -868,9 +865,6 @@ markParenC (AnnParens o c) = do
markParenC (AnnParensHash o c) = do
c' <- markEpToken c
return (AnnParensHash o c')
-markParenC (AnnParensSquare o c) = do
- c' <- markEpToken c
- return (AnnParensSquare o c')
-- ---------------------------------------------------------------------
-- Bare bones Optics
@@ -1014,15 +1008,14 @@ lsnd k parent = fmap (\new -> (fst parent, new))
-- -------------------------------------
-- data AnnExplicitSum
-- = AnnExplicitSum {
--- aesOpen :: EpaLocation,
+-- aesParens :: AnnParen,
-- aesBarsBefore :: [EpToken "|"],
--- aesBarsAfter :: [EpToken "|"],
--- aesClose :: EpaLocation
+-- aesBarsAfter :: [EpToken "|"]
-- } deriving Data
-laesOpen :: Lens AnnExplicitSum EpaLocation
-laesOpen k parent = fmap (\new -> parent { aesOpen = new })
- (k (aesOpen parent))
+laesParens :: Lens AnnExplicitSum AnnParen
+laesParens k parent = fmap (\new -> parent { aesParens = new })
+ (k (aesParens parent))
laesBarsBefore :: Lens AnnExplicitSum [EpToken "|"]
laesBarsBefore k parent = fmap (\new -> parent { aesBarsBefore = new })
@@ -1032,10 +1025,6 @@ laesBarsAfter :: Lens AnnExplicitSum [EpToken "|"]
laesBarsAfter k parent = fmap (\new -> parent { aesBarsAfter = new })
(k (aesBarsAfter parent))
-laesClose :: Lens AnnExplicitSum EpaLocation
-laesClose k parent = fmap (\new -> parent { aesClose = new })
- (k (aesClose parent))
-
-- -------------------------------------
-- data AnnFieldLabel
-- = AnnFieldLabel {
@@ -1182,12 +1171,12 @@ lga_sep k parent = fmap (\new -> parent { ga_sep = new })
-- ---------------------------------------------------------------------
-- data EpAnnSumPat = EpAnnSumPat
--- { sumPatParens :: (EpaLocation, EpaLocation)
+-- { sumPatParens :: AnnParen
-- , sumPatVbarsBefore :: [EpToken "|"]
-- , sumPatVbarsAfter :: [EpToken "|"]
-- } deriving Data
-lsumPatParens :: Lens EpAnnSumPat (EpaLocation, EpaLocation)
+lsumPatParens :: Lens EpAnnSumPat AnnParen
lsumPatParens k parent = fmap (\new -> parent { sumPatParens = new })
(k (sumPatParens parent))
@@ -2929,23 +2918,21 @@ instance ExactPrint (HsExpr GhcPs) where
expr' <- markAnnotated expr
return (SectionR an op' expr')
- exact (ExplicitTuple (o,c) args b) = do
- o0 <- if b == Boxed then printStringAtAA o "("
- else printStringAtAA o "(#"
+ exact (ExplicitTuple an args b) = do
+ an0 <- markOpeningParen an
args' <- mapM markAnnotated args
- c0 <- if b == Boxed then printStringAtAA c ")"
- else printStringAtAA c "#)"
+ an1 <- markClosingParen an0
debugM $ "ExplicitTuple done"
- return (ExplicitTuple (o0,c0) args' b)
+ return (ExplicitTuple an1 args' b)
exact (ExplicitSum an alt arity expr) = do
- an0 <- markLensFun an laesOpen (\loc -> printStringAtAA loc "(#")
+ an0 <- markLensFun an laesParens markOpeningParen
an1 <- markLensFun an0 laesBarsBefore (\locs -> mapM markEpToken locs)
expr' <- markAnnotated expr
an2 <- markLensFun an1 laesBarsAfter (\locs -> mapM markEpToken locs)
- an3 <- markLensFun an2 laesClose (\loc -> printStringAtAA loc "#)")
+ an3 <- markLensFun an2 laesParens markClosingParen
return (ExplicitSum an3 alt arity expr')
exact (HsCase an e alts) = do
@@ -3954,11 +3941,11 @@ instance ExactPrint (HsType GhcPs) where
(mult', ty1') <- markMultAnnOf mult (markAnnotated ty1)
ty2' <- markAnnotated ty2
return (HsFunTy an mult' ty1' ty2')
- exact (HsListTy an tys) = do
- an0 <- markOpeningParen an
- tys' <- markAnnotated tys
- an1 <- markClosingParen an0
- return (HsListTy an1 tys')
+ exact (HsListTy (o,c) t) = do
+ o' <- markEpToken o
+ t' <- markAnnotated t
+ c' <- markEpToken c
+ return (HsListTy (o',c') t')
exact (HsTupleTy an con tys) = do
an0 <- markOpeningParen an
tys' <- markAnnotated tys
@@ -4010,14 +3997,14 @@ instance ExactPrint (HsType GhcPs) where
tys' <- markAnnotated tys
c' <- markEpToken c
return (HsExplicitListTy (sq',o',c') prom tys')
- exact (HsExplicitTupleTy (sq, o, c) prom tys) = do
+ exact (HsExplicitTupleTy (sq, an) prom tys) = do
sq' <- if (isPromoted prom)
then markEpToken sq
else return sq
- o' <- markEpToken o
+ an0 <- markOpeningParen an
tys' <- markAnnotated tys
- c' <- markEpToken c
- return (HsExplicitTupleTy (sq', o', c') prom tys')
+ an1 <- markClosingParen an0
+ return (HsExplicitTupleTy (sq', an1) prom tys')
exact (HsTyLit an lit) = do
lit' <- withPpr lit
return (HsTyLit an lit')
@@ -4688,22 +4675,18 @@ instance ExactPrint (Pat GhcPs) where
(an', pats') <- markAnnList' an (markAnnotated pats)
return (ListPat an' pats')
- exact (TuplePat (o,c) pats boxity) = do
- o0 <- case boxity of
- Boxed -> printStringAtAA o "("
- Unboxed -> printStringAtAA o "(#"
+ exact (TuplePat an pats boxity) = do
+ an0 <- markOpeningParen an
pats' <- markAnnotated pats
- c0 <- case boxity of
- Boxed -> printStringAtAA c ")"
- Unboxed -> printStringAtAA c "#)"
- return (TuplePat (o0,c0) pats' boxity)
+ an1 <- markClosingParen an0
+ return (TuplePat an1 pats' boxity)
exact (SumPat an pat alt arity) = do
- an0 <- markLensFun an (lsumPatParens . lfst) (\loc -> printStringAtAA loc "(#")
+ an0 <- markLensFun an lsumPatParens markOpeningParen
an1 <- markLensFun an0 lsumPatVbarsBefore (\locs -> mapM markEpToken locs)
pat' <- markAnnotated pat
an2 <- markLensFun an1 lsumPatVbarsAfter (\locs -> mapM markEpToken locs)
- an3 <- markLensFun an2 (lsumPatParens . lsnd) (\loc -> printStringAtAA loc "#)")
+ an3 <- markLensFun an2 lsumPatParens markClosingParen
return (SumPat an3 pat' alt arity)
exact (OrPat an pats) = do
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ef641c0749cadb3badf2cb87ee9d76c…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/ef641c0749cadb3badf2cb87ee9d76c…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc] Pushed new branch wip/int-index/epa-parens
by Vladislav Zavialov (@int-index) 01 Apr '26
by Vladislav Zavialov (@int-index) 01 Apr '26
01 Apr '26
Vladislav Zavialov pushed new branch wip/int-index/epa-parens at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/int-index/epa-parens
You're receiving this email because of your account on gitlab.haskell.org.
1
0
01 Apr '26
Apoorv Ingle pushed to branch wip/ani/better-expansion at Glasgow Haskell Compiler / GHC
Commits:
09d78751 by Apoorv Ingle at 2026-03-31T19:37:21-05:00
Do expansions properly
- move splitHsTypes out of tcApp
- splitHsApps now looks through HsExpansions
- - - - -
10 changed files:
- compiler/GHC/Tc/Gen/App.hs
- − compiler/GHC/Tc/Gen/App.hs-boot
- + compiler/GHC/Tc/Gen/Expand.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Expr.hs-boot
- compiler/GHC/Tc/Gen/Head.hs
- compiler/GHC/Tc/Gen/Match.hs
- compiler/GHC/Tc/Utils/TcMType.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/ghc.cabal.in
Changes:
=====================================
compiler/GHC/Tc/Gen/App.hs
=====================================
@@ -11,7 +11,6 @@
module GHC.Tc.Gen.App
( tcApp
- , tcExprSigma
, tcExprPrag ) where
import {-# SOURCE #-} GHC.Tc.Gen.Expr( tcPolyExpr )
@@ -165,34 +164,6 @@ Note [Instantiation variables are short lived]
-}
-{- *********************************************************************
-* *
- tcInferSigma
-* *
-********************************************************************* -}
-
--- Very similar to tcApp, but returns a sigma (uninstantiated) type
--- CAUTION: Any changes to tcApp should be reflected here
--- cf. T19167. the head is an expanded expression applied to a type
--- Caution: Currently we assume that the expression is compiler generated/expanded
--- Because that is what T19167 test case expects.
--- This function should go away after MR!15778 lands
-tcExprSigma :: Bool -> CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType)
-tcExprSigma inst fun_orig rn_expr
- = do { (fun@(rn_fun,fun_lspan), rn_args) <- splitHsApps rn_expr
- ; do_ql <- wantQuickLook rn_fun
- ; (tc_fun, fun_sigma) <- tcInferAppHead fun
- ; inGenCode <- inGeneratedCode
- ; traceTc "tcExprSigma" (vcat [ text "rn_expr:" <+> ppr rn_expr
- , text "tc_fun" <+> ppr tc_fun
- , text "inGeneratedCode:" <+> ppr inGenCode])
- ; (inst_args, app_res_sigma) <- tcInstFun do_ql inst (fun_orig, rn_fun, fun_lspan)
- tc_fun fun_sigma rn_args
- ; tc_args <- tcValArgs do_ql (rn_fun, fun_lspan) inst_args
- ; let tc_expr = rebuildHsApps (tc_fun, fun_lspan) tc_args
- ; return (tc_expr, app_res_sigma) }
-
-
{- *********************************************************************
* *
Typechecking n-ary applications
@@ -379,24 +350,22 @@ Unify result type /before/ typechecking the args
The latter is much better. That is why we call `checkResultTy` before tcValArgs.
-}
--- CAUTION: Any changes to tcApp should be reflected in tcExprSigma
-tcApp :: HsExpr GhcRn
+
+--------------------
+tcApp :: HsExpr GhcRn -- The whole application
+ -> HsExpr GhcRn -> [HsExprArg 'TcpRn] -- Function and arguments
-> ExpRhoType -- When checking, -XDeepSubsumption <=> deeply skolemised
-> TcM (HsExpr GhcTc)
-- See Note [tcApp: typechecking applications]
-tcApp rn_expr exp_res_ty
- = do { -- Step 1: Split the application chain
- (fun@(rn_fun, fun_lspan), rn_args) <- splitHsApps rn_expr
- ; inGenCode <- inGeneratedCode
+tcApp rn_expr rn_fun rn_args exp_res_ty
+ = do { fun_lspan <- getFunSrcSpan rn_args
; traceTc "tcApp {" $
- vcat [ text "generated? " <+> ppr inGenCode
- , text "rn_expr:" <+> ppr rn_expr
- , text "rn_fun:" <+> ppr rn_fun
+ vcat [ text "rn_fun:" <+> ppr rn_fun
, text "fun_lspan:" <+> ppr fun_lspan
, text "rn_args:" <+> ppr rn_args ]
-- Step 2: Infer the type of `fun`, the head of the application
- ; (tc_fun, fun_sigma) <- tcInferAppHead fun
+ ; (tc_fun, fun_sigma) <- tcInferAppHead (rn_fun, fun_lspan)
; let tc_head = (tc_fun, fun_lspan)
-- inst_final: top-instantiate the result type of the application,
-- EXCEPT if we are trying to infer a sigma-type
@@ -411,22 +380,12 @@ tcApp rn_expr exp_res_ty
-- Step 3.1: Instantiate the function type (taking a quick look at args)
; do_ql <- wantQuickLook rn_fun
- -- Setp 3.2 Set the correct origin to blame for the error message
- -- What should be the origin for this function call?
- -- If the head of the function is user written
- -- then it can be used in the error message
- -- If it is generated code location span, blame it on the
- -- origin that can be retrived from the top of the error ctxt stack.
- -- See Note [Error contexts in generated code]
- ; fun_orig <- mk_origin fun_lspan rn_fun
-
; traceTc "tcApp:inferAppHead" $
vcat [ text "tc_fun:" <+> ppr tc_fun
, text "fun_sigma:" <+> ppr fun_sigma
- , text "fun_origin" <+> ppr fun_orig
, text "do_ql:" <+> ppr do_ql]
; (inst_args, app_res_rho)
- <- tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_args
+ <- tcInstFun do_ql inst_final (rn_fun, fun_lspan) tc_fun fun_sigma rn_args
-- See (TCAPP1) and (TCAPP2) in
-- Note [tcApp: typechecking applications]
@@ -440,7 +399,7 @@ tcApp rn_expr exp_res_ty
-- Step 4.2: typecheck the arguments
; tc_args <- tcValArgs NoQL (rn_fun, fun_lspan) inst_args
-- Step 4.3: wrap up
- ; finishApp tc_head tc_args app_res_rho res_wrap }
+ ; finishApp tc_fun tc_args app_res_rho res_wrap }
DoQL -> do { traceTc "tcApp:DoQL" (ppr rn_fun $$ ppr app_res_rho)
@@ -458,7 +417,7 @@ tcApp rn_expr exp_res_ty
; res_wrap <- checkResultTy rn_expr tc_head inst_args
app_res_rho exp_res_ty
-- Step 5.5: wrap up
- ; finishApp tc_head tc_args app_res_rho res_wrap } }
+ ; finishApp tc_fun tc_args app_res_rho res_wrap } }
quickLookResultType :: TcRhoType -> ExpRhoType -> TcM ()
-- This function implements the shaded bit of rule APP-Downarrow in
@@ -466,16 +425,16 @@ quickLookResultType :: TcRhoType -> ExpRhoType -> TcM ()
quickLookResultType app_res_rho (Check exp_rho) = qlUnify app_res_rho exp_rho
quickLookResultType _ _ = return ()
-finishApp :: (HsExpr GhcTc, SrcSpan) -> [HsExprArg 'TcpTc]
+finishApp :: HsExpr GhcTc -> [HsExprArg 'TcpTc]
-> TcRhoType -> HsWrapper
-> TcM (HsExpr GhcTc)
-- Do final checks and wrap up the result
-finishApp tc_head@(tc_fun,_) tc_args app_res_rho res_wrap
+finishApp tc_fun tc_args app_res_rho res_wrap
= do {
-- Reconstruct, with a horrible special case for tagToEnum#.
res_expr <- if isTagToEnum tc_fun
- then tcTagToEnum tc_head tc_args app_res_rho
- else return (rebuildHsApps tc_head tc_args)
+ then tcTagToEnum tc_fun tc_args app_res_rho
+ else return (rebuildHsApps tc_fun tc_args)
; traceTc "End tcApp }" (ppr tc_fun)
; return (mkHsWrap res_wrap res_expr) }
@@ -488,11 +447,12 @@ checkResultTy :: HsExpr GhcRn
-- expose foralls, but maybe not /deeply/ instantiated
-> ExpRhoType -- Expected type; this is deeply skolemised
-> TcM HsWrapper
-checkResultTy rn_expr (tc_fun, _) _ app_res_rho (Infer inf_res)
+checkResultTy rn_expr (tc_fun,_) _ app_res_rho (Infer inf_res)
= do { ds_flag <- getDeepSubsumptionFlag_DataConHead tc_fun
+ -- Why the "DataConHead" bit? See (IIR5) in
+ -- Note [Instantiation of InferResult] in GHC.Tc.Utils.Unify.
; fillInferResult ds_flag (exprCtOrigin rn_expr) app_res_rho inf_res }
-
checkResultTy rn_expr (tc_fun, fun_loc) inst_args app_res_rho (Check res_ty)
-- Unify with expected type from the context
-- See Note [Unify with expected type before typechecking arguments]
@@ -561,7 +521,7 @@ tcValArgs do_ql (fun, fun_lspan) args = go do_ql 0 args
| EValArgQL{} <- arg
= pos + 1
| ETypeArg{ ea_loc_span = l } <- arg
- , not (isGeneratedSrcSpan l)
+ , not (isGeneratedSrcSpan (locA l))
= pos + 1
| otherwise
= pos
@@ -618,7 +578,7 @@ tcValArg _ pos (fun, fun_lspan) (EValArgQL {
, eaql_loc_span = lspan
, eaql_arg_ty = sc_arg_ty
, eaql_larg = larg@(L arg_loc rn_expr)
- , eaql_tc_fun = tc_head
+ , eaql_tc_fun = tc_head@(tc_fun,_)
, eaql_rn_fun = rn_fun
, eaql_fun_ue = head_ue
, eaql_args = inst_args
@@ -636,7 +596,8 @@ tcValArg _ pos (fun, fun_lspan) (EValArgQL {
, text "app_lspan" <+> ppr lspan
, text "head_lspan" <+> ppr fun_lspan
, text "tc_head" <+> ppr tc_head])
- ; ds_flag <- getDeepSubsumptionFlag_DataConHead (fst tc_head)
+ ; ds_flag <- getDeepSubsumptionFlag
+ -- NB: whether to do deep /skolemisation/ is independent of data constructors
; (wrap, arg')
<- tcScalingUsage mult $
tcSkolemise ds_flag GenSigCtxt exp_arg_ty $ \ exp_arg_rho ->
@@ -656,7 +617,7 @@ tcValArg _ pos (fun, fun_lspan) (EValArgQL {
; app_res_rho <- liftZonkM $ zonkTcType app_res_rho
; res_wrap <- checkResultTy rn_expr tc_head inst_args
app_res_rho (mkCheckExpType exp_arg_rho)
- ; finishApp tc_head tc_args app_res_rho res_wrap }
+ ; finishApp tc_fun tc_args app_res_rho res_wrap }
; traceTc "tcEValArgQL }" $
vcat [ text "app_res_rho:" <+> ppr app_res_rho ]
@@ -690,26 +651,48 @@ tcInstFun :: QLFlag
-- always return a rho-type (but not a deep-rho type)
-- Generally speaking we pass in True; in Fig 5 of the paper
-- |-inst returns a rho-type
- -> (CtOrigin, HsExpr GhcRn, SrcSpan)
+ -> (HsExpr GhcRn, SrcSpan)
-> HsExpr GhcTc
-> TcSigmaType -> [HsExprArg 'TcpRn]
-> TcM ( [HsExprArg 'TcpInst]
, TcSigmaType ) -- Does not instantiate trailing invisible foralls
--- This crucial function implements the |-inst judgement in Fig 4, plus the
--- modification in Fig 5, of the QL paper:
+-- This crucial function implements the |-inst judgement in Fig 4,
+-- plus the modification in Fig 5, of the QL paper:
-- "A quick look at impredicativity" (ICFP'20).
-tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_args
- = do { traceTc "tcInstFun" (vcat [ text "origin" <+> ppr fun_orig
- , text "tc_fun" <+> ppr tc_fun
+tcInstFun do_ql inst_final rn_head@(_, fun_lspan) tc_fun fun_sigma rn_args
+ = do { traceTc "tcInstFun" (vcat [ text "tc_fun" <+> ppr tc_fun
+ , text "rn_fun" <+> ppr rn_head
, text "fun_sigma" <+> ppr fun_sigma
, text "args:" <+> ppr rn_args
- , text "do_ql" <+> ppr do_ql
- , text "ctx" <+> ppr fun_lspan])
- ; res@(_, fun_ty) <- go 1 [] fun_sigma rn_args
+ , text "do_ql" <+> ppr do_ql])
+ ; fun_origin <- mk_origin rn_head
+ ; res@(_, fun_ty) <- go fun_origin 1 [] fun_sigma rn_args
; traceTc "tcInstFun:ret" (ppr fun_ty)
; return res
}
where
+ -- What should be the origin for this function call?
+ -- If the head of the function is user written
+ -- then it can be used in the error message
+ -- If it is generated code location span, blame it on the
+ -- origin that can be retrived from the top of the error ctxt stack.
+ -- See Note [Error contexts in generated code]
+ mk_origin :: (HsExpr GhcRn, SrcSpan) -- The head of the application chain and its location
+ -> TcM CtOrigin
+ mk_origin (rn_fun, fun_lspan)
+ | not (isGeneratedSrcSpan fun_lspan)
+ = return $ exprCtOrigin rn_fun
+
+ | otherwise -- If the location is generated, the best we can do is to
+ -- approximate by looking on top of the error message stack
+ = do { err_ctxt_stack <- getErrCtxt
+ ; let hs_ctxt = case err_ctxt_stack of
+ (c:_) -> c
+ [] -> pprPanic "mk_origin" (ppr rn_fun)
+ ; traceTc "mk_origin" (pprHsCtxt hs_ctxt)
+ ; return $ hsCtxtCtOrigin hs_ctxt
+ }
+
-- These are the type variables which must be instantiated to concrete
-- types. See Note [Representation-polymorphic Ids with no binding]
-- in GHC.Tc.Utils.Concrete
@@ -741,34 +724,35 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
inst_fun _ = isInferredForAllTyFlag
-----------
- go, go1 :: Int -- Value-argument position of next arg
+ go, go1 :: CtOrigin -- Of the function
+ -> Int -- Value-argument position of next arg
-> [HsExprArg 'TcpInst] -- Accumulator, reversed
-> TcSigmaType -> [HsExprArg 'TcpRn]
-> TcM ([HsExprArg 'TcpInst], TcSigmaType)
-- go: If fun_ty=kappa, look it up in Theta
- go pos acc fun_ty args
+ go fun_orig pos acc fun_ty args
| Just kappa <- getTyVar_maybe fun_ty
, isQLInstTyVar kappa
= do { cts <- readMetaTyVar kappa
; case cts of
- Indirect fun_ty' -> go pos acc fun_ty' args
- Flexi -> go1 pos acc fun_ty args }
+ Indirect fun_ty' -> go fun_orig pos acc fun_ty' args
+ Flexi -> go1 fun_orig pos acc fun_ty args }
| otherwise
- = go1 pos acc fun_ty args
+ = go1 fun_orig pos acc fun_ty args
-- go1: fun_ty is not filled-in instantiation variable
-- ('go' dealt with that case)
-- Handle out-of-scope functions gracefully
- go1 pos acc fun_ty (arg : rest_args)
+ go1 fun_orig pos acc fun_ty (arg : rest_args)
| fun_is_out_of_scope, looks_like_type_arg arg -- See Note [VTA for out-of-scope functions]
- = go pos acc fun_ty rest_args
+ = go fun_orig pos acc fun_ty rest_args
-- Rule IALL from Fig 4 of the QL paper; applies even if args = []
-- Instantiate invisible foralls and dictionaries.
-- c.f. GHC.Tc.Utils.Instantiate.topInstantiate
- go1 pos acc fun_ty args
+ go1 fun_orig pos acc fun_ty args
| (tvs, body1) <- tcSplitSomeForAllTyVars (inst_fun args) fun_ty
, (theta, body2) <- if inst_fun args Inferred
then tcSplitPhiTy body1
@@ -797,12 +781,12 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
-- argument of (#,#) to @LiftedRep, but want to rule out the
-- second instantiation @r.
- ; go pos (addArgWrap wrap acc) fun_rho args }
+ ; go fun_orig pos (addArgWrap wrap acc) fun_rho args }
-- Going around again means we deal easily with
-- nested forall a. Eq a => forall b. Show b => blah
-- Rule IRESULT from Fig 4 of the QL paper; no more arguments
- go1 _pos acc fun_ty []
+ go1 _fun_orig _pos acc fun_ty []
| XExpr (ConLikeTc (RealDataCon dc)) <- tc_fun
, isNewDataCon dc
, [Scaled _ orig_arg_ty] <- dataConOrigArgTys dc
@@ -822,30 +806,30 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
= return (reverse acc, fun_ty)
-- Rule ITVDQ from the GHC Proposal #281
- go1 pos acc fun_ty ((EValArg { ea_arg = arg }) : rest_args)
+ go1 fun_orig pos acc fun_ty ((EValArg { ea_arg = arg }) : rest_args)
| Just (tvb, body) <- tcSplitForAllTyVarBinder_maybe fun_ty
= assertPpr (binderFlag tvb == Required) (ppr fun_ty $$ ppr arg) $
-- Any invisible binders have been instantiated by IALL above,
-- so this forall must be visible (i.e. Required)
do { (ty_arg, inst_body) <- tcVDQ fun_conc_tvs (tvb, body) arg
; let wrap = mkWpTyApps [ty_arg]
- ; go (pos+1) (addArgWrap wrap acc) inst_body rest_args }
+ ; go fun_orig (pos+1) (addArgWrap wrap acc) inst_body rest_args }
- go1 pos acc fun_ty (EWrap w : args)
- = go1 pos (EWrap w : acc) fun_ty args
+ go1 fun_orig pos acc fun_ty (EWrap w : args)
+ = go1 fun_orig pos (EWrap w : acc) fun_ty args
- go1 pos acc fun_ty (EPrag sp prag : args)
- = go1 pos (EPrag sp prag : acc) fun_ty args
+ go1 fun_orig pos acc fun_ty (EPrag sp prag : args)
+ = go1 fun_orig pos (EPrag sp prag : acc) fun_ty args
-- Rule ITYARG from Fig 4 of the QL paper
- go1 pos acc fun_ty ( ETypeArg { ea_loc_span = ctxt, ea_hs_ty = hs_ty }
- : rest_args )
+ go1 fun_orig pos acc fun_ty ( ETypeArg { ea_loc_span = ctxt, ea_hs_ty = hs_ty }
+ : rest_args )
= do { (ty_arg, inst_ty) <- tcVTA fun_conc_tvs fun_ty hs_ty
; let arg' = ETypeArg { ea_loc_span = ctxt, ea_hs_ty = hs_ty, ea_ty_arg = ty_arg }
- ; go pos (arg' : acc) inst_ty rest_args }
+ ; go fun_orig pos (arg' : acc) inst_ty rest_args }
-- Rule IVAR from Fig 4 of the QL paper:
- go1 pos acc fun_ty args@(EValArg {} : _)
+ go1 fun_orig pos acc fun_ty args@(EValArg {} : _)
| Just kappa <- getTyVar_maybe fun_ty
, isQLInstTyVar kappa
= -- Function type was of form f :: forall a b. t1 -> t2 -> b
@@ -861,7 +845,7 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
-- - We must be sure to actually update the variable right now,
-- not defer in any way, because this is a QL instantiation variable.
-- It's easier just to do the job directly here.
- do { arg_tys <- zipWithM new_arg_ty (leadingValArgs args) [pos..]
+ do { arg_tys <- zipWithM (new_arg_ty fun_orig) (leadingValArgs args) [pos..]
; res_ty <- newOpenFlexiTyVarTyQL do_ql TauTv
; let fun_ty' = mkScaledFunTys arg_tys res_ty
@@ -877,12 +861,12 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
-- Then fun_ty :: kk, fun_ty' :: Type, kind_co :: Type ~ kk
-- co_wrap :: (fun_ty' |> kind_co) ~ fun_ty'
- ; go pos acc' fun_ty' args }
+ ; go fun_orig pos acc' fun_ty' args }
-- Rule IARG from Fig 4 of the QL paper:
- go1 pos acc fun_ty
+ go1 fun_orig pos acc fun_ty
(EValArg { ea_arg = arg, ea_loc_span = ctxt } : rest_args)
- = do { let herald = mk_herald tc_fun (unLoc arg)
+ = do { let herald = mk_herald fun_orig tc_fun (unLoc arg)
; (fun_co, arg_ty, res_ty) <-
-- NB: matchActualFunTy does the rep-poly check.
-- For example, suppose we have f :: forall r (a::TYPE r). a -> Int
@@ -894,16 +878,15 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
matchActualFunTy herald
(Just $ HsExprTcThing tc_fun)
(n_val_args, fun_sigma) fun_ty
- ; ds_flag <- getDeepSubsumptionFlag_DataConHead tc_fun
- ; arg' <- quickLookArg ds_flag do_ql pos ctxt (rn_fun, fun_lspan) arg arg_ty
+ ; arg' <- quickLookArg do_ql pos ctxt rn_head arg arg_ty
; let acc' = arg' : addArgWrap (mkWpCastN fun_co) acc
- ; go (pos+1) acc' res_ty rest_args }
+ ; go fun_orig (pos+1) acc' res_ty rest_args }
- new_arg_ty :: LHsExpr GhcRn -> Int -> TcM (Scaled TcType)
+ new_arg_ty :: CtOrigin -> LHsExpr GhcRn -> Int -> TcM (Scaled TcType)
-- Make a fresh nus for each argument in rule IVAR
- new_arg_ty (L _ arg) i
+ new_arg_ty fun_orig (L _ arg) i
= do { arg_nu <- newArgTyVarTyQL do_ql $
- FRRExpectedFunTy (mk_herald tc_fun arg) i
+ FRRExpectedFunTy (mk_herald fun_orig tc_fun arg) i
-- Following matchActualFunTy, we create nu_i :: TYPE kappa_i[conc],
-- thereby ensuring that the arguments have concrete runtime representations
@@ -913,12 +896,13 @@ tcInstFun do_ql inst_final (fun_orig, rn_fun, fun_lspan) tc_fun fun_sigma rn_arg
; return (mkScaled mult_ty arg_nu) }
- mk_herald :: HsExpr GhcTc -> HsExpr GhcRn -> ExpectedFunTyCtxt
- mk_herald tc_fun arg
+ mk_herald :: CtOrigin -> HsExpr GhcTc -> HsExpr GhcRn -> ExpectedFunTyCtxt
+ mk_herald fun_orig tc_fun arg
= case fun_orig of
DoStmtOrigin -> ExpectedFunTySyntaxOp DoStmtOrigin tc_fun
_ -> ExpectedFunTyArg (HsExprTcThing tc_fun) arg
+
-- Is the argument supposed to instantiate a forall?
--
-- In other words, given a function application `fn arg`,
@@ -1883,23 +1867,23 @@ This turned out to be more subtle than I expected. Wrinkles:
-}
-quickLookArg :: DeepSubsumptionFlag -> QLFlag -> Int
- -> SrcSpan -- ^ location span of the whole application
+quickLookArg :: QLFlag -> Int
+ -> HsExprLoc -- ^ location span of the whole application
-> (HsExpr GhcRn, SrcSpan) -- ^ Head of the application chain and its source span
-> LHsExpr GhcRn -- ^ Argument
-> Scaled TcSigmaTypeFRR -- ^ Type expected by the function
-> TcM (HsExprArg 'TcpInst)
-- See Note [Quick Look at value arguments]
-quickLookArg _ NoQL _ app_lspan _ larg orig_arg_ty
+quickLookArg NoQL _ app_lspan _ larg orig_arg_ty
= skipQuickLook app_lspan larg orig_arg_ty
-quickLookArg ds_flag DoQL pos app_lspan fun_and_lspan larg orig_arg_ty
- = do { is_rho <- tcIsDeepRho ds_flag (scaledThing orig_arg_ty)
+quickLookArg DoQL pos app_lspan fun_and_lspan larg orig_arg_ty
+ = do { is_rho <- qlArgHasRhoType (scaledThing orig_arg_ty)
; traceTc "qla" (ppr orig_arg_ty $$ ppr is_rho)
; if not is_rho
then skipQuickLook app_lspan larg orig_arg_ty
else quickLookArg1 pos app_lspan fun_and_lspan larg orig_arg_ty }
-skipQuickLook :: SrcSpan -> LHsExpr GhcRn -> Scaled TcRhoType
+skipQuickLook :: HsExprLoc -> LHsExpr GhcRn -> Scaled TcRhoType
-> TcM (HsExprArg 'TcpInst)
skipQuickLook app_lspan larg arg_ty
= return (EValArg { ea_loc_span = app_lspan
@@ -1910,13 +1894,26 @@ whenQL :: QLFlag -> ZonkM () -> TcM ()
whenQL DoQL thing_inside = liftZonkM thing_inside
whenQL NoQL _ = return ()
-tcIsDeepRho :: DeepSubsumptionFlag -> TcType -> TcM Bool
--- This top-level zonk step, which is the reason we need a local 'go' loop,
--- is subtle. See Section 9 of the QL paper
+qlArgHasRhoType :: TcType -> TcM Bool
+-- `qlArgHasRhoType` checks that the expected argument type in rule
+-- App-lightning-bolt (Fig 5 in the paper) is indeed a rho-type.
+--
+-- It must apply the current QL substitution, so it any QLInstTyVar that it
+-- comes across. Why? See Section 5.7 in the paper; argument order matters.
+--
+-- What if we find an /un-filled/ QLInstVar? We treat this as a rho-type
+-- even though a later argument might force it to be sigma-type. See
+-- Section 9 in the paper.
+--
+-- With -XDeepSubsunption we need a /deep/ rho-type.
+-- (We don't need getDeepSubsumptionFlag_DataConHead here because this
+-- is only about QuickLook.)
-tcIsDeepRho ds_flag = go
+qlArgHasRhoType ty
+ = do { ds_flag <- getDeepSubsumptionFlag
+ ; go ds_flag ty }
where
- go ty
+ go ds_flag ty
| isSigmaTy ty
= return False
@@ -1924,12 +1921,12 @@ tcIsDeepRho ds_flag = go
, isQLInstTyVar kappa
= do { info <- readMetaTyVar kappa
; case info of
- Indirect arg_ty' -> go arg_ty'
+ Indirect arg_ty' -> go ds_flag arg_ty'
Flexi -> return True }
| Deep {} <- ds_flag
, Just (_, res_ty) <- tcSplitFunTy_maybe ty
- = go res_ty
+ = go ds_flag res_ty
| otherwise
= return True
@@ -1940,14 +1937,20 @@ isGuardedTy ty
| Just {} <- tcSplitAppTy_maybe ty = True
| otherwise = False
-quickLookArg1 :: Int -> SrcSpan -> (HsExpr GhcRn, SrcSpan) -> LHsExpr GhcRn
+quickLookArg1 :: Int -> HsExprLoc -> (HsExpr GhcRn, SrcSpan) -> LHsExpr GhcRn
-> Scaled TcRhoType -- Deeply skolemised
-> TcM (HsExprArg 'TcpInst)
-- quickLookArg1 implements the "QL Argument" judgement in Fig 5 of the paper
quickLookArg1 pos app_lspan (fun, fun_lspan) larg@(L _ arg) sc_arg_ty@(Scaled _ orig_arg_rho)
= addArgCtxt pos (fun, fun_lspan) larg $ -- Context needed for constraints
- -- generated by calls in arg
- do { ((rn_fun_arg, fun_lspan_arg), rn_args) <- splitHsApps arg
+ -- generated by calls in arg
+ do { traceTc "qla1" (ppr arg)
+
+ ; (rn_fun_arg, rn_args) <- splitHsApps arg
+
+ ; traceTc "qla2" (ppr arg)
+
+ ; fun_lspan_arg <- getFunSrcSpan rn_args
-- Step 1: get the type of the head of the argument
; (fun_ue, mb_fun_ty) <- tcCollectingUsage $ tcInferAppHead_maybe rn_fun_arg
@@ -1970,17 +1973,15 @@ quickLookArg1 pos app_lspan (fun, fun_lspan) larg@(L _ arg) sc_arg_ty@(Scaled _
do { let arg_tc_head = (tc_fun_arg_head, fun_lspan_arg)
; do_ql <- wantQuickLook rn_fun_arg
- ; arg_orig <- mk_origin fun_lspan_arg rn_fun_arg
; ((inst_args, app_res_rho), wanted)
<- captureConstraints $
- tcInstFun do_ql True (arg_orig, rn_fun_arg, fun_lspan_arg) tc_fun_arg_head fun_sigma_arg_head rn_args
+ tcInstFun do_ql True (rn_fun_arg, fun_lspan_arg) tc_fun_arg_head fun_sigma_arg_head rn_args
-- We must capture type-class and equality constraints here, but
-- not usage information. See (QLA6) in Note [Quick Look at
-- value arguments]
; traceTc "quickLookArg 2" $
vcat [ text "arg:" <+> ppr arg
- , text "orig:" <+> ppr arg_orig
, text "orig_arg_rho:" <+> ppr orig_arg_rho
, text "app_res_rho:" <+> ppr app_res_rho ]
@@ -2018,24 +2019,6 @@ quickLookArg1 pos app_lspan (fun, fun_lspan) larg@(L _ arg) sc_arg_ty@(Scaled _
, eaql_res_rho = app_res_rho }) }}}
-mk_origin :: SrcSpan -- SrcSpan of the function
- -> HsExpr GhcRn -- The head of the expression application chain
- -> TcM CtOrigin
-mk_origin fun_lspan rn_fun
- | not (isGeneratedSrcSpan fun_lspan)
- = return $ exprCtOrigin rn_fun
-
- | otherwise -- If the location is generated, the best we can do is to
- -- approximate by looking on top of the error message stack
- = do { err_ctxt_stack <- getErrCtxt
- ; let hs_ctxt = case err_ctxt_stack of
- (c:_) -> c
- [] -> pprPanic "mk_origin" (ppr rn_fun)
- ; traceTc "mk_origin" (pprHsCtxt hs_ctxt)
- ; return $ hsCtxtCtOrigin hs_ctxt
- }
-
-
{- *********************************************************************
* *
Folding over instantiation variables
@@ -2437,12 +2420,11 @@ isTagToEnum :: HsExpr GhcTc -> Bool
isTagToEnum (HsVar _ (L _ fun_id)) = fun_id `hasKey` tagToEnumKey
isTagToEnum _ = False
-tcTagToEnum :: (HsExpr GhcTc, SrcSpan) -> [HsExprArg 'TcpTc]
- -> TcRhoType
+tcTagToEnum :: HsExpr GhcTc -> [HsExprArg 'TcpTc] -> TcRhoType
-> TcM (HsExpr GhcTc)
-- tagToEnum# :: forall a. Int# -> a
-- See Note [tagToEnum#] Urgh!
-tcTagToEnum (tc_fun, fun_lspan) tc_args res_ty
+tcTagToEnum tc_fun tc_args res_ty
| [val_arg] <- dropWhile (not . isHsValArg) tc_args
= do { res_ty <- liftZonkM $ zonkTcType res_ty
@@ -2464,14 +2446,14 @@ tcTagToEnum (tc_fun, fun_lspan) tc_args res_ty
; let rep_ty = mkTyConApp rep_tc rep_args
tc_fun' = mkHsWrap (WpTyApp rep_ty) tc_fun
df_wrap = mkWpCastR (mkSymCo coi)
- tc_expr = rebuildHsApps (tc_fun', fun_lspan) [val_arg]
+ tc_expr = rebuildHsApps tc_fun' [val_arg]
; return (mkHsWrap df_wrap tc_expr) }}}}}
| otherwise
= failWithTc TcRnTagToEnumMissingValArg
where
- vanilla_result = return (rebuildHsApps (tc_fun, fun_lspan) tc_args)
+ vanilla_result = return (rebuildHsApps tc_fun tc_args)
check_enumeration ty' tc
| -- isTypeDataTyCon: see wrinkle (W1) in
=====================================
compiler/GHC/Tc/Gen/App.hs-boot deleted
=====================================
@@ -1,12 +0,0 @@
-module GHC.Tc.Gen.App where
-
-import GHC.Hs ( HsExpr )
-import GHC.Tc.Types ( TcM )
-import GHC.Tc.Types.Origin ( CtOrigin )
-import GHC.Tc.Utils.TcType ( TcSigmaType )
-import GHC.Hs.Extension ( GhcRn, GhcTc )
-
-
-import GHC.Prelude (Bool)
-
-tcExprSigma :: Bool -> CtOrigin -> HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType)
=====================================
compiler/GHC/Tc/Gen/Expand.hs
=====================================
@@ -0,0 +1,107 @@
+{-# LANGUAGE TypeFamilies #-}
+
+{-
+(c) The University of Glasgow 2006
+(c) The GRASP/AQUA Project, Glasgow University, 1992-1998
+-}
+
+module GHC.Tc.Gen.Expand( tcExpand ) where
+
+import GHC.Prelude
+
+import {-# SOURCE #-} GHC.Tc.Gen.Splice( getUntypedSpliceBody )
+
+import GHC.Hs
+
+import GHC.Tc.Utils.Monad
+import GHC.Tc.Types.ErrCtxt
+
+import GHC.Rename.Utils
+
+{- Note [Typechecking by expansion: overview]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For many constructs, rather than typechecking the user-written code
+directly, it's much easier to
+ * Expand (or desugar) the code to something simpler
+ * Typecheck that simpler expression
+
+Example: Typechecking the do expression. The typechecker looks (somewhat) like this:
+
+ tcExpr e@(HsDo _ stmts) rho = do { hse <- expandDoStmts stmts
+ ; tcHsExpansion hse rho }
+
+The `expandDoStmts` replaces the HsDo { x <- e1; return x }
+with something like
+ HSE { hse_ctxt = ExprCtxt e
+ , hse_exp = e1 >>= \ x -> x }
+and we then typecheck the expression `e1 >>= \ x -> x`
+
+See also Note [Handling overloaded and rebindable constructs]
+ and Note [Doing XXExprGhcRn in the Renamer vs Typechecker]
+
+The Big Question is how to ensure that error messages mention
+only user-written source code, and never talk about the expanded code.
+The rest of this Note explains how that is done.
+
+* The expansion process typically takes a user written thing
+ L lspan ue
+ and returns
+ L lspan (XExpr (ExpandedThingRn (HSE { hse_ctxt = ue
+ , hse_exp = ee } ))
+ where `ee` is the expansion of the user written thing `ue`
+
+* The type checker context has 3 key fields that describe the context:
+ TcLclCtxt { tcl_loc :: RealSrcSpan
+ , tcl_in_gen_code :: Bool
+ , tcl_err_ctxt :: ErrCtxtStack
+ , ... }
+ Note `tcl_loc` always points to a real place in the source code,
+ hence `RealSrcSpan`.
+
+ The `tcl_err_ctxt` is a stack of contexts, each saying something
+ like "In the expression: x+y" or "In second argument of `$` namely 'r { x=2 }'"
+
+ The `tcl_in_gen_code` is a boolean that keeps track of whether
+ the current expression being typechecked is compiler generated
+ or user generated.
+
+ INVARIANT: `tcl_loc` and `tcl_in_gen_code` are modified only in `setSrcSpan`.
+
+* Now, when
+ tcMonoLExpr :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
+ gets a located expression, it does 3 things:
+ (a) Calls `setSrcSpanA` to set the ambient source-code location
+ (b) Calls `addExprCtxt` to push a suitable `HsCtxt` on top of the `tcl_err_ctxt`.
+ (c) Calls `tcExpr` to typecheck the expression.
+
+* In these calls, if the `span` is generated (see `isGeneratedSrcSpan`), then
+ - `setSrcSpanA` sets `tcl_in_gen_code` to `True`, and leaves `tcl_loc` unchanged
+ - `addExprCtxt` is a no-op if `tcl_in_gen_code` is True
+ The result is that `tcl_loc` has the span from the innermost /user/ tree node;
+ and the ErrCtxtStack in `tcl_err_ctxt` only has contexts arisign from user code.
+
+* Note that inside an expansion we have sub-expressions from the original program.
+ As soon as we enter one of those, identified by a /user/ span, `setSrcSpanA` will
+ sets the `tcl_loc` to reflect that span, and switch off `tcl_in_gen_code`. Nice!
+-}
+
+---------------
+tcExpand :: HsExpr GhcRn -> TcM (Maybe (HsExpansion GhcRn))
+tcExpand e@(OpApp _ arg1 op arg2)
+ = return $ Just $
+ HSE { hse_ctxt = ExprCtxt e
+ , hse_exp = foldl ap op [arg1,arg2] }
+ where
+ ap f a = wrapGenSpan (HsApp noExtField f a)
+
+tcExpand (XExpr (ExpandedThingRn hse))
+ = return (Just hse)
+
+tcExpand e@(HsUntypedSplice splice_res _)
+-- See Note [Looking through Template Haskell splices in splitHsApps]
+ = do { fun <- getUntypedSpliceBody splice_res
+ ; return $ Just $
+ HSE { hse_ctxt = ExprCtxt e
+ , hse_exp = wrapGenSpan fun } }
+
+tcExpand _ = return Nothing
=====================================
compiler/GHC/Tc/Gen/Expr.hs
=====================================
@@ -13,7 +13,7 @@
module GHC.Tc.Gen.Expr
( tcCheckPolyExpr, tcCheckPolyExprNC,
tcCheckMonoExpr, tcCheckMonoExprNC,
- tcInferExpr, tcInferSigma,
+ tcInferExpr, tcInferSigma, tcInferExprSigma,
tcInferRho, tcInferRhoNC,
tcMonoLExpr, tcMonoLExprNC,
tcInferRhoFRR, tcInferRhoFRRNC,
@@ -30,10 +30,10 @@ import {-# SOURCE #-} GHC.Tc.Gen.Splice
import GHC.Hs
import GHC.Hs.Syn.Type
-
import GHC.Rename.Utils
import GHC.Rename.Env ( addUsedGRE, getUpdFieldLbls )
+import GHC.Tc.Gen.Expand( tcExpand )
import GHC.Tc.Gen.App
import GHC.Tc.Gen.Head
import GHC.Tc.Gen.Do
@@ -237,6 +237,9 @@ tcPolyExprCheck expr res_ty
tcInferSigma :: LHsExpr GhcRn -> TcM (LHsExpr GhcTc, TcSigmaType)
tcInferSigma = tcInferExpr IIF_Sigma
+tcInferExprSigma :: HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType)
+tcInferExprSigma e = runInfer IIF_Sigma IFRR_Any (tcExpr e)
+
tcInferRho, tcInferRhoNC :: LHsExpr GhcRn -> TcM (LHsExpr GhcTc, TcRhoType)
-- Infer a *rho*-type. The return type is always instantiated.
tcInferRho = tcInferExpr IIF_DeepRho
@@ -291,6 +294,12 @@ tcMonoLExprNC (L loc expr) res_ty
do { expr' <- tcExpr expr res_ty
; return (L loc expr') }
+---------------
+tcCollectApp :: HsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
+tcCollectApp the_app res_ty
+ = do { (fun, args) <- splitHsApps the_app
+ ; tcApp the_app fun args res_ty }
+
---------------
tcExpr :: HsExpr GhcRn
-> ExpRhoType -- DeepSubsumption <=> when checking, this type
@@ -312,19 +321,11 @@ tcExpr :: HsExpr GhcRn
-- - ones taken apart by GHC.Tc.Gen.Head.splitHsApps
-- - ones understood by GHC.Tc.Gen.Head.tcInferAppHead_maybe
-- See Note [Application chains and heads] in GHC.Tc.Gen.App
-tcExpr e@(HsVar {}) res_ty = tcApp e res_ty
-tcExpr e@(HsApp {}) res_ty = tcApp e res_ty
-tcExpr e@(OpApp {}) res_ty = tcApp e res_ty
-tcExpr e@(HsAppType {}) res_ty = tcApp e res_ty
-tcExpr e@(ExprWithTySig {}) res_ty = tcApp e res_ty
-tcExpr e@(XExpr (HsRecSelRn{})) res_ty = tcApp e res_ty
-
--- Renamer expanded expressions (eg. Right/Left sections)
--- or tcExpr expanded expressions (eg. Do statements and Record updates)
--- are type checked using tcHsExpansion.
--- See Note [Typechecking by expansion: overview]
-tcExpr (XExpr (ExpandedThingRn hse)) res_ty = tcHsExpansion hse res_ty
-
+tcExpr e@(HsVar {}) res_ty = tcApp e e [] res_ty
+tcExpr e@(ExprWithTySig {}) res_ty = tcApp e e [] res_ty
+tcExpr e@(XExpr (HsRecSelRn{})) res_ty = tcApp e e [] res_ty
+tcExpr e@(HsAppType {}) res_ty = tcCollectApp e res_ty
+tcExpr e@(HsApp {}) res_ty = tcCollectApp e res_ty
-- Typecheck an occurrence of an unbound Id
--
@@ -392,7 +393,7 @@ tcExpr e@(HsOverLit _ lit) res_ty
-- See Note [Short cut for overloaded literals] in GHC.Tc.Utils.TcMType
; case mb_res of
Just lit' -> return (HsOverLit noExtField lit')
- Nothing -> tcApp e res_ty }
+ Nothing -> tcApp e e [] res_ty }
-- Why go via tcApp? See Note [Typechecking overloaded literals]
{- Note [Typechecking overloaded literals]
@@ -530,8 +531,9 @@ tcExpr (HsCase ctxt scrut matches) res_ty
tcExpr (HsIf x pred b1 b2) res_ty
= do { pred' <- tcCheckMonoExpr pred boolTy
- ; (u1,b1') <- tcCollectingUsage $ tcMonoLExpr b1 res_ty
- ; (u2,b2') <- tcCollectingUsage $ tcMonoLExpr b2 res_ty
+ ; let res_ty' = adjustExpTypeForCaseBranches res_ty [b1,b2]
+ ; (u1,b1') <- tcCollectingUsage $ tcMonoLExpr b1 res_ty'
+ ; (u2,b2') <- tcCollectingUsage $ tcMonoLExpr b2 res_ty'
; tcEmitBindingUsage (supUE u1 u2)
; return (HsIf x pred' b1' b2') }
@@ -730,19 +732,6 @@ tcExpr e@(RecordUpd { rupd_flds = OverloadedRecUpdFields {}}) _
tcExpr (ArithSeq _ witness seq) res_ty
= tcArithSeq witness seq res_ty
-{-
-************************************************************************
-* *
- Record dot syntax
-* *
-************************************************************************
--}
-
--- These terms have been replaced by their expanded expressions in the renamer. See
--- Note [Overview of record dot syntax].
-tcExpr (HsGetField _ _ _) _ = panic "GHC.Tc.Gen.Expr: tcExpr: HsGetField: Not implemented"
-tcExpr (HsProjection _ _) _ = panic "GHC.Tc.Gen.Expr: tcExpr: HsProjection: Not implemented"
-
{-
************************************************************************
* *
@@ -755,17 +744,7 @@ tcExpr (HsProjection _ _) _ = panic "GHC.Tc.Gen.Expr: tcExpr: HsProjection: Not
-- See Note [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.
tcExpr (HsTypedSplice ext splice) res_ty = tcTypedSplice ext splice res_ty
tcExpr e@(HsTypedBracket _ext body) res_ty = tcTypedBracket e body res_ty
-
tcExpr e@(HsUntypedBracket ps body) res_ty = tcUntypedBracket e body ps res_ty
-tcExpr (HsUntypedSplice splice _) res_ty
- -- Since `tcApp` deals with `HsUntypedSplice` (in `splitHsApps`), you might
- -- wonder why we don't delegate to `tcApp` as we do for `HsVar`, etc.
- -- (See the initial block of equations for `tcExpr`.) But we can't do this
- -- for `HsUntypedSplice`; to see why, read Wrinkle (UTS1) in
- -- Note [Looking through Template Haskell splices in splitHsApps] in
- -- GHC.Tc.Gen.Head.
- = do { expr <- getUntypedSpliceBody splice
- ; tcExpr expr res_ty }
{-
************************************************************************
@@ -775,10 +754,12 @@ tcExpr (HsUntypedSplice splice _) res_ty
************************************************************************
-}
-tcExpr (HsOverLabel {}) ty = pprPanic "tcExpr:HsOverLabel" (ppr ty)
-tcExpr (SectionL {}) ty = pprPanic "tcExpr:SectionL" (ppr ty)
-tcExpr (SectionR {}) ty = pprPanic "tcExpr:SectionR" (ppr ty)
-
+-- See Note [Typechecking by expansion: overview]
+tcExpr e res_ty
+ = do { mb_hse <- tcExpand e
+ ; case mb_hse of
+ Just hse -> tcHsExpansion hse res_ty
+ Nothing -> pprPanic "tcExpr: unhandled case:" (ppr e) }
{-
************************************************************************
@@ -788,73 +769,6 @@ tcExpr (SectionR {}) ty = pprPanic "tcExpr:SectionR" (ppr ty)
************************************************************************
-}
-{- Note [Typechecking by expansion: overview]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-For many constructs, rather than typechecking the user-written code
-directly, it's much easier to
- * Expand (or desugar) the code to something simpler
- * Typecheck that simpler expression
-
-Example: Typechecking the do expression. The typechecker looks (somewhat) like this:
-
- tcExpr e@(HsDo _ stmts) rho = do { hse <- expandDoStmts stmts
- ; tcHsExpansion hse rho }
-
-The `expandDoStmts` replaces the HsDo { x <- e1; return x }
-with something like
- HSE { hse_ctxt = ExprCtxt e
- , hse_exp = e1 >>= \ x -> x }
-and we then typecheck the expression `e1 >>= \ x -> x`
-
-See also Note [Handling overloaded and rebindable constructs]
- and Note [Doing XXExprGhcRn in the Renamer vs Typechecker]
-
-The Big Question is how to ensure that error messages mention
-only user-written source code, and never talk about the expanded code.
-The rest of this Note explains how that is done.
-
-* The expansion process typically takes a user written thing
- L lspan ue
- and returns
- L lspan (XExpr (ExpandedThingRn (HSE { hse_ctxt = ue
- , hse_exp = ee } ))
- where `ee` is the expansion of the user written thing `ue`
-
-* The type checker context has 3 key fields that describe the context:
- TcLclCtxt { tcl_loc :: RealSrcSpan
- , tcl_in_gen_code :: Bool
- , tcl_err_ctxt :: ErrCtxtStack
- , ... }
- Note `tcl_loc` always points to a real place in the source code,
- hence `RealSrcSpan`.
-
- The `tcl_err_ctxt` is a stack of contexts, each saying something
- like "In the expression: x+y" or "In second argument of `$` namely 'r { x=2 }'"
-
- The `tcl_in_gen_code` is a boolean that keeps track of whether
- the current expression being typechecked is compiler generated
- or user generated.
-
- INVARIANT: `tcl_loc` and `tcl_in_gen_code` are modified only in `setSrcSpan`.
-
-* Now, when
- tcMonoLExpr :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
- gets a located expression, it does 3 things:
- (a) Calls `setSrcSpanA` to set the ambient source-code location
- (b) Calls `addExprCtxt` to push a suitable `HsCtxt` on top of the `tcl_err_ctxt`.
- (c) Calls `tcExpr` to typecheck the expression.
-
-* In these calls, if the `span` is generated (see `isGeneratedSrcSpan`), then
- - `setSrcSpanA` sets `tcl_in_gen_code` to `True`, and leaves `tcl_loc` unchanged
- - `addExprCtxt` is a no-op if `tcl_in_gen_code` is True
- The result is that `tcl_loc` has the span from the innermost /user/ tree node;
- and the ErrCtxtStack in `tcl_err_ctxt` only has contexts arisign from user code.
-
-* Note that inside an expansion we have sub-expressions from the original program.
- As soon as we enter one of those, identified by a /user/ span, `setSrcSpanA` will
- sets the `tcl_loc` to reflect that span, and switch off `tcl_in_gen_code`. Nice!
--}
-
tcHsExpansion :: HsExpansion GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
tcHsExpansion (HSE { hse_ctxt = o, hse_exp = e }) res_ty
= do { e' <- tcMonoLExpr e res_ty
=====================================
compiler/GHC/Tc/Gen/Expr.hs-boot
=====================================
@@ -35,6 +35,8 @@ tcInferRho, tcInferRhoNC ::
tcInferRhoFRR, tcInferRhoFRRNC ::
FixedRuntimeRepContext -> LHsExpr GhcRn -> TcM (LHsExpr GhcTc, TcRhoType)
+tcInferExprSigma :: HsExpr GhcRn -> TcM (HsExpr GhcTc, TcSigmaType)
+
tcInferExpr :: InferInstFlag -> LHsExpr GhcRn -> TcM (LHsExpr GhcTc, TcType)
tcSyntaxOp :: CtOrigin
=====================================
compiler/GHC/Tc/Gen/Head.hs
=====================================
@@ -9,9 +9,9 @@
-}
module GHC.Tc.Gen.Head
- ( HsExprArg(..), TcPass(..), QLFlag(..), EWrap(..)
+ ( HsExprArg(..), HsExprLoc, TcPass(..), QLFlag(..), EWrap(..)
, splitHsApps, rebuildHsApps
- , addArgWrap, isHsValArg
+ , addArgWrap, isHsValArg, getFunSrcSpan
, leadingValArgs, isVisibleArg, getDeepSubsumptionFlag_DataConHead
, tcInferAppHead, tcInferAppHead_maybe
@@ -22,16 +22,13 @@ module GHC.Tc.Gen.Head
, pprArgInst, addFunResCtxt ) where
import {-# SOURCE #-} GHC.Tc.Gen.Expr( tcExpr, tcCheckPolyExprNC, tcPolyLExprSig )
-import {-# SOURCE #-} GHC.Tc.Gen.Splice( getUntypedSpliceBody )
-import {-# SOURCE #-} GHC.Tc.Gen.App( tcExprSigma )
import GHC.Prelude
import GHC.Hs
import GHC.Hs.Syn.Type
-import GHC.Rename.Utils (mkExpandedTc, mkExpandedExprTc)
-
import GHC.Tc.Gen.HsType
+import GHC.Tc.Gen.Expand( tcExpand )
import GHC.Tc.Gen.Bind( chooseInferredQuantifiers )
import GHC.Tc.Gen.Sig( tcUserTypeSig, tcInstSig )
import GHC.Tc.TyCl.PatSyn( patSynBuilderOcc )
@@ -86,7 +83,7 @@ import GHC.Data.Maybe
The data type HsExprArg :: TcPass -> Type
is a very local type, used only within this module and GHC.Tc.Gen.App
-* It's really a zipper for an application chain
+* It's just a bog-standard zipper for an application chain
See Note [Application chains and heads] in GHC.Tc.Gen.App for
what an "application chain" is.
@@ -147,6 +144,8 @@ takes apart either an HsApp, or an infix OpApp, returning
* We do not look through expanded expressions (except PopErrCtxt.)
-}
+type HsExprLoc = EpAnn AnnListItem -- The location attached to a HsExpr
+
data TcPass = TcpRn -- Arguments decomposed
| TcpInst -- Function instantiated
| TcpTc -- Typechecked
@@ -154,34 +153,34 @@ data TcPass = TcpRn -- Arguments decomposed
data HsExprArg (p :: TcPass) where -- See Note [HsExprArg]
-- Data constructor EValArg represents a value argument
- EValArg :: { ea_loc_span :: SrcSpan
- , ea_arg_ty :: !(XEVAType p)
- , ea_arg :: LHsExpr (GhcPass (XPass p)) }
+ EValArg :: { ea_loc_span :: HsExprLoc
+ , ea_arg_ty :: !(XEVAType p)
+ , ea_arg :: LHsExpr (GhcPass (XPass p)) }
-> HsExprArg p
-- Data constructor EValArgQL represents an argument that has been
-- partly-type-checked by Quick Look; see Note [EValArgQL]
- EValArgQL :: { eaql_loc_span :: SrcSpan
- , eaql_arg_ty :: Scaled TcSigmaType -- Argument type expected by function
- , eaql_larg :: LHsExpr GhcRn -- Original application, for
- -- location and error msgs
- , eaql_rn_fun :: HsExpr GhcRn -- Head of the argument if it is an application
- , eaql_tc_fun :: (HsExpr GhcTc, SrcSpan) -- Typechecked head and its location span
- , eaql_fun_ue :: UsageEnv -- Usage environment of the typechecked head (QLA5)
- , eaql_args :: [HsExprArg 'TcpInst] -- Args: instantiated, not typechecked
- , eaql_wanted :: WantedConstraints
- , eaql_encl :: Bool -- True <=> we have already qlUnified
- -- eaql_arg_ty and eaql_res_rho
- , eaql_res_rho :: TcRhoType } -- Result type of the application
+ EValArgQL :: { eaql_loc_span :: HsExprLoc
+ , eaql_arg_ty :: Scaled TcSigmaType -- Argument type expected by function
+ , eaql_larg :: LHsExpr GhcRn -- Original application, for
+ -- location and error msgs
+ , eaql_rn_fun :: HsExpr GhcRn -- Head of the argument if it is an application
+ , eaql_tc_fun :: (HsExpr GhcTc, SrcSpan) -- Typechecked head and its location span
+ , eaql_fun_ue :: UsageEnv -- Usage environment of the typechecked head (QLA5)
+ , eaql_args :: [HsExprArg 'TcpInst] -- Args: instantiated, not typechecked
+ , eaql_wanted :: WantedConstraints
+ , eaql_encl :: Bool -- True <=> we have already qlUnified
+ -- eaql_arg_ty and eaql_res_rho
+ , eaql_res_rho :: TcRhoType } -- Result type of the application
-> HsExprArg 'TcpInst -- Only exists in TcpInst phase
- ETypeArg :: { ea_loc_span :: SrcSpan
- , ea_hs_ty :: LHsWcType GhcRn -- The type arg
- , ea_ty_arg :: !(XETAType p) } -- Kind-checked type arg
+ ETypeArg :: { ea_loc_span :: HsExprLoc
+ , ea_hs_ty :: LHsWcType GhcRn -- The type arg
+ , ea_ty_arg :: !(XETAType p) } -- Kind-checked type arg
-> HsExprArg p
- EPrag :: SrcSpan -> (HsPragE (GhcPass (XPass p))) -> HsExprArg p
- EWrap :: EWrap -> HsExprArg p
+ EPrag :: HsExprLoc -> (HsPragE (GhcPass (XPass p))) -> HsExprArg p
+ EWrap :: EWrap -> HsExprArg p
type family XETAType (p :: TcPass) where -- Type arguments
XETAType 'TcpRn = NoExtField
@@ -193,8 +192,8 @@ type family XEVAType (p :: TcPass) where -- Value arguments
data QLFlag = DoQL | NoQL
-data EWrap = EPar SrcSpan
- | EExpand (HsExpr GhcRn)
+data EWrap = EPar HsExprLoc
+ | EExpand HsExprLoc HsCtxt
| EHsWrap HsWrapper
@@ -207,11 +206,11 @@ type family XPass (p :: TcPass) where
XPass 'TcpInst = 'Renamed
XPass 'TcpTc = 'Typechecked
-mkEValArg :: SrcSpan -> LHsExpr GhcRn -> HsExprArg 'TcpRn
+mkEValArg :: HsExprLoc -> LHsExpr GhcRn -> HsExprArg 'TcpRn
mkEValArg src_loc e = EValArg { ea_arg = e, ea_loc_span = src_loc
, ea_arg_ty = noExtField }
-mkETypeArg :: SrcSpan -> LHsWcType GhcRn -> HsExprArg 'TcpRn
+mkETypeArg :: HsExprLoc -> LHsWcType GhcRn -> HsExprArg 'TcpRn
mkETypeArg src_loc hs_ty =
ETypeArg { ea_loc_span = src_loc
, ea_hs_ty = hs_ty
@@ -223,74 +222,17 @@ addArgWrap wrap args
| otherwise = EWrap (EHsWrap wrap) : args
-splitHsApps :: HsExpr GhcRn
- -> TcM ( (HsExpr GhcRn, SrcSpan) -- Head
- , [HsExprArg 'TcpRn]) -- Args
--- See Note [splitHsApps].
---
--- This uses the TcM monad solely because we must run modFinalizers when looking
--- through HsUntypedSplices
--- (see Note [Looking through Template Haskell splices in splitHsApps]).
-splitHsApps e = go e noSrcSpan []
- where
- go :: HsExpr GhcRn -> SrcSpan -> [HsExprArg 'TcpRn]
- -> TcM ((HsExpr GhcRn, SrcSpan), [HsExprArg 'TcpRn])
- -- Modify the SrcSpan as we walk inwards, so it describes the next argument
- go (HsPar _ (L l fun)) lspan args = go fun (locA l) (EWrap (EPar lspan) : args)
- go (HsPragE _ p (L l fun)) lspan args = go fun (locA l) (EPrag lspan p : args)
- go (HsAppType _ (L l fun) ty) lspan args = go fun (locA l) (mkETypeArg lspan ty : args)
- go (HsApp _ (L l fun) arg) lspan args = go fun (locA l) (mkEValArg lspan arg : args)
-
- -- See Note [Looking through Template Haskell splices in splitHsApps]
- go e@(HsUntypedSplice splice_res splice) _ args
- = do { fun <- getUntypedSpliceBody splice_res
- ; go fun lspan' (EWrap (EExpand e) : args) }
- where
- lspan' :: SrcSpan
- lspan' = case splice of
- HsUntypedSpliceExpr _ (L l _) -> locA l -- l :: SrcAnn AnnListItem
- HsQuasiQuote _ _ (L l _) -> locA l -- l :: SrcAnn NoEpAnns
- (XUntypedSplice (HsImplicitLiftSplice _ _ _ (L l _))) -> locA l
-
- -- See Note [Desugar OpApp in the typechecker]
- go e@(OpApp _ arg1 (L l op) arg2) _ args
- = pure ( (op, locA l)
- , mkEValArg noSrcSpan arg1
- : mkEValArg noSrcSpan arg2
- -- noSrcSpan because this the span of the call,
- -- and its hard to say exactly what that is
- : EWrap (EExpand e)
- : args )
-
- go e lspan args = pure ((e, lspan), args)
-
-
--- | Rebuild an application: takes a type-checked application head
--- expression together with arguments in the form of typechecked 'HsExprArg's
--- and returns a typechecked application of the head to the arguments.
-rebuildHsApps :: (HsExpr GhcTc, SrcSpan)
- -- ^ the function being applied
- -> [HsExprArg 'TcpTc]
- -- ^ the arguments to the function
- -> HsExpr GhcTc
-rebuildHsApps (fun, _) [] = fun
-rebuildHsApps (fun, sloc) (arg : args)
- = case arg of
- EValArg { ea_arg = arg, ea_loc_span = sloc' }
- -> rebuildHsApps (HsApp noExtField lfun arg, sloc') args
- ETypeArg { ea_hs_ty = hs_ty, ea_ty_arg = ty, ea_loc_span = sloc' }
- -> rebuildHsApps (HsAppType ty lfun hs_ty, sloc') args
- EPrag sloc' p
- -> rebuildHsApps (HsPragE noExtField p lfun, sloc') args
- EWrap (EPar sloc')
- -> rebuildHsApps (gHsPar lfun, sloc') args
- EWrap (EExpand o)
- -> rebuildHsApps (mkExpandedExprTc o fun, sloc) args
- EWrap (EHsWrap wrap)
- -> rebuildHsApps (mkHsWrap wrap fun, sloc) args
- where
- lfun = L (noAnnSrcSpan sloc) fun
+--------------------
+getFunSrcSpan :: [HsExprArg 'TcpRn] -> TcM SrcSpan
+getFunSrcSpan [] = getSrcSpanM
+getFunSrcSpan (ETypeArg { ea_loc_span = l } : _) = return (locA l)
+getFunSrcSpan (EValArg { ea_loc_span = l } : _) = return (locA l)
+getFunSrcSpan (EPrag l _ : _) = return (locA l)
+getFunSrcSpan (EWrap (EPar l) : _) = return (locA l)
+getFunSrcSpan (EWrap (EExpand l _) : _) = return (locA l)
+getFunSrcSpan (EWrap (EHsWrap {}) : args) = getFunSrcSpan args
+--------------------
isHsValArg :: HsExprArg id -> Bool
isHsValArg (EValArg {}) = True
isHsValArg _ = False
@@ -334,13 +276,60 @@ pprArgInst (EValArgQL { eaql_tc_fun = fun, eaql_args = args, eaql_res_rho = ty})
2 (vcat [ vcat (map pprArgInst args), text "ea_ql_ty:" <+> ppr ty ])
instance Outputable EWrap where
- ppr (EPar _) = text "EPar"
- ppr (EHsWrap w) = text "EHsWrap" <+> ppr w
- ppr (EExpand orig) = text "EExpand" <+> ppr orig
+ ppr (EPar _) = text "EPar"
+ ppr (EHsWrap w) = text "EHsWrap" <+> ppr w
+ ppr (EExpand _ _) = text "EExpand" -- No Outputable instance for HsCtxt yet
+
+
+
+{- *********************************************************************
+* *
+ Splitting and rebuilding
+* *
+********************************************************************* -}
+
+splitHsApps :: HsExpr GhcRn -> TcM (HsExpr GhcRn, [HsExprArg 'TcpRn])
+splitHsApps e = go e []
+ where
+ go (HsPar _ (L l fun)) args = go fun (EWrap (EPar l) : args)
+ go (HsPragE _ p (L l fun)) args = go fun (EPrag l p : args)
+ go (HsAppType _ (L l fun) ty) args = go fun (mkETypeArg l ty : args)
+ go (HsApp _ (L l fun) arg) args = go fun (mkEValArg l arg : args)
+ go fun args = do { mb_hse <- tcExpand fun
+ ; case mb_hse of
+ Just (HSE { hse_ctxt = orig, hse_exp = L l fun' })
+ -> go fun' (EWrap (EExpand l orig) : args)
+ Nothing
+ -> return (fun, args) }
+
+-- | Rebuild an application: takes a type-checked application head
+-- expression together with arguments in the form of typechecked 'HsExprArg's
+-- and returns a typechecked application of the head to the arguments.
+rebuildHsApps :: HsExpr GhcTc
+ -- ^ the function being applied
+ -> [HsExprArg 'TcpTc]
+ -- ^ the arguments to the function
+ -> HsExpr GhcTc
+rebuildHsApps fun [] = fun
+rebuildHsApps fun (arg : args)
+ = case arg of
+ EValArg { ea_arg = arg, ea_loc_span = l }
+ -> rebuildHsApps (HsApp noExtField (L l fun) arg) args
+ ETypeArg { ea_hs_ty = hs_ty, ea_ty_arg = ty, ea_loc_span = l }
+ -> rebuildHsApps (HsAppType ty (L l fun) hs_ty) args
+ EPrag l p
+ -> rebuildHsApps (HsPragE noExtField p (L l fun)) args
+ EWrap (EPar l)
+ -> rebuildHsApps (HsPar noExtField (L l fun)) args
+ EWrap (EExpand l o)
+ -> rebuildHsApps (XExpr (ExpandedThingTc (HSE o (L l fun)))) args
+ EWrap (EHsWrap wrap)
+ -> rebuildHsApps (mkHsWrap wrap fun) args
+
{- Note [Desugar OpApp in the typechecker]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Operator sections are desugared in the renamer; see GHC.Rename.Expr
+pOperator sections are desugared in the renamer; see GHC.Rename.Expr
Note [Handling overloaded and rebindable constructs].
But for reasons explained there, we rename OpApp to OpApp. Then,
here in the typechecker, we desugar it to a use of ExpandedThingRn.
@@ -401,6 +390,8 @@ handling splices and quasiquotes has already been performed by the renamer by
the time we get to `splitHsApps`.
Wrinkle (UTS1):
+*** TODO *** put this somewhere else
+
`tcExpr` has a separate case for `HsUntypedSplice`s that do /not/ occur at the
head of an application. This is important to handle programs like this one:
@@ -446,9 +437,7 @@ tcInferAppHead (fun,fun_lspan)
do { mb_tc_fun <- tcInferAppHead_maybe fun
; case mb_tc_fun of
Just (fun', fun_sigma) -> return (fun', fun_sigma)
- Nothing -> runInferRho (tcExpr fun)
-
- }
+ Nothing -> runInferRho (tcExpr fun) }
tcInferAppHead_maybe :: HsExpr GhcRn
-> TcM (Maybe (HsExpr GhcTc, TcSigmaType))
@@ -457,23 +446,11 @@ tcInferAppHead_maybe :: HsExpr GhcRn
-- XExpr's although complicated needs to be looked through, useful for QL things when
-- the argument is an XExpr
tcInferAppHead_maybe fun = case fun of
- HsVar _ nm
- -> Just <$> tcInferId nm
- ExprWithTySig _ e hs_ty
- -> Just <$>tcExprWithSig e hs_ty
- HsOverLit _ lit
- -> Just <$> tcInferOverLit lit
- XExpr (HsRecSelRn f)
- -> Just <$> tcInferRecSelId f
- XExpr (ExpandedThingRn (HSE o (L loc e)))
- -> setSrcSpan (locA loc) $ Just <$>
- do { (e', ty) <- tcExprSigma False (hsCtxtCtOrigin o) e
- ; return (mkExpandedTc o (L loc e'), ty) }
- -- We do not want to instantiate the type of the head as there may be
- -- visible type applications in the argument.
- -- c.f. T19167
- _
- -> return Nothing
+ HsVar _ nm -> Just <$> tcInferId nm
+ ExprWithTySig _ e hs_ty -> Just <$> tcExprWithSig e hs_ty
+ HsOverLit _ lit -> Just <$> tcInferOverLit lit
+ XExpr (HsRecSelRn f) -> Just <$> tcInferRecSelId f
+ _ -> return Nothing
{- *********************************************************************
* *
=====================================
compiler/GHC/Tc/Gen/Match.hs
=====================================
@@ -219,10 +219,10 @@ tcMatches :: (AnnoBody body, Outputable (body GhcTc))
-> MatchGroup GhcRn (LocatedA (body GhcRn))
-> TcM (MatchGroup GhcTc (LocatedA (body GhcTc)))
-tcMatches ctxt tc_body pat_tys rhs_ty (MG { mg_alts = L l matches
+tcMatches ctxt tc_body pat_tys exp_ty (MG { mg_alts = L l matches
, mg_ext = origin })
| null matches -- Deal with case e of {}
- -- Since there are no branches, no one else will fill in rhs_ty
+ -- Since there are no branches, no one else will fill in exp_ty
-- when in inference mode, so we must do it ourselves,
-- here, using expTypeToType
= do { tcEmitBindingUsage bottomUE
@@ -233,17 +233,19 @@ tcMatches ctxt tc_body pat_tys rhs_ty (MG { mg_alts = L l matches
[ExpForAllPatTy tvb] -> failWithTc $ TcRnEmptyCase ctxt (EmptyCaseForall tvb)
[] -> panic "tcMatches: no arguments in EmptyCase"
_t1:(_t2:_ts) -> panic "tcMatches: multiple arguments in EmptyCase"
- ; rhs_ty <- expTypeToType rhs_ty
+ ; rhs_ty <- expTypeToType exp_ty
; return (MG { mg_alts = L l []
, mg_ext = MatchGroupTc [pat_ty] rhs_ty origin
}) }
| otherwise
- = do { umatches <- mapM (tcCollectingUsage . tcMatch tc_body pat_tys rhs_ty) matches
- ; let (usages, matches') = unzip umatches
+ = do { let exp_ty' = adjustExpTypeForCaseBranches exp_ty matches
+ tc_match match = tcCollectingUsage $
+ tcMatch tc_body pat_tys exp_ty' match
+ ; (usages, matches') <- mapAndUnzipM tc_match matches
; tcEmitBindingUsage $ supUEs usages
; pat_tys <- mapM readScaledExpType (filter_out_forall_pat_tys pat_tys)
- ; rhs_ty <- readExpType rhs_ty
+ ; rhs_ty <- readExpType exp_ty
; traceTc "tcMatches" (ppr matches' $$ ppr pat_tys $$ ppr rhs_ty)
; return (MG { mg_alts = L l matches'
, mg_ext = MatchGroupTc pat_tys rhs_ty origin
=====================================
compiler/GHC/Tc/Utils/TcMType.hs
=====================================
@@ -63,7 +63,7 @@ module GHC.Tc.Utils.TcMType (
mkCheckExpType, newInferExpType, newInferExpTypeFRR,
runInfer, runInferRho, runInferSigma, runInferKind, runInferRhoFRR, runInferSigmaFRR,
readExpType, readExpType_maybe, readScaledExpType,
- expTypeToType, scaledExpTypeToType,
+ expTypeToType, scaledExpTypeToType, adjustExpTypeForCaseBranches,
checkingExpType_maybe, checkingExpType,
inferResultToType, ensureMonoType, promoteTcType,
@@ -499,6 +499,17 @@ inferResultToType (IR { ir_uniq = u, ir_lvl = tc_lvl
; let conc_orig = ConcreteFRR $ FixedRuntimeRepOrigin tau frr
; return tau }
+adjustExpTypeForCaseBranches :: ExpRhoType -> [branch] -> ExpRhoType
+-- See Note [fillInferResult: multiple branches]
+adjustExpTypeForCaseBranches exp_ty branches
+ = case exp_ty of
+ Infer ir | IR { ir_inst = IIF_Sigma } <- ir
+ , branches `lengthAtLeast` 2
+ -> Infer (ir { ir_inst = IIF_DeepRho })
+ | otherwise
+ -> exp_ty
+ Check {} -> exp_ty
+
{- Note [inferResultToType]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
expTypeToType and inferResultType convert an InferResult to a monotype.
=====================================
compiler/GHC/Tc/Utils/Unify.hs
=====================================
@@ -99,13 +99,12 @@ import qualified GHC.LanguageExtensions as LangExt
import GHC.Builtin.Types
import GHC.Types.Name
-import GHC.Types.Id( idType, isDataConId )
+import GHC.Types.Id( idType )
import GHC.Types.Var as Var
import GHC.Types.Var.Set
import GHC.Types.Var.Env
import GHC.Types.Basic
import GHC.Types.Unique.Set (nonDetEltsUniqSet)
-import GHC.Types.SrcLoc (unLoc, GenLocated (..))
import GHC.Utils.Misc
import GHC.Utils.Outputable as Outputable
@@ -426,7 +425,7 @@ Some examples:
tcSkolemiseGeneral
:: HasDebugCallStack
- => DeepSubsumptionFlag
+ => DeepSubsumptionFlag -- Ignores the DeepSubsumptionDepth
-> UserTypeCtxt
-> TcType -> TcType -- top_ty and expected_ty
-- Here, top_ty is the type we started to skolemise; used only in SigSkol
@@ -1169,7 +1168,7 @@ fillInferResultNoInst act_res_ty (IR { ir_uniq = u
; return final_co } }
-fillInferResult :: DeepSubsumptionFlag -> CtOrigin -> TcType -> InferResult -> TcM HsWrapper
+fillInferResult :: DeepSubsumptionFlag -> CtOrigin -> TcSigmaType -> InferResult -> TcM HsWrapper
-- See Note [Instantiation of InferResult]
fillInferResult ds_flag ct_orig res_ty ires@(IR { ir_inst = iif })
= case iif of
@@ -1203,7 +1202,7 @@ There are two things to worry about:
T1 -> e1
T2 -> e2
-Our typing rules are:
+In general our typing rules are:
* The RHS of a existential or GADT alternative must always be a
monotype, regardless of the number of alternatives.
@@ -1218,17 +1217,13 @@ Our typing rules are:
We use choice (2) in that Section.
(GHC 8.10 and earlier used choice (1).)
- But note that
- case e of
- True -> hr
- False -> \x -> hr x
- will fail, because we still /infer/ both branches, so the \x will get
- a (monotype) unification variable, which will fail to unify with
- (forall a. a->a)
+Note [fillInferResult: GADTs and existentials]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We can detect the GADT/existential situation, case (1) of Note [fillInferResult],
+by seeing that the current TcLevel is greater than that stored in ir_lvl of the
+Infer ExpType. We bump the level whenever we go past a GADT/existential match.
-For (1) we can detect the GADT/existential situation by seeing that
-the current TcLevel is greater than that stored in ir_lvl of the Infer
-ExpType. We bump the level whenever we go past a GADT/existential match.
+We insist that the RHS has a monotype, regardless of the number of alternatives.
Then, before filling the hole use promoteTcType to promote the type
to the outer ir_lvl. promoteTcType does this
@@ -1239,11 +1234,6 @@ That forces the type to be a monotype (since unification variables can
only unify with monotypes); and catches skolem-escapes because the
alpha is untouchable until the equality floats out.
-For (2), we simply look to see if the hole is filled already.
- - if not, we promote (as above) and fill the hole
- - if it is filled, we simply unify with the type that is
- already there
-
(FIR1) There is one wrinkle. Suppose we have
case e of
T1 -> e1 :: (forall a. a->a) -> Int
@@ -1258,7 +1248,47 @@ For (2), we simply look to see if the hole is filled already.
So if we check G2 second, we still want to emit a constraint that restricts
the RHS to be a monotype. This is done by ensureMonoType, and it works
by simply generating a constraint (alpha ~ ty), where alpha is a fresh
-unification variable. We discard the evidence.
+ unification variable. We discard the evidence.
+
+Note [fillInferResult: multiple branches]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If there are multiple case branches, case (2) of Note [fillInferResult]
+we simply look to see if the hole is filled already.
+ - if not, we promote (as above) and fill the hole
+ - if it is filled, we simply unify with the type that is already there
+
+But consider
+ case x of
+ True -> True
+ False -> undefined
+and suppose we call `tcInferSigma` on this expression, so that the `ir_inst`
+field of the expected result type is `IIF_Sigma`. The danger is that we'll
+fill the hole with `Bool` (from the `True`) and then reject when we try to
+unify that with `forall a. a->a`, from the call to `undefined`.
+
+Another example:
+ case x of
+ True -> (e1 :: forall a b. a->b)
+ False -> (e3 :: forall b a. a->b)
+
+To avoid this, we never infer a sigma-type from a multi-branch `case`. Instead
+we just zap the `IIF_Sigma` to `IIF_DeepRho` when walking inside the branches
+of multi-arm case-expression, or an if-expression. See calls to
+`adjustExpTypeForCaseBranches`.
+
+This does mean that this would work:
+ (let x = 77+55 in h x x) @Int
+where
+ h :: Int -> Int -> forall a. a->a
+The `@Int` would instantiate the `forall a`.
+
+Note that
+ case e of
+ True -> hr
+ False -> \x -> hr x
+ where hr :: (forall a. a->a) -> Int
+will fail, because we still /infer/ both branches, so the \x will get a
+(monotype) unification variable, which will fail to unify with (forall a. a->a)
Note [Instantiation of InferResult]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1274,7 +1304,7 @@ Usually this field is `IIF_DeepRho` meaning "return a (possibly deep) rho-type".
Why is this the common case? See #17173 for discussion. Here are some examples
of why:
-1. Consider
+(IIR1) Consider
f x = (*)
We want to instantiate the type of (*) before returning, else we
will infer the type
@@ -1286,21 +1316,46 @@ of why:
instantiating. This could perhaps be worked around, but it may be
hard to know even when instantiation should happen.
-2. Another reason. Consider
+(IIR2) Another reason. Consider
f :: (?x :: Int) => a -> a
g y = let ?x = 3::Int in f
Here want to instantiate f's type so that the ?x::Int constraint
gets discharged by the enclosing implicit-parameter binding.
-3. Suppose one defines plus = (+). If we instantiate lazily, we will
+(IIR3) Suppose one defines plus = (+). If we instantiate lazily, we will
infer plus :: forall a. Num a => a -> a -> a. However, the monomorphism
restriction compels us to infer
plus :: Integer -> Integer -> Integer
(or similar monotype). Indeed, the only way to know whether to apply
the monomorphism restriction at all is to instantiate
-HOWEVER, not always! Here are places where we want `IIF_Sigma` meaning
-"return a sigma-type":
+(IIR4) When -XDeepSubsumption is on, we /deeply/ instantiate. Why isn't
+ top-instantiation enough? Answer: to accept the following program (T26225b) with
+ -XDeepSubsumption, we need to deeply instantiate when inferring in checkResultTy:
+
+ f :: Int -> (forall a. a->a)
+ g :: Int -> Bool -> Bool
+
+ test b = case b of
+ True -> f
+ False -> g
+
+ If we don't deeply instantiate in the branches of the case expression, we will
+ try to unify the type of `f` with that of `g`, which fails. If we instead
+ deeply instantiate `f`, we will fill the `InferResult` with `Int -> alpha -> alpha`
+ which then successfully unifies with the type of `g` when we come to fill the
+ `InferResult` hole a second time for the second case branch.
+
+(IIR5) When inferring, even /without/ -XDeepSubsumption, we must deeply instantiate
+ the types of data constructors. E.g
+ data T = MkT Int int
+ f = MkT 3
+ We must infer MkT 3 :: Int ->{mu} T (fresh mu)
+ and not MkT 3 :: Int ->{one} T
+ See Note [Typechecking data constructors] in GHC.Tc.Gen.Head
+ Hence the use of `getDeepSubsumptionFlag_DataConHead` in `checkResultTy`.
+
+HOWEVER, `ir_inst` is not always `IIF_DeepRho`! Here are places when it isn't:
* IIF_Sigma: In GHC.Tc.Module.tcRnExpr, which implements GHCi's :type
command, we want to return a completely uninstantiated type.
@@ -1316,23 +1371,6 @@ HOWEVER, not always! Here are places where we want `IIF_Sigma` meaning
but /not/ deeply instantiate (#26331). See Note [View patterns and polymorphism]
in GHC.Tc.Gen.Pat. This the only place we use IIF_ShallowRho.
-Why do we want to deeply instantiate, ever? Why isn't top-instantiation enough?
-Answer: to accept the following program (T26225b) with -XDeepSubsumption, we
-need to deeply instantiate when inferring in checkResultTy:
-
- f :: Int -> (forall a. a->a)
- g :: Int -> Bool -> Bool
-
- test b =
- case b of
- True -> f
- False -> g
-
-If we don't deeply instantiate in the branches of the case expression, we will
-try to unify the type of 'f' with that of 'g', which fails. If we instead
-deeply instantiate 'f', we will fill the 'InferResult' with 'Int -> alpha -> alpha'
-which then successfully unifies with the type of 'g' when we come to fill the
-'InferResult' hole a second time for the second case branch.
-}
{-
@@ -2068,24 +2106,14 @@ getDeepSubsumptionFlag =
-- | Variant of 'getDeepSubsumptionFlag' which enables a top-level subsumption
-- in order to implement the plan of Note [Typechecking data constructors].
getDeepSubsumptionFlag_DataConHead :: HsExpr GhcTc -> TcM DeepSubsumptionFlag
-getDeepSubsumptionFlag_DataConHead app_head =
- do { user_ds <- xoptM LangExt.DeepSubsumption
- ; traceTc "getDeepSubsumptionFlag_DataConHead" (ppr app_head)
- ; return $
- if | user_ds
- -> Deep DeepSub
- | otherwise
- -> go app_head
- }
+getDeepSubsumptionFlag_DataConHead app_head
+ = do { user_ds <- xoptM LangExt.DeepSubsumption
+ ; return $ if | user_ds -> Deep DeepSub
+ | dc_head app_head -> Deep TopSub
+ | otherwise -> Shallow }
where
- go :: HsExpr GhcTc -> DeepSubsumptionFlag
- go (XExpr (ConLikeTc (RealDataCon {}))) = Deep TopSub
- go (XExpr (ExpandedThingTc (HSE _ (L _ f)))) = go f
- go (XExpr (WrapExpr _ f)) = go f
- go (HsApp _ f _) = go (unLoc f)
- go (HsAppType _ f _) = go (unLoc f)
- go _ = Shallow
-
+ dc_head (XExpr (ConLikeTc (RealDataCon {}))) = True
+ dc_head _ = False
-- | 'tc_sub_type_deep' is where the actual work happens for deep subsumption.
--
=====================================
compiler/ghc.cabal.in
=====================================
@@ -832,6 +832,7 @@ Library
GHC.Tc.Gen.Bind
GHC.Tc.Gen.Default
GHC.Tc.Gen.Do
+ GHC.Tc.Gen.Expand
GHC.Tc.Gen.Export
GHC.Tc.Gen.Expr
GHC.Tc.Gen.Foreign
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/09d78751a64284518c32fd219e5c513…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/09d78751a64284518c32fd219e5c513…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/int-index/unify-wc-hole] 13 commits: ghc-boot: remove unused SizedSeq instances and functions
by Vladislav Zavialov (@int-index) 01 Apr '26
by Vladislav Zavialov (@int-index) 01 Apr '26
01 Apr '26
Vladislav Zavialov pushed to branch wip/int-index/unify-wc-hole at Glasgow Haskell Compiler / GHC
Commits:
cf942119 by Cheng Shao at 2026-03-30T15:24:37-04:00
ghc-boot: remove unused SizedSeq instances and functions
This commit removes unused `SizedSeq` instances and functions, only
keeping the bits we need for hpc tick sequence for now.
- - - - -
22c5b7cc by Cheng Shao at 2026-03-30T15:24:38-04:00
ghci: remove unused GHCi.BinaryArray
This patch removes the unused `GHCi.BinaryArray` module from `ghci`.
Closes #27108.
- - - - -
77abb4ab by Cheng Shao at 2026-03-30T15:25:21-04:00
testsuite: mark T17912 as fragile on Windows
T17912 is still fragile on Windows, it sometimes unexpectedly pass in
CI. This especially strains our already scarce Windows CI runner
resources. Mark it as fragile on Windows for the time being.
- - - - -
d741a6cc by Andreas Klebinger at 2026-03-31T04:39:33-04:00
Bump minimum shake version for hadrian.
We also add the shake version we want to stack.yaml
Fixes #26884
- - - - -
5e556f9e by Vladislav Zavialov at 2026-03-31T04:40:16-04:00
Status check for the HsType~HsExpr refactoring (#25121)
Add a test case to track the status of a refactoring project within GHC
whose goal is to arrive at the following declaration:
type HsType = HsExpr
The rationale for this is to increase code reuse between the term- and
type-level code in the compiler front-end (AST, parser, renamer, type checker).
The status report is saved to testsuite/tests/ghc-api/T25121_status.stdout
and provides useful insights into what needs to happen to make progress on
the ticket.
- - - - -
acffb1b1 by fendor at 2026-03-31T04:41:02-04:00
Extract Binary instances to `GHC.ByteCode.Binary`
- - - - -
e2ea8e25 by fendor at 2026-03-31T04:41:02-04:00
Add `seqNonEmpty` for evaluating `NonEmpty a`
- - - - -
048b00b7 by fendor at 2026-03-31T04:41:02-04:00
Record `LinkableUsage` instead of `Linkable` in `LoaderState`
Retaining a ByteCode `Linkable` after it has been loaded retains its
`UnlinkedBCO`, keeping it alive for the remainder of the program.
This starts accumulating a lot of `UnlinkedBCO` and memory over time.
However, the `Linkable` is merely used to later record its usage in
`mkObjectUsage`, which is used for recompilation checking.
However, this is incorrect, as the interface file and bytecode objects
could be in different states, e.g. the interface changes, but the
bytecode library hasn't changed so we don't need to recompile and vice
versa.
By computing a `Fingerprint` for the `ModuleByteCode`, and recording it
in the `LinkableUsage`, we know precisely whether the `ByteCode` object
on disk is outdated.
Thus, parts of this commit just makes sure that we efficiently compute a
`Fingerprint` for `ModuleByteCode` and store it in the on-disk
representation of `ModuleByteCode`.
We change the `LoaderState` to retain `LinkableUsage`, which is smaller
representation of a `Linkable`. This allows us to free the unneeded
fields of `Linkable` after linking them.
We declare the following memory invariants that this commit implements:
* No `LinkablePart` should be retained from `LoaderState`.
* `Linkable`s should be unloaded after they have been loaded.
These invariants are unfortunately tricky to automatically uphold, so we
are simply documenting our assumptions for now.
We introduce the `linkable-space` test which makes sure that after
loading, no `DotGBC` or `UnlinkedBCO` is retained.
-------------------------
Metric Increase:
MultiLayerModulesTH_OneShot
-------------------------
We allocate a bit more, but the peak number of bytes doesn't change.
While a bit unfortunate, accepting the metric increase.
We add multiple new performance measurements where we were able to
observe the desired memory invariants. Further, we add regression tests
to validate that the recompilation checker behaves more correct than
before.
- - - - -
2d1c1997 by Simon Jakobi at 2026-03-31T04:41:46-04:00
Eliminate dictionary-passing in ListMap operations
Mark the ListMap helpers 'INLINABLE' so importing modules can specialise
the 'TrieMap (ListMap m)' methods and avoid recursive dictionary-passing.
See Note [Making ListMap operations specialisable].
Fixes #27097
- - - - -
ed2c6570 by Cheng Shao at 2026-03-31T04:42:33-04:00
testsuite: fix testdir cleanup logic on Windows
testdir cleanup is unreliable on Windows (#13162) and despite existing
hacks in the driver, new failure mode has occurred. This patch makes
it print the warning and carry on when failed to clean up a testdir,
instead of reporting a spurious framework failure. See added comment
for detailed explanation.
- - - - -
d9388e29 by Simon Jakobi at 2026-03-31T13:14:59-04:00
Add regression test for #18177
Closes #18177.
Assisted-by: Codex
- - - - -
6a10045c by mangoiv at 2026-03-31T13:15:43-04:00
ci: allow metric decrease for two tests on i386
There has been a nightly failure on i386 due to a compiler runtime
improvement on i386 debian 12. We allow that.
Metric Decrease (test_env='i386-linux-deb12'):
T12707 T8095
- - - - -
0ab75247 by Vladislav Zavialov at 2026-04-01T03:31:11+03:00
Refactor HsWildCardTy to use HoleKind (#27111)
The payload of this patch is that the extension fields of HsWildCardTy
and HsHole now match:
type instance XWildCardTy Ghc{Ps,Rn} = HoleKind
type instance XHole Ghc{Ps,Rn} = HoleKind
This is progress towards unification of HsExpr and HsType.
Test case: T25121_status
In addition to that, exact-printing of infix holes is fixed.
Test case: PprInfixHole
- - - - -
74 changed files:
- + compiler/GHC/ByteCode/Binary.hs
- + compiler/GHC/ByteCode/Recomp/Binary.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/Data/TrieMap.hs
- compiler/GHC/Driver/Hooks.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Hs/Expr.hs-boot
- compiler/GHC/Hs/Type.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Recomp/Types.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Unit/Home/ModInfo.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/Status.hs
- compiler/GHC/Utils/Misc.hs
- compiler/ghc.cabal.in
- ghc/GHCi/Leak.hs
- hadrian/hadrian.cabal
- hadrian/stack.yaml
- libraries/base/tests/IO/all.T
- libraries/ghc-boot/GHC/Data/SizedSeq.hs
- − libraries/ghci/GHCi/BinaryArray.hs
- libraries/ghci/ghci.cabal.in
- testsuite/driver/testlib.py
- + testsuite/tests/bytecode/TLinkable/BCOTemplate.hs
- + testsuite/tests/bytecode/TLinkable/LinkableUsage01.stderr
- + testsuite/tests/bytecode/TLinkable/LinkableUsage02.stderr
- + testsuite/tests/bytecode/TLinkable/Makefile
- + testsuite/tests/bytecode/TLinkable/all.T
- + testsuite/tests/bytecode/TLinkable/genLinkables.sh
- + testsuite/tests/bytecode/TLinkable/linkable-space.hs
- + testsuite/tests/bytecode/TLinkable/linkable-space.stdout
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/driver/T18177.hs
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_recomp_th.stdout
- + testsuite/tests/driver/recomp022/A1.hs
- + testsuite/tests/driver/recomp022/A2.hs
- + testsuite/tests/driver/recomp022/A3.hs
- + testsuite/tests/driver/recomp022/B.hs
- + testsuite/tests/driver/recomp022/C.hs
- + testsuite/tests/driver/recomp022/Makefile
- + testsuite/tests/driver/recomp022/all.T
- + testsuite/tests/driver/recomp022/recomp022a.stdout
- + testsuite/tests/driver/recomp022/recomp022b.stdout
- + testsuite/tests/ghc-api/T25121_status.hs
- + testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/all.T
- testsuite/tests/ghci/should_run/BinaryArray.hs
- testsuite/tests/printer/Makefile
- + testsuite/tests/printer/PprInfixHole.hs
- testsuite/tests/printer/all.T
- utils/check-exact/ExactPrint.hs
- utils/haddock/haddock-api/src/Haddock/Backends/Hoogle.hs
- utils/haddock/haddock-api/src/Haddock/Convert.hs
- utils/haddock/haddock-api/src/Haddock/GhcUtils.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5a69c8d698d1d48bab79332d181803…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/5a69c8d698d1d48bab79332d181803…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/int-index/low-hanging-unify] 16 commits: ghc-boot: remove unused SizedSeq instances and functions
by Vladislav Zavialov (@int-index) 01 Apr '26
by Vladislav Zavialov (@int-index) 01 Apr '26
01 Apr '26
Vladislav Zavialov pushed to branch wip/int-index/low-hanging-unify at Glasgow Haskell Compiler / GHC
Commits:
cf942119 by Cheng Shao at 2026-03-30T15:24:37-04:00
ghc-boot: remove unused SizedSeq instances and functions
This commit removes unused `SizedSeq` instances and functions, only
keeping the bits we need for hpc tick sequence for now.
- - - - -
22c5b7cc by Cheng Shao at 2026-03-30T15:24:38-04:00
ghci: remove unused GHCi.BinaryArray
This patch removes the unused `GHCi.BinaryArray` module from `ghci`.
Closes #27108.
- - - - -
77abb4ab by Cheng Shao at 2026-03-30T15:25:21-04:00
testsuite: mark T17912 as fragile on Windows
T17912 is still fragile on Windows, it sometimes unexpectedly pass in
CI. This especially strains our already scarce Windows CI runner
resources. Mark it as fragile on Windows for the time being.
- - - - -
d741a6cc by Andreas Klebinger at 2026-03-31T04:39:33-04:00
Bump minimum shake version for hadrian.
We also add the shake version we want to stack.yaml
Fixes #26884
- - - - -
5e556f9e by Vladislav Zavialov at 2026-03-31T04:40:16-04:00
Status check for the HsType~HsExpr refactoring (#25121)
Add a test case to track the status of a refactoring project within GHC
whose goal is to arrive at the following declaration:
type HsType = HsExpr
The rationale for this is to increase code reuse between the term- and
type-level code in the compiler front-end (AST, parser, renamer, type checker).
The status report is saved to testsuite/tests/ghc-api/T25121_status.stdout
and provides useful insights into what needs to happen to make progress on
the ticket.
- - - - -
acffb1b1 by fendor at 2026-03-31T04:41:02-04:00
Extract Binary instances to `GHC.ByteCode.Binary`
- - - - -
e2ea8e25 by fendor at 2026-03-31T04:41:02-04:00
Add `seqNonEmpty` for evaluating `NonEmpty a`
- - - - -
048b00b7 by fendor at 2026-03-31T04:41:02-04:00
Record `LinkableUsage` instead of `Linkable` in `LoaderState`
Retaining a ByteCode `Linkable` after it has been loaded retains its
`UnlinkedBCO`, keeping it alive for the remainder of the program.
This starts accumulating a lot of `UnlinkedBCO` and memory over time.
However, the `Linkable` is merely used to later record its usage in
`mkObjectUsage`, which is used for recompilation checking.
However, this is incorrect, as the interface file and bytecode objects
could be in different states, e.g. the interface changes, but the
bytecode library hasn't changed so we don't need to recompile and vice
versa.
By computing a `Fingerprint` for the `ModuleByteCode`, and recording it
in the `LinkableUsage`, we know precisely whether the `ByteCode` object
on disk is outdated.
Thus, parts of this commit just makes sure that we efficiently compute a
`Fingerprint` for `ModuleByteCode` and store it in the on-disk
representation of `ModuleByteCode`.
We change the `LoaderState` to retain `LinkableUsage`, which is smaller
representation of a `Linkable`. This allows us to free the unneeded
fields of `Linkable` after linking them.
We declare the following memory invariants that this commit implements:
* No `LinkablePart` should be retained from `LoaderState`.
* `Linkable`s should be unloaded after they have been loaded.
These invariants are unfortunately tricky to automatically uphold, so we
are simply documenting our assumptions for now.
We introduce the `linkable-space` test which makes sure that after
loading, no `DotGBC` or `UnlinkedBCO` is retained.
-------------------------
Metric Increase:
MultiLayerModulesTH_OneShot
-------------------------
We allocate a bit more, but the peak number of bytes doesn't change.
While a bit unfortunate, accepting the metric increase.
We add multiple new performance measurements where we were able to
observe the desired memory invariants. Further, we add regression tests
to validate that the recompilation checker behaves more correct than
before.
- - - - -
2d1c1997 by Simon Jakobi at 2026-03-31T04:41:46-04:00
Eliminate dictionary-passing in ListMap operations
Mark the ListMap helpers 'INLINABLE' so importing modules can specialise
the 'TrieMap (ListMap m)' methods and avoid recursive dictionary-passing.
See Note [Making ListMap operations specialisable].
Fixes #27097
- - - - -
ed2c6570 by Cheng Shao at 2026-03-31T04:42:33-04:00
testsuite: fix testdir cleanup logic on Windows
testdir cleanup is unreliable on Windows (#13162) and despite existing
hacks in the driver, new failure mode has occurred. This patch makes
it print the warning and carry on when failed to clean up a testdir,
instead of reporting a spurious framework failure. See added comment
for detailed explanation.
- - - - -
d9388e29 by Simon Jakobi at 2026-03-31T13:14:59-04:00
Add regression test for #18177
Closes #18177.
Assisted-by: Codex
- - - - -
6a10045c by mangoiv at 2026-03-31T13:15:43-04:00
ci: allow metric decrease for two tests on i386
There has been a nightly failure on i386 due to a compiler runtime
improvement on i386 debian 12. We allow that.
Metric Decrease (test_env='i386-linux-deb12'):
T12707 T8095
- - - - -
203b0161 by Vladislav Zavialov at 2026-04-01T03:07:42+03:00
Refactor HsWildCardTy to use HoleKind (#27111)
The payload of this patch is that the extension fields of HsWildCardTy
and HsHole now match:
type instance XWildCardTy Ghc{Ps,Rn} = HoleKind
type instance XHole Ghc{Ps,Rn} = HoleKind
This is progress towards unification of HsExpr and HsType.
In addition to that, exact-printing of infix holes is fixed.
Test case: PprInfixHole
- - - - -
361a72ad by Vladislav Zavialov at 2026-04-01T03:07:42+03:00
WIP: Low-hanging fruit for T25121
- - - - -
270969cc by Vladislav Zavialov at 2026-04-01T03:07:42+03:00
WIP: HsSigWcType
- - - - -
e760b8a0 by Vladislav Zavialov at 2026-04-01T03:07:42+03:00
WIP: HsTupArgOf
- - - - -
126 changed files:
- + compiler/GHC/ByteCode/Binary.hs
- + compiler/GHC/ByteCode/Recomp/Binary.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/Data/TrieMap.hs
- compiler/GHC/Driver/Hooks.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Expr.hs-boot
- compiler/GHC/Hs/Instances.hs
- compiler/GHC/Hs/Pat.hs
- compiler/GHC/Hs/Syn/Type.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Match.hs
- compiler/GHC/HsToCore/Pmc/Desugar.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/HsToCore/Utils.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Recomp/Types.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Errors/Ppr.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Rename/Bind.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/Fixity.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Module.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Rename/Splice.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/Head.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Sig.hs
- compiler/GHC/Tc/Gen/Splice.hs
- compiler/GHC/Tc/Gen/Splice.hs-boot
- compiler/GHC/Tc/Module.hs
- compiler/GHC/Tc/TyCl/PatSyn.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Instantiate.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Unit/Home/ModInfo.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/Status.hs
- compiler/GHC/Utils/Misc.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Type.hs
- compiler/ghc.cabal.in
- ghc/GHCi/Leak.hs
- ghc/GHCi/UI/Info.hs
- hadrian/hadrian.cabal
- hadrian/stack.yaml
- libraries/base/tests/IO/all.T
- libraries/ghc-boot/GHC/Data/SizedSeq.hs
- − libraries/ghci/GHCi/BinaryArray.hs
- libraries/ghci/ghci.cabal.in
- testsuite/driver/testlib.py
- + testsuite/tests/bytecode/TLinkable/BCOTemplate.hs
- + testsuite/tests/bytecode/TLinkable/LinkableUsage01.stderr
- + testsuite/tests/bytecode/TLinkable/LinkableUsage02.stderr
- + testsuite/tests/bytecode/TLinkable/Makefile
- + testsuite/tests/bytecode/TLinkable/all.T
- + testsuite/tests/bytecode/TLinkable/genLinkables.sh
- + testsuite/tests/bytecode/TLinkable/linkable-space.hs
- + testsuite/tests/bytecode/TLinkable/linkable-space.stdout
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/driver/T18177.hs
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_recomp_th.stdout
- + testsuite/tests/driver/recomp022/A1.hs
- + testsuite/tests/driver/recomp022/A2.hs
- + testsuite/tests/driver/recomp022/A3.hs
- + testsuite/tests/driver/recomp022/B.hs
- + testsuite/tests/driver/recomp022/C.hs
- + testsuite/tests/driver/recomp022/Makefile
- + testsuite/tests/driver/recomp022/all.T
- + testsuite/tests/driver/recomp022/recomp022a.stdout
- + testsuite/tests/driver/recomp022/recomp022b.stdout
- + testsuite/tests/ghc-api/T25121_status.hs
- + testsuite/tests/ghc-api/T25121_status.stdout
- testsuite/tests/ghc-api/all.T
- testsuite/tests/ghci/should_run/BinaryArray.hs
- testsuite/tests/module/mod185.stderr
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpParsedAstComments.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- testsuite/tests/parser/should_compile/DumpSemis.stderr
- testsuite/tests/parser/should_compile/DumpTypecheckedAst.stderr
- testsuite/tests/parser/should_compile/KindSigs.stderr
- testsuite/tests/parser/should_compile/T14189.stderr
- testsuite/tests/parser/should_compile/T20846.stderr
- testsuite/tests/perf/compiler/hard_hole_fits.hs
- testsuite/tests/perf/compiler/hard_hole_fits.stderr
- testsuite/tests/plugins/simple-plugin/Simple/RemovePlugin.hs
- testsuite/tests/printer/Makefile
- + testsuite/tests/printer/PprInfixHole.hs
- testsuite/tests/printer/Test20297.stdout
- testsuite/tests/printer/Test24533.stdout
- testsuite/tests/printer/all.T
- testsuite/tests/quasiquotation/T7918.hs
- utils/check-exact/ExactPrint.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/Interface/RenameType.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/f05925321d9506a17ff6c3da345396…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f05925321d9506a17ff6c3da345396…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 4 commits: Refactor eta-expansion in Prep
by Marge Bot (@marge-bot) 31 Mar '26
by Marge Bot (@marge-bot) 31 Mar '26
31 Mar '26
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
e9623c94 by Simon Peyton Jones at 2026-03-31T21:01:01+01:00
Refactor eta-expansion in Prep
The Prep pass does eta-expansion but I found cases where it was
doing bad things. So I refactored and simplified it quite a bit.
In the new design
* There is no distinction between `rhs` and `body`; in particular,
lambdas can now appear anywhere, rather than just as the RHS of
a let-binding.
* This change led to a significant simplification of Prep, and
a more straightforward explanation of eta-expansion. See the new
Note [Eta expansion]
* The consequences is that CoreToStg needs to handle naked lambdas.
This is very easy; but it does need a unique supply, which forces
some simple refactoring. Having a unique supply to hand is probably
a good thing anyway.
- - - - -
3a33983b by Simon Peyton Jones at 2026-03-31T21:01:02+01:00
Clarify Note [Interesting dictionary arguments]
Ticket #26831 ended up concluding that the code for
GHC.Core.Opt.Specialise.interestingDict was good, but the
commments were a bit inadequate.
This commit improves the comments slightly.
- - - - -
690acec9 by Simon Peyton Jones at 2026-03-31T21:01:02+01:00
Make inlining a bit more eager for overloaded functions
If we have
f d = ... (class-op d x y) ...
we should be eager to inline `f`, because that may change the
higher order call (class-op d x y) into a call to a statically
known function.
See the discussion on #26831.
Even though this does a bit /more/ inlining, compile times
decrease by an average of 0.4%.
Compile time changes:
DsIncompleteRecSel3(normal) 431,786,104 -2.2%
ManyAlternatives(normal) 670,883,768 -1.6%
ManyConstructors(normal) 3,758,493,832 -2.6% GOOD
MultilineStringsPerf(normal) 29,900,576 -2.8%
T14052Type(ghci) 1,047,600,848 -1.2%
T17836(normal) 392,852,328 -5.2%
T18478(normal) 442,785,768 -1.4%
T21839c(normal) 341,536,992 -14.1% GOOD
T3064(normal) 174,086,152 +5.3% BAD
T5631(normal) 506,867,800 +1.0%
hard_hole_fits(normal) 209,530,736 -1.3%
info_table_map_perf(normal) 19,523,093,184 -1.2%
parsing001(normal) 377,810,528 -1.1%
pmcOrPats(normal) 60,075,264 -0.5%
geo. mean -0.4%
minimum -14.1%
maximum +5.3%
Runtime changes
haddock.Cabal(normal) 27,351,988,792 -0.7%
haddock.base(normal) 26,997,212,560 -0.6%
haddock.compiler(normal) 219,531,332,960 -1.0%
Metric Decrease:
ManyConstructors
T17949
T21839c
T13035
TcPlugin_RewritePerf
hard_hole_fits
Metric Increase:
T3064
- - - - -
c7ac6d41 by Simon Jakobi at 2026-03-31T19:20:43-04:00
Add perf test for #13960
Closes #13960.
- - - - -
20 changed files:
- compiler/GHC/Builtin/PrimOps.hs
- compiler/GHC/Core/Opt/Arity.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Tidy.hs
- compiler/GHC/Core/Unfold.hs
- compiler/GHC/CoreToStg.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Stg/Lint.hs
- compiler/GHC/Types/Id.hs
- compiler/GHC/Types/Id/Info.hs
- testsuite/tests/arityanal/should_compile/Arity01.stderr
- testsuite/tests/arityanal/should_compile/Arity05.stderr
- testsuite/tests/arityanal/should_compile/Arity08.stderr
- testsuite/tests/arityanal/should_compile/Arity11.stderr
- testsuite/tests/arityanal/should_compile/Arity14.stderr
- + testsuite/tests/perf/compiler/T13960.hs
- testsuite/tests/perf/compiler/all.T
- testsuite/tests/simplCore/should_compile/T15205.stderr
- testsuite/tests/wasm/should_run/control-flow/LoadCmmGroup.hs
Changes:
=====================================
compiler/GHC/Builtin/PrimOps.hs
=====================================
@@ -807,16 +807,23 @@ the former has an additional type binder. Hmmm....
Note [Eta expanding primops]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
STG requires that primop applications be saturated. This makes code generation
significantly simpler since otherwise we would need to define a calling
convention for curried applications that can accommodate representation
polymorphism.
-To ensure saturation, CorePrep eta expands all primop applications as
-described in Note [Eta expansion of hasNoBinding things in CorePrep] in
+To ensure saturation, CorePrep eta expands all primop applications
+as described in Note [Eta expansion of unsaturated calls] in
GHC.Core.Prep.
+Side note: this decision is somewhat in flux: see comments with `hasNoBinding`.
+The question is: do we generate a trivial wrapper for each primop
+ (+#) x y = (+#) x y
+and now we can call that wrapper unsaturated. But in practice we
+might never call it because in practice Prep eta-expands all partial
+applications!
+
+
Historical Note:
For a short period around GHC 8.8 we rewrote unsaturated primop applications to
=====================================
compiler/GHC/Core/Opt/Arity.hs
=====================================
@@ -2551,9 +2551,6 @@ This reduces clutter, sometimes a lot. See Note [Do not eta-expand PAPs]
in GHC.Core.Opt.Simplify.Utils, where we are careful not to eta-expand
a PAP. If eta-expanding is bad, then eta-reducing is good!
-Also the code generator likes eta-reduced PAPs; see GHC.CoreToStg.Prep
-Note [No eta reduction needed in rhsToBody].
-
But note that we don't want to eta-reduce
\x y. f <expensive> x y
to
=====================================
compiler/GHC/Core/Opt/Specialise.hs
=====================================
@@ -3247,9 +3247,14 @@ case we can clearly specialise. But there are wrinkles:
(ID6) The Main Plan says that it's worth specialising if the argument is an application
of a dictionary contructor. But what if the dictionary has no methods? Then we
- gain nothing by specialising, unless the /superclasses/ are interesting. A case
- in point is constraint tuples (% d1, .., dn %); a constraint N-tuple is a class
- with N superclasses and no methods.
+ gain nothing by specialising, unless the /superclasses/ are interesting.
+
+ So if there are no methods, we recursively call `interestingDict` on the
+ superclasses. Why recurse? If we have
+ \d1 d2. f (CTuple d1 d2)
+ If `d1 and `d2` are uninteresting dictionaries, then so is (CTuple d1 d2).
+ (Remember: a constraint tuple is just a class with N superclasses and no methods.)
+ See discussion on #26831.
(ID7) A unary (single-method) class is currently represented by (meth |> co). We
will unwrap the cast (see (ID5)) and then want to reply "yes" if the method
=====================================
compiler/GHC/Core/Tidy.hs
=====================================
@@ -165,6 +165,7 @@ computeCbvInfo fun_id rhs
map mkMark val_args
cbv_bndr | any isMarkedCbv cbv_marks
+ -- isMarkedCbv: see (CBV2) in Note [CBV Function Ids: overview]
= cbv_marks `seqList` setIdCbvMarks fun_id cbv_marks
-- seqList: avoid retaining the original rhs
@@ -176,6 +177,7 @@ computeCbvInfo fun_id rhs
-- We don't set CBV marks on functions which take unboxed tuples or sums as
-- arguments. Doing so would require us to compute the result of unarise
-- here in order to properly determine argument positions at runtime.
+ -- See (CBV1) in Note [CBV Function Ids: overview]
--
-- In practice this doesn't matter much. Most "interesting" functions will
-- get a W/W split which will eliminate unboxed tuple arguments, and unboxed
=====================================
compiler/GHC/Core/Unfold.hs
=====================================
@@ -779,22 +779,28 @@ litSize _other = 0 -- Must match size of nullary constructors
classOpSize :: UnfoldingOpts -> Class -> [Id] -> [CoreExpr] -> ExprSize
-- See (IA1) in Note [Interesting arguments] in GHC.Core.Opt.Simplify.Utils
-classOpSize opts cls top_args args
- | isUnaryClass cls
- = sizeZero -- See (UCM4) in Note [Unary class magic] in GHC.Core.TyCon
- | otherwise
- = case args of
- [] -> sizeZero
- (arg1:other_args) -> SizeIs (size other_args) (arg_discount arg1) 0
+classOpSize _opts _cls _top_args []
+ = sizeZero -- A non-applied classop
+classOpSize opts cls top_args (dict_arg:other_val_args)
+ = SizeIs size (arg_discount dict_arg) 0
where
- size other_args = 20 + (10 * length other_args)
+ size | isUnaryClass cls = 0 -- See (UCM4) in Note [Unary class magic] in GHC.Core.TyCon
+ | otherwise = 20 + (10 * length other_val_args)
-- If the class op is scrutinising a lambda bound dictionary then
-- give it a discount, to encourage the inlining of this function
- -- The actual discount is rather arbitrarily chosen
- arg_discount (Var dict) | dict `elem` top_args
- = unitBag (dict, unfoldingDictDiscount opts)
- arg_discount _ = emptyBag
+ arg_discount (Cast arg _co) = arg_discount arg
+ arg_discount (Var dict) | dict `elem` top_args = unitBag (dict, dict_discount)
+ arg_discount _ = emptyBag
+
+ -- If we have (class-op d arg1 .. argn) then it's super-good to inline
+ -- to expose `d`; not only can we do the dictionary selection
+ -- (class-op d), but that will likely expose a lambda which we can then
+ -- apply. In that case (n > 0), we add `unfoldingFunAppDiscount`.
+ -- See the discussion on #26831, esp "Delicate inlining".
+ dict_discount
+ | null other_val_args = unfoldingDictDiscount opts
+ | otherwise = unfoldingDictDiscount opts + unfoldingFunAppDiscount opts
-- | The size of a function call
callSize
=====================================
compiler/GHC/CoreToStg.hs
=====================================
@@ -39,6 +39,8 @@ import GHC.Types.Basic ( Arity, TypeOrConstraint(..) )
import GHC.Types.Literal
import GHC.Types.ForeignCall
import GHC.Types.IPE
+import GHC.Types.Unique.Supply
+import GHC.Types.Unique
import GHC.Unit.Module
import GHC.Platform ( Platform )
@@ -49,297 +51,309 @@ import GHC.Utils.Outputable
import GHC.Utils.Monad
import GHC.Utils.Misc (HasDebugCallStack)
import GHC.Utils.Panic
+import GHC.Data.FastString
import Control.Monad (ap)
--- Note [Live vs free]
--- ~~~~~~~~~~~~~~~~~~~
---
--- The two are not the same. Liveness is an operational property rather
--- than a semantic one. A variable is live at a particular execution
--- point if it can be referred to directly again. In particular, a dead
--- variable's stack slot (if it has one):
---
--- - should be stubbed to avoid space leaks, and
--- - may be reused for something else.
---
--- There ought to be a better way to say this. Here are some examples:
---
--- let v = [q] \[x] -> e
--- in
--- ...v... (but no q's)
---
--- Just after the `in', v is live, but q is dead. If the whole of that
--- let expression was enclosed in a case expression, thus:
---
--- case (let v = [q] \[x] -> e in ...v...) of
--- alts[...q...]
---
--- (ie `alts' mention `q'), then `q' is live even after the `in'; because
--- we'll return later to the `alts' and need it.
---
--- Let-no-escapes make this a bit more interesting:
---
--- let-no-escape v = [q] \ [x] -> e
--- in
--- ...v...
---
--- Here, `q' is still live at the `in', because `v' is represented not by
--- a closure but by the current stack state. In other words, if `v' is
--- live then so is `q'. Furthermore, if `e' mentions an enclosing
--- let-no-escaped variable, then its free variables are also live if `v' is.
+{- Note [Live vs free]
+~~~~~~~~~~~~~~~~~~~~~~
+The two are not the same. Liveness is an operational property rather
+than a semantic one. A variable is live at a particular execution
+point if it can be referred to directly again. In particular, a dead
+variable's stack slot (if it has one):
--- Note [What are these SRTs all about?]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
--- Consider the Core program,
---
--- fibs = go 1 1
--- where go a b = let c = a + c
--- in c : go b c
--- add x = map (\y -> x*y) fibs
---
--- In this case we have a CAF, 'fibs', which is quite large after evaluation and
--- has only one possible user, 'add'. Consequently, we want to ensure that when
--- all references to 'add' die we can garbage collect any bit of 'fibs' that we
--- have evaluated.
---
--- However, how do we know whether there are any references to 'fibs' still
--- around? Afterall, the only reference to it is buried in the code generated
--- for 'add'. The answer is that we record the CAFs referred to by a definition
--- in its info table, namely a part of it known as the Static Reference Table
--- (SRT).
---
--- Since SRTs are so common, we use a special compact encoding for them in: we
--- produce one table containing a list of CAFs in a module and then include a
--- bitmap in each info table describing which entries of this table the closure
--- references.
---
--- See also: commentary/rts/storage/gc/CAFs on the GHC Wiki.
+ - should be stubbed to avoid space leaks, and
+ - may be reused for something else.
--- Note [What is a non-escaping let]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
--- NB: Nowadays this is recognized by the occurrence analyser by turning a
--- "non-escaping let" into a join point. The following is then an operational
--- account of join points.
---
--- Consider:
---
--- let x = fvs \ args -> e
--- in
--- if ... then x else
--- if ... then x else ...
---
--- `x' is used twice (so we probably can't unfold it), but when it is
--- entered, the stack is deeper than it was when the definition of `x'
--- happened. Specifically, if instead of allocating a closure for `x',
--- we saved all `x's fvs on the stack, and remembered the stack depth at
--- that moment, then whenever we enter `x' we can simply set the stack
--- pointer(s) to these remembered (compile-time-fixed) values, and jump
--- to the code for `x'.
---
--- All of this is provided x is:
--- 1. non-updatable;
--- 2. guaranteed to be entered before the stack retreats -- ie x is not
--- buried in a heap-allocated closure, or passed as an argument to
--- something;
--- 3. all the enters have exactly the right number of arguments,
--- no more no less;
--- 4. all the enters are tail calls; that is, they return to the
--- caller enclosing the definition of `x'.
---
--- Under these circumstances we say that `x' is non-escaping.
---
--- An example of when (4) does not hold:
---
--- let x = ...
--- in case x of ...alts...
---
--- Here, `x' is certainly entered only when the stack is deeper than when
--- `x' is defined, but here it must return to ...alts... So we can't just
--- adjust the stack down to `x''s recalled points, because that would lost
--- alts' context.
---
--- Things can get a little more complicated. Consider:
---
--- let y = ...
--- in let x = fvs \ args -> ...y...
--- in ...x...
---
--- Now, if `x' is used in a non-escaping way in ...x..., and `y' is used in a
--- non-escaping way in ...y..., then `y' is non-escaping.
---
--- `x' can even be recursive! Eg:
---
--- letrec x = [y] \ [v] -> if v then x True else ...
--- in
--- ...(x b)...
+There ought to be a better way to say this. Here are some examples:
--- Note [Cost-centre initialization plan]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
---
--- Previously `coreToStg` was initializing cost-centre stack fields as `noCCS`,
--- and the fields were then fixed by a separate pass `stgMassageForProfiling`.
--- We now initialize these correctly. The initialization works like this:
---
--- - For non-top level bindings always use `currentCCS`.
---
--- - For top-level bindings, check if the binding is a CAF
---
--- - CAF: If -fcaf-all is enabled, create a new CAF just for this CAF
--- and use it. Note that these new cost centres need to be
--- collected to be able to generate cost centre initialization
--- code, so `coreToTopStgRhs` now returns `CollectedCCs`.
---
--- If -fcaf-all is not enabled, use "all CAFs" cost centre.
---
--- - Non-CAF: Top-level (static) data is not counted in heap profiles; nor
--- do we set CCCS from it; so we just slam in
--- dontCareCostCentre.
-
--- Note [Coercion tokens]
--- ~~~~~~~~~~~~~~~~~~~~~~
--- In coreToStgArgs, we drop type arguments completely, but we replace
--- coercions with a special coercionToken# placeholder. Why? Consider:
---
--- f :: forall a. Int ~# Bool -> a
--- f = /\a. \(co :: Int ~# Bool) -> error "impossible"
---
--- If we erased the coercion argument completely, we’d end up with just
--- f = error "impossible", but then f `seq` () would be ⊥!
---
--- This is an artificial example, but back in the day we *did* treat
--- coercion lambdas like type lambdas, and we had bug reports as a
--- result. So now we treat coercion lambdas like value lambdas, but we
--- treat coercions themselves as zero-width arguments — coercionToken#
--- has representation VoidRep — which gets the best of both worlds.
---
--- (For the gory details, see also the (unpublished) paper, “Practical
--- aspects of evidence-based compilation in System FC.”)
+ let v = [q] \[x] -> e
+ in
+ ...v... (but no q's)
--- Note [Saturation of data constructors in STG]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- We guarantee that `StgConApp` is an exactly-saturated application of a data
--- constructor worker.
---
--- * If the data constructor is /under/-saturated we just fall through to build
--- a `StgApp`. Remember, data constructor workers have a regular top-level definition
--- (injected by GHC.CoreToStg.Prep.mkDataConWorkers) so we can partially apply
--- that function.
---
--- * If the data constructor is /over/-saturated, which can happen (see #23865) we again
--- fall through to `StgApp`. That will fail horribly at runtime (by applying data
--- constructor to an argument) but it should be in dead code, and at least the compiler
--- itself won't crash. (We could inject an error-thunk instead.)
+Just after the `in', v is live, but q is dead. If the whole of that
+let expression was enclosed in a case expression, thus:
+
+ case (let v = [q] \[x] -> e in ...v...) of
+ alts[...q...]
+
+(ie `alts' mention `q'), then `q' is live even after the `in'; because
+we'll return later to the `alts' and need it.
+
+Let-no-escapes make this a bit more interesting:
+
+ let-no-escape v = [q] \ [x] -> e
+ in
+ ...v...
+
+Here, `q' is still live at the `in', because `v' is represented not by
+a closure but by the current stack state. In other words, if `v' is
+live then so is `q'. Furthermore, if `e' mentions an enclosing
+let-no-escaped variable, then its free variables are also live if `v' is.
+
+Note [What are these SRTs all about?]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Consider the Core program,
+
+ fibs = go 1 1
+ where go a b = let c = a + c
+ in c : go b c
+ add x = map (\y -> x*y) fibs
+
+In this case we have a CAF, 'fibs', which is quite large after evaluation and
+has only one possible user, 'add'. Consequently, we want to ensure that when
+all references to 'add' die we can garbage collect any bit of 'fibs' that we
+have evaluated.
+
+However, how do we know whether there are any references to 'fibs' still
+around? Afterall, the only reference to it is buried in the code generated
+for 'add'. The answer is that we record the CAFs referred to by a definition
+in its info table, namely a part of it known as the Static Reference Table
+(SRT).
+Since SRTs are so common, we use a special compact encoding for them in: we
+produce one table containing a list of CAFs in a module and then include a
+bitmap in each info table describing which entries of this table the closure
+references.
+
+See also: commentary/rts/storage/gc/CAFs on the GHC Wiki.
+
+Note [What is a non-escaping let]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+NB: Nowadays this is recognized by the occurrence analyser by turning a
+"non-escaping let" into a join point. The following is then an operational
+account of join points.
+
+Consider:
+
+ let x = fvs \ args -> e
+ in
+ if ... then x else
+ if ... then x else ...
+
+`x' is used twice (so we probably can't unfold it), but when it is
+entered, the stack is deeper than it was when the definition of `x'
+happened. Specifically, if instead of allocating a closure for `x',
+we saved all `x's fvs on the stack, and remembered the stack depth at
+that moment, then whenever we enter `x' we can simply set the stack
+pointer(s) to these remembered (compile-time-fixed) values, and jump
+to the code for `x'.
+
+All of this is provided x is:
+ 1. non-updatable;
+ 2. guaranteed to be entered before the stack retreats -- ie x is not
+ buried in a heap-allocated closure, or passed as an argument to
+ something;
+ 3. all the enters have exactly the right number of arguments,
+ no more no less;
+ 4. all the enters are tail calls; that is, they return to the
+ caller enclosing the definition of `x'.
+
+Under these circumstances we say that `x' is non-escaping.
+
+An example of when (4) does not hold:
+
+ let x = ...
+ in case x of ...alts...
+
+Here, `x' is certainly entered only when the stack is deeper than when
+`x' is defined, but here it must return to ...alts... So we can't just
+adjust the stack down to `x''s recalled points, because that would lost
+alts' context.
+
+Things can get a little more complicated. Consider:
+
+ let y = ...
+ in let x = fvs \ args -> ...y...
+ in ...x...
+
+Now, if `x' is used in a non-escaping way in ...x..., and `y' is used in a
+non-escaping way in ...y..., then `y' is non-escaping.
+
+`x' can even be recursive! Eg:
+
+ letrec x = [y] \ [v] -> if v then x True else ...
+ in
+ ...(x b)...
+
+Note [Cost-centre initialization plan]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Previously `coreToStg` was initializing cost-centre stack fields as `noCCS`,
+and the fields were then fixed by a separate pass `stgMassageForProfiling`.
+We now initialize these correctly. The initialization works like this:
+
+ - For non-top level bindings always use `currentCCS`.
+
+ - For top-level bindings, check if the binding is a CAF
+
+ - CAF: If -fcaf-all is enabled, create a new CAF just for this CAF
+ and use it. Note that these new cost centres need to be
+ collected to be able to generate cost centre initialization
+ code, so `coreToTopStgRhs` now returns `CollectedCCs`.
+
+ If -fcaf-all is not enabled, use "all CAFs" cost centre.
+
+ - Non-CAF: Top-level (static) data is not counted in heap profiles; nor
+ do we set CCCS from it; so we just slam in
+ dontCareCostCentre.
+
+Note [Coercion tokens]
+~~~~~~~~~~~~~~~~~~~~~~
+In coreToStgArgs, we drop type arguments completely, but we replace
+coercions with a special coercionToken# placeholder. Why? Consider:
+
+ f :: forall a. Int ~# Bool -> a
+ f = /\a. \(co :: Int ~# Bool) -> error "impossible"
+
+If we erased the coercion argument completely, we’d end up with just
+f = error "impossible", but then f `seq` () would be ⊥!
+
+This is an artificial example, but back in the day we *did* treat
+coercion lambdas like type lambdas, and we had bug reports as a
+result. So now we treat coercion lambdas like value lambdas, but we
+treat coercions themselves as zero-width arguments — coercionToken#
+has representation VoidRep — which gets the best of both worlds.
+
+(For the gory details, see also the (unpublished) paper, “Practical
+aspects of evidence-based compilation in System FC.”)
+
+Note [Saturation of data constructors in STG]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We guarantee that `StgConApp` is an exactly-saturated application of a data
+constructor worker.
+
+* If the data constructor is /under/-saturated we just fall through to build
+ a `StgApp`. Remember, data constructor workers have a regular top-level definition
+ (injected by GHC.CoreToStg.Prep.mkDataConWorkers) so we can partially apply
+ that function.
+
+* If the data constructor is /over/-saturated, which can happen (see #23865) we again
+ fall through to `StgApp`. That will fail horribly at runtime (by applying data
+ constructor to an argument) but it should be in dead code, and at least the compiler
+ itself won't crash. (We could inject an error-thunk instead.)
+
+Note [Naked lambdas in coreToStgExpr]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider
+ f x = case x of
+ True -> \y. y+x
+ False -> blah
+If `f` is not eta expanded (which would have happened in Prep if it was
+going to happen at all, the code for f must allocate a closure for the
+(\y. y+x). So the STG code we want has
+
+ True -> let pap = \y. y+x
+ in pap
+
+The Lam case of `coreToStgExpr` deals with adding this `StgLet`. It's the
+main reason we need a unique supply in the monad.
+
+Historical note: in the past, Prep guaranteed there would be no such naked
+lambdas, so we didn't need a unique supply at all. But that proved too hard
+in the end (see Note [Eta expansion and the CorePrep invariants]) so we
+just deal with it here; it's very easy.
+-}
-- --------------------------------------------------------------
-- Setting variable info: top-level, binds, RHSs
-- --------------------------------------------------------------
-coreToStg :: CoreToStgOpts -> Module -> ModLocation -> CoreProgram
- -> ([StgTopBinding], InfoTableProvMap, CollectedCCs)
-coreToStg opts@CoreToStgOpts
- { coreToStg_ways = ways
- , coreToStg_AutoSccsOnIndividualCafs = opt_AutoSccsOnIndividualCafs
- , coreToStg_InfoTableMap = opt_InfoTableMap
- , coreToStg_stgDebugOpts = stgDebugOpts
- } this_mod ml pgm
- = (pgm'', denv, final_ccs)
+coreToStg :: CoreToStgOpts -> Module -> ModLocation
+ -> CoreProgram
+ -> IO ([StgTopBinding], InfoTableProvMap, CollectedCCs)
+coreToStg opts this_mod ml pgm
+ = do { us <- mkSplitUniqSupply StgTag
+ ; let (_, (local_ccs, local_cc_stacks), pgm')
+ = initCts opts us $
+ coreTopBindsToStg opts this_mod emptyCollectedCCs pgm
+
+ -- See Note [Mapping Info Tables to Source Positions]
+ (!pgm'', !denv)
+ | opt_InfoTableMap
+ = collectDebugInformation stgDebugOpts ml pgm'
+ | otherwise = (pgm', emptyInfoTableProvMap)
+
+ final_ccs
+ | prof && opt_AutoSccsOnIndividualCafs
+ = (local_ccs,local_cc_stacks) -- don't need "all CAFs" CC
+ | prof
+ = (all_cafs_cc:local_ccs, all_cafs_ccs:local_cc_stacks)
+ | otherwise
+ = emptyCollectedCCs
+
+ ; return (pgm'', denv, final_ccs) }
where
- (_, (local_ccs, local_cc_stacks), pgm')
- = coreTopBindsToStg opts this_mod emptyVarEnv emptyCollectedCCs pgm
-
- -- See Note [Mapping Info Tables to Source Positions]
- (!pgm'', !denv)
- | opt_InfoTableMap
- = collectDebugInformation stgDebugOpts ml pgm'
- | otherwise = (pgm', emptyInfoTableProvMap)
+ CoreToStgOpts { coreToStg_ways = ways
+ , coreToStg_AutoSccsOnIndividualCafs = opt_AutoSccsOnIndividualCafs
+ , coreToStg_InfoTableMap = opt_InfoTableMap
+ , coreToStg_stgDebugOpts = stgDebugOpts }
+ = opts
prof = hasWay ways WayProf
-
- final_ccs
- | prof && opt_AutoSccsOnIndividualCafs
- = (local_ccs,local_cc_stacks) -- don't need "all CAFs" CC
- | prof
- = (all_cafs_cc:local_ccs, all_cafs_ccs:local_cc_stacks)
- | otherwise
- = emptyCollectedCCs
-
(all_cafs_cc, all_cafs_ccs) = getAllCAFsCC this_mod
coreTopBindsToStg
:: CoreToStgOpts
-> Module
- -> IdEnv HowBound -- environment for the bindings
-> CollectedCCs
-> CoreProgram
- -> (IdEnv HowBound, CollectedCCs, [StgTopBinding])
+ -> CtsM (IdEnv HowBound, CollectedCCs, [StgTopBinding])
+
+coreTopBindsToStg _ _ ccs []
+ = do { env <- getCtsEnv
+ ; return (env, ccs, []) }
-coreTopBindsToStg _ _ env ccs []
- = (env, ccs, [])
-coreTopBindsToStg opts this_mod env ccs (b:bs)
+coreTopBindsToStg opts this_mod ccs (b:bs)
| NonRec _ rhs <- b, isTyCoArg rhs
- = coreTopBindsToStg opts this_mod env1 ccs1 bs
+ = coreTopBindsToStg opts this_mod ccs bs
| otherwise
- = (env2, ccs2, b':bs')
- where
- (env1, ccs1, b' ) = coreTopBindToStg opts this_mod env ccs b
- (env2, ccs2, bs') = coreTopBindsToStg opts this_mod env1 ccs1 bs
+ = do { (env1, ccs1, b' ) <- coreTopBindToStg opts this_mod ccs b
+ ; (env2, ccs2, bs') <- setCtsEnv env1 $
+ coreTopBindsToStg opts this_mod ccs1 bs
+ ; return (env2, ccs2, b':bs') }
coreTopBindToStg
:: CoreToStgOpts
-> Module
- -> IdEnv HowBound
-> CollectedCCs
-> CoreBind
- -> (IdEnv HowBound, CollectedCCs, StgTopBinding)
+ -> CtsM (IdEnv HowBound, CollectedCCs, StgTopBinding)
-coreTopBindToStg _ _ env ccs (NonRec id e)
+coreTopBindToStg _ _ ccs (NonRec id e)
| Just str <- exprIsTickedString_maybe e
-- top-level string literal
-- See Note [Core top-level string literals] in GHC.Core
- = let
- env' = extendVarEnv env id how_bound
- how_bound = LetBound TopLet 0
- in (env', ccs, StgTopStringLit id str)
-
-coreTopBindToStg opts@CoreToStgOpts
- { coreToStg_platform = platform
- } this_mod env ccs (NonRec id rhs)
- = let
- env' = extendVarEnv env id how_bound
- how_bound = LetBound TopLet $! manifestArity rhs
-
- (ccs', (id', stg_rhs)) =
- initCts platform env $
- coreToTopStgRhs opts this_mod ccs (id,rhs)
-
- bind = StgTopLifted $ StgNonRec id' stg_rhs
- in
- -- NB: previously the assertion printed 'rhs' and 'bind'
- -- as well as 'id', but that led to a black hole
- -- where printing the assertion error tripped the
- -- assertion again!
- (env', ccs', bind)
-
-coreTopBindToStg opts@CoreToStgOpts
- { coreToStg_platform = platform
- } this_mod env ccs (Rec pairs)
+ = do { env <- getCtsEnv
+ ; let env' = extendVarEnv env id how_bound
+ how_bound = LetBound TopLet 0
+ ; return (env', ccs, StgTopStringLit id str) }
+
+coreTopBindToStg opts this_mod ccs (NonRec id rhs)
+ = do { (ccs', (id', stg_rhs)) <- coreToTopStgRhs opts this_mod ccs (id,rhs)
+
+ ; env <- getCtsEnv
+ ; let env' = extendVarEnv env id how_bound
+ how_bound = LetBound TopLet $! manifestArity rhs
+ bind = StgTopLifted $ StgNonRec id' stg_rhs
+ ; return (env', ccs', bind) }
+
+coreTopBindToStg opts this_mod ccs (Rec pairs)
= assert (not (null pairs)) $
- let
- extra_env' = [ (b, LetBound TopLet $! manifestArity rhs)
- | (b, rhs) <- pairs ]
- env' = extendVarEnvList env extra_env'
-
- -- generate StgTopBindings and CAF cost centres created for CAFs
- (ccs', stg_rhss)
- = initCts platform env' $ mapAccumLM (coreToTopStgRhs opts this_mod) ccs pairs
- bind = StgTopLifted $ StgRec stg_rhss
- in
- (env', ccs', bind)
+ do { env <- getCtsEnv
+ ; let extra_env' = [ (b, LetBound TopLet $! manifestArity rhs)
+ | (b, rhs) <- pairs ]
+ env' = extendVarEnvList env extra_env'
+
+ -- Generate StgTopBindings and CAF cost centres created for CAFs
+ ; (ccs', stg_rhss) <- setCtsEnv env' $
+ mapAccumLM (coreToTopStgRhs opts this_mod) ccs pairs
+ ; let bind = StgTopLifted $ StgRec stg_rhss
+
+ ; return (env', ccs', bind) }
coreToTopStgRhs
:: CoreToStgOpts
@@ -420,16 +434,24 @@ coreToStgExpr expr@(App _ _)
res_ty = exprType expr
(app_head, args, ticks) = myCollectArgs expr res_ty
-coreToStgExpr expr@(Lam _ _)
- = let
- (args, body) = myCollectBinders expr
- in
- case filterStgBinders args of
-
- [] -> coreToStgExpr body
-
- _ -> pprPanic "coretoStgExpr" $
- text "Unexpected value lambda:" $$ ppr expr
+coreToStgExpr expr@(Lam {})
+ | null val_bndrs
+ = coreToStgExpr body
+ | otherwise
+ = -- See Note [Naked lambdas in coreToStgExpr]
+ do { body' <- extendVarEnvCts [ (a, LambdaBound) | a <- val_bndrs ] $
+ coreToStgExpr body
+ ; uniq <- getCtsUnique
+ ; let body_ty = exprType body
+ fun_ty = mkLamTypes val_bndrs body_ty
+ -- This type is a bit ill-formed but it doesn't matter
+ rhs = StgRhsClosure noExtFieldSilent currentCCS
+ ReEntrant val_bndrs body' body_ty
+ tmp_fun = mkSysLocal (fsLit "pap") uniq ManyTy fun_ty
+ ; return (StgLet noExtFieldSilent (StgNonRec tmp_fun rhs) $
+ StgApp tmp_fun []) }
+ where
+ (val_bndrs, body) = myCollectBinders NotJoinPoint expr
coreToStgExpr (Tick tick expr)
= do
@@ -634,8 +656,13 @@ coreToStgArgs (arg : args) = do -- Non-type argument
stg_arg_rep = stgArgRep arg'
bad_args = not (primRepsCompatible platform arg_rep stg_arg_rep)
- massertPpr (length ticks' <= 1) (text "More than one Tick in trivial arg:" <+> ppr arg)
- warnPprTraceM bad_args "Dangerous-looking argument. Probable cause: bad unsafeCoerce#" (ppr arg)
+ -- Yikes! This assert FAILS in tests T13658, T14779b
+ -- It has been so for ages, but without the "() <-" it was lazily dropped
+ -- Hence commenting it out: see #27132
+ -- massertPpr (length ticks' <= 1) (text "More than one Tick in trivial arg:" <+> ppr arg)
+
+ () <- warnPprTraceM bad_args
+ "Dangerous-looking argument. Probable cause: bad unsafeCoerce#" (ppr arg)
return (arg' : stg_args, ticks' ++ ticks)
@@ -710,12 +737,11 @@ coreToStgRhs (bndr, rhs) = do
-- coreToStgExpr that can handle value lambdas.
coreToMkStgRhs :: HasDebugCallStack => Id -> CoreExpr -> CtsM MkStgRhs
coreToMkStgRhs bndr expr = do
- let (args, body) = myCollectBinders expr
- let args' = filterStgBinders args
- extendVarEnvCts [ (a, LambdaBound) | a <- args' ] $ do
+ let (bndrs, body) = myCollectBinders (idJoinPointHood bndr) expr
+ extendVarEnvCts [ (a, LambdaBound) | a <- bndrs ] $ do
body' <- coreToStgExpr body
let mk_rhs = MkStgRhs
- { rhs_args = args'
+ { rhs_args = bndrs
, rhs_expr = body'
, rhs_type = exprType body
, rhs_is_join = isJoinId bndr
@@ -733,7 +759,7 @@ coreToMkStgRhs bndr expr = do
newtype CtsM a = CtsM
{ unCtsM :: Platform -- Needed for checking for bad coercions in coreToStgArgs
-> IdEnv HowBound
- -> a
+ -> UniqSM a
}
deriving (Functor)
@@ -769,20 +795,22 @@ data LetInfo
-- The std monad functions:
-initCts :: Platform -> IdEnv HowBound -> CtsM a -> a
-initCts platform env m = unCtsM m platform env
-
+initCts :: CoreToStgOpts -> UniqSupply -> CtsM a -> a
+initCts opts us cts_m
+ = initUs_ us $
+ unCtsM cts_m (coreToStg_platform opts) emptyVarEnv
{-# INLINE thenCts #-}
{-# INLINE returnCts #-}
returnCts :: a -> CtsM a
-returnCts e = CtsM $ \_ _ -> e
+returnCts e = CtsM $ \_ _ -> return e
thenCts :: CtsM a -> (a -> CtsM b) -> CtsM b
-thenCts m k = CtsM $ \platform env
- -> unCtsM (k (unCtsM m platform env)) platform env
+thenCts m k = CtsM $ \platform env ->
+ do { v <- unCtsM m platform env
+ ; unCtsM (k v) platform env }
instance Applicative CtsM where
pure = returnCts
@@ -792,17 +820,26 @@ instance Monad CtsM where
(>>=) = thenCts
getPlatform :: CtsM Platform
-getPlatform = CtsM const
+getPlatform = CtsM $ \platform _ -> return platform
-- Functions specific to this monad:
+setCtsEnv :: IdEnv HowBound -> CtsM a -> CtsM a
+setCtsEnv env thing = CtsM $ \platform _ -> unCtsM thing platform env
+
+getCtsEnv :: CtsM (IdEnv HowBound)
+getCtsEnv = CtsM $ \_ env -> return env
+
+getCtsUnique :: CtsM Unique
+getCtsUnique = CtsM $ \_ _ -> getUniqueM
+
extendVarEnvCts :: [(Id, HowBound)] -> CtsM a -> CtsM a
extendVarEnvCts ids_w_howbound expr
= CtsM $ \platform env
-> unCtsM expr platform (extendVarEnvList env ids_w_howbound)
lookupVarCts :: Id -> CtsM HowBound
-lookupVarCts v = CtsM $ \_ env -> lookupBinding env v
+lookupVarCts v = CtsM $ \_ env -> return (lookupBinding env v)
lookupBinding :: IdEnv HowBound -> Id -> HowBound
lookupBinding env v = case lookupVarEnv env v of
@@ -814,13 +851,26 @@ lookupBinding env v = case lookupVarEnv env v of
filterStgBinders :: [Var] -> [Var]
filterStgBinders bndrs = filter isId bndrs
-myCollectBinders :: Expr Var -> ([Var], Expr Var)
-myCollectBinders expr
+myCollectBinders :: JoinPointHood -> Expr Var -> ([Var], Expr Var)
+-- Collect the binders from a lambda:
+-- * Dropping type lambdas
+-- * Stopping at join-point arity
+myCollectBinders NotJoinPoint expr
= go [] expr
where
- go bs (Lam b e) = go (b:bs) e
- go bs (Cast e _) = go bs e
- go bs e = (reverse bs, e)
+ go bs (Lam b e) | isRuntimeVar b = go (b:bs) e
+ | otherwise = go bs e
+ go bs (Cast e _) = go bs e
+ go bs e = (reverse bs, e)
+
+myCollectBinders (JoinPoint n) expr
+ = go n [] expr
+ where
+ go n bs e | n==0 = (reverse bs, e)
+ go n bs (Lam b e) | isRuntimeVar b = go (n-1) (b:bs) e
+ | otherwise = go (n-1) bs e
+ go n bs (Cast e _) = go n bs e
+ go _ bs e = (reverse bs, e)
-- | If the argument expression is (potential chain of) 'App', return the head
-- of the app chain, and collect ticks/args along the chain.
=====================================
compiler/GHC/CoreToStg/Prep.hs
=====================================
@@ -144,16 +144,13 @@ Here is the syntax of the Core produced by CorePrep:
Expressions
body ::= app
- | let(rec) x = rhs in body -- Boxed only
+ | let(rec) x = body in body -- Boxed only
| case body of pat -> body
- | /\a. body | /\c. body
+ | /\a. body | /\c. body | \x. body
| body |> co
- Right hand sides (only place where value lambdas can occur)
- rhs ::= /\a.rhs | \x.rhs | body
-
-We define a synonym for each of these non-terminals. Functions
-with the corresponding name produce a result in that syntax.
+We define a synonym for each of these non-terminals, CpeArg, CpeApp, and
+CpeBody. Functions with the corresponding name produce a result in that syntax.
Note [Cloning in CorePrep]
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -218,7 +215,6 @@ So our plan is:
type CpeArg = CoreExpr -- Non-terminal 'arg'
type CpeApp = CoreExpr -- Non-terminal 'app'
type CpeBody = CoreExpr -- Non-terminal 'body'
-type CpeRhs = CoreExpr -- Non-terminal 'rhs'
{-
************************************************************************
@@ -261,7 +257,7 @@ corePrepExpr logger config expr = do
withTiming logger (text "CorePrep [expr]") (\e -> e `seq` ()) $ do
us <- mkSplitUniqSupply StgTag
let initialCorePrepEnv = mkInitialCorePrepEnv config
- let new_expr = initUs_ us (cpeBodyNF initialCorePrepEnv expr)
+ let new_expr = initUs_ us (cpeBody initialCorePrepEnv expr)
putDumpFileMaybe logger Opt_D_dump_prep "CorePrep" FormatCore (ppr new_expr)
return new_expr
@@ -665,16 +661,16 @@ cpeBind top_lvl env (Rec pairs)
---------------
cpePair :: TopLevelFlag -> RecFlag -> Demand -> Levity
-> CorePrepEnv -> OutId -> CoreExpr
- -> UniqSM (Floats, CpeRhs)
+ -> UniqSM (Floats, CpeBody)
-- Used for all bindings
-- The binder is already cloned, hence an OutId
cpePair top_lvl is_rec dmd lev env0 bndr rhs
= assert (isNothing $ joinPointBinding_maybe bndr rhs) $ -- those should use cpeJoinPair
- do { (floats1, rhs1) <- cpeRhsE env rhs
+ do { (floats1, rhs1) <- cpeBodyF env rhs
-- See if we are allowed to float this stuff out of the RHS
; let dec = want_float_from_rhs floats1 rhs1
- ; (floats2, rhs2) <- executeFloatDecision env dec floats1 rhs1
+ (floats2, rhs2) = executeFloatDecision dec floats1 rhs1
-- Make the arity match up
; (floats3, rhs3)
@@ -717,7 +713,7 @@ it seems good for CorePrep to be robust.
---------------
cpeJoinPair :: CorePrepEnv -> JoinId -> CoreExpr
- -> UniqSM (JoinId, CpeRhs)
+ -> UniqSM (JoinId, CpeBody)
-- Used for all join bindings
-- No eta-expansion: see Note [Do not eta-expand join points] in GHC.Core.Opt.Simplify.Utils
cpeJoinPair env bndr rhs
@@ -729,7 +725,7 @@ cpeJoinPair env bndr rhs
; (env', bndrs') <- cpCloneBndrs env bndrs
- ; body' <- cpeBodyNF env' body -- Will let-bind the body if it starts
+ ; body' <- cpeBody env' body -- Will let-bind the body if it starts
-- with a lambda
; let rhs' = mkCoreLams bndrs' body'
@@ -757,10 +753,20 @@ for us to mess with the arity because a join point is never exported.
-}
-- ---------------------------------------------------------------------------
--- CpeRhs: produces a result satisfying CpeRhs
+-- cpeBodyF: produces a result satisfying CpeBody
-- ---------------------------------------------------------------------------
-cpeRhsE :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeRhs)
+cpeBodyF :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeBody)
+-- | Convert a 'CoreExpr' so it satisfies 'CpeBody'; also produce
+-- a list of 'Floats' which are being propagated upwards. In
+-- fact, this function is used in only two cases: to
+-- implement 'cpeBody' (which is what you usually want),
+-- and in the case when a let-binding is in a case scrutinee--here,
+-- we can always float out:
+--
+-- case (let x = y in z) of ...
+-- ==> let x = y in case z of ...
+--
-- If
-- e ===> (bs, e')
-- then
@@ -769,32 +775,32 @@ cpeRhsE :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeRhs)
-- For example
-- f (g x) ===> ([v = g x], f v)
-cpeRhsE env (Type ty)
+cpeBodyF env (Type ty)
= return (emptyFloats, Type (cpSubstTy env ty))
-cpeRhsE env (Coercion co)
+cpeBodyF env (Coercion co)
= return (emptyFloats, Coercion (cpSubstCo env co))
-cpeRhsE env expr@(Lit lit)
+cpeBodyF env expr@(Lit lit)
| LitNumber LitNumBigNat i <- lit
= cpeBigNatLit env i
| otherwise = return (emptyFloats, expr)
-cpeRhsE env expr@(Var {}) = cpeApp env expr
-cpeRhsE env expr@(App {}) = cpeApp env expr
+cpeBodyF env expr@(Var {}) = cpeApp env expr
+cpeBodyF env expr@(App {}) = cpeApp env expr
-cpeRhsE env (Let bind body)
+cpeBodyF env (Let bind body)
= do { (env', bind_floats, maybe_bind') <- cpeBind NotTopLevel env bind
- ; (body_floats, body') <- cpeRhsE env' body
+ ; (body_floats, body') <- cpeBodyF env' body
; let expr' = case maybe_bind' of Just bind' -> Let bind' body'
Nothing -> body'
; return (bind_floats `appFloats` body_floats, expr') }
-cpeRhsE env (Tick tickish expr)
+cpeBodyF env (Tick tickish expr)
-- Pull out ticks if they are allowed to be floated.
| tickishFloatable tickish
- = do { (floats, body) <- cpeRhsE env expr
+ = do { (floats, body) <- cpeBodyF env expr
-- See [Floating Ticks in CorePrep]
; return (FloatTick tickish `consFloat` floats, body) }
| otherwise
- = do { body <- cpeBodyNF env expr
+ = do { body <- cpeBody env expr
; return (emptyFloats, mkTick tickish' body) }
where
tickish' | Breakpoint ext bid fvs <- tickish
@@ -803,17 +809,17 @@ cpeRhsE env (Tick tickish expr)
| otherwise
= tickish
-cpeRhsE env (Cast expr co)
- = do { (floats, expr') <- cpeRhsE env expr
+cpeBodyF env (Cast expr co)
+ = do { (floats, expr') <- cpeBodyF env expr
; return (floats, Cast expr' (cpSubstCo env co)) }
-cpeRhsE env expr@(Lam {})
+cpeBodyF env expr@(Lam {})
= do { let (bndrs,body) = collectBinders expr
; (env', bndrs') <- cpCloneBndrs env bndrs
- ; body' <- cpeBodyNF env' body
+ ; body' <- cpeBody env' body
; return (emptyFloats, mkLams bndrs' body') }
-cpeRhsE env (Case scrut bndr _ alts@[Alt con [covar] _])
+cpeBodyF env (Case scrut bndr _ alts@[Alt con [covar] _])
-- See (U3) in Note [Implementing unsafeCoerce]
-- We need make the Case float, otherwise we get
-- let x = case ... of UnsafeRefl co ->
@@ -828,7 +834,7 @@ cpeRhsE env (Case scrut bndr _ alts@[Alt con [covar] _])
-- Note that `x` is a value here. This is visible in the GHCi debugger tests
-- (such as `print003`).
| Just rhs <- isUnsafeEqualityCase scrut bndr alts
- = do { (floats_scrut, scrut) <- cpeBody env scrut
+ = do { (floats_scrut, scrut) <- cpeBodyF env scrut
; (env, bndr') <- cpCloneBndr env bndr
; (env, covar') <- cpCloneCoVarBndr env covar
@@ -836,19 +842,19 @@ cpeRhsE env (Case scrut bndr _ alts@[Alt con [covar] _])
-- See Note [Cloning CoVars and TyVars]
-- Up until here this should do exactly the same as the regular code
- -- path of `cpeRhsE Case{}`.
- ; (floats_rhs, rhs) <- cpeBody env rhs
+ -- path of `cpeBodyF Case{}`.
+ ; (floats_rhs, rhs) <- cpeBodyF env rhs
-- ... but we want to float `floats_rhs` as in (U3) so that rhs' might
-- become a value
; let case_float = UnsafeEqualityCase scrut bndr' con [covar']
-- NB: It is OK to "evaluate" the proof eagerly.
-- Usually there's the danger that we float the unsafeCoerce out of
-- a branching Case alt. Not so here, because the regular code path
- -- for `cpeRhsE Case{}` will not float out of alts.
+ -- for `cpeBodyF Case{}` will not float out of alts.
floats = snocFloat floats_scrut case_float `appFloats` floats_rhs
; return (floats, rhs) }
-cpeRhsE env (Case scrut bndr _ [Alt (DataAlt dc) [token_out, res] rhs])
+cpeBodyF env (Case scrut bndr _ [Alt (DataAlt dc) [token_out, res] rhs])
-- See item (SEQ4) of Note [seq# magic]. We want to match
-- case seq# @a @RealWorld <ok-to-discard> s of (# s', _ #) -> rhs[s']
-- and simplify to rhs[s]. Triggers in T15226.
@@ -869,10 +875,10 @@ cpeRhsE env (Case scrut bndr _ [Alt (DataAlt dc) [token_out, res] rhs])
-- often zaps the OccInfo on case-alternative binders (see Note [DataAlt occ info]
-- in GHC.Core.Opt.Simplify.Iteration) because the scrutinee is not a
-- variable, and in that case the zapping doesn't happen; see that Note.
- = cpeRhsE (extendCorePrepEnv env token_out token_in') rhs
+ = cpeBodyF (extendCorePrepEnv env token_out token_in') rhs
-cpeRhsE env (Case scrut bndr ty alts)
- = do { (floats, scrut') <- cpeBody env scrut
+cpeBodyF env (Case scrut bndr ty alts)
+ = do { (floats, scrut') <- cpeBodyF env scrut
; (env', bndr2) <- cpCloneBndr env bndr
; let bndr3 = bndr2 `setIdUnfolding` evaldUnfolding
; let alts'
@@ -885,7 +891,7 @@ cpeRhsE env (Case scrut bndr ty alts)
, not (altsAreExhaustive alts)
= addDefault alts (Just err)
| otherwise = alts
- where err = mkImpossibleExpr ty "cpeRhsE: missing case alternative"
+ where err = mkImpossibleExpr ty "cpeBodyF: missing case alternative"
; alts'' <- mapM (sat_alt env') alts'
; case alts'' of
@@ -896,7 +902,7 @@ cpeRhsE env (Case scrut bndr ty alts)
where
sat_alt env (Alt con bs rhs)
= do { (env2, bs') <- cpCloneBndrs env bs
- ; rhs' <- cpeBodyNF env2 rhs
+ ; rhs' <- cpeBody env2 rhs
; return (Alt con bs' rhs') }
-- ---------------------------------------------------------------------------
@@ -908,74 +914,10 @@ cpeRhsE env (Case scrut bndr ty alts)
-- let-bound using 'wrapBinds'). Generally you want this, esp.
-- when you've reached a binding form (e.g., a lambda) and
-- floating any further would be incorrect.
-cpeBodyNF :: CorePrepEnv -> CoreExpr -> UniqSM CpeBody
-cpeBodyNF env expr
- = do { (floats, body) <- cpeBody env expr
- ; return (wrapBinds floats body) }
-
--- | Convert a 'CoreExpr' so it satisfies 'CpeBody'; also produce
--- a list of 'Floats' which are being propagated upwards. In
--- fact, this function is used in only two cases: to
--- implement 'cpeBodyNF' (which is what you usually want),
--- and in the case when a let-binding is in a case scrutinee--here,
--- we can always float out:
---
--- case (let x = y in z) of ...
--- ==> let x = y in case z of ...
---
-cpeBody :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeBody)
+cpeBody :: CorePrepEnv -> CoreExpr -> UniqSM CpeBody
cpeBody env expr
- = do { (floats1, rhs) <- cpeRhsE env expr
- ; (floats2, body) <- rhsToBody env rhs
- ; return (floats1 `appFloats` floats2, body) }
-
---------
-rhsToBody :: CorePrepEnv -> CpeRhs -> UniqSM (Floats, CpeBody)
--- Remove top level lambdas by let-binding
-
-rhsToBody env (Tick t expr)
- | tickishHasNoScope t -- only float out of non-scoped annotations
- = do { (floats, expr') <- rhsToBody env expr
- ; return (floats, mkTick t expr') }
-
-rhsToBody env (Cast e co)
- -- You can get things like
- -- case e of { p -> coerce t (\s -> ...) }
- = do { (floats, e') <- rhsToBody env e
- ; return (floats, Cast e' co) }
-
-rhsToBody env expr@(Lam {}) -- See Note [No eta reduction needed in rhsToBody]
- | all isTyVar bndrs -- Type lambdas are ok
- = return (emptyFloats, expr)
- | otherwise -- Some value lambdas
- = do { let rhs = cpeEtaExpand (exprArity expr) expr
- ; fn <- newVar env (exprType rhs)
- ; let float = Float (NonRec fn rhs) LetBound TopLvlFloatable
- ; return (unitFloat float, Var fn) }
- where
- (bndrs,_) = collectBinders expr
-
-rhsToBody _env expr = return (emptyFloats, expr)
-
-
-{- Note [No eta reduction needed in rhsToBody]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Historical note. In the olden days we used to have a Prep-specific
-eta-reduction step in rhsToBody:
- rhsToBody expr@(Lam {})
- | Just no_lam_result <- tryEtaReducePrep bndrs body
- = return (emptyFloats, no_lam_result)
-
-The goal was to reduce
- case x of { p -> \xs. map f xs }
- ==> case x of { p -> map f }
-
-to avoid allocating a lambda. Of course, we'd allocate a PAP
-instead, which is hardly better, but that's the way it was.
-
-Now we simply don't bother with this. It doesn't seem to be a win,
-and it's extra work.
--}
+ = do { (floats, body) <- cpeBodyF env expr
+ ; return (wrapBinds floats body) }
-- ---------------------------------------------------------------------------
-- CpeApp: produces a result satisfying CpeApp
@@ -1060,8 +1002,8 @@ body of the eta-expansion lambda, resulting in
which is unproblematic.
-}
-cpeApp :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeRhs)
--- May return a CpeRhs (instead of CpeApp) because of saturating primops
+cpeApp :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeBody)
+-- May return a CpeBody (instead of CpeApp) because of saturating primops
cpeApp top_env expr
= do { let (terminal, args) = collect_args expr
-- ; pprTraceM "cpeApp" $ (ppr expr)
@@ -1103,7 +1045,7 @@ cpeApp top_env expr
cpe_app :: CorePrepEnv
-> CoreExpr -- The thing we are calling
-> [ArgInfo]
- -> UniqSM (Floats, CpeRhs)
+ -> UniqSM (Floats, CpeBody)
cpe_app env (Var f) (AIApp Type{} : AIApp arg : args)
| f `hasKey` lazyIdKey -- Replace (lazy a) with a, and
-- See Note [lazyId magic] in GHC.Types.Id.Make
@@ -1156,7 +1098,7 @@ cpeApp top_env expr
-- case thing of res { __DEFAULT -> (# token, res#) } },
-- allocating CaseBound Floats for token and thing as needed
= do { (floats1, token) <- cpeArg env topDmd token
- ; (floats2, thing) <- cpeBody env thing
+ ; (floats2, thing) <- cpeBodyF env thing
; case_bndr <- (`setIdUnfolding` evaldUnfolding) <$> newVar env ty
; let tup = mkCoreUnboxedTuple [token, Var case_bndr]
; let float = mkCaseFloat case_bndr thing
@@ -1173,9 +1115,10 @@ cpeApp top_env expr
then Just $! idArity v_hd
else Nothing
Nothing -> Nothing
- -- ; pprTraceM "cpe_app:stricts:" (ppr v <+> ppr args $$ ppr stricts $$ ppr (idCbvMarks_maybe v))
; (app, floats, unsat_ticks) <- rebuild_app env args e2 emptyFloats stricts min_arity
- ; mb_saturate hd app floats unsat_ticks depth }
+ ; case hd of
+ Nothing -> do { massert (null unsat_ticks); return (floats, app) }
+ Just fn_id -> return (floats, maybeSaturate fn_id app depth unsat_ticks) }
where
depth = val_args args
stricts = case idDmdSig v of
@@ -1190,8 +1133,8 @@ cpeApp top_env expr
-- partial application might be seq'd
-- We inlined into something that's not a var and has no args.
- -- Bounce it back up to cpeRhsE.
- cpe_app env fun [] = cpeRhsE env fun
+ -- Bounce it back up to cpeBodyF.
+ cpe_app env fun [] = cpeBodyF env fun
-- Here we get:
-- N-variable fun, better let-bind it
@@ -1202,7 +1145,8 @@ cpeApp top_env expr
-- If evalDmd says that it's sure to be evaluated,
-- we'll end up case-binding it
; (app, floats,unsat_ticks) <- rebuild_app env args fun' fun_floats [] Nothing
- ; mb_saturate Nothing app floats unsat_ticks (val_args args) }
+ ; massert (null unsat_ticks)
+ ; return (floats, app) }
-- Count the number of value arguments *and* coercions (since we don't eliminate the later in STG)
val_args :: [ArgInfo] -> Int
@@ -1223,13 +1167,6 @@ cpeApp top_env expr
| isTypeArg e = n
| otherwise = n+1
- -- Saturate if necessary
- mb_saturate head app floats unsat_ticks depth =
- case head of
- Just fn_id -> do { sat_app <- maybeSaturate fn_id app depth unsat_ticks
- ; return (floats, sat_app) }
- _other -> do { massert (null unsat_ticks)
- ; return (floats, app) }
-- Deconstruct and rebuild the application, floating any non-atomic
-- arguments to the outside. We collect the type of the expression,
@@ -1561,11 +1498,11 @@ Wrinkles:
cpeArg :: CorePrepEnv -> Demand
-> CoreArg -> UniqSM (Floats, CpeArg)
cpeArg env dmd arg
- = do { (floats1, arg1) <- cpeRhsE env arg -- arg1 can be a lambda
+ = do { (floats1, arg1) <- cpeBodyF env arg -- arg1 can be a lambda
; let arg_ty = exprType arg1
lev = typeLevity arg_ty
dec = wantFloatLocal NonRecursive dmd lev floats1 arg1
- ; (floats2, arg2) <- executeFloatDecision env dec floats1 arg1
+ (floats2, arg2) = executeFloatDecision dec floats1 arg1
-- Else case: arg1 might have lambdas, and we can't
-- put them inside a wrapBinds
@@ -1580,7 +1517,12 @@ cpeArg env dmd arg
arg3 = cpeEtaExpand arity arg2
-- See Note [Eta expansion of arguments in CorePrep]
; let (arg_float, v') = mkNonRecFloat env lev v arg3
- ---; pprTraceM "cpeArg" (ppr arg1 $$ ppr dec $$ ppr arg2)
+-- ; pprTraceM "cpeArg" (vcat [ text "arg1" <+> ppr arg1
+-- , text "decision" <+> ppr dec
+-- , text "arg2" <+> ppr arg2
+-- , text "arity" <+> ppr arity
+-- , text "arg3" <+> ppr arg3
+-- ])
; return (snocFloat floats2 arg_float, varToCoreExpr v') }
}
@@ -1617,59 +1559,56 @@ eta_would_wreck_join (Tick _ e) = eta_would_wreck_join e
eta_would_wreck_join (Case _ _ _ alts) = any eta_would_wreck_join (rhssOfAlts alts)
eta_would_wreck_join _ = False
-maybeSaturate :: Id -> CpeApp -> Int -> [CoreTickish] -> UniqSM CpeRhs
+maybeSaturate :: Id -> CpeApp
+ -> Int -- Number of value arguments in the application
+ -> [CoreTickish]
+ -> CpeBody
maybeSaturate fn expr n_args unsat_ticks
- | hasNoBinding fn -- There's no binding
- -- See Note [Eta expansion of hasNoBinding things in CorePrep]
- = return $ wrapLamBody (\body -> foldr mkTick body unsat_ticks) sat_expr
-
- | mark_arity > 0 -- A call-by-value function.
- -- See Note [CBV Function Ids: overview]
- , not applied_marks
- = assertPpr
- ( not (isJoinId fn)) -- See Note [Do not eta-expand join points]
- ( ppr fn $$ text "expr:" <+> ppr expr $$ text "n_args:" <+> ppr n_args $$
- text "marks:" <+> ppr (idCbvMarks_maybe fn) $$
- text "join_arity" <+> ppr (idJoinPointHood fn) $$
- text "fn_arity" <+> ppr fn_arity
- ) $
- -- pprTrace "maybeSat"
- -- ( ppr fn $$ text "expr:" <+> ppr expr $$ text "n_args:" <+> ppr n_args $$
- -- text "marks:" <+> ppr (idCbvMarks_maybe fn) $$
- -- text "join_arity" <+> ppr (isJoinId_maybe fn) $$
- -- text "fn_arity" <+> ppr fn_arity $$
- -- text "excess_arity" <+> ppr excess_arity $$
- -- text "mark_arity" <+> ppr mark_arity
- -- ) $
- return sat_expr
+ | isJoinId fn -- Never eta-expand a call to a join point
+ -- See Note [Do not eta-expand join points]
+ = assertPpr (not must_eta_expand) (ppr expr) $
+ -- assertPpr: check that all arguments that need to be passed cbv
+ -- are visible, so the backend can evalaute them if required
+ expr
+
+ | must_eta_expand || desirable_to_eta_expand
+ -- n_args > 0: do not eta-expand a naked variable!
+ = wrapLamBody (mkTicks unsat_ticks) $
+ cpeEtaExpand excess_arity expr
| otherwise
- = assert (null unsat_ticks) $
- return expr
+ = expr
+
where
- mark_arity = idCbvMarkArity fn
- fn_arity = idArity fn
- excess_arity = (max fn_arity mark_arity) - n_args
- sat_expr = cpeEtaExpand excess_arity expr
- applied_marks = n_args >= (length . dropWhile (not . isMarkedCbv) .
- reverse . expectJust $ (idCbvMarks_maybe fn))
- -- For join points we never eta-expand (See Note [Do not eta-expand join points])
- -- so we assert all arguments that need to be passed cbv are visible so that the
- -- backend can evalaute them if required..
+ must_eta_expand
+ = (hasNoBinding fn && fn_arity > n_args)
+ -- hasNoBinding functions must be saturated
+ || (mark_arity > n_args)
+ -- CBV functions must be CBV-saturated
+
+ desirable_to_eta_expand = fn_arity > n_args && n_args > 0
+ -- n_args > 0: do not eta-expand a naked variable unless we have to
+
+ mark_arity = idCbvMarkArity fn
+ fn_arity = idArity fn
+ excess_arity = (max fn_arity mark_arity) - n_args
{- Note [Eta expansion]
~~~~~~~~~~~~~~~~~~~~~~~
-Eta expand to match the arity claimed by the binder Remember,
-CorePrep must not change arity
+Eta expand to match the arity claimed by the binder.
+Remember, CorePrep must not change arity
Eta expansion might not have happened already, because it is done by
the simplifier only when there at least one lambda already.
-NB1:we could refrain when the RHS is trivial (which can happen
- for exported things). This would reduce the amount of code
- generated (a little) and make things a little worse for
- code compiled without -O. The case in point is data constructor
- wrappers.
+We do eta-expansion (via `cpeEtaExpand`) in three places:
+
+* At let-bindings; in `cpePair`
+
+* On function arguments: in `cpeArg`
+ See Note [Eta expansion of arguments in CorePrep]
+
+* At un-saturated function calls: in `maybeSaturate`
NB2: we have to be careful that the result of etaExpand doesn't
invalidate any of the assumptions that CorePrep is attempting
@@ -1677,12 +1616,37 @@ NB2: we have to be careful that the result of etaExpand doesn't
an SCC note - we're now careful in etaExpand to make sure the
SCC is pushed inside any new lambdas that are generated.
-Note [Eta expansion of hasNoBinding things in CorePrep]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-maybeSaturate deals with eta expanding to saturate things that can't deal
-with unsaturated applications (identified by 'hasNoBinding', currently
-foreign calls, unboxed tuple/sum constructors, and representation-polymorphic
-primitives such as 'coerce' and 'unsafeCoerce#').
+Note [Eta expansion for let-bindings]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Given f = rhs, we eta-expand `rhs` to match f's arity.
+
+We could refrain when the RHS is trivial (which can happen for exported things).
+This would reduce the amount of code generated (a little) and make things a
+little worse for code compiled without -O. The case in point is data
+constructor wrappers.
+
+Note [Eta expansion of unsaturated calls]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Give a call (f a1..an), where `f` is a known function with arity greater than `n`,
+there are three reasons we might want to eta-expand:
+
+* Must eta-expand: if `f` is a `hasNoBinding` function, we must saturate
+ it, because the function has no (curried) binding to call. Currently
+ this includes:
+ - foreign calls,
+ - unboxed tuple/sum constructors
+ - representation-polymorphic primitives such as 'coerce' and 'unsafeCoerce#'
+ - primops (for now anyway; see comments in `hasNoBinding`)
+
+* Must eta-expand: if `f` has a call-by-value calling convention, we /must/
+ call it with evaluated arguments. The back end deals with adding the
+ necessary evaluation at the call site, but we must first ensure that it is
+ saturated.
+
+* May eta-expand: consider
+ \x -> f x True
+ where `f` has arity 3. Then it's much better to eta-expand f so we have
+ \xy -> f x True y
Historical Note: Note that eta expansion in CorePrep used to be very fragile
due to the "prediction" of CAFfyness that we used to make during tidying. We
@@ -1694,7 +1658,7 @@ Note [Eta expansion and the CorePrep invariants]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It turns out to be much much easier to do eta expansion
*after* the main CorePrep stuff. But that places constraints
-on the eta expander: given a CpeRhs, it must return a CpeRhs.
+on the eta expander: given a CpeBody, it must return a CpeBody.
For example here is what we do not want:
f = /\a -> g (h 3) -- h has arity 2
@@ -1706,6 +1670,26 @@ and now we do NOT want eta expansion to give
Instead GHC.Core.Opt.Arity.etaExpand gives
f = /\a -> \y -> let s = h 3 in g s y
+Another example:
+ f x = case x of
+ A -> \y. e
+ B -> hnb 3 -- where `hnb` has no binding
+ C -> z
+Then we may eta-expand `hnb` to get
+ f x = case x of
+ A -> \y. e
+ B -> \y. hnb 3 y
+ C -> z
+Now we come to the binding of `f` itself, and eta-expand that, to give
+ f x y = case x of
+ A -> e
+ B -> hnb 3 y
+ C -> z y
+Notice how important it is that the eta-expansion for `f` doesn't
+generate any crap like
+ B -> (\y. hnb 3 y) y
+Fortunately, the eta-expander is careful not to do so.
+
Note [Eta expansion of arguments in CorePrep]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Suppose `g = \x y. blah` and consider the expression `f (g x)`; we ANFise to
@@ -1798,7 +1782,7 @@ There is a nasty Wrinkle:
#24471 is a good example, where Prep took 25% of compile time!
-}
-cpeEtaExpand :: Arity -> CpeRhs -> CpeRhs
+cpeEtaExpand :: Arity -> CpeBody -> CpeBody
cpeEtaExpand arity expr
| arity == 0 = expr
| otherwise = etaExpand arity expr
@@ -2165,9 +2149,6 @@ isEmptyFloats (Floats _ b) = isNilOL b
getFloats :: Floats -> OrdList FloatingBind
getFloats = fs_binds
-unitFloat :: FloatingBind -> Floats
-unitFloat = snocFloat emptyFloats
-
floatInfo :: FloatingBind -> FloatInfo
floatInfo (Float _ _ info) = info
floatInfo UnsafeEqualityCase{} = LazyContextFloatable -- See Note [Floating in CorePrep]
@@ -2255,7 +2236,7 @@ decideFloatInfo FIA{fia_levity=lev, fia_demand=dmd, fia_is_hnf=is_hnf,
| Lifted <- lev = (LetBound, TopLvlFloatable)
-- And these float freely but can't be speculated, hence LetBound
-mkCaseFloat :: Id -> CpeRhs -> FloatingBind
+mkCaseFloat :: Id -> CpeBody -> FloatingBind
mkCaseFloat bndr scrut
= -- pprTrace "mkCaseFloat" (ppr bndr <+> ppr (bound,info)
-- -- <+> ppr is_lifted <+> ppr is_strict
@@ -2273,7 +2254,7 @@ mkCaseFloat bndr scrut
-- (ok-for-spec case bindings are unlikely anyway.)
}
-mkNonRecFloat :: CorePrepEnv -> Levity -> Id -> CpeRhs -> (FloatingBind, Id)
+mkNonRecFloat :: CorePrepEnv -> Levity -> Id -> CpeBody -> (FloatingBind, Id)
mkNonRecFloat env lev bndr rhs
= -- pprTrace "mkNonRecFloat" (ppr bndr <+> ppr (bound,info)
-- <+> if is_strict then text "strict" else if is_lifted then text "lazy" else text "unlifted"
@@ -2413,24 +2394,18 @@ instance Outputable FloatDecision where
ppr FloatNone = text "none"
ppr FloatAll = text "all"
-executeFloatDecision :: CorePrepEnv -> FloatDecision -> Floats -> CpeRhs -> UniqSM (Floats, CpeRhs)
-executeFloatDecision env dec floats rhs
+executeFloatDecision :: FloatDecision -> Floats -> CpeBody -> (Floats, CpeBody)
+executeFloatDecision dec floats rhs
= case dec of
- FloatAll -> return (floats, rhs)
- FloatNone
- | isEmptyFloats floats -> return (emptyFloats, rhs)
- | otherwise -> do { (floats', body) <- rhsToBody env rhs
- ; return (emptyFloats, wrapBinds floats $
- wrapBinds floats' body) }
- -- FloatNone case: `rhs` might have lambdas, and we can't
- -- put them inside a wrapBinds, which expects a `CpeBody`.
+ FloatAll -> (floats, rhs)
+ FloatNone -> (emptyFloats, wrapBinds floats rhs)
wantFloatTop :: Floats -> FloatDecision
wantFloatTop fs
| fs_info fs `floatsAtLeastAsFarAs` TopLvlFloatable = FloatAll
| otherwise = FloatNone
-wantFloatLocal :: RecFlag -> Demand -> Levity -> Floats -> CpeRhs -> FloatDecision
+wantFloatLocal :: RecFlag -> Demand -> Levity -> Floats -> CpeBody -> FloatDecision
-- See Note [wantFloatLocal]
wantFloatLocal is_rec rhs_dmd rhs_lev floats rhs
| isEmptyFloats floats -- Well yeah...
@@ -2479,7 +2454,7 @@ zero free variables.)
In general, the inliner is good at eliminating these let-bindings. However,
there is one case where these trivial updatable thunks can arise: when
we are optimizing away 'lazy' (see Note [lazyId magic], and also
-'cpeRhsE'.) Then, we could have started with:
+'cpeBodyF'.) Then, we could have started with:
let x :: ()
x = lazy @() y
@@ -2783,8 +2758,7 @@ wrapTicks floats expr
-- ---------------------------------------------------------------------------
-- | Converts Bignum literals into their final CoreExpr
-cpeBigNatLit
- :: CorePrepEnv -> Integer -> UniqSM (Floats, CpeRhs)
+cpeBigNatLit :: CorePrepEnv -> Integer -> UniqSM (Floats, CpeBody)
cpeBigNatLit env i = assert (i >= 0) $ do
let
platform = cp_platform (cpe_config env)
=====================================
compiler/GHC/Driver/Main.hs
=====================================
@@ -2434,8 +2434,8 @@ myCoreToStg :: Logger -> DynFlags -> [Var]
, CollectedCCs -- CAF cost centre info (declared and used)
, StgCgInfos )
myCoreToStg logger dflags ic_inscope for_bytecode this_mod ml prepd_binds = do
- let (stg_binds, denv, cost_centre_info)
- = {-# SCC "Core2Stg" #-}
+ (stg_binds, denv, cost_centre_info)
+ <- {-# SCC "Core2Stg" #-}
coreToStg (initCoreToStgOpts dflags) this_mod ml prepd_binds
(stg_binds_with_fvs,stg_cg_info)
=====================================
compiler/GHC/Stg/Lint.hs
=====================================
@@ -105,7 +105,7 @@ import GHC.Core ( AltCon(..) )
import GHC.Core.Type
import GHC.Core.Lint ( lintMessage )
-import GHC.Types.Basic ( TopLevelFlag(..), isTopLevel, isMarkedCbv )
+import GHC.Types.Basic ( TopLevelFlag(..), isTopLevel )
import GHC.Types.CostCentre ( isCurrentCCS )
import GHC.Types.Id
import GHC.Types.Var.Set
@@ -123,12 +123,9 @@ import GHC.Unit.Module ( Module )
import GHC.Data.Bag ( Bag, emptyBag, isEmptyBag, snocBag, bagToList )
import Control.Monad
-import Data.Maybe
-import GHC.Utils.Misc
import GHC.Core.Multiplicity (scaledThing)
import GHC.Settings (Platform)
import GHC.Core.TyCon (primRepCompatible, primRepsCompatible)
-import GHC.Utils.Panic.Plain (panic)
lintStgTopBindings :: forall a . (OutputablePass a, BinderP a ~ Id)
=> Platform
@@ -174,36 +171,37 @@ lintStgTopBindings platform logger diag_opts opts extra_vars this_mod unarised w
lint_bind (StgTopStringLit v _) = return [v]
lintStgConArg :: StgArg -> LintM ()
-lintStgConArg arg = do
- unarised <- lf_unarised <$> getLintFlags
- when unarised $ case stgArgRep_maybe arg of
- -- Note [Post-unarisation invariants], invariant 4
- Just [_] -> pure ()
- badRep -> addErrL $
- text "Non-unary constructor arg: " <> ppr arg $$
- text "Its PrimReps are: " <> ppr badRep
-
- case arg of
- StgLitArg _ -> pure ()
- StgVarArg v -> lintStgVar v
+lintStgConArg arg
+ = do { lintStgArg arg
+
+ ; unarised <- lf_unarised <$> getLintFlags
+ ; when unarised $ case stgArgRep_maybe arg of
+ -- Note [Post-unarisation invariants], invariant 4
+ Just [_] -> pure ()
+ badRep -> addErrL $
+ text "Non-unary constructor arg: " <> ppr arg $$
+ text "Its PrimReps are: " <> ppr badRep }
lintStgFunArg :: StgArg -> LintM ()
-lintStgFunArg arg = do
- unarised <- lf_unarised <$> getLintFlags
- when unarised $ case stgArgRep_maybe arg of
- -- Note [Post-unarisation invariants], invariant 3
- Just [] -> pure ()
- Just [_] -> pure ()
- badRep -> addErrL $
- text "Function arg is not unary or void: " <> ppr arg $$
- text "Its PrimReps are: " <> ppr badRep
-
- case arg of
- StgLitArg _ -> pure ()
- StgVarArg v -> lintStgVar v
-
-lintStgVar :: Id -> LintM ()
-lintStgVar id = checkInScope id
+lintStgFunArg arg
+ = do { lintStgArg arg
+
+ ; unarised <- lf_unarised <$> getLintFlags
+ ; when unarised $ case stgArgRep_maybe arg of
+ -- Note [Post-unarisation invariants], invariant 3
+ Just [] -> pure ()
+ Just [_] -> pure ()
+ badRep -> addErrL $
+ text "Function arg is not unary or void: " <> ppr arg $$
+ text "Its PrimReps are: " <> ppr badRep }
+
+lintStgArg :: StgArg -> LintM ()
+lintStgArg (StgLitArg _) = pure ()
+lintStgArg (StgVarArg v) = do { lintStgVarOcc v
+ ; lintAppCbvMarks v [] }
+
+lintStgVarOcc :: Id -> LintM ()
+lintStgVarOcc id = checkInScope id
lintStgBinds
:: (OutputablePass a, BinderP a ~ Id)
@@ -275,13 +273,11 @@ lintStgExpr :: (OutputablePass a, BinderP a ~ Id) => GenStgExpr a -> LintM ()
lintStgExpr (StgLit _) = return ()
-lintStgExpr e@(StgApp fun args) = do
- lintStgVar fun
- mapM_ lintStgFunArg args
- lintAppCbvMarks e
- lintStgAppReps fun args
-
-
+lintStgExpr (StgApp fun args)
+ = do { lintStgVarOcc fun
+ ; mapM_ lintStgFunArg args
+ ; lintAppCbvMarks fun args
+ ; lintStgAppReps fun args }
lintStgExpr app@(StgConApp con _n args _arg_tys) = do
-- unboxed sums should vanish during unarise
@@ -413,22 +409,20 @@ lintStgAppReps fun args = do
match_args actual_arg_reps fun_arg_tys_reps
-lintAppCbvMarks :: OutputablePass pass
- => GenStgExpr pass -> LintM ()
-lintAppCbvMarks e@(StgApp fun args) = do
- lf <- getLintFlags
- when (lf_unarised lf) $ do
+lintAppCbvMarks :: Id -> [StgArg] -> LintM ()
+lintAppCbvMarks fun args
+ | idCbvMarkArity fun > length args
-- A function which expects a unlifted argument as n'th argument
-- always needs to be applied to n arguments.
-- See Note [CBV Function Ids: overview].
- let marks = fromMaybe [] $ idCbvMarks_maybe fun
- when (length (dropWhileEndLE (not . isMarkedCbv) marks) > length args) $ do
- addErrL $ hang (text "Undersatured cbv marked ID in App" <+> ppr e ) 2 $
- (text "marks" <> ppr marks $$
- text "args" <> ppr args $$
- text "arity" <> ppr (idArity fun) $$
- text "join_arity" <> ppr (idJoinPointHood fun))
-lintAppCbvMarks _ = panic "impossible - lintAppCbvMarks"
+ = addErrL $ hang (text "Undersatured cbv marked ID in App" <+> ppr fun)
+ 2 (vcat [ text "marks" <> ppr (idCbvMarks_maybe fun)
+ , text "args" <> ppr args
+ , text "arity" <> ppr (idArity fun)
+ , text "join_arity" <> ppr (idJoinPointHood fun) ])
+
+ | otherwise
+ = return ()
{-
************************************************************************
=====================================
compiler/GHC/Types/Id.hs
=====================================
@@ -852,7 +852,7 @@ idCbvMarks_maybe id = case idDetails id of
_ -> Nothing
-- Id must be called with at least this arity in order to allow arguments to
--- be passed unlifted.
+-- be passed unlifted. Return 0 if there are no CBV marks.
idCbvMarkArity :: Id -> Arity
idCbvMarkArity fn = maybe 0 length (idCbvMarks_maybe fn)
=====================================
compiler/GHC/Types/Id/Info.hs
=====================================
@@ -210,6 +210,7 @@ data IdDetails
-- Can also work as a WorkerLikeId if given `CbvMark`s.
-- See Note [CBV Function Ids: overview]
-- The [CbvMark] is always empty (and ignored) until after Tidy.
+
| WorkerLikeId [CbvMark]
-- ^ An 'Id' for a worker like function, which might expect some arguments to be
-- passed both evaluated and tagged.
@@ -217,8 +218,10 @@ data IdDetails
-- aren't used unapplied.
-- See Note [CBV Function Ids: overview]
-- See Note [EPT enforcement]
- -- The [CbvMark] is always empty (and ignored) until after Tidy for ids from the current
- -- module.
+ -- Invariants:
+ -- - the [CbvMark] is always empty (and ignored) until after Tidy
+ -- for ids from the current module
+ -- - If non-empty, at least is isMarkedCbbv; see (CBV2)
data RecSelInfo
= RSI { rsi_def :: [ConLike] -- Record selector defined for these
@@ -297,9 +300,7 @@ Here's how it all works:
to identify strict arguments. See Note [Call-by-value for worker args] for
how a worker guarantees to be strict in strict datacon fields.
- TODO: We currently don't do this for arguments that are unboxed sums or tuples,
- because then we'd have to predict the result of unarisation. But it would be nice to
- do so. See `computeCbvInfo`.
+ See (CBV1) and (CBV2).
* During CorePrep calls to CBV Ids are eta expanded.
See `GHC.CoreToStg.Prep.maybeSaturate`.
@@ -319,6 +320,16 @@ Here's how it all works:
* Imported functions may be CBV, and then there is no point in eta-reducing
them; we'll just have to eta-expand later; see GHC.Core.Opt.Arity.cantEtaReduceFun.
+Wrinkles
+
+(CBV1) We do not set the CBV-marks for a function that takes an unboxed sum or tuple,
+ as an argument, because then we'd have to predict the result of unarisation.
+ It would be nice to do so in future. See `computeCbvInfo`.
+
+(CBV2) We do not set CBV-marks if none of them are `isMarkedCbv`. Why not?
+ Because if none are CBV then there is nothing special to do for this function;
+ in particular, we don't need to saturate its calls. See `computeCbvInfo`.
+
*** SPJ really? Andreas? ****
We only use this for workers and specialized versions of SpecConstr
But we also check other functions during tidy and potentially turn some of them into
=====================================
testsuite/tests/arityanal/should_compile/Arity01.stderr
=====================================
@@ -5,19 +5,19 @@ Result size of Tidy Core = {terms: 71, types: 43, coercions: 0, joins: 0/0}
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F1.f2 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F1.f2 = GHC.Num.Integer.IS 1#
+F1.f2 = GHC.Internal.Bignum.Integer.IS 1#
Rec {
-- RHS size: {terms: 24, types: 6, coercions: 0, joins: 0/0}
F1.f1_h1 [Occ=LoopBreaker] :: Integer -> Integer -> Integer -> Integer
[GblId, Arity=3, Str=<1L><1L><SL>, Unf=OtherCon []]
F1.f1_h1
- = \ (n :: Integer) (x :: Integer) (eta [OS=OneShot] :: Integer) ->
+ = \ (n :: Integer) (x [OS=OneShot] :: Integer) (eta [OS=OneShot] :: Integer) ->
case x of x1 { __DEFAULT ->
case n of y1 { __DEFAULT ->
- case GHC.Num.Integer.integerLt# x1 y1 of {
+ case GHC.Internal.Bignum.Integer.integerLt# x1 y1 of {
__DEFAULT -> eta;
- 1# -> F1.f1_h1 y1 (GHC.Num.Integer.integerAdd x1 F1.f2) (GHC.Num.Integer.integerAdd x1 eta)
+ 1# -> F1.f1_h1 y1 (GHC.Internal.Bignum.Integer.integerAdd x1 F1.f2) (GHC.Internal.Bignum.Integer.integerAdd x1 eta)
}
}
}
@@ -26,7 +26,7 @@ end Rec }
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F1.f3 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F1.f3 = GHC.Num.Integer.IS 5#
+F1.f3 = GHC.Internal.Bignum.Integer.IS 5#
-- RHS size: {terms: 4, types: 0, coercions: 0, joins: 0/0}
f1 :: Integer
@@ -36,27 +36,27 @@ f1 = F1.f1_h1 F1.f3 F1.f2 F1.f3
-- RHS size: {terms: 14, types: 5, coercions: 0, joins: 0/0}
g :: Integer -> Integer -> Integer -> Integer -> Integer -> Integer
[GblId, Arity=5, Str=<1L><SL><SL><SL><SL>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 0 0 0 0] 120 0}]
-g = \ (x1 :: Integer) (x2 :: Integer) (x3 :: Integer) (x4 :: Integer) (x5 :: Integer) -> GHC.Num.Integer.integerAdd (GHC.Num.Integer.integerAdd (GHC.Num.Integer.integerAdd (GHC.Num.Integer.integerAdd x1 x2) x3) x4) x5
+g = \ (x1 :: Integer) (x2 :: Integer) (x3 :: Integer) (x4 :: Integer) (x5 :: Integer) -> GHC.Internal.Bignum.Integer.integerAdd (GHC.Internal.Bignum.Integer.integerAdd (GHC.Internal.Bignum.Integer.integerAdd (GHC.Internal.Bignum.Integer.integerAdd x1 x2) x3) x4) x5
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F1.s1 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F1.s1 = GHC.Num.Integer.IS 3#
+F1.s1 = GHC.Internal.Bignum.Integer.IS 3#
-- RHS size: {terms: 8, types: 7, coercions: 0, joins: 0/0}
s :: forall {t1} {t2}. Num t1 => (t1 -> t2) -> t2
-[GblId, Arity=2, Str=<MP(A,A,A,A,A,A,1C(1,L))><1C(1,L)>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [30 60] 50 0}]
+[GblId, Arity=2, Str=<MP(A,A,A,A,A,A,1C(1,L))><1C(1,L)>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [90 60] 50 0}]
s = \ (@t) (@t1) ($dNum :: Num t) (f :: t -> t1) -> f (fromInteger @t $dNum F1.s1)
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F1.h1 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F1.h1 = GHC.Num.Integer.IS 24#
+F1.h1 = GHC.Internal.Bignum.Integer.IS 24#
-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
h :: Integer -> Integer
[GblId, Arity=1, Str=<SL>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 30 0}]
-h = \ (x5 :: Integer) -> GHC.Num.Integer.integerAdd F1.h1 x5
+h = \ (x5 :: Integer) -> GHC.Internal.Bignum.Integer.integerAdd F1.h1 x5
=====================================
testsuite/tests/arityanal/should_compile/Arity05.stderr
=====================================
@@ -5,27 +5,27 @@ Result size of Tidy Core = {terms: 42, types: 44, coercions: 0, joins: 0/0}
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F5.f5g1 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F5.f5g1 = GHC.Num.Integer.IS 1#
+F5.f5g1 = GHC.Internal.Bignum.Integer.IS 1#
-- RHS size: {terms: 12, types: 9, coercions: 0, joins: 0/0}
f5g :: forall {a} {t}. Num a => (t -> a) -> t -> a
-[GblId, Arity=3, Str=<SP(1C(1,C(1,L)),A,A,A,A,A,MC(1,L))><MC(1,L)><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [60 60 0] 90 0}]
+[GblId, Arity=3, Str=<SP(1C(1,C(1,L)),A,A,A,A,A,MC(1,L))><MC(1,L)><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [180 60 0] 90 0}]
f5g = \ (@a) (@t) ($dNum :: Num a) (h :: t -> a) (z :: t) -> + @a $dNum (h z) (fromInteger @a $dNum F5.f5g1)
-- RHS size: {terms: 17, types: 12, coercions: 0, joins: 0/0}
f5h :: forall {a} {t}. Num a => (t -> a) -> t -> (t -> a) -> a
-[GblId, Arity=4, Str=<SP(SC(S,C(1,L)),A,A,A,A,A,MC(1,L))><MC(1,L)><L><MC(1,L)>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [90 60 0 60] 150 0}]
+[GblId, Arity=4, Str=<SP(SC(S,C(1,L)),A,A,A,A,A,MC(1,L))><MC(1,L)><L><MC(1,L)>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [270 60 0 60] 150 0}]
f5h = \ (@a) (@t) ($dNum :: Num a) (f :: t -> a) (x :: t) (g :: t -> a) -> + @a $dNum (f x) (+ @a $dNum (g x) (fromInteger @a $dNum F5.f5g1))
-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
f5y :: Integer -> Integer
[GblId, Arity=1, Str=<1L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 30 0}]
-f5y = \ (y :: Integer) -> GHC.Num.Integer.integerAdd y F5.f5g1
+f5y = \ (y :: Integer) -> GHC.Internal.Bignum.Integer.integerAdd y F5.f5g1
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
f5 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-f5 = GHC.Num.Integer.IS 3#
+f5 = GHC.Internal.Bignum.Integer.IS 3#
=====================================
testsuite/tests/arityanal/should_compile/Arity08.stderr
=====================================
@@ -4,7 +4,7 @@ Result size of Tidy Core = {terms: 24, types: 18, coercions: 0, joins: 0/0}
-- RHS size: {terms: 20, types: 10, coercions: 0, joins: 0/0}
f8f :: forall {p}. Num p => Bool -> p -> p -> p
-[GblId, Arity=4, Str=<LP(SC(S,C(1,L)),A,MC(1,C(1,L)),A,A,A,A)><1L><L><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [90 30 0 0] 140 0}]
+[GblId, Arity=4, Str=<LP(SC(S,C(1,L)),A,MC(1,C(1,L)),A,A,A,A)><1L><L><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [270 30 0 0] 140 0}]
f8f
= \ (@p) ($dNum :: Num p) (b :: Bool) (x :: p) (y :: p) ->
case b of {
@@ -15,7 +15,7 @@ f8f
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
f8 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-f8 = GHC.Num.Integer.IS 2#
+f8 = GHC.Internal.Bignum.Integer.IS 2#
=====================================
testsuite/tests/arityanal/should_compile/Arity11.stderr
=====================================
@@ -5,57 +5,23 @@ Result size of Tidy Core = {terms: 136, types: 75, coercions: 0, joins: 2/7}
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.fib3 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.fib3 = GHC.Num.Integer.IS 1#
+F11.fib3 = GHC.Internal.Bignum.Integer.IS 1#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.fib2 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.fib2 = GHC.Num.Integer.IS 2#
-
-Rec {
--- RHS size: {terms: 38, types: 13, coercions: 0, joins: 2/2}
-F11.f11_fib [Occ=LoopBreaker] :: Integer -> Integer
-[GblId, Arity=1, Str=<SL>, Unf=OtherCon []]
-F11.f11_fib
- = \ (ds :: Integer) ->
- join {
- $j [Dmd=ML] :: Integer
- [LclId[JoinId(0)(Nothing)]]
- $j
- = join {
- $j1 [Dmd=ML] :: Integer
- [LclId[JoinId(0)(Nothing)]]
- $j1 = GHC.Num.Integer.integerAdd (F11.f11_fib (GHC.Num.Integer.integerSub ds F11.fib3)) (F11.f11_fib (GHC.Num.Integer.integerSub ds F11.fib2)) } in
- case ds of {
- GHC.Num.Integer.IS x1 ->
- case x1 of {
- __DEFAULT -> jump $j1;
- 1# -> F11.fib3
- };
- GHC.Num.Integer.IP x1 -> jump $j1;
- GHC.Num.Integer.IN x1 -> jump $j1
- } } in
- case ds of {
- GHC.Num.Integer.IS x1 ->
- case x1 of {
- __DEFAULT -> jump $j;
- 0# -> F11.fib3
- };
- GHC.Num.Integer.IP x1 -> jump $j;
- GHC.Num.Integer.IN x1 -> jump $j
- }
-end Rec }
+F11.fib2 = GHC.Internal.Bignum.Integer.IS 2#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.fib1 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.fib1 = GHC.Num.Integer.IS 0#
+F11.fib1 = GHC.Internal.Bignum.Integer.IS 0#
-- RHS size: {terms: 54, types: 27, coercions: 0, joins: 0/5}
-fib :: forall {t} {a}. (Eq t, Num t, Num a) => t -> a
-[GblId, Arity=4, Str=<SP(SC(S,C(1,L)),A)><LP(A,LC(L,C(1,L)),A,A,A,A,L)><LP(LC(S,C(1,L)),A,A,A,A,A,MC(1,L))><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [60 150 60 0] 480 0}]
+fib :: forall {t1} {t2}. (Eq t1, Num t1, Num t2) => t1 -> t2
+[GblId, Arity=4, Str=<SP(SC(S,C(1,L)),A)><LP(A,LC(L,C(1,L)),A,A,A,A,L)><LP(LC(S,C(1,L)),A,A,A,A,A,MC(1,L))><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [180 450 180 0] 480 0}]
fib
- = \ (@t) (@a) ($dEq :: Eq t) ($dNum :: Num t) ($dNum1 :: Num a) (eta :: t) ->
+ = \ (@t) (@t1) ($dEq :: Eq t) ($dNum :: Num t) ($dNum1 :: Num t1) (eta :: t) ->
let {
lvl :: t
[LclId]
@@ -65,32 +31,66 @@ fib
[LclId]
lvl1 = fromInteger @t $dNum F11.fib2 } in
let {
- lvl2 :: a
+ lvl2 :: t1
[LclId]
- lvl2 = fromInteger @a $dNum1 F11.fib3 } in
+ lvl2 = fromInteger @t1 $dNum1 F11.fib3 } in
let {
lvl3 :: t
[LclId]
lvl3 = fromInteger @t $dNum F11.fib1 } in
letrec {
- fib4 [Occ=LoopBreaker, Dmd=SC(S,L)] :: t -> a
+ fib4 [Occ=LoopBreaker, Dmd=SC(S,L)] :: t -> t1
[LclId, Arity=1, Str=<L>, Unf=OtherCon []]
fib4
= \ (ds :: t) ->
case == @t $dEq ds lvl3 of {
False ->
case == @t $dEq ds lvl of {
- False -> + @a $dNum1 (fib4 (- @t $dNum ds lvl)) (fib4 (- @t $dNum ds lvl1));
+ False -> + @t1 $dNum1 (fib4 (- @t $dNum ds lvl)) (fib4 (- @t $dNum ds lvl1));
True -> lvl2
};
True -> lvl2
}; } in
fib4 eta
+Rec {
+-- RHS size: {terms: 38, types: 13, coercions: 0, joins: 2/2}
+F11.f11_fib [Occ=LoopBreaker] :: Integer -> Integer
+[GblId, Arity=1, Str=<SL>, Unf=OtherCon []]
+F11.f11_fib
+ = \ (ds :: Integer) ->
+ join {
+ $j [Dmd=ML] :: Integer
+ [LclId[JoinId(0)(Nothing)]]
+ $j
+ = join {
+ $j1 [Dmd=ML] :: Integer
+ [LclId[JoinId(0)(Nothing)]]
+ $j1 = GHC.Internal.Bignum.Integer.integerAdd (F11.f11_fib (GHC.Internal.Bignum.Integer.integerSub ds F11.fib3)) (F11.f11_fib (GHC.Internal.Bignum.Integer.integerSub ds F11.fib2)) } in
+ case ds of {
+ GHC.Internal.Bignum.Integer.IS x ->
+ case x of {
+ __DEFAULT -> jump $j1;
+ 1# -> F11.fib3
+ };
+ GHC.Internal.Bignum.Integer.IP x -> jump $j1;
+ GHC.Internal.Bignum.Integer.IN x -> jump $j1
+ } } in
+ case ds of {
+ GHC.Internal.Bignum.Integer.IS x ->
+ case x of {
+ __DEFAULT -> jump $j;
+ 0# -> F11.fib3
+ };
+ GHC.Internal.Bignum.Integer.IP x -> jump $j;
+ GHC.Internal.Bignum.Integer.IN x -> jump $j
+ }
+end Rec }
+
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.f3 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.f3 = GHC.Num.Integer.IS 1000#
+F11.f3 = GHC.Internal.Bignum.Integer.IS 1000#
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.f11_x :: Integer
@@ -100,7 +100,7 @@ F11.f11_x = F11.f11_fib F11.f3
-- RHS size: {terms: 4, types: 1, coercions: 0, joins: 0/0}
F11.f11f1 :: Integer -> Integer
[GblId, Arity=1, Str=<SL>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 30 0}]
-F11.f11f1 = \ (y :: Integer) -> GHC.Num.Integer.integerAdd F11.f11_x y
+F11.f11f1 = \ (y :: Integer) -> GHC.Internal.Bignum.Integer.integerAdd F11.f11_x y
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
f11f :: forall {p}. p -> Integer -> Integer
@@ -110,22 +110,22 @@ f11f = \ (@p) _ [Occ=Dead] -> F11.f11f1
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.f5 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.f5 = GHC.Num.Integer.IS 6#
+F11.f5 = GHC.Internal.Bignum.Integer.IS 6#
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
F11.f4 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False, WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 30 0}]
-F11.f4 = GHC.Num.Integer.integerAdd F11.f11_x F11.f5
+F11.f4 = GHC.Internal.Bignum.Integer.integerAdd F11.f11_x F11.f5
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F11.f2 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F11.f2 = GHC.Num.Integer.IS 8#
+F11.f2 = GHC.Internal.Bignum.Integer.IS 8#
-- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
F11.f1 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False, WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 30 0}]
-F11.f1 = GHC.Num.Integer.integerAdd F11.f11_x F11.f2
+F11.f1 = GHC.Internal.Bignum.Integer.integerAdd F11.f11_x F11.f2
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
f11 :: (Integer, Integer)
@@ -133,7 +133,4 @@ f11 :: (Integer, Integer)
f11 = (F11.f4, F11.f1)
------- Local rules for imported ids --------
-"SPEC fib @Integer @Integer" forall ($dEq :: Eq Integer) ($dNum :: Num Integer) ($dNum1 :: Num Integer). fib @Integer @Integer $dEq $dNum $dNum1 = F11.f11_fib
-
=====================================
testsuite/tests/arityanal/should_compile/Arity14.stderr
=====================================
@@ -3,18 +3,18 @@
Result size of Tidy Core = {terms: 44, types: 38, coercions: 0, joins: 0/3}
-- RHS size: {terms: 3, types: 2, coercions: 0, joins: 0/0}
-F14.f1 :: forall {t}. t -> t
+F14.f1 :: forall t. t -> t
[GblId, Arity=1, Str=<1L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=True)}]
F14.f1 = \ (@t) (y :: t) -> y
-- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
F14.f2 :: Integer
[GblId, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
-F14.f2 = GHC.Num.Integer.IS 1#
+F14.f2 = GHC.Internal.Bignum.Integer.IS 1#
-- RHS size: {terms: 36, types: 23, coercions: 0, joins: 0/3}
f14 :: forall {t}. (Ord t, Num t) => t -> t -> t -> t
-[GblId, Arity=4, Str=<SP(A,A,SC(S,C(1,L)),A,A,A,A,A)><LP(LC(L,C(1,L)),A,A,A,A,A,MC(1,L))><L><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [30 90 0 0] 310 0}]
+[GblId, Arity=4, Str=<SP(A,A,SC(S,C(1,L)),A,A,A,A,A)><LP(LC(L,C(1,L)),A,A,A,A,A,MC(1,L))><L><L>, Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [90 270 0 0] 310 0}]
f14
= \ (@t) ($dOrd :: Ord t) ($dNum :: Num t) (eta :: t) (eta1 :: t) ->
let {
@@ -25,7 +25,7 @@ f14
f3 [Occ=LoopBreaker, Dmd=SC(S,C(1,L))] :: t -> t -> t -> t
[LclId, Arity=2, Str=<L><L>, Unf=OtherCon []]
f3
- = \ (n :: t) (x :: t) ->
+ = \ (n :: t) (x [OS=OneShot] :: t) ->
case < @t $dOrd x n of {
False -> F14.f1 @t;
True ->
=====================================
testsuite/tests/perf/compiler/T13960.hs
=====================================
@@ -0,0 +1,72 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- GHC used to run out of simplifier ticks due to inlining the internals of
+-- `toStrict . toLazyByteString`.
+module T13960 (breaks) where
+
+import Data.ByteString (ByteString)
+import Data.ByteString.Builder (Builder, stringUtf8, toLazyByteString)
+import Data.ByteString.Lazy (toStrict)
+import Data.String (IsString(..))
+
+newtype Query = Query ByteString
+
+toByteString :: Builder -> ByteString
+toByteString x = toStrict (toLazyByteString x)
+
+instance IsString Query where
+ fromString = Query . toByteString . stringUtf8
+
+breaks :: [(Query, Query)]
+breaks =
+ [ ("query001a", "query001b")
+ , ("query002a", "query002b")
+ , ("query003a", "query003b")
+ , ("query004a", "query004b")
+ , ("query005a", "query005b")
+ , ("query006a", "query006b")
+ , ("query007a", "query007b")
+ , ("query008a", "query008b")
+ , ("query009a", "query009b")
+ , ("query010a", "query010b")
+ , ("query011a", "query011b")
+ , ("query012a", "query012b")
+ , ("query013a", "query013b")
+ , ("query014a", "query014b")
+ , ("query015a", "query015b")
+ , ("query016a", "query016b")
+ , ("query017a", "query017b")
+ , ("query018a", "query018b")
+ , ("query019a", "query019b")
+ , ("query020a", "query020b")
+ , ("query021a", "query021b")
+ , ("query022a", "query022b")
+ , ("query023a", "query023b")
+ , ("query024a", "query024b")
+ , ("query025a", "query025b")
+ , ("query026a", "query026b")
+ , ("query027a", "query027b")
+ , ("query028a", "query028b")
+ , ("query029a", "query029b")
+ , ("query030a", "query030b")
+ , ("query031a", "query031b")
+ , ("query032a", "query032b")
+ , ("query033a", "query033b")
+ , ("query034a", "query034b")
+ , ("query035a", "query035b")
+ , ("query036a", "query036b")
+ , ("query037a", "query037b")
+ , ("query038a", "query038b")
+ , ("query039a", "query039b")
+ , ("query040a", "query040b")
+ , ("query041a", "query041b")
+ , ("query042a", "query042b")
+ , ("query043a", "query043b")
+ , ("query044a", "query044b")
+ , ("query045a", "query045b")
+ , ("query046a", "query046b")
+ , ("query047a", "query047b")
+ , ("query048a", "query048b")
+ , ("query049a", "query049b")
+ , ("query050a", "query050b")
+ ]
=====================================
testsuite/tests/perf/compiler/all.T
=====================================
@@ -686,6 +686,12 @@ test ('T13820',
],
compile,
['-v0'])
+test ('T13960',
+ [ collect_compiler_stats('peak_megabytes_allocated', 20),
+ collect_compiler_stats('bytes allocated', 2),
+ ],
+ compile,
+ ['-O'])
test ('T14766',
[ collect_compiler_stats('bytes allocated',2),
pre_cmd('python3 genT14766.py > T14766.hs'),
=====================================
testsuite/tests/simplCore/should_compile/T15205.stderr
=====================================
@@ -10,7 +10,7 @@ f :: forall a b. C a b => a -> b
Str=<1P(A,1C(1,C(1,L)))><L>,
Unf=Unf{Src=<vanilla>, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
- Guidance=IF_ARGS [30 0] 40 0}]
+ Guidance=IF_ARGS [90 0] 40 0}]
f = \ (@a) (@b) ($dC :: C a b) (x :: a) -> op @a @b $dC x x
=====================================
testsuite/tests/wasm/should_run/control-flow/LoadCmmGroup.hs
=====================================
@@ -91,12 +91,17 @@ stgify :: ModSummary -> ModGuts -> Ghc [StgTopBinding]
stgify summary guts = do
hsc_env <- getSession
let dflags = hsc_dflags hsc_env
- prepd_binds <- liftIO $ do
+ liftIO $ do
cp_cfg <- initCorePrepConfig hsc_env
- corePrepPgm (hsc_logger hsc_env) cp_cfg (initCorePrepPgmConfig dflags (interactiveInScope $ hsc_IC hsc_env)) this_mod core_binds
- return $ fstOf3 $ coreToStg (initCoreToStgOpts dflags) (ms_mod summary) (ms_location summary) prepd_binds
- where this_mod = mg_module guts
- core_binds = mg_binds guts
+ prepd_binds <- corePrepPgm (hsc_logger hsc_env) cp_cfg
+ (initCorePrepPgmConfig dflags (interactiveInScope $ hsc_IC hsc_env))
+ this_mod core_binds
+ (binds, _, _) <- coreToStg (initCoreToStgOpts dflags) (ms_mod summary)
+ (ms_location summary) prepd_binds
+ return binds
+ where
+ this_mod = mg_module guts
+ core_binds = mg_binds guts
slurpCmm :: HscEnv -> FilePath -> IO (CmmGroup)
slurpCmm hsc_env filename = runHsc hsc_env $ do
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d4a4055f8dc1b5b4a2acc4260cdd0e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d4a4055f8dc1b5b4a2acc4260cdd0e…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/dcoutts/posix-ticker] 58 commits: Check that shift values are valid
by Duncan Coutts (@dcoutts) 31 Mar '26
by Duncan Coutts (@dcoutts) 31 Mar '26
31 Mar '26
Duncan Coutts pushed to branch wip/dcoutts/posix-ticker at Glasgow Haskell Compiler / GHC
Commits:
aa5dfe67 by Sylvain Henry at 2026-03-26T03:48:56-04:00
Check that shift values are valid
In GHC's codebase in non-DEBUG builds we silently substitute shiftL/R
with unsafeShiftL/R for performance reasons. However we were not
checking that the shift value was valid for unsafeShiftL/R, leading to
wrong computations, but only in non-DEBUG builds.
This patch adds the necessary checks and reports an error when a wrong
shift value is passed.
- - - - -
c8a7b588 by Sylvain Henry at 2026-03-26T03:48:56-04:00
Implement basic value range analysis (#25718)
Perform basic value range analysis to try to determine at compile time
the result of the application of some comparison primops (ltWord#, etc.).
This subsumes the built-in rewrite rules used previously to check if one
of the comparison argument was a bound (e.g. (x :: Word8) <= 255 is
always True). Our analysis is more powerful and handles type
conversions: e.g. word8ToWord x <= 255 is now detected as always True too.
We also use value range analysis to filter unreachable alternatives in
case-expressions. To support this, we had to allow case-expressions for
primitive types to not have a DEFAULT alternative (as was assumed before
and checked in Core lint).
- - - - -
a5ec467e by ARATA Mizuki at 2026-03-26T03:49:49-04:00
rts: Align stack to 64-byte boundary in StgRun on x86
When LLVM spills AVX/AVX-512 vector registers to the stack, it requires
32-byte (__m256) or 64-byte (__m512) alignment. If the stack is not
sufficiently aligned, LLVM inserts a realignment prologue that reserves
%rbp as a frame pointer, conflicting with GHC's use of %rbp as an STG
callee-saved register and breaking the tail-call-based calling convention.
Previously, GHC worked around this by lying to LLVM about the stack
alignment and rewriting aligned vector loads/stores (VMOVDQA, VMOVAPS)
to unaligned ones (VMOVDQU, VMOVUPS) in the LLVM Mangler. This had two
problems:
- It did not extend to AVX-512, which requires 64-byte alignment. (#26595)
- When Haskell calls a C function that takes __m256/__m512 arguments on
the stack, the callee requires genuine alignment, which could cause a
segfault. (#26822)
This patch genuinely aligns the stack to 64 bytes in StgRun by saving
the original stack pointer before alignment and restoring it in
StgReturn. We now unconditionally advertise 64-byte stack alignment to
LLVM for all x86 targets, making rewriteAVX in the LLVM Mangler
unnecessary. STG_RUN_STACK_FRAME_SIZE is increased from 48 to 56 bytes
on non-Windows x86-64 to store the saved stack pointer.
Closes #26595 and #26822
Co-Authored-By: Claude Opus 4.5 <noreply(a)anthropic.com>
- - - - -
661da815 by Teo Camarasu at 2026-03-26T03:50:33-04:00
ghc-internal: Float Generics to near top of module graph
We remove GHC.Internal.Generics from the critical path of the
`ghc-internal` module graph. GHC.Internal.Generics used to be in the
middle of the module graph, but now it is nearer the top (built later).
This change thins out the module graph and allows us to get rid of the
ByteOrder hs-boot file.
We implement this by moving Generics instances from the module where the
datatype is defined to the GHC.Internal.Generics module. This trades off
increasing the compiled size of GHC.Internal.Generics with reducing the
dependency footprint of datatype modules.
Not all instances are moved to GHC.Internal.Generics. For instance,
`GHC.Internal.Control.Monad.Fix` keeps its instance as it is one of the
very last modules compiled in `ghc-internal` and so inverting the
relationship here would risk adding GHC.Internal.Generics back onto the
critical path.
We also don't change modules that are re-exported from the `template-haskell` or `ghc-heap`.
This is done to make it easy to eventually move `Generics` to `base`
once something like #26657 is implemented.
Resolves #26930
Metric Decrease:
T21839c
- - - - -
45428f88 by sheaf at 2026-03-26T03:51:31-04:00
Avoid infinite loop in deep subsumption
This commit ensures we only unify after we recur in the deep subsumption
code in the FunTy vs non-FunTy case of GHC.Tc.Utils.Unify.tc_sub_type_deep,
to avoid falling into an infinite loop.
See the new Wrinkle [Avoiding a loop in tc_sub_type_deep] in
Note [FunTy vs non-FunTy case in tc_sub_type_deep] in GHC.Tc.Utils.Unify.
Fixes #26823
Co-authored-by: simonpj <simon.peytonjones(a)gmail.com>
- - - - -
2823b039 by Ian Duncan at 2026-03-26T03:52:21-04:00
AArch64: fix MOVK regUsageOfInstr to mark dst as both read and written
MOVK (move with keep) modifies only a 16-bit slice of the destination
register, so the destination is both read and written. The register
allocator must know this to avoid clobbering live values. Update
regUsageOfInstr to list the destination in both src and dst sets.
No regression test: triggering the misallocation requires specific
register pressure around a MOVK sequence, which is difficult to
reliably provoke from Haskell source.
- - - - -
57b7878d by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #12002
Closes #12002.
- - - - -
c8f9df2d by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #12046
Closes #12046.
Co-authored-by: Andreas Klebinger <klebinger.andreas(a)gmx.at>
- - - - -
615d72ac by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #13180
Closes #13180.
- - - - -
423eebcf by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #11141
Closes #11141.
- - - - -
286849a4 by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #11505
Closes #11505.
- - - - -
7db149d9 by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression perf test for #13820
Closes #13820.
- - - - -
e73c4adb by Simon Jakobi at 2026-03-26T03:53:07-04:00
Add regression test for #10381
Closes #10381.
- - - - -
5ebcfb57 by Benjamin Maurer at 2026-03-26T03:54:02-04:00
Generate assembly on x86 for word2float (#22252)
We used to emit C function call for MO_UF_Conv primitive.
Now emits direct assembly instead.
Co-Authored-By: Sylvain Henry <sylvain(a)haskus.fr>
Co-Authored-By: Claude Sonnet 4.6 <noreply(a)anthropic.com>
- - - - -
5b550754 by Matthew Pickering at 2026-03-26T03:54:51-04:00
rts: forward clone-stack messages after TSO migration
MSG_CLONE_STACK assumed that the target TSO was still owned by the
capability that received the message. This is not always true: the TSO
can migrate before the inbox entry is handled.
When that happened, handleCloneStackMessage could clone a live stack from
the wrong capability and use the wrong capability for allocation and
performTryPutMVar, leading to stack sanity failures such as
checkStackFrame: weird activation record found on stack.
Fix this by passing the current capability into
handleCloneStackMessage, rechecking msg->tso->cap at handling time, and
forwarding the message if the TSO has migrated. Once ownership matches,
use the executing capability consistently for cloneStack, rts_apply, and
performTryPutMVar.
Fixes #27008
- - - - -
ef0a1bd2 by mangoiv at 2026-03-26T03:55:34-04:00
release tracking: adopt release tracking ticket from #16816
- - - - -
a7f40fd9 by mangoiv at 2026-03-26T03:55:34-04:00
release tracking: add a release tracking ticket
Brings the information in the release tracking ticket up to date with
https://gitlab.haskell.org/ghc/ghc-hq/-/blob/main/release-management.mkd
Resolves #26691
- - - - -
161d3285 by Teo Camarasu at 2026-03-26T03:56:18-04:00
Revert "Set default eventlog-flush-interval to 5s"
Flushing the eventlog forces a synchronisation of all the capabilities
and there was a worry that this might lead to a performance cost for
some highly parallel workloads.
This reverts commit 66b96e2a591d8e3d60e74af3671344dfe4061cf2.
- - - - -
36eed985 by Cheng Shao at 2026-03-26T03:57:03-04:00
ghc-boot: move GHC.Data.SmallArray to ghc-boot
This commit moves `GHC.Data.SmallArray` from the `ghc` library to
`ghc-boot`, so that it can be used by `ghci` as well:
- The `Binary` (from `ghc`) instance of `SmallArray` is moved to
`GHC.Utils.Binary`
- Util functions `replicateSmallArrayIO`, `mapSmallArrayIO`,
`mapSmallArrayM_`, `imapSmallArrayM_` , `smallArrayFromList` and
`smallArrayToList` are added
- The `Show` instance is added
- The `Binary` (from `binary`) instance is added
- - - - -
fdf828ae by Cheng Shao at 2026-03-26T03:57:03-04:00
compiler: use `Binary` instance of `BCOByteArray` for bytecode objects
This commit defines `Binary` (from `compiler`) instance of
`BCOByteArray` which serializes the underlying buffer directly, and
uses it directly in bytecode object serialization. Previously we reuse
the `Binary` (from `binary`) instance, and this change allows us to
avoid double-copying via an intermediate `ByteString` when using
`put`/`get` in `binnary`. Also see added comment for explanation.
- - - - -
3bf62d0a by Cheng Shao at 2026-03-26T03:57:03-04:00
ghci: use SmallArray directly in ResolvedBCO
This patch makes ghci use `SmallArray` directly in `ResolvedBCO` when
applicable, making the memory representation more compact and reducing
marshaling overhead. Closes #27058.
- - - - -
3d6492ce by Wen Kokke at 2026-03-26T03:57:53-04:00
Fix race condition between flushEventLog and start/endEventLogging.
This commit changes `flushEventLog` to acquire/release the `state_change` mutex to prevent interleaving with `startEventLogging` and `endEventLogging`. In the current RTS, `flushEventLog` _does not_ acquire this mutex, which may lead to eventlog corruption on the following interleaving:
- `startEventLogging` writes the new `EventLogWriter` to `event_log_writer`.
- `flushEventLog` flushes some events to `event_log_writer`.
- `startEventLogging` writes the eventlog header to `event_log_writer`.
This causes the eventlog to be written out in an unreadable state, with one or more events preceding the eventlog header.
This commit renames the old function to `flushEventLog_` and defines `flushEventLog` simply as:
```c
void flushEventLog(Capability **cap USED_IF_THREADS)
{
ACQUIRE_LOCK(&state_change_mutex);
flushEventLog_(cap);
RELEASE_LOCK(&state_change_mutex);
}
```
The old function is still needed internally within the compilation unit, where it is used in `endEventLogging` in a context where the `state_change` mutex has already been acquired. I've chosen to mark `flushEventLog_` as static and let other uses of `flushEventLog` within the RTS refer to the new version. There is one use in `hs_init_ghc` via `flushTrace`, where the new locking behaviour should be harmless, and one use in `handle_tick`, which I believe was likely vulnerable to the same race condition, so the new locking behaviour is desirable.
I have not added a test. The behaviour is highly non-deterministic and requires a program that concurrently calls `flushEventLog` and `startEventLogging`/`endEventLogging`. I encountered the issue while developing `eventlog-socket` and within that context have verified that my patch likely addresses the issue: a test that used to fail within the first dozen or so runs now has been running on repeat for several hours.
- - - - -
7b9a75f0 by Phil Hazelden at 2026-03-26T03:58:37-04:00
Fix build with werror on glibc 2.43.
We've been defining `_XOPEN_SOURCE` and `_POSIX_C_SOURCE` to the same
values as defined in glibc prior to 2.43. But in 2.43, glibc changes
them to new values, which means we get a warning when redefining them.
By `#undef`ing them first, we no longer get a warning.
Closes #27076.
- - - - -
fe6e76c5 by Tobias Haslop at 2026-03-26T03:59:30-04:00
Fix broken Haddock link to Bifunctor class in description of Functor class
- - - - -
404b71c1 by Luite Stegeman at 2026-03-27T04:40:49-04:00
Fix assert in Interpreter.c
If we skip exactly the number of words on the stack we end up on
the first word in the next chunk.
- - - - -
a85bd503 by Luite Stegeman at 2026-03-27T04:40:49-04:00
Support arbitrary size unboxed tuples in bytecode
This stores the size (number of words on the stack) of the next
expected tuple in the TSO, ctoi_spill_size field, eliminating
the need of stg_ctoi_tN frames for each size.
Note: On 32 bit platform there is still a bytecode tuple size
limit of 255 words on the stack.
Fixes #26946
- - - - -
e2209031 by Luite Stegeman at 2026-03-27T04:40:49-04:00
Add specialized frames for small tuples
Small tuples are now returned more efficiently to the interpreter.
They use one less word of stack space and don't need manipulation
of the TSO anymore.
- - - - -
b26bb2ea by VeryMilkyJoe at 2026-03-27T04:41:38-04:00
Remove backwards compatibility pattern synonym `ModLocation`
Fixes #24932
- - - - -
66e5e324 by Vladislav Zavialov at 2026-03-27T04:42:25-04:00
Extend HsExpr with the StarIsType syntax (#26587, #26967)
This patch allows kinds of the form `k -> *` and `* -> k` to occur in
expression syntax, i.e. to be used as required type arguments.
For example:
{-# LANGUAGE RequiredTypeArguments, StarIsType #-}
x1 = f (* -> * -> *)
x2 = f (forall k. k -> *)
x3 = f ((* -> *) -> Constraint)
Summary of the changes:
* Introduce the HsStar constructor of HsExpr and its extension field XStar.
It is analogous to HsStarTy in HsType.
* Refactor HsStarTy to store the unicode flag as TokStar, defined as
type TokStar = EpUniToken "*" "★" -- similar to TokForall, TokRArrow, etc.
The token is stored in the extension field and replaces the Bool field.
* Extend the `infixexp2` nonterminal to parse `*` as a direct argument of `->`.
This is more limited than the full StarIsType syntax, but has the nice
property of not conflicting with the multiplication operator `a * b`.
Test case: T26967 T26967_tyop
- - - - -
f8de456f by Sylvain Henry at 2026-03-27T04:43:22-04:00
STM: don't create a transaction in the rhs of catchRetry# (#26028)
We don't need to create a transaction for the rhs of (catchRetry#)
because contrary to the lhs we don't need to abort it on retry. Moreover
it is particularly harmful if we have code such as (#26028):
let cN = readTVar vN >> retry
tree = c1 `orElse` (c2 `orElse` (c3 `orElse` ...))
atomically tree
Because it will stack transactions for the rhss and the read-sets of all
the transactions will be iteratively merged in O(n^2) after the
execution of the most nested retry.
This is the second attempt at implementing this. The first attempt
triggered segfaults (#26291) and has been reverted.
Co-Authored-By: Claude Sonnet 4.6 <noreply(a)anthropic.com>
- - - - -
fcf092dd by Luite Stegeman at 2026-03-27T04:44:17-04:00
Windows: remove StgAsyncIOResult and fix crash/leaks
In stg_block_async{_void}, a stack slot was reserved for
an StgAsyncIOResult. This slot would be filled by the IO
manager upon completion of the async call.
However, if the blocked thread was interrupted by an async
exception, we would end up in an invalid state:
- If the blocked computation was never re-entered, the
StgAsyncIOResult would never be freed.
- If the blocked computation was re-entered, the thread would
find an unitialized stack slot for the StgAsyncIOResult,
leading to a crash reading its fields, or freeing the pointer.
We fix this by removing the StgAsyncIOResult altogether and writing
the result directly to the stack.
Fixes #26341
- - - - -
05094993 by Luite Stegeman at 2026-03-27T04:45:12-04:00
Don't refine DEFAULT alt for unary typeclasses
A non-DEFAULT data alt for a unary typeclass dictionary would
interfere with Unary Class Magic, leading to segfaults.
fixes #27071
- - - - -
4ee260cf by sheaf at 2026-03-27T04:46:06-04:00
Fix several oversights in hsExprType
This commit fixes several oversights in GHC.Hs.Syn.Type.hsExprType:
- The 'RecordCon' case was returning the type of the constructor,
instead of the constructor application. This is fixed by using
'splitFunTys'.
- The 'ExplicitTuple' case failed to take into account tuple sections,
and was also incorrectly handling 1-tuples (e.g. 'Solo') which can
be constructed using Template Haskell.
- The 'NegApp' case was returning the type of the negation operator,
again failing to apply it to the argument. Fixed by using
'funResultTy'.
- The 'HsProc' case was computing the result type of the arrow proc
block, without taking into account the argument type. Fix that by
adding a new field to 'CmdTopTc' that stores the arrow type, so that
we can construct the correct result type `arr a b` for
`proc (pat :: a) -> (cmd :: b)`.
- The 'ArithSeq' and 'NegApp' cases were failing to take into account
the result 'HsWrapper', which could e.g. silently drop casts.
This is fixed by introducing 'syntaxExpr_wrappedFunResTy' which, on
top of taking the result type, applies the result 'HsWrapper'.
These fixes are validated by the new GHC API test T26910.
Fixes #26910
- - - - -
e97232ce by Hai at 2026-03-27T04:47:04-04:00
Parser.y: avoid looking at token with QualifiedDo
This changes the behavior of 'hintQualifiedDo' so that the supplied
token is not inspected when the QualifiedDo language extension bit is
set.
- - - - -
9831385b by Vladislav Zavialov at 2026-03-27T17:22:30-04:00
Infix holes in types (#11107)
This patch introduces several improvements that follow naturally from
refactoring HsOpTy to represent the operator as an HsType, aligning it
with the approach taken by OpApp and HsExpr.
User-facing changes:
1. Infix holes (t1 `_` t2) are now permitted in types, following the
precedent set by term-level expressions.
Test case: T11107
2. Error messages for illegal promotion ticks are now reported at more
precise source locations.
Test case: T17865
Internal changes:
* The definition of HsOpTy now mirrors that of OpApp:
| HsOpTy (XOpTy p) (LHsType p) (LHsType p) (LHsType p)
| OpApp (XOpApp p) (LHsExpr p) (LHsExpr p) (LHsExpr p)
This moves us one step closer to unifying HsType and HsExpr.
* Ignoring locations,
the old pattern match (HsOpTy x prom lhs op rhs)
is now written as (HsOpTy x lhs (HsTyVar x' prom op) rhs)
but we also handle (HsOpTy x lhs (HsWildCardTy x') rhs)
Constructors other than HsTyVar and HsWildCardTy never appear
in the operator position.
* The various definitions across the compiler have been updated to work
with the new representation, drawing inspiration from the term-level
pipeline where appropriate. For example,
ppr_infix_ty <=> ppr_infix_expr
get_tyop <=> get_op
lookupTypeFixityRn <=> lookupExprFixityRn
(the latter is factored out from rnExpr)
Test cases: T11107 T17865
- - - - -
5b6757d7 by mangoiv at 2026-03-27T17:23:19-04:00
ci: build i386 non-validate for deb12
This is a small fix that will unlock ghcup metadata to run, i386 debian
12 was missing as a job.
- - - - -
cf942119 by Cheng Shao at 2026-03-30T15:24:37-04:00
ghc-boot: remove unused SizedSeq instances and functions
This commit removes unused `SizedSeq` instances and functions, only
keeping the bits we need for hpc tick sequence for now.
- - - - -
22c5b7cc by Cheng Shao at 2026-03-30T15:24:38-04:00
ghci: remove unused GHCi.BinaryArray
This patch removes the unused `GHCi.BinaryArray` module from `ghci`.
Closes #27108.
- - - - -
77abb4ab by Cheng Shao at 2026-03-30T15:25:21-04:00
testsuite: mark T17912 as fragile on Windows
T17912 is still fragile on Windows, it sometimes unexpectedly pass in
CI. This especially strains our already scarce Windows CI runner
resources. Mark it as fragile on Windows for the time being.
- - - - -
d741a6cc by Andreas Klebinger at 2026-03-31T04:39:33-04:00
Bump minimum shake version for hadrian.
We also add the shake version we want to stack.yaml
Fixes #26884
- - - - -
5e556f9e by Vladislav Zavialov at 2026-03-31T04:40:16-04:00
Status check for the HsType~HsExpr refactoring (#25121)
Add a test case to track the status of a refactoring project within GHC
whose goal is to arrive at the following declaration:
type HsType = HsExpr
The rationale for this is to increase code reuse between the term- and
type-level code in the compiler front-end (AST, parser, renamer, type checker).
The status report is saved to testsuite/tests/ghc-api/T25121_status.stdout
and provides useful insights into what needs to happen to make progress on
the ticket.
- - - - -
acffb1b1 by fendor at 2026-03-31T04:41:02-04:00
Extract Binary instances to `GHC.ByteCode.Binary`
- - - - -
e2ea8e25 by fendor at 2026-03-31T04:41:02-04:00
Add `seqNonEmpty` for evaluating `NonEmpty a`
- - - - -
048b00b7 by fendor at 2026-03-31T04:41:02-04:00
Record `LinkableUsage` instead of `Linkable` in `LoaderState`
Retaining a ByteCode `Linkable` after it has been loaded retains its
`UnlinkedBCO`, keeping it alive for the remainder of the program.
This starts accumulating a lot of `UnlinkedBCO` and memory over time.
However, the `Linkable` is merely used to later record its usage in
`mkObjectUsage`, which is used for recompilation checking.
However, this is incorrect, as the interface file and bytecode objects
could be in different states, e.g. the interface changes, but the
bytecode library hasn't changed so we don't need to recompile and vice
versa.
By computing a `Fingerprint` for the `ModuleByteCode`, and recording it
in the `LinkableUsage`, we know precisely whether the `ByteCode` object
on disk is outdated.
Thus, parts of this commit just makes sure that we efficiently compute a
`Fingerprint` for `ModuleByteCode` and store it in the on-disk
representation of `ModuleByteCode`.
We change the `LoaderState` to retain `LinkableUsage`, which is smaller
representation of a `Linkable`. This allows us to free the unneeded
fields of `Linkable` after linking them.
We declare the following memory invariants that this commit implements:
* No `LinkablePart` should be retained from `LoaderState`.
* `Linkable`s should be unloaded after they have been loaded.
These invariants are unfortunately tricky to automatically uphold, so we
are simply documenting our assumptions for now.
We introduce the `linkable-space` test which makes sure that after
loading, no `DotGBC` or `UnlinkedBCO` is retained.
-------------------------
Metric Increase:
MultiLayerModulesTH_OneShot
-------------------------
We allocate a bit more, but the peak number of bytes doesn't change.
While a bit unfortunate, accepting the metric increase.
We add multiple new performance measurements where we were able to
observe the desired memory invariants. Further, we add regression tests
to validate that the recompilation checker behaves more correct than
before.
- - - - -
2d1c1997 by Simon Jakobi at 2026-03-31T04:41:46-04:00
Eliminate dictionary-passing in ListMap operations
Mark the ListMap helpers 'INLINABLE' so importing modules can specialise
the 'TrieMap (ListMap m)' methods and avoid recursive dictionary-passing.
See Note [Making ListMap operations specialisable].
Fixes #27097
- - - - -
ed2c6570 by Cheng Shao at 2026-03-31T04:42:33-04:00
testsuite: fix testdir cleanup logic on Windows
testdir cleanup is unreliable on Windows (#13162) and despite existing
hacks in the driver, new failure mode has occurred. This patch makes
it print the warning and carry on when failed to clean up a testdir,
instead of reporting a spurious framework failure. See added comment
for detailed explanation.
- - - - -
d9388e29 by Simon Jakobi at 2026-03-31T13:14:59-04:00
Add regression test for #18177
Closes #18177.
Assisted-by: Codex
- - - - -
6a10045c by mangoiv at 2026-03-31T13:15:43-04:00
ci: allow metric decrease for two tests on i386
There has been a nightly failure on i386 due to a compiler runtime
improvement on i386 debian 12. We allow that.
Metric Decrease (test_env='i386-linux-deb12'):
T12707 T8095
- - - - -
f8dcfcd3 by Duncan Coutts at 2026-03-31T22:13:01+01:00
Remove signal-based ticker implementations
Fixes issue #27073
All supported platforms should work with the pthreads + nanosleep based
ticker implementation. This avoids all the problems with using signals.
In practice, all supported platforms were probably using the non-signal
tickers already, which is probably why we do not get lots of reports
about deadlocks and other weirdness: we were definately using functions
that are not async signal safe in the tick handler (such as fflush to
flussh the eventlog).
Only Solaris was explicitly using the timer_create ticker impl, and even
Solaris could probably use the pthreads one (if anyone cared: Solaris is
no longer a Teir 3 supported platform).
Plausibly the only supported platform that this will change will be AIX,
which should now use the pthreads impl.
- - - - -
8c11e214 by Duncan Coutts at 2026-03-31T22:13:01+01:00
Tidy up some timer/ticker comments elsewhere
- - - - -
be0771f3 by Duncan Coutts at 2026-03-31T22:13:01+01:00
Remove now-unused install_vtalrm_handler
Support function used by both of the signal-based ticker
implementations.
- - - - -
c9602817 by Duncan Coutts at 2026-03-31T22:13:01+01:00
No longer probe for timer_create in rts/configure
It was only used by the TimerCreate.c ticker impl.
- - - - -
54fa5ebf by Duncan Coutts at 2026-03-31T22:13:02+01:00
Note that rtsTimerSignal is deprecated.
- - - - -
01993fb2 by Duncan Coutts at 2026-03-31T23:54:34+01:00
Add a rts posix FdWakup utility module
This will be used to implement wakeupIOManager for in-RTS I/O managers.
It provides a notification/wakeup mechanism using FDs, suitable for
situations when a thread is blocked on a set of fds anyway. It uses the
classic self-pipe trick, or equivalently eventfd on supported platforms.
This will initially be used to implement prompt interrupt or shutdown of
the posix ticker thread.
- - - - -
19e5cfab by Duncan Coutts at 2026-03-31T23:54:34+01:00
Add prompt shutdown to the pthread ticker implementation.
The Linux timerfd ticker monitors a pipe which is used by exitTicker to
ensure a prompt wakeup and shutdown. The pthread ticker lacked this and
so would only exit at the next ticker wakeup (10ms by default).
This patch adds the same mechanism to the pthread ticker.
This changes the pthread ticker from waiting by using nanosleep() to
waiting using either ppoll() or select(), so that it can wait on both
a time and a file descriptor. On Linux at least, a test program to
compare the timing jitter of these APIs shows that using nanpsleep,
ppoll or select makes no statistical difference to the maximum or
average jitter.
This is a step towards unifying the posix ticker implementations, so
that we can have just one portable one (albeit with some limited cpp).
It is also a step towards using the ticker as part of a more general
implementation of wakeUpRts, since this will require a method to wake
the rts from a signal handler context (ctl-c handler).
- - - - -
e18990a2 by Duncan Coutts at 2026-03-31T23:54:34+01:00
Update ticker header commentary
It was antique and didn't apply even to the previous implementation, and
certainly not to the updated one.
- - - - -
b4860f1d by Duncan Coutts at 2026-03-31T23:54:34+01:00
Remove the timerfd-based ticker implementation
There does not appear to be any remaining advantage on Linux to using
the timerfd ticker implementation over the portable one (using ppoll on
Linux for precise timing).
The eventfd implementation was originally added at a time when Linux was
still using a signal based implementation. So it made sense at the time.
See (closed) issue #10840.
- - - - -
4f1b216f by Duncan Coutts at 2026-03-31T23:54:34+01:00
Consolidate to a single posix ticker implementation
Previously we had four implementations, two using signals and two using
threads. Having just one should make behaviour more consistent between
platforms, and should make maintenance easier.
- - - - -
292 changed files:
- .gitlab/generate-ci/gen_ci.hs
- + .gitlab/issue_templates/release_tracking.md
- .gitlab/jobs.yaml
- .gitlab/rel_eng/mk-ghcup-metadata/mk_ghcup_metadata.py
- compiler/GHC.hs
- compiler/GHC/ByteCode/Asm.hs
- + compiler/GHC/ByteCode/Binary.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Linker.hs
- + compiler/GHC/ByteCode/Recomp/Binary.hs
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Cmm/Node.hs
- compiler/GHC/CmmToAsm/AArch64/Instr.hs
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToLlvm.hs
- compiler/GHC/CmmToLlvm/Config.hs
- compiler/GHC/CmmToLlvm/Mangler.hs
- compiler/GHC/Core.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Opt/ConstantFold.hs
- + compiler/GHC/Core/Opt/Range.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/AddImplicitBinds.hs
- compiler/GHC/Data/TrieMap.hs
- compiler/GHC/Driver/Config/CmmToLlvm.hs
- compiler/GHC/Driver/Hooks.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Driver/Plugins.hs
- compiler/GHC/Hs/Expr.hs
- compiler/GHC/Hs/Syn/Type.hs
- compiler/GHC/Hs/Type.hs
- compiler/GHC/Hs/Utils.hs
- compiler/GHC/HsToCore/Arrows.hs
- compiler/GHC/HsToCore/Expr.hs
- compiler/GHC/HsToCore/Quote.hs
- compiler/GHC/HsToCore/Ticks.hs
- compiler/GHC/HsToCore/Usage.hs
- compiler/GHC/Iface/Ext/Ast.hs
- compiler/GHC/Iface/Recomp.hs
- compiler/GHC/Iface/Recomp/Types.hs
- compiler/GHC/Iface/Type.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Parser.y
- compiler/GHC/Parser/Annotation.hs
- compiler/GHC/Parser/Errors/Types.hs
- compiler/GHC/Parser/PostProcess.hs
- compiler/GHC/Prelude/Basic.hs
- compiler/GHC/Rename/Expr.hs
- compiler/GHC/Rename/Fixity.hs
- compiler/GHC/Rename/HsType.hs
- compiler/GHC/Rename/Pat.hs
- compiler/GHC/Runtime/Loader.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/StgToCmm/Expr.hs
- compiler/GHC/StgToCmm/Utils.hs
- compiler/GHC/Tc/Errors/Ppr.hs
- compiler/GHC/Tc/Errors/Types.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Tc/Gen/Arrow.hs
- compiler/GHC/Tc/Gen/Expr.hs
- compiler/GHC/Tc/Gen/HsType.hs
- compiler/GHC/Tc/Gen/Pat.hs
- compiler/GHC/Tc/Gen/Sig.hs
- compiler/GHC/Tc/Types.hs
- compiler/GHC/Tc/Types/Origin.hs
- compiler/GHC/Tc/Utils/Monad.hs
- compiler/GHC/Tc/Utils/Unify.hs
- compiler/GHC/Tc/Zonk/Type.hs
- compiler/GHC/ThToHs.hs
- compiler/GHC/Unit/Home/ModInfo.hs
- compiler/GHC/Unit/Module/Deps.hs
- compiler/GHC/Unit/Module/Location.hs
- compiler/GHC/Unit/Module/Status.hs
- compiler/GHC/Utils/Binary.hs
- compiler/GHC/Utils/Misc.hs
- compiler/GHC/Utils/Outputable.hs
- compiler/Language/Haskell/Syntax/Expr.hs
- compiler/Language/Haskell/Syntax/Extension.hs
- compiler/Language/Haskell/Syntax/Type.hs
- compiler/ghc.cabal.in
- docs/users_guide/9.16.1-notes.rst
- docs/users_guide/exts/required_type_arguments.rst
- ghc/GHCi/Leak.hs
- hadrian/hadrian.cabal
- hadrian/stack.yaml
- libraries/base/tests/IO/all.T
- libraries/ghc-boot/GHC/Data/SizedSeq.hs
- compiler/GHC/Data/SmallArray.hs → libraries/ghc-boot/GHC/Data/SmallArray.hs
- libraries/ghc-boot/ghc-boot.cabal.in
- libraries/ghc-internal/src/GHC/Internal/Base.hs
- libraries/ghc-internal/src/GHC/Internal/ByteOrder.hs
- − libraries/ghc-internal/src/GHC/Internal/ByteOrder.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Char.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Foldable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor/Const.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Functor/Identity.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Monoid.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Semigroup/Internal.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Traversable.hs
- libraries/ghc-internal/src/GHC/Internal/Data/Version.hs
- libraries/ghc-internal/src/GHC/Internal/Event/Control.hs
- libraries/ghc-internal/src/GHC/Internal/Functor/ZipList.hs
- libraries/ghc-internal/src/GHC/Internal/Generics.hs
- libraries/ghc-internal/src/GHC/Internal/IO/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/RTS/Flags.hsc
- libraries/ghc-internal/src/GHC/Internal/Read.hs
- libraries/ghc-internal/src/GHC/Internal/Unicode/Bits.hs
- − libraries/ghci/GHCi/BinaryArray.hs
- libraries/ghci/GHCi/CreateBCO.hs
- libraries/ghci/GHCi/ResolvedBCO.hs
- libraries/ghci/ghci.cabal.in
- − m4/fp_check_timer_create.m4
- rts/Apply.cmm
- rts/CloneStack.c
- rts/CloneStack.h
- rts/Continuation.c
- rts/ContinuationOps.cmm
- rts/HeapStackCheck.cmm
- rts/IOManager.c
- rts/Interpreter.c
- rts/Messages.c
- rts/PrimOps.cmm
- rts/Printer.c
- rts/RaiseAsync.c
- rts/RtsFlags.c
- rts/RtsSymbols.c
- rts/STM.c
- rts/STM.h
- rts/Schedule.c
- rts/StgCRun.c
- rts/StgMiscClosures.cmm
- rts/Threads.c
- rts/Threads.h
- rts/Timer.c
- rts/configure.ac
- rts/eventlog/EventLog.c
- rts/include/rts/Bytecodes.h
- rts/include/rts/Constants.h
- rts/include/rts/PosixSource.h
- rts/include/rts/Timer.h
- rts/include/rts/storage/TSO.h
- rts/include/stg/MiscClosures.h
- rts/include/stg/SMP.h
- + rts/posix/FdWakeup.c
- + rts/posix/FdWakeup.h
- rts/posix/Signals.c
- rts/posix/Signals.h
- rts/posix/Ticker.c
- − rts/posix/ticker/Pthread.c
- − rts/posix/ticker/Setitimer.c
- − rts/posix/ticker/TimerCreate.c
- − rts/posix/ticker/TimerFd.c
- rts/rts.cabal
- rts/win32/AsyncMIO.c
- rts/win32/AsyncMIO.h
- testsuite/driver/testlib.py
- + testsuite/tests/bytecode/TLinkable/BCOTemplate.hs
- + testsuite/tests/bytecode/TLinkable/LinkableUsage01.stderr
- + testsuite/tests/bytecode/TLinkable/LinkableUsage02.stderr
- + testsuite/tests/bytecode/TLinkable/Makefile
- + testsuite/tests/bytecode/TLinkable/all.T
- + testsuite/tests/bytecode/TLinkable/genLinkables.sh
- + testsuite/tests/bytecode/TLinkable/linkable-space.hs
- + testsuite/tests/bytecode/TLinkable/linkable-space.stdout
- + testsuite/tests/bytecode/tuplestress/ByteCode.hs
- + testsuite/tests/bytecode/tuplestress/Common.hs-incl
- + testsuite/tests/bytecode/tuplestress/Obj.hs
- + testsuite/tests/bytecode/tuplestress/TupleStress.hs
- + testsuite/tests/bytecode/tuplestress/TupleStress.stdout
- + testsuite/tests/bytecode/tuplestress/all.T
- testsuite/tests/codeGen/should_run/Word2Float32.hs
- testsuite/tests/codeGen/should_run/Word2Float32.stdout
- testsuite/tests/codeGen/should_run/Word2Float64.hs
- testsuite/tests/codeGen/should_run/Word2Float64.stdout
- + testsuite/tests/concurrent/should_run/T26341.hs
- + testsuite/tests/concurrent/should_run/T26341.stdout
- + testsuite/tests/concurrent/should_run/T26341a.hs
- + testsuite/tests/concurrent/should_run/T26341a.stdout
- + testsuite/tests/concurrent/should_run/T26341b.hs
- + testsuite/tests/concurrent/should_run/T26341b.stdout
- testsuite/tests/concurrent/should_run/all.T
- testsuite/tests/count-deps/CountDepsAst.stdout
- testsuite/tests/count-deps/CountDepsParser.stdout
- + testsuite/tests/driver/T18177.hs
- testsuite/tests/driver/all.T
- testsuite/tests/driver/multipleHomeUnits/multipleHomeUnits_recomp_th.stdout
- + testsuite/tests/driver/recomp022/A1.hs
- + testsuite/tests/driver/recomp022/A2.hs
- + testsuite/tests/driver/recomp022/A3.hs
- + testsuite/tests/driver/recomp022/B.hs
- + testsuite/tests/driver/recomp022/C.hs
- + testsuite/tests/driver/recomp022/Makefile
- + testsuite/tests/driver/recomp022/all.T
- + testsuite/tests/driver/recomp022/recomp022a.stdout
- + testsuite/tests/driver/recomp022/recomp022b.stdout
- + testsuite/tests/ghc-api/T25121_status.hs
- + testsuite/tests/ghc-api/T25121_status.stdout
- + testsuite/tests/ghc-api/T26910.hs
- + testsuite/tests/ghc-api/T26910.stdout
- + testsuite/tests/ghc-api/T26910_Input.hs
- testsuite/tests/ghc-api/all.T
- testsuite/tests/ghc-api/fixed-nodes/FixedNodes.hs
- testsuite/tests/ghc-api/fixed-nodes/ModuleGraphInvariants.hs
- testsuite/tests/ghci/scripts/ListTuplePunsPpr.stdout
- testsuite/tests/ghci/scripts/T10963.stderr
- testsuite/tests/ghci/scripts/ghci064.stdout
- testsuite/tests/ghci/should_run/BinaryArray.hs
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/base-exports.stdout-ws-32
- + testsuite/tests/lib/stm/T26028.hs
- + testsuite/tests/lib/stm/T26028.stdout
- + testsuite/tests/lib/stm/T26291a.hs
- + testsuite/tests/lib/stm/T26291a.stdout
- + testsuite/tests/lib/stm/T26291b.hs
- + testsuite/tests/lib/stm/T26291b.stdout
- + testsuite/tests/lib/stm/all.T
- testsuite/tests/parser/should_compile/DumpParsedAst.stderr
- testsuite/tests/parser/should_compile/DumpRenamedAst.stderr
- + testsuite/tests/parser/should_compile/T12002.hs
- + testsuite/tests/parser/should_compile/T12002.stderr
- testsuite/tests/parser/should_compile/all.T
- testsuite/tests/parser/should_fail/T17865.stderr
- + testsuite/tests/partial-sigs/should_compile/T11107.hs
- + testsuite/tests/partial-sigs/should_compile/T11107.stderr
- testsuite/tests/partial-sigs/should_compile/all.T
- + testsuite/tests/perf/compiler/T13820.hs
- testsuite/tests/perf/compiler/all.T
- + testsuite/tests/rebindable/T10381.hs
- testsuite/tests/rebindable/all.T
- testsuite/tests/rts/all.T
- + testsuite/tests/rts/cloneThreadStackMigrating.hs
- + testsuite/tests/simd/should_run/StackAlignment32.hs
- + testsuite/tests/simd/should_run/StackAlignment32.stdout
- + testsuite/tests/simd/should_run/StackAlignment32_main.c
- + testsuite/tests/simd/should_run/StackAlignment64.hs
- + testsuite/tests/simd/should_run/StackAlignment64.stdout
- + testsuite/tests/simd/should_run/StackAlignment64_main.c
- testsuite/tests/simd/should_run/all.T
- + testsuite/tests/simplCore/should_compile/T19166.hs
- + testsuite/tests/simplCore/should_compile/T19166.stderr
- + testsuite/tests/simplCore/should_compile/T25718.hs
- + testsuite/tests/simplCore/should_compile/T25718.stderr
- + testsuite/tests/simplCore/should_compile/T25718a.hs
- + testsuite/tests/simplCore/should_compile/T25718a.stderr
- + testsuite/tests/simplCore/should_compile/T25718b.hs
- + testsuite/tests/simplCore/should_compile/T25718b.stderr
- + testsuite/tests/simplCore/should_compile/T25718c.hs
- + testsuite/tests/simplCore/should_compile/T25718c.stderr-ws-32
- + testsuite/tests/simplCore/should_compile/T25718c.stderr-ws-64
- testsuite/tests/simplCore/should_compile/all.T
- + testsuite/tests/simplCore/should_run/T27071.hs
- + testsuite/tests/simplCore/should_run/T27071.stdout
- testsuite/tests/simplCore/should_run/all.T
- + testsuite/tests/typecheck/T13180/T13180.hs
- + testsuite/tests/typecheck/T13180/T13180.hs-boot
- + testsuite/tests/typecheck/T13180/T13180.stderr
- + testsuite/tests/typecheck/T13180/T13180A.hs
- + testsuite/tests/typecheck/T13180/all.T
- + testsuite/tests/typecheck/should_compile/T11141.hs
- + testsuite/tests/typecheck/should_compile/T11141.stderr
- + testsuite/tests/typecheck/should_compile/T11505Bar.hs
- + testsuite/tests/typecheck/should_compile/T11505Foo.hs
- + testsuite/tests/typecheck/should_compile/T11505Foo.hs-boot
- + testsuite/tests/typecheck/should_compile/T12046.hs
- testsuite/tests/typecheck/should_compile/T26225.hs
- testsuite/tests/typecheck/should_compile/all.T
- + testsuite/tests/typecheck/should_fail/T26823.hs
- + testsuite/tests/typecheck/should_fail/T26823.stderr
- testsuite/tests/typecheck/should_fail/all.T
- + testsuite/tests/vdq-rta/should_compile/T26967.hs
- + testsuite/tests/vdq-rta/should_compile/T26967.stderr
- + testsuite/tests/vdq-rta/should_compile/T26967_tyop.hs
- + testsuite/tests/vdq-rta/should_compile/T26967_tyop.stderr
- testsuite/tests/vdq-rta/should_compile/all.T
- utils/check-exact/ExactPrint.hs
- utils/deriveConstants/Main.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/Rename.hs
- utils/haddock/haddock-api/src/Haddock/Interface/RenameType.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/31e49b58a0fedfea716cf2cc047dd6…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/31e49b58a0fedfea716cf2cc047dd6…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/romes/25636] 5 commits: Allocate static constructors for bytecode
by Rodrigo Mesquita (@alt-romes) 31 Mar '26
by Rodrigo Mesquita (@alt-romes) 31 Mar '26
31 Mar '26
Rodrigo Mesquita pushed to branch wip/romes/25636 at Glasgow Haskell Compiler / GHC
Commits:
4f4eb634 by Rodrigo Mesquita at 2026-03-31T23:09:20+01:00
Allocate static constructors for bytecode
This commit adds support for static constructors when compiling and
linking ByteCode objects.
Top-level StgRhsCon get lowered to ProtoStaticCons rather than to
ProtoBCOs. A ProtoStaticCon gets allocated directly as a data con
application on the heap (using the new primop newConApp#).
Previously, we would allocate a ProtoBCO which, when evaluated, would
PACK and return the constructor.
A few more details are given in Note [Static constructors in Bytecode].
Secondly, this commit also fixes issue #25636 which was caused by
linking *unlifted* constructors in BCO instructions as
- (1) a thunk indexing the array of BCOs in a module
- (2) which evaluated to a BCO which still had to be evaluated to
return the unlifted constructor proper.
The (2) issue has been resolved by allocating the static constructors
directly. The (1) issue can be resolved by ensuring that we allocate all
unlifted top-level constructors eagerly, and leave the knot-tying for
the lifted BCOs and top-level constructors only.
The top-level unlifted constructors are never mutually recursive, so we
can allocate them all in one go as long as we do it in topological
order. Lifted fields of unlifted constructors can still be filled by the
knot-tied lifted variables since in those fields it is fine to keep
those thunks. See Note [Tying the knot in createBCOs] for more details.
Fixes #25636
- - - - -
acbe7869 by Rodrigo Mesquita at 2026-03-31T23:09:20+01:00
Revert "StgToByteCode: Assert that PUSH_G'd values are lifted"
This reverts commit ec26c54d818e0cd328276196930313f66b780905.
Ever since f7a22c0f4e9ae0dc767115d4c53fddbd8372b777, we now do support
and will link top-level unlifted constructors into evaluated and
properly tagged values which we can reference with PUSH_G.
This assertion is no longer true and triggered a failure in T25636
- - - - -
fb3b08da by Rodrigo Mesquita at 2026-03-31T23:09:20+01:00
refactor: Tag more remote Ptrs as RemotePtr
Pure refactor which improves the API of
- GHC.ByteCode.Linker
- GHC.Runtime.Interpreter
- GHC.Runtime.Interpreter.Types.SymbolCache
by using `RemotePtr` for more functions which used to return `Ptr`s that
could potentially be in a foreign process. E.g. `lookupIE`,
`lookupStaticPtr`, etc...
- - - - -
c5057f26 by Rodrigo Mesquita at 2026-03-31T23:09:20+01:00
Add float# and subword tests for #25636
These tests cover that static constructors in bytecode work correctly
for Float# and subword values (Word8#, Word16#)
- - - - -
50aa3bf8 by Rodrigo Mesquita at 2026-03-31T23:09:20+01:00
test: Validate topoSort logic in createBCOs
This test validates that the topological sorting and ordering of the
unlifted constructors and lifted constructors in `createBCOs` is
correct.
See `Note [Tying the knot in createBCOs]` for why tying the knot for the
created BCOs is slightly difficult and why the topological sorting is
necessary.
This test fails when `let topoSortedObjs = topSortObjs objs` is
substituted by `let topoSortedObjs = zip [0..] objs`, thus witnessing
the toposort logic is correct and necessary.
The test calls the ghci `createBCOs` directly because it is currently
impossible to construct in Source Haskell a situation where a top-level
static unlifted constructor depends on another (we don't have top-level
unlifted constructors except for nullary constructors like `Leaf ::
(UTree :: UnliftedType)`).
This is another test for fix for #25636
- - - - -
52 changed files:
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/ByteCode/Asm.hs
- compiler/GHC/ByteCode/Binary.hs
- compiler/GHC/ByteCode/Instr.hs
- compiler/GHC/ByteCode/Linker.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/Cmm/Liveness.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/Runtime/Interpreter/Types/SymbolCache.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/StgToCmm/DataCon.hs
- compiler/GHC/StgToCmm/Layout.hs
- compiler/GHC/StgToCmm/Prim.hs
- compiler/GHC/StgToJS/Prim.hs
- libraries/ghci/GHCi/CreateBCO.hs
- libraries/ghci/GHCi/ResolvedBCO.hs
- rts/Interpreter.c
- rts/PrimOps.cmm
- rts/RtsSymbols.c
- rts/include/Rts.h
- rts/include/rts/storage/ClosureMacros.h
- rts/include/stg/MiscClosures.h
- + testsuite/tests/codeGen/should_run/T23146/T25636.stdout
- + testsuite/tests/codeGen/should_run/T25636a/T25636a.script
- + testsuite/tests/codeGen/should_run/T25636a/T25636a.stdout
- + testsuite/tests/codeGen/should_run/T25636a/all.T
- + testsuite/tests/codeGen/should_run/T25636b/T25636b.script
- + testsuite/tests/codeGen/should_run/T25636b/T25636b.stdout
- + testsuite/tests/codeGen/should_run/T25636b/all.T
- + testsuite/tests/codeGen/should_run/T25636c/T25636c.script
- + testsuite/tests/codeGen/should_run/T25636c/T25636c.stdout
- + testsuite/tests/codeGen/should_run/T25636c/all.T
- + testsuite/tests/codeGen/should_run/T25636d/T25636d.script
- + testsuite/tests/codeGen/should_run/T25636d/T25636d.stdout
- + testsuite/tests/codeGen/should_run/T25636d/all.T
- + testsuite/tests/codeGen/should_run/T25636e/T25636e.script
- + testsuite/tests/codeGen/should_run/T25636e/T25636e.stdout
- + testsuite/tests/codeGen/should_run/T25636e/all.T
- testsuite/tests/ghci.debugger/scripts/print034.stdout
- + testsuite/tests/ghci/should_run/T25636f.hs
- + testsuite/tests/ghci/should_run/T25636f.stdout
- testsuite/tests/ghci/should_run/all.T
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/base-exports.stdout-ws-32
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout
- testsuite/tests/interface-stability/ghc-experimental-exports.stdout-mingw32
- testsuite/tests/interface-stability/ghc-prim-exports.stdout
- testsuite/tests/interface-stability/ghc-prim-exports.stdout-mingw32
- utils/deriveConstants/Main.hs
The diff was not included because it is too large.
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/428665006dc2cdecbb402963e5620e…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/428665006dc2cdecbb402963e5620e…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/ani/better-expansion] remove unnecessary import
by Apoorv Ingle (@ani) 31 Mar '26
by Apoorv Ingle (@ani) 31 Mar '26
31 Mar '26
Apoorv Ingle pushed to branch wip/ani/better-expansion at Glasgow Haskell Compiler / GHC
Commits:
f648f9eb by Apoorv Ingle at 2026-03-31T16:59:59-05:00
remove unnecessary import
- - - - -
1 changed file:
- compiler/GHC/Tc/Gen/Expr.hs-boot
Changes:
=====================================
compiler/GHC/Tc/Gen/Expr.hs-boot
=====================================
@@ -1,6 +1,6 @@
module GHC.Tc.Gen.Expr where
import GHC.Hs ( HsExpr, LHsExpr, SyntaxExprRn
- , SyntaxExprTc, HsExpansion )
+ , SyntaxExprTc )
import GHC.Tc.Utils.TcType ( TcType, TcRhoType, TcSigmaType, TcSigmaTypeFRR
, SyntaxOpType, InferInstFlag
, ExpType, ExpRhoType, ExpSigmaType )
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f648f9eb085be13367d35ca6af210cb…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/f648f9eb085be13367d35ca6af210cb…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/dcoutts/issue-26717] 7 commits: Use BlockInfoForceNonClosure in the select I/O manager
by Duncan Coutts (@dcoutts) 31 Mar '26
by Duncan Coutts (@dcoutts) 31 Mar '26
31 Mar '26
Duncan Coutts pushed to branch wip/dcoutts/issue-26717 at Glasgow Haskell Compiler / GHC
Commits:
8a6f5bed by Duncan Coutts at 2026-03-31T22:40:15+01:00
Use BlockInfoForceNonClosure in the select I/O manager
- - - - -
d8b111e6 by Duncan Coutts at 2026-03-31T22:40:15+01:00
Use BlockInfoForceNonClosure in the win32-legacy I/O manager
for the BlockedOn{Read,Write} since these use the non-heap allocated
StgAsyncIOResult.
- - - - -
bdfb64dd by Duncan Coutts at 2026-03-31T22:40:15+01:00
Enforce the why_blocked and block_info rules in checkTSO
We now check the cases wher IsBlockInfoClosure should hold, the cases
that are supposed to use block_info.unused == END_TSO_QUEUE, and which
cases are allowed to use BlockInfoForceNonClosure.
This partially enforces the use of why_blocked as a tag for the
block_info union. We could be stricter and check for the correct
expected info table for the closure cases.
- - - - -
943d57f0 by Duncan Coutts at 2026-03-31T22:40:15+01:00
Use IsBlockInfoClosure to simplify several tests
In GC and generic traversal we need to know if we should look at the
block_info.closure or not. Now we can do just that using a cheap bit
test on the why_blocked tag.
This fixes issue 26717, where the problem was that some GC modes did not
know when to look at block_info.closure, because the poll I/O manager
uses a closure for BlockedOn{Read,Write} while the select I/O manager
uses a non-closure. Now this information is in the why_blocked tag
itself.
- - - - -
0a1f8f68 by Duncan Coutts at 2026-03-31T22:40:15+01:00
Remove the now-unused scavengeTSOIOManager
The GC no longer has to delegate to the I/O manager, since it can use
IsBlockInfoClosure to decide things itself.
- - - - -
f20248b4 by Duncan Coutts at 2026-03-31T22:40:15+01:00
Remove duplicate assertion
- - - - -
d84c48aa by Duncan Coutts at 2026-03-31T22:40:15+01:00
Follow atomic access rules more consistently for tso->why_blocked
The rule is this:
store block_info *before* why_blocked
store why_blocked using store release
load why_blocked using load acquire
load block_info *after* why_blocked
This is a an atomic store release / load acquire pair and (if the reads
are in a separate thread to the writes, and the read receives the value
stored) then this guarantees a full "happens before" relationship of
these stores and loads.
In some cases, we do not need a full load acquire, because we don't read
the block_info at all and so do not need any ordering. In this case we
just need an atomic relaxed load.
This was being followed in most places, but not all. If there's good
reason in any case that we don't need atomic access, then we should
document that in a comment. In the absence of that I think it's easier
to follow the rule everywhere.
- - - - -
15 changed files:
- rts/IOManager.c
- rts/IOManager.h
- rts/PrimOps.cmm
- rts/RaiseAsync.c
- rts/Schedule.c
- rts/Threads.c
- rts/TraverseHeap.c
- rts/posix/Poll.c
- rts/posix/Select.c
- rts/posix/Timeout.c
- rts/sm/Compact.c
- rts/sm/NonMovingMark.c
- rts/sm/Sanity.c
- rts/sm/Scav.c
- rts/win32/AsyncMIO.c
Changes:
=====================================
rts/IOManager.c
=====================================
@@ -611,41 +611,6 @@ void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap)
}
-void scavengeTSOIOManager(StgTSO *tso)
-{
- switch (iomgr_type) {
-
- /* case IO_MANAGER_SELECT:
- * BlockedOn{Read,Write} uses block_info.fd
- * BlockedOnDelay uses block_info.target
- * both of these are not GC pointers, so there is nothing to do.
- */
-
-#if defined(IOMGR_ENABLED_POLL)
- case IO_MANAGER_POLL:
- /* BlockedOn{Read,Write} uses block_info.aiop
- * BlockedOnDelay uses block_info.timeout
- * both of these are heap allocated, so we can do the same in all
- * cases, which is why we can use the generic block_info.closure.
- */
- evacuate(&tso->block_info.closure);
- break;
-#endif
-
- /* case IO_MANAGER_WIN32_LEGACY:
- * BlockedOn{Read,Write,DoProc} uses block_info.async_reqID
- * which is a plain integer, so nothing to scavenge.
- */
-
- default:
- /* All the other I/O managers do not use I/O-related why_blocked
- * reasons, so there are no cases to handle.
- */
- break;
- }
-}
-
-
/* Declared in rts/IOInterface.h. Used only by the MIO threaded I/O manager on
* Unix platforms.
*/
@@ -807,16 +772,16 @@ bool syncIOWaitReady(Capability *cap,
#if defined(IOMGR_ENABLED_SELECT)
case IO_MANAGER_SELECT:
{
- StgWord why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite;
+ StgWord why_blocked = (rw == IORead ? BlockedOnRead : BlockedOnWrite)
+ | BlockInfoForceNonClosure;
tso->block_info.fd = fd;
- RELEASE_STORE(&tso->why_blocked, why_blocked);
appendToIOBlockedQueue(cap, tso);
+ RELEASE_STORE(&tso->why_blocked, why_blocked);
return true;
}
#endif
#if defined(IOMGR_ENABLED_POLL)
case IO_MANAGER_POLL:
- ASSERT(tso->why_blocked == NotBlocked);
return syncIOWaitReadyPoll(cap, tso, rw, fd);
#endif
default:
@@ -868,8 +833,8 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
{
LowResTime target = getDelayTarget(us_delay);
tso->block_info.target = target;
- RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
insertIntoSleepingQueue(cap, tso, target);
+ RELEASE_STORE(&tso->why_blocked, BlockedOnDelay | BlockInfoForceNonClosure);
return true;
}
#endif
@@ -889,8 +854,8 @@ bool syncDelay(Capability *cap, StgTSO *tso, HsInt us_delay)
* simplifies matters, so set the status to OnDoProc and put the
* delayed thread on the blocked_queue.
*/
- RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
appendToIOBlockedQueue(cap, tso);
+ RELEASE_STORE(&tso->why_blocked, BlockedOnDoProc);
return true;
}
#endif
@@ -906,6 +871,7 @@ void syncDelayCancel(Capability *cap, StgTSO *tso)
switch (iomgr_type) {
#if defined(IOMGR_ENABLED_SELECT)
case IO_MANAGER_SELECT:
+ ASSERT(tso->why_blocked == (BlockedOnDelay | BlockInfoForceNonClosure));
removeThreadFromQueue(cap, &cap->iomgr->sleeping_queue, tso);
break;
#endif
=====================================
rts/IOManager.h
=====================================
@@ -291,11 +291,6 @@ void wakeupIOManager(void);
void markCapabilityIOManager(evac_fn evac, void *user, Capability *cap);
-/* GC hook: scavenge I/O related tso->block_info. Used by scavengeTSO.
- */
-void scavengeTSOIOManager(StgTSO *tso);
-
-
/* Several code paths are almost identical between read and write paths. In
* such cases we use a shared code path with an enum to say which we're doing.
*/
=====================================
rts/PrimOps.cmm
=====================================
@@ -2275,7 +2275,8 @@ stg_asyncReadzh ( W_ fd, W_ is_sock, W_ len, W_ buf )
StgTSO_block_info(CurrentTSO) = reqID;
ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
- %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32;
+ %release StgTSO_why_blocked(CurrentTSO) = BlockedOnRead::I32
+ | BlockInfoForceNonClosure::I32;
ccall appendToIOBlockedQueue(MyCapability() "ptr", CurrentTSO "ptr");
jump stg_block_async();
@@ -2294,7 +2295,8 @@ stg_asyncWritezh ( W_ fd, W_ is_sock, W_ len, W_ buf )
StgTSO_block_info(CurrentTSO) = reqID;
ASSERT(StgTSO_why_blocked(CurrentTSO) == NotBlocked::I32);
- %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32;
+ %release StgTSO_why_blocked(CurrentTSO) = BlockedOnWrite::I32
+ | BlockInfoForceNonClosure::I32;
ccall appendToIOBlockedQueue(MyCapability() "ptr", CurrentTSO "ptr");
jump stg_block_async();
=====================================
rts/RaiseAsync.c
=====================================
@@ -233,7 +233,6 @@ throwTo (Capability *cap, // the Capability we hold
uint32_t
throwToMsg (Capability *cap, MessageThrowTo *msg)
{
- StgWord status;
StgTSO *target = ACQUIRE_LOAD(&msg->target);
Capability *target_cap;
@@ -268,9 +267,9 @@ check_target:
return THROWTO_BLOCKED;
}
- status = ACQUIRE_LOAD(&target->why_blocked);
+ StgWord why_blocked = ACQUIRE_LOAD(&target->why_blocked);
- switch (UntagWhyBlocked(status)) {
+ switch (UntagWhyBlocked(why_blocked)) {
case NotBlocked:
{
if ((target->flags & TSO_BLOCKEX) == 0) {
@@ -370,8 +369,9 @@ check_target:
// we have the MVar, let's check whether the thread
// is still blocked on the same MVar.
- if ((target->why_blocked != BlockedOnMVar
- && target->why_blocked != BlockedOnMVarRead)
+ StgWord why_blocked_still = ACQUIRE_LOAD(&target->why_blocked);
+ if (( why_blocked_still != BlockedOnMVar
+ && why_blocked_still != BlockedOnMVarRead)
|| target->block_info.mvar != mvar) {
unlockClosure((StgClosure *)mvar, info);
goto retry;
@@ -490,7 +490,7 @@ check_target:
goto retry;
default:
- barf("throwTo: unrecognised why_blocked (%d)", target->why_blocked);
+ barf("throwTo: unrecognised why_blocked (%ld)", why_blocked);
}
barf("throwTo");
}
@@ -667,7 +667,7 @@ removeFromMVarBlockedQueue (StgTSO *tso)
static void
removeFromQueues(Capability *cap, StgTSO *tso)
{
- switch (UntagWhyBlocked(tso->why_blocked)) {
+ switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
case NotBlocked:
case ThreadMigrating:
@@ -721,8 +721,8 @@ removeFromQueues(Capability *cap, StgTSO *tso)
}
done:
- RELAXED_STORE(&tso->why_blocked, NotBlocked);
appendToRunQueue(cap, tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
}
/* -----------------------------------------------------------------------------
@@ -1105,9 +1105,9 @@ done:
IF_DEBUG(sanity, checkTSO(tso));
// wake it up
- if (tso->why_blocked != NotBlocked) {
- tso->why_blocked = NotBlocked;
+ if (RELAXED_LOAD(&tso->why_blocked) != NotBlocked) {
appendToRunQueue(cap,tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
}
return tso;
=====================================
rts/Schedule.c
=====================================
@@ -1098,7 +1098,7 @@ schedulePostRunThread (Capability *cap, StgTSO *t)
//
// and a is never equal to b given a consistent view of memory.
//
- if (t -> trec != NO_TREC && t -> why_blocked == NotBlocked) {
+ if (t -> trec != NO_TREC && RELAXED_LOAD(&t->why_blocked) == NotBlocked) {
if (!stmValidateNestOfTransactions(cap, t -> trec, true)) {
debugTrace(DEBUG_sched | DEBUG_stm,
"trec %p found wasting its time", t);
@@ -2523,9 +2523,9 @@ suspendThread (StgRegTable *reg, bool interruptible)
tso->block_info.unused = END_TSO_QUEUE;
if (interruptible) {
- tso->why_blocked = BlockedOnCCall_Interruptible;
+ RELEASE_STORE(&tso->why_blocked, BlockedOnCCall_Interruptible);
} else {
- tso->why_blocked = BlockedOnCCall;
+ RELEASE_STORE(&tso->why_blocked, BlockedOnCCall);
}
// Hand back capability
@@ -2583,16 +2583,25 @@ resumeThread (void *task_)
tso = incall->suspended_tso;
incall->suspended_tso = NULL;
incall->suspended_cap = NULL;
+
+ // we set why_blocked previously in suspendThread
+ ASSERT(tso->why_blocked == BlockedOnCCall ||
+ tso->why_blocked == BlockedOnCCall_Interruptible);
+
// we will modify tso->_link
IF_NONMOVING_WRITE_BARRIER_ENABLED {
updateRemembSetPushClosure(cap, (StgClosure *)tso->_link);
}
tso->_link = END_TSO_QUEUE;
+ // but no need to modify tso->block_info.prev as coincidentally
+ // it has the value we want already (since in suspendThread we set
+ // tso->block_info.unused to END_TSO_QUEUE for BlockedOnCCall).
+ ASSERT(tso->block_info.prev == END_TSO_QUEUE);
traceEventRunThread(cap, tso);
/* Reset blocking status */
- tso->why_blocked = NotBlocked;
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
if ((tso->flags & TSO_BLOCKEX) == 0) {
// avoid locking the TSO if we don't have to
@@ -2944,8 +2953,9 @@ deleteThread (StgTSO *tso)
// The TSO must be on the run queue of the Capability we own, or
// we must own all Capabilities.
- if (tso->why_blocked != BlockedOnCCall &&
- tso->why_blocked != BlockedOnCCall_Interruptible) {
+ StgWord why_blocked = RELAXED_LOAD(&tso->why_blocked);
+ if (why_blocked != BlockedOnCCall &&
+ why_blocked != BlockedOnCCall_Interruptible) {
throwToSingleThreaded(tso->cap,tso,NULL);
}
}
@@ -2956,10 +2966,12 @@ deleteThread_(StgTSO *tso)
{ // for forkProcess only:
// like deleteThread(), but we delete threads in foreign calls, too.
- if (tso->why_blocked == BlockedOnCCall ||
- tso->why_blocked == BlockedOnCCall_Interruptible) {
+ StgWord why_blocked = RELAXED_LOAD(&tso->why_blocked);
+ if (why_blocked == BlockedOnCCall ||
+ why_blocked == BlockedOnCCall_Interruptible) {
tso->what_next = ThreadKilled;
appendToRunQueue(tso->cap, tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
} else {
deleteThread(tso);
}
@@ -3310,7 +3322,7 @@ resurrectThreads (StgTSO *threads)
// Wake up the thread on the Capability it was last on
cap = tso->cap;
- switch (tso->why_blocked) {
+ switch (RELAXED_LOAD(&tso->why_blocked)) {
case BlockedOnMVar:
case BlockedOnMVarRead:
/* Called by GC - sched_mutex lock is currently held. */
=====================================
rts/Threads.c
=====================================
@@ -335,8 +335,8 @@ tryWakeupThread (Capability *cap, StgTSO *tso)
unblock:
// just run the thread now, if the BH is not really available,
// we'll block again.
- tso->why_blocked = NotBlocked;
appendToRunQueue(cap,tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
// We used to set the context switch flag here, which would
// trigger a context switch a short time in the future (at the end
@@ -368,7 +368,7 @@ migrateThread (Capability *from, StgTSO *tso, Capability *to)
// ThreadMigrating tells the target cap that it needs to be added to
// the run queue when it receives the MSG_TRY_WAKEUP.
tso->block_info.unused = END_TSO_QUEUE;
- tso->why_blocked = ThreadMigrating;
+ RELEASE_STORE(&tso->why_blocked, ThreadMigrating);
tso->cap = to;
tryWakeupThread(from, tso);
}
@@ -1017,7 +1017,7 @@ printAllThreads(void)
debugBelch("other threads:\n");
for (g = 0; g < RtsFlags.GcFlags.generations; g++) {
for (t = generations[g].threads; t != END_TSO_QUEUE; t = next) {
- if (t->why_blocked != NotBlocked) {
+ if (RELAXED_LOAD(&t->why_blocked) != NotBlocked) {
printThreadStatus(t);
}
next = t->global_link;
=====================================
rts/TraverseHeap.c
=====================================
@@ -1242,15 +1242,12 @@ inner_loop:
traversePushClosure(ts, (StgClosure *) tso->blocked_exceptions, c, sep, child_data);
traversePushClosure(ts, (StgClosure *) tso->bq, c, sep, child_data);
traversePushClosure(ts, (StgClosure *) tso->trec, c, sep, child_data);
- switch (ACQUIRE_LOAD(&tso->why_blocked)) {
- case BlockedOnMVar:
- case BlockedOnMVarRead:
- case BlockedOnBlackHole:
- case BlockedOnMsgThrowTo:
+
+ StgWord why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
+ if (IsBlockInfoClosure(why_blocked) && why_blocked != NotBlocked) {
+ // The NotBlocked case uses block_info.prev as a TSO back link.
+ // Do not follow in that case or we'll get into a loop.
traversePushClosure(ts, tso->block_info.closure, c, sep, child_data);
- break;
- default:
- break;
}
goto loop;
}
=====================================
rts/posix/Poll.c
=====================================
@@ -146,8 +146,9 @@ bool syncIOWaitReadyPoll(Capability *cap, StgTSO *tso,
aiop->notify.tso = tso;
aiop->notify_type = NotifyTSO;
aiop->live = &stg_ASYNCIO_LIVE0_closure;
- tso->why_blocked = rw == IORead ? BlockedOnRead : BlockedOnWrite;
tso->block_info.aiop = aiop;
+ RELEASE_STORE(&tso->why_blocked, rw == IORead ? BlockedOnRead
+ : BlockedOnWrite);
return asyncIOWaitReadyPoll(cap, aiop, rw, fd);
}
@@ -258,10 +259,9 @@ static void notifyIOCompletion(Capability *cap, StgAsyncIOOp *aiop)
* cap because the tso was not on the run queue of any cap and
* so is not subject to thread migration.
*/
- StgTSO *tso = aiop->notify.tso;
- tso->why_blocked = NotBlocked;
- tso->_link = END_TSO_QUEUE;
+ StgTSO *tso = aiop->notify.tso;
pushOnRunQueue(cap, tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
}
break;
}
=====================================
rts/posix/Select.c
=====================================
@@ -105,11 +105,10 @@ static bool wakeUpSleepingThreads (Capability *cap, LowResTime now)
break;
}
iomgr->sleeping_queue = tso->_link;
- RELAXED_STORE(&tso->why_blocked, NotBlocked);
- tso->_link = END_TSO_QUEUE;
IF_DEBUG(scheduler, debugBelch("Waking up sleeping thread %"
FMT_StgThreadID "\n", tso->id));
pushOnRunQueue(cap,tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
flag = true;
}
return flag;
@@ -397,7 +396,7 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait)
int fd;
enum FdState fd_state = RTS_FD_IS_BLOCKING;
- switch (UntagWhyBlocked(tso->why_blocked)) {
+ switch (UntagWhyBlocked(ACQUIRE_LOAD(&tso->why_blocked))) {
case BlockedOnRead:
fd = tso->block_info.fd;
@@ -436,9 +435,8 @@ awaitCompletedTimeoutsOrIOSelect(Capability *cap, bool wait)
IF_DEBUG(scheduler,
debugBelch("Waking up blocked thread %" FMT_StgThreadID "\n",
tso->id));
- tso->why_blocked = NotBlocked;
- tso->_link = END_TSO_QUEUE;
pushOnRunQueue(cap,tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
break;
case RTS_FD_IS_BLOCKING:
if (prev == NULL)
=====================================
rts/posix/Timeout.c
=====================================
@@ -48,8 +48,8 @@ bool syncDelayTimeout(Capability *cap, StgTSO *tso, HsInt us_delay)
initElemTimeoutQueue(timeout, notify, NotifyTSO, cap->r.rCCCS);
ASSERT(tso->why_blocked == NotBlocked);
- tso->why_blocked = BlockedOnDelay;
tso->block_info.timeout = timeout;
+ RELEASE_STORE(&tso->why_blocked, BlockedOnDelay);
insertTimeoutQueue(&cap->iomgr->timeout_queue, timeout, target);
@@ -118,10 +118,10 @@ static void notifyTimeoutCompletion(Capability *cap, StgTimeout *timeout)
switch (timeout->notify_type) {
case NotifyTSO:
{
- StgTSO *tso = timeout->notify.tso;
- tso->why_blocked = NotBlocked;
- tso->_link = END_TSO_QUEUE;
+ StgTSO *tso = timeout->notify.tso;
+ tso->_link = END_TSO_QUEUE;
pushOnRunQueue(cap, tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
break;
}
case NotifyMVar:
=====================================
rts/sm/Compact.c
=====================================
@@ -468,16 +468,10 @@ thread_TSO (StgTSO *tso)
thread_(&tso->_link);
thread_(&tso->global_link);
- switch (ACQUIRE_LOAD(&tso->why_blocked)) {
- case BlockedOnMVar:
- case BlockedOnMVarRead:
- case BlockedOnBlackHole:
- case BlockedOnMsgThrowTo:
- case NotBlocked:
+ if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
+ /* This also follows the block_info.prev back-link in
+ * the NotBlocked case, which may not be necessary. */
thread_(&tso->block_info.closure);
- break;
- default:
- break;
}
thread_(&tso->blocked_exceptions);
thread_(&tso->bq);
=====================================
rts/sm/NonMovingMark.c
=====================================
@@ -1055,16 +1055,10 @@ trace_tso (MarkQueue *queue, StgTSO *tso)
if (tso->label != NULL) {
markQueuePushClosure_(queue, (StgClosure *) tso->label);
}
- switch (ACQUIRE_LOAD(&tso->why_blocked)) {
- case BlockedOnMVar:
- case BlockedOnMVarRead:
- case BlockedOnBlackHole:
- case BlockedOnMsgThrowTo:
- case NotBlocked:
+ if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
+ /* This also follows the block_info.prev back-link in
+ * the NotBlocked case, which may not be necessary. */
markQueuePushClosure_(queue, tso->block_info.closure);
- break;
- default:
- break;
}
}
=====================================
rts/sm/Sanity.c
=====================================
@@ -779,13 +779,45 @@ checkTSO(StgTSO *tso)
info == &stg_WHITEHOLE_info); // used to happen due to STM doing
// lockTSO(), might not happen now
- if ( tso->why_blocked == BlockedOnMVar
- || tso->why_blocked == BlockedOnMVarRead
- || tso->why_blocked == BlockedOnBlackHole
- || tso->why_blocked == BlockedOnMsgThrowTo
- || tso->why_blocked == NotBlocked
- ) {
+ unsigned why_blocked = ACQUIRE_LOAD(&tso->why_blocked);
+ switch (why_blocked) {
+ case NotBlocked:
+ case BlockedOnMVar:
+ case BlockedOnMVarRead:
+ case BlockedOnBlackHole:
+ case BlockedOnMsgThrowTo:
+ case BlockedOnRead:
+ case BlockedOnWrite:
+ case BlockedOnDelay:
+ //TODO: we could be more specific and check BlockedOnMVar has an MVar,
+ // BlockedOnBlackHole has a message, BlockedOnRead has an AIOP etc.
+ ASSERT(IsBlockInfoClosure(why_blocked));
ASSERT(LOOKS_LIKE_CLOSURE_PTR(tso->block_info.closure));
+ break;
+
+ case BlockedOnSTM:
+ case BlockedOnCCall:
+ case BlockedOnCCall_Interruptible:
+ case ThreadMigrating:
+#if defined(mingw32_HOST_OS) && !defined(THREADED_RTS)
+ case BlockedOnDoProc:
+#endif
+ ASSERT(!IsBlockInfoClosure(why_blocked));
+ ASSERT(tso->block_info.unused == END_TSO_QUEUE);
+ break;
+
+#if !defined(THREADED_RTS)
+ // Only these three can use BlockInfoForceNonClosure
+ case BlockedOnRead | BlockInfoForceNonClosure:
+ case BlockedOnWrite | BlockInfoForceNonClosure:
+ case BlockedOnDelay | BlockInfoForceNonClosure:
+ ASSERT(!IsBlockInfoClosure(why_blocked));
+ break;
+#endif
+
+ default:
+ barf("checkTSO: strange tso->why_blocked: %d for TSO %"
+ FMT_StgThreadID " (%p)", why_blocked, tso->id, tso);
}
ASSERT(LOOKS_LIKE_CLOSURE_PTR(tso->bq));
=====================================
rts/sm/Scav.c
=====================================
@@ -138,21 +138,9 @@ scavengeTSO (StgTSO *tso)
evacuate((StgClosure **)&tso->label);
}
- switch (ACQUIRE_LOAD(&tso->why_blocked)) {
- case BlockedOnMVar:
- case BlockedOnMVarRead:
- case BlockedOnBlackHole:
- case BlockedOnMsgThrowTo:
- case NotBlocked:
+ if (IsBlockInfoClosure(ACQUIRE_LOAD(&tso->why_blocked))) {
evacuate(&tso->block_info.closure);
- break;
- case BlockedOnRead:
- case BlockedOnWrite:
- case BlockedOnDelay:
- case BlockedOnDoProc:
- scavengeTSOIOManager(tso);
- break;
- default:
+ } else {
#if defined(THREADED_RTS)
// in the THREADED_RTS, block_info.closure must always point to a
// valid closure, because we assume this in throwTo(). In the
@@ -160,7 +148,6 @@ scavengeTSO (StgTSO *tso)
// BlockedOnRead/BlockedOnWrite) or a time value (BlockedOnDelay)
ASSERT(tso->block_info.unused == END_TSO_QUEUE);
#endif
- break;
}
tso->dirty = gct->failed_to_evac;
=====================================
rts/win32/AsyncMIO.c
=====================================
@@ -318,8 +318,6 @@ start:
}
// Terminates the run queue + this inner for-loop.
- tso->_link = END_TSO_QUEUE;
- tso->why_blocked = NotBlocked;
// For stg_block_async frames (read/write/doProc),
// write len and errCode directly to the stack.
// For stg_block_noregs frames (delay), nothing
@@ -329,14 +327,14 @@ start:
tso->stackobj->sp[2] = (W_)errCode;
}
pushOnRunQueue(&MainCapability, tso);
+ RELEASE_STORE(&tso->why_blocked, NotBlocked);
break;
}
break;
- default:
- if (tso->why_blocked != NotBlocked) {
- barf("awaitRequests: odd thread state");
- }
+ case NotBlocked:
break;
+ default:
+ barf("awaitRequests: odd thread state");
}
prev = tso;
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d098ecbe3abe3d2ab3bd3f7f12dc11…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/d098ecbe3abe3d2ab3bd3f7f12dc11…
You're receiving this email because of your account on gitlab.haskell.org.
1
0