Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC

Commits:

20 changed files:

Changes:

  • compiler/GHC/Core/Opt/CprAnal.hs
    1
    +{-# LANGUAGE MultiWayIf #-}
    
    1 2
     
    
    2 3
     -- | Constructed Product Result analysis. Identifies functions that surely
    
    3 4
     -- return heap-allocated records on every code path, so that we can eliminate
    
    ... ... @@ -22,12 +23,15 @@ import GHC.Types.Demand
    22 23
     import GHC.Types.Cpr
    
    23 24
     import GHC.Types.Unique.MemoFun
    
    24 25
     
    
    26
    +import GHC.Core
    
    25 27
     import GHC.Core.FamInstEnv
    
    26 28
     import GHC.Core.DataCon
    
    27 29
     import GHC.Core.Type
    
    28 30
     import GHC.Core.Utils
    
    29
    -import GHC.Core
    
    31
    +import GHC.Core.Coercion
    
    32
    +import GHC.Core.Reduction
    
    30 33
     import GHC.Core.Seq
    
    34
    +import GHC.Core.TyCon
    
    31 35
     import GHC.Core.Opt.WorkWrap.Utils
    
    32 36
     
    
    33 37
     import GHC.Data.Graph.UnVar -- for UnVarSet
    
    ... ... @@ -216,9 +220,13 @@ cprAnal' _ (Type ty) = (topCprType, Type ty) -- Doesn't happen, in fact
    216 220
     cprAnal' _ (Coercion co) = (topCprType, Coercion co)
    
    217 221
     
    
    218 222
     cprAnal' env (Cast e co)
    
    219
    -  = (cpr_ty, Cast e' co)
    
    223
    +  = (cpr_ty', Cast e' co)
    
    220 224
       where
    
    221 225
         (cpr_ty, e') = cprAnal env e
    
    226
    +    cpr_ty'
    
    227
    +      | cpr_ty == topCprType                    = topCprType -- cheap case first
    
    228
    +      | isRecNewTyConApp env (coercionRKind co) = topCprType -- See Note [CPR for recursive data constructors]
    
    229
    +      | otherwise                               = cpr_ty
    
    222 230
     
    
    223 231
     cprAnal' env (Tick t e)
    
    224 232
       = (cpr_ty, Tick t e')
    
    ... ... @@ -391,6 +399,19 @@ cprTransformDataConWork env con args
    391 399
     mAX_CPR_SIZE :: Arity
    
    392 400
     mAX_CPR_SIZE = 10
    
    393 401
     
    
    402
    +isRecNewTyConApp :: AnalEnv -> Type -> Bool
    
    403
    +-- See Note [CPR for recursive newtype constructors]
    
    404
    +isRecNewTyConApp env ty
    
    405
    +  --- | pprTrace "isRecNewTyConApp" (ppr ty) False = undefined
    
    406
    +  | Just (tc, tc_args) <- splitTyConApp_maybe ty =
    
    407
    +      if | Just (HetReduction (Reduction _ rhs) _) <- topReduceTyFamApp_maybe (ae_fam_envs env) tc tc_args
    
    408
    +         -> isRecNewTyConApp env rhs
    
    409
    +         | Just dc <- newTyConDataCon_maybe tc
    
    410
    +         -> ae_rec_dc env dc == DefinitelyRecursive
    
    411
    +         | otherwise
    
    412
    +         -> False
    
    413
    +  | otherwise = False
    
    414
    +
    
    394 415
     --
    
    395 416
     -- * Bindings
    
    396 417
     --
    
    ... ... @@ -414,12 +435,18 @@ cprFix orig_env orig_pairs
    414 435
                    | otherwise    = orig_pairs
    
    415 436
         init_env = extendSigEnvFromIds orig_env (map fst init_pairs)
    
    416 437
     
    
    438
    +    -- If fixed-point iteration does not yield a result we use this instead
    
    439
    +    -- See Note [Safe abortion in the fixed-point iteration]
    
    440
    +    abort :: (AnalEnv, [(Id,CoreExpr)])
    
    441
    +    abort = step (nonVirgin orig_env) [(setIdCprSig id topCprSig, rhs) | (id, rhs) <- orig_pairs ]
    
    442
    +
    
    417 443
         -- The fixed-point varies the idCprSig field of the binders and and their
    
    418 444
         -- entries in the AnalEnv, and terminates if that annotation does not change
    
    419 445
         -- any more.
    
    420 446
         loop :: Int -> AnalEnv -> [(Id,CoreExpr)] -> (AnalEnv, [(Id,CoreExpr)])
    
    421 447
         loop n env pairs
    
    422 448
           | found_fixpoint = (reset_env', pairs')
    
    449
    +      | n == 10        = pprTraceUserWarning (text "cprFix aborts. This is not terrible, but worth reporting a GHC issue." <+> ppr (map fst pairs)) $ abort
    
    423 450
           | otherwise      = loop (n+1) env' pairs'
    
    424 451
           where
    
    425 452
             -- In all but the first iteration, delete the virgin flag
    
    ... ... @@ -519,8 +546,9 @@ cprAnalBind env id rhs
    519 546
         -- possibly trim thunk CPR info
    
    520 547
         rhs_ty'
    
    521 548
           -- See Note [CPR for thunks]
    
    522
    -      | stays_thunk = trimCprTy rhs_ty
    
    523
    -      | otherwise   = rhs_ty
    
    549
    +      | rhs_ty == topCprType = topCprType -- cheap case first
    
    550
    +      | stays_thunk          = trimCprTy rhs_ty
    
    551
    +      | otherwise            = rhs_ty
    
    524 552
         -- See Note [Arity trimming for CPR signatures]
    
    525 553
         sig  = mkCprSigForArity (idArity id) rhs_ty'
    
    526 554
         -- See Note [OPAQUE pragma]
    
    ... ... @@ -639,7 +667,7 @@ data AnalEnv
    639 667
       , ae_fam_envs :: FamInstEnvs
    
    640 668
       -- ^ Needed when expanding type families and synonyms of product types.
    
    641 669
       , ae_rec_dc :: DataCon -> IsRecDataConResult
    
    642
    -  -- ^ Memoised result of 'GHC.Core.Opt.WorkWrap.Utils.isRecDataCon'
    
    670
    +  -- ^ Memoised result of 'GHC.Core.Opt.WorkWrap.Utils.isRecDataType
    
    643 671
       }
    
    644 672
     
    
    645 673
     instance Outputable AnalEnv where
    
    ... ... @@ -1042,10 +1070,11 @@ Eliminating the shared 'c' binding in the process. And then
    1042 1070
     
    
    1043 1071
     What can we do about it?
    
    1044 1072
     
    
    1045
    - A. Don't CPR functions that return a *recursive data type* (the list in this
    
    1046
    -    case). This is the solution we adopt. Rationale: the benefit of CPR on
    
    1047
    -    recursive data structures is slight, because it only affects the outer layer
    
    1048
    -    of a potentially massive data structure.
    
    1073
    + A. Don't give recursive data constructors or casts representing recursive newtype constructors
    
    1074
    +    the CPR property (the list in this case). This is the solution we adopt.
    
    1075
    +    Rationale: the benefit of CPR on recursive data structures is slight,
    
    1076
    +    because it only affects the outer layer of a potentially massive data
    
    1077
    +    structure.
    
    1049 1078
      B. Don't CPR any *recursive function*. That would be quite conservative, as it
    
    1050 1079
         would also affect e.g. the factorial function.
    
    1051 1080
      C. Flat CPR only for recursive functions. This prevents the asymptotic
    
    ... ... @@ -1055,10 +1084,15 @@ What can we do about it?
    1055 1084
         `c` in the second eqn of `replicateC`). But we'd need to know which paths
    
    1056 1085
         were hot. We want such static branch frequency estimates in #20378.
    
    1057 1086
     
    
    1058
    -We adopt solution (A) It is ad-hoc, but appears to work reasonably well.
    
    1059
    -Deciding what a "recursive data constructor" is is quite tricky and ad-hoc, too:
    
    1060
    -See Note [Detecting recursive data constructors]. We don't have to be perfect
    
    1061
    -and can simply keep on unboxing if unsure.
    
    1087
    +We adopt solution (A). It is ad-hoc, but appears to work reasonably well.
    
    1088
    +Specifically:
    
    1089
    +
    
    1090
    +* For data constructors, in `cprTransformDataConWork` we check for a recursive
    
    1091
    +  data constructor by calling `ae_rec_dc env`, which is just a memoised version
    
    1092
    +  of `isRecDataCon`.  See Note [Detecting recursive data constructors]
    
    1093
    +* For newtypes, in the `Cast` case of `cprAnal`, we check for a recursive newtype
    
    1094
    +  by calling `isRecNewTyConApp`, which in turn calls `ae_rec_dc env`.
    
    1095
    +  See Note [CPR for recursive newtype constructors]
    
    1062 1096
     
    
    1063 1097
     Note [Detecting recursive data constructors]
    
    1064 1098
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -1075,12 +1109,15 @@ looks inside the following class of types, represented by `ty` (and responds
    1075 1109
         types of its data constructors and check `tc_args` for recursion.
    
    1076 1110
      C. If `ty = F tc_args`, `F` is a `FamTyCon` and we can reduce `F tc_args` to
    
    1077 1111
         `rhs`, look into the `rhs` type.
    
    1112
    + D. If `ty = f a`, then look into `f` and `a`
    
    1113
    + E. If `ty = ty' |> co`, then look into `ty'`
    
    1078 1114
     
    
    1079 1115
     A few perhaps surprising points:
    
    1080 1116
     
    
    1081 1117
       1. It deems any function type as non-recursive, because it's unlikely that
    
    1082 1118
          a recursion through a function type builds up a recursive data structure.
    
    1083
    -  2. It doesn't look into kinds or coercion types because there's nothing to unbox.
    
    1119
    +  2. It doesn't look into kinds, literals or coercion types because we are
    
    1120
    +     ultimately looking for value-level recursion.
    
    1084 1121
          Same for promoted data constructors.
    
    1085 1122
       3. We don't care whether an AlgTyCon app `T tc_args` is fully saturated or not;
    
    1086 1123
          we simply look at its definition/DataCons and its field tys and look for
    
    ... ... @@ -1153,6 +1190,22 @@ I've played with the idea to make points (1) through (3) of 'isRecDataCon'
    1153 1190
     configurable like (4) to enable more re-use throughout the compiler, but haven't
    
    1154 1191
     found a killer app for that yet, so ultimately didn't do that.
    
    1155 1192
     
    
    1193
    +Note [CPR for recursive newtype constructors]
    
    1194
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    1195
    +A newtype constructor is considered recursive iff the data constructor of the
    
    1196
    +equivalent datatype definition is recursive.
    
    1197
    +See Note [CPR for recursive data constructors].
    
    1198
    +Detection is a bit complicated by the fact that newtype constructor applications
    
    1199
    +reflect as Casts in Core:
    
    1200
    +
    
    1201
    +  newtype List a = C (Maybe (a, List a))
    
    1202
    +  xs = C (Just (0, C Nothing))
    
    1203
    +  ==> {desugar to Core}
    
    1204
    +  xs = Just (0, Nothing |> sym N:List) |> sym N:List
    
    1205
    +
    
    1206
    +So the check for `isRecNewTyConApp` is in the Cast case of `cprAnal` rather than
    
    1207
    +in `cprTransformDataConWork` as for data constructors.
    
    1208
    +
    
    1156 1209
     Note [CPR examples]
    
    1157 1210
     ~~~~~~~~~~~~~~~~~~~
    
    1158 1211
     Here are some examples (stranal/should_compile/T10482a) of the
    

  • compiler/GHC/Core/Opt/WorkWrap/Utils.hs
    ... ... @@ -63,6 +63,7 @@ import Data.List ( unzip4 )
    63 63
     
    
    64 64
     import GHC.Types.RepType
    
    65 65
     import GHC.Unit.Types
    
    66
    +import GHC.Core.TyCo.Rep
    
    66 67
     
    
    67 68
     {-
    
    68 69
     ************************************************************************
    
    ... ... @@ -1426,23 +1427,29 @@ isRecDataCon fam_envs fuel orig_dc
    1426 1427
                         | arg_ty <- map scaledThing (dataConRepArgTys dc) ]
    
    1427 1428
     
    
    1428 1429
         go_arg_ty :: IntWithInf -> TyConSet -> Type -> IsRecDataConResult
    
    1429
    -    go_arg_ty fuel visited_tcs ty
    
    1430
    -      --- | pprTrace "arg_ty" (ppr ty) False = undefined
    
    1430
    +    go_arg_ty fuel visited_tcs ty = -- pprTrace "arg_ty" (ppr ty) $
    
    1431
    +      case coreFullView ty of
    
    1432
    +        TyConApp tc tc_args -> go_tc_app fuel visited_tcs tc tc_args
    
    1433
    +          -- See Note [Detecting recursive data constructors], points (B) and (C)
    
    1431 1434
     
    
    1432
    -      | Just (_tcv, ty') <- splitForAllTyCoVar_maybe ty
    
    1433
    -      = go_arg_ty fuel visited_tcs ty'
    
    1435
    +        ForAllTy _ ty' -> go_arg_ty fuel visited_tcs ty'
    
    1434 1436
               -- See Note [Detecting recursive data constructors], point (A)
    
    1435 1437
     
    
    1436
    -      | Just (tc, tc_args) <- splitTyConApp_maybe ty
    
    1437
    -      = go_tc_app fuel visited_tcs tc tc_args
    
    1438
    +        CastTy ty' _ -> go_arg_ty fuel visited_tcs ty'
    
    1438 1439
     
    
    1439
    -      | otherwise
    
    1440
    -      = NonRecursiveOrUnsure
    
    1440
    +        AppTy f a -> go_arg_ty fuel visited_tcs f `combineIRDCR` go_arg_ty fuel visited_tcs a
    
    1441
    +          -- See Note [Detecting recursive data constructors], point (D)
    
    1442
    +
    
    1443
    +        FunTy{} -> NonRecursiveOrUnsure
    
    1444
    +          -- See Note [Detecting recursive data constructors], point (1)
    
    1445
    +
    
    1446
    +        -- (TyVarTy{} | LitTy{} | CastTy{})
    
    1447
    +        _ -> NonRecursiveOrUnsure
    
    1441 1448
     
    
    1442 1449
         go_tc_app :: IntWithInf -> TyConSet -> TyCon -> [Type] -> IsRecDataConResult
    
    1443 1450
         go_tc_app fuel visited_tcs tc tc_args =
    
    1444 1451
           case tyConDataCons_maybe tc of
    
    1445
    -      --- | pprTrace "tc_app" (vcat [ppr tc, ppr tc_args]) False = undefined
    
    1452
    +        ---_ | pprTrace "tc_app" (vcat [ppr tc, ppr tc_args]) False -> undefined
    
    1446 1453
             _ | Just (HetReduction (Reduction _ rhs) _) <- topReduceTyFamApp_maybe fam_envs tc tc_args
    
    1447 1454
               -- This is the only place where we look at tc_args, which might have
    
    1448 1455
               -- See Note [Detecting recursive data constructors], point (C) and (5)
    

  • compiler/GHC/Driver/Errors.hs
    ... ... @@ -12,6 +12,7 @@ import GHC.Prelude
    12 12
     import GHC.Types.SrcLoc
    
    13 13
     import GHC.Types.SourceError
    
    14 14
     import GHC.Types.Error
    
    15
    +import GHC.Utils.Json
    
    15 16
     import GHC.Utils.Error
    
    16 17
     import GHC.Utils.Outputable
    
    17 18
     import GHC.Utils.Logger
    
    ... ... @@ -46,9 +47,22 @@ printMessages logger msg_opts opts = mapM_ (printMessage logger msg_opts opts) .
    46 47
     
    
    47 48
     printMessage :: forall a. (Diagnostic a) => Logger -> DiagnosticOpts a -> DiagOpts -> MsgEnvelope a -> IO ()
    
    48 49
     printMessage logger msg_opts opts message
    
    49
    -  | log_diags_as_json = logJsonMsg logger messageClass message
    
    50
    +  | log_diags_as_json = do
    
    51
    +      decorated <- decorateDiagnostic logflags messageClass location doc
    
    52
    +      let
    
    53
    +        rendered :: String
    
    54
    +        rendered = renderWithContext (log_default_user_context logflags) decorated
    
    55
    +
    
    56
    +        jsonMessage :: JsonDoc
    
    57
    +        jsonMessage = jsonDiagnostic rendered message
    
    58
    +
    
    59
    +      logJsonMsg logger messageClass jsonMessage
    
    60
    +
    
    50 61
       | otherwise = logMsg logger messageClass location doc
    
    51 62
       where
    
    63
    +    logflags :: LogFlags
    
    64
    +    logflags = logFlags logger
    
    65
    +
    
    52 66
         doc :: SDoc
    
    53 67
         doc = updSDocContext (\_ -> ctx) (messageWithHints diagnostic)
    
    54 68
     
    

  • compiler/GHC/Types/Error.hs
    ... ... @@ -73,6 +73,9 @@ module GHC.Types.Error
    73 73
        , mkLocMessage
    
    74 74
        , mkLocMessageWarningGroups
    
    75 75
        , getCaretDiagnostic
    
    76
    +
    
    77
    +   , jsonDiagnostic
    
    78
    +
    
    76 79
        -- * Queries
    
    77 80
        , isIntrinsicErrorMessage
    
    78 81
        , isExtrinsicErrorMessage
    
    ... ... @@ -109,7 +112,7 @@ import GHC.Utils.Panic
    109 112
     
    
    110 113
     import GHC.Version (cProjectVersion)
    
    111 114
     import Data.Bifunctor
    
    112
    -import Data.Foldable    ( fold, toList )
    
    115
    +import Data.Foldable
    
    113 116
     import Data.List.NonEmpty ( NonEmpty (..) )
    
    114 117
     import qualified Data.List.NonEmpty as NE
    
    115 118
     import Data.List ( intercalate )
    
    ... ... @@ -171,9 +174,6 @@ instance Diagnostic e => Outputable (Messages e) where
    171 174
                    pprDiagnostic (errMsgDiagnostic envelope)
    
    172 175
                  ]
    
    173 176
     
    
    174
    -instance (Diagnostic e) => ToJson (Messages e) where
    
    175
    -  json msgs =  JSArray . toList $ json <$> getMessages msgs
    
    176
    -
    
    177 177
     {- Note [Discarding Messages]
    
    178 178
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    179 179
     
    
    ... ... @@ -573,7 +573,7 @@ instance ToJson DiagnosticCode where
    573 573
     {- Note [Diagnostic Message JSON Schema]
    
    574 574
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    575 575
     The below instance of ToJson must conform to the JSON schema
    
    576
    -specified in docs/users_guide/diagnostics-as-json-schema-1_1.json.
    
    576
    +specified in docs/users_guide/diagnostics-as-json-schema-1_2.json.
    
    577 577
     When the schema is altered, please bump the version.
    
    578 578
     If the content is altered in a backwards compatible way,
    
    579 579
     update the minor version (e.g. 1.3 ~> 1.4).
    
    ... ... @@ -586,15 +586,17 @@ https://json-schema.org
    586 586
     -}
    
    587 587
     
    
    588 588
     schemaVersion :: String
    
    589
    -schemaVersion = "1.1"
    
    589
    +schemaVersion = "1.2"
    
    590
    +
    
    590 591
     -- See Note [Diagnostic Message JSON Schema] before editing!
    
    591
    -instance Diagnostic e => ToJson (MsgEnvelope e) where
    
    592
    -  json m = JSObject $ [
    
    592
    +jsonDiagnostic :: forall e. Diagnostic e => String -> MsgEnvelope e -> JsonDoc
    
    593
    +jsonDiagnostic rendered m = JSObject $ [
    
    593 594
         ("version", JSString schemaVersion),
    
    594 595
         ("ghcVersion", JSString $ "ghc-" ++ cProjectVersion),
    
    595 596
         ("span", json $ errMsgSpan m),
    
    596 597
         ("severity", json $ errMsgSeverity m),
    
    597 598
         ("code", maybe JSNull json (diagnosticCode diag)),
    
    599
    +    ("rendered", JSString rendered),
    
    598 600
         ("message", JSArray $ map renderToJSString diagMsg),
    
    599 601
         ("hints", JSArray $ map (renderToJSString . ppr) (diagnosticHints diag) ) ]
    
    600 602
         ++ [ ("reason", reasonJson)
    

  • compiler/GHC/Utils/Logger.hs
    ... ... @@ -62,6 +62,8 @@ module GHC.Utils.Logger
    62 62
         , logJsonMsg
    
    63 63
         , logDumpMsg
    
    64 64
     
    
    65
    +    , decorateDiagnostic
    
    66
    +
    
    65 67
         -- * Dumping
    
    66 68
         , defaultDumpAction
    
    67 69
         , putDumpFile
    
    ... ... @@ -419,26 +421,62 @@ defaultLogActionWithHandles out err logflags msg_class srcSpan msg
    419 421
           MCInfo                       -> printErrs msg
    
    420 422
           MCFatal                      -> printErrs msg
    
    421 423
           MCDiagnostic SevIgnore _ _   -> pure () -- suppress the message
    
    422
    -      MCDiagnostic _sev _rea _code -> printDiagnostics
    
    424
    +      MCDiagnostic _sev _rea _code -> decorateDiagnostic logflags msg_class srcSpan msg >>= printErrs
    
    423 425
         where
    
    424 426
           printOut   = defaultLogActionHPrintDoc  logflags False out
    
    425 427
           printErrs  = defaultLogActionHPrintDoc  logflags False err
    
    426 428
           putStrSDoc = defaultLogActionHPutStrDoc logflags False out
    
    429
    +
    
    430
    +-- This function is used by `defaultLogActionWithHandles` for non-JSON output,
    
    431
    +-- and also by `GHC.Driver.Errors.printMessages` to produce the `rendered`
    
    432
    +-- message on `-fdiagnostics-as-json`.
    
    433
    +--
    
    434
    +-- We would want to eventually consolidate this.  However, this is currently
    
    435
    +-- not feasible for the following reasons:
    
    436
    +--
    
    437
    +-- 1. Some parts of the compiler sidestep `printMessages`, for that reason we
    
    438
    +--    can not decorate the message in `printMessages`.
    
    439
    +--
    
    440
    +-- 2. GHC uses two different code paths for JSON and non-JSON diagnostics.  For
    
    441
    +--    that reason we can not decorate the message in `defaultLogActionWithHandles`.
    
    442
    +--
    
    443
    +--    See also Note [JSON Error Messages]:
    
    444
    +--
    
    445
    +--      `jsonLogAction` should be removed along with -ddump-json
    
    446
    +--
    
    447
    +-- Also note that (1) is the reason why some parts of the compiler produce
    
    448
    +-- diagnostics that don't respect `-fdiagnostics-as-json`.
    
    449
    +--
    
    450
    +-- The plan as I see it is as follows:
    
    451
    +--
    
    452
    +--  1. Refactor all places in the compiler that report diagnostics to go
    
    453
    +--     through `GHC.Driver.Errors.printMessages`.
    
    454
    +--
    
    455
    +--     (It's easy to find all those places by looking for who creates
    
    456
    +--     MCDiagnostic, either directly or via `mkMCDiagnostic` or
    
    457
    +--     `errorDiagnostic`.)
    
    458
    +--
    
    459
    +--  2. Get rid of `-ddump-json`, `jsonLogAction` and consolidate message
    
    460
    +--     decoration at one place (either `printMessages` or
    
    461
    +--     `defaultLogActionWithHandles`)
    
    462
    +--
    
    463
    +-- This story is tracked by #24113.
    
    464
    +decorateDiagnostic :: LogFlags -> MessageClass -> SrcSpan -> SDoc -> IO SDoc
    
    465
    +decorateDiagnostic logflags msg_class srcSpan msg = addCaret
    
    466
    +    where
    
    427 467
           -- Pretty print the warning flag, if any (#10752)
    
    468
    +      message :: SDoc
    
    428 469
           message = mkLocMessageWarningGroups (log_show_warn_groups logflags) msg_class srcSpan msg
    
    429 470
     
    
    430
    -      printDiagnostics = do
    
    471
    +      addCaret :: IO SDoc
    
    472
    +      addCaret = do
    
    431 473
             caretDiagnostic <-
    
    432 474
                 if log_show_caret logflags
    
    433 475
                 then getCaretDiagnostic msg_class srcSpan
    
    434 476
                 else pure empty
    
    435
    -        printErrs $ getPprStyle $ \style ->
    
    477
    +        return $ getPprStyle $ \style ->
    
    436 478
               withPprStyle (setStyleColoured True style)
    
    437 479
                 (message $+$ caretDiagnostic $+$ blankLine)
    
    438
    -        -- careful (#2302): printErrs prints in UTF-8,
    
    439
    -        -- whereas converting to string first and using
    
    440
    -        -- hPutStr would just emit the low 8 bits of
    
    441
    -        -- each unicode char.
    
    442 480
     
    
    443 481
     -- | Like 'defaultLogActionHPutStrDoc' but appends an extra newline.
    
    444 482
     defaultLogActionHPrintDoc :: LogFlags -> Bool -> Handle -> SDoc -> IO ()
    
    ... ... @@ -603,8 +641,8 @@ defaultTraceAction logflags title doc x =
    603 641
     logMsg :: Logger -> MessageClass -> SrcSpan -> SDoc -> IO ()
    
    604 642
     logMsg logger mc loc msg = putLogMsg logger (logFlags logger) mc loc msg
    
    605 643
     
    
    606
    -logJsonMsg :: ToJson a => Logger -> MessageClass -> a -> IO ()
    
    607
    -logJsonMsg logger mc d = putJsonLogMsg logger (logFlags logger) mc  (json d)
    
    644
    +logJsonMsg :: Logger -> MessageClass -> JsonDoc -> IO ()
    
    645
    +logJsonMsg logger mc = putJsonLogMsg logger (logFlags logger) mc
    
    608 646
     
    
    609 647
     -- | Dump something
    
    610 648
     logDumpFile :: Logger -> PprStyle -> DumpFlag -> String -> DumpFormat -> SDoc -> IO ()
    

  • docs/users_guide/9.14.1-notes.rst
    ... ... @@ -147,6 +147,11 @@ Compiler
    147 147
       integer operations.  Also, ``shuffleFloatX4#`` and ``shuffleDoubleX2#`` no longer
    
    148 148
       require ``-mavx``.
    
    149 149
     
    
    150
    +- JSON diagnostics produced with (:ghc-flag:`-fdiagnostics-as-json`) now
    
    151
    +  include the `rendered` diagnostics message, in the exact same format as what
    
    152
    +  GHC would have produced without -fdiagnostics-as-json (including ANSI escape
    
    153
    +  sequences).
    
    154
    +
    
    150 155
     GHCi
    
    151 156
     ~~~~
    
    152 157
     
    

  • docs/users_guide/diagnostics-as-json-schema-1_2.json
    1
    +{
    
    2
    +  "$schema": "https://json-schema.org/draft/2020-12/schema",
    
    3
    +  "title": "JSON Diagnostic Schema",
    
    4
    +  "description": "A Schema for specifying GHC diagnostics output as JSON",
    
    5
    +  "type": "object",
    
    6
    +  "properties": {
    
    7
    +    "version": {
    
    8
    +      "description": "The current JSON schema version this object conforms to",
    
    9
    +      "type": "string"
    
    10
    +    },
    
    11
    +    "ghcVersion": {
    
    12
    +      "description": "The GHC version",
    
    13
    +      "type": "string"
    
    14
    +    },
    
    15
    +    "span": {
    
    16
    +      "oneOf": [
    
    17
    +        { "$ref": "#/$defs/span" },
    
    18
    +        { "type": "null" }
    
    19
    +      ]
    
    20
    +    },
    
    21
    +    "severity": {
    
    22
    +      "description": "The diagnostic severity",
    
    23
    +      "type": "string",
    
    24
    +      "enum": [
    
    25
    +        "Warning",
    
    26
    +        "Error"
    
    27
    +      ]
    
    28
    +    },
    
    29
    +    "code": {
    
    30
    +      "description": "The diagnostic code (if it exists)",
    
    31
    +      "type": [
    
    32
    +        "integer",
    
    33
    +        "null"
    
    34
    +      ]
    
    35
    +    },
    
    36
    +    "rendered": {
    
    37
    +      "description": "The rendered diagnostics message, in the exact same format as what GHC would have produced without -fdiagnostics-as-json (including ANSI escape sequences)",
    
    38
    +      "type": "string"
    
    39
    +    },
    
    40
    +    "message": {
    
    41
    +      "description": "The string output of the diagnostic message by GHC",
    
    42
    +      "type": "array",
    
    43
    +      "items": {
    
    44
    +        "type": "string"
    
    45
    +      }
    
    46
    +    },
    
    47
    +    "hints": {
    
    48
    +      "description": "The suggested fixes",
    
    49
    +      "type": "array",
    
    50
    +      "items": {
    
    51
    +        "type": "string"
    
    52
    +      }
    
    53
    +    },
    
    54
    +    "reason" : {
    
    55
    +      "description": "The GHC flag that was responsible for the emission of the diagnostic message",
    
    56
    +      "oneOf": [
    
    57
    +        {
    
    58
    +          "type": "object",
    
    59
    +          "description": "The diagnostic message was controlled by one or more GHC flags",
    
    60
    +          "properties": {
    
    61
    +            "flags": {
    
    62
    +              "type": "array",
    
    63
    +              "items": {
    
    64
    +                "description": "The name of a GHC flag controlling the diagnostic message",
    
    65
    +                "type": "string"
    
    66
    +              },
    
    67
    +              "minItems": 1
    
    68
    +            }
    
    69
    +          },
    
    70
    +          "required": ["flags"]
    
    71
    +        },
    
    72
    +        {
    
    73
    +          "type": "object",
    
    74
    +          "description": "The diagnostic message was controlled by a GHC diagnostic message category",
    
    75
    +          "properties": {
    
    76
    +            "category": {
    
    77
    +              "description": "The name of the GHC diagnostic message category controlling the diagnostic message",
    
    78
    +              "type": "string"
    
    79
    +            }
    
    80
    +          },
    
    81
    +          "required": ["category"]
    
    82
    +        }
    
    83
    +      ]
    
    84
    +    }
    
    85
    +  },
    
    86
    +
    
    87
    +  "$comment": "NOTE: \"rendered\" is not a required field so that the schema is backward compatible with version 1.1. If you bump the schema version to 2.0 the please also add \"rendered\" to the \"required\" fields.",
    
    88
    +  "required": [
    
    89
    +    "version",
    
    90
    +    "ghcVersion",
    
    91
    +    "span",
    
    92
    +    "severity",
    
    93
    +    "code",
    
    94
    +    "message",
    
    95
    +    "hints"
    
    96
    +  ],
    
    97
    +
    
    98
    +  "additionalProperties": false,
    
    99
    +  "$defs": {
    
    100
    +    "span": {
    
    101
    +      "description": "The span of the diagnostic",
    
    102
    +      "type": "object",
    
    103
    +      "properties": {
    
    104
    +        "file": {
    
    105
    +          "description": "The file in which the diagnostic occurs",
    
    106
    +          "type": "string"
    
    107
    +        },
    
    108
    +        "start": {
    
    109
    +          "description": "The start location of the diagnostic",
    
    110
    +          "$ref": "#/$defs/location"
    
    111
    +        },
    
    112
    +        "end": {
    
    113
    +          "description": "The end location of the diagnostic",
    
    114
    +          "$ref": "#/$defs/location"
    
    115
    +        }
    
    116
    +      },
    
    117
    +      "required": [
    
    118
    +        "file",
    
    119
    +        "start",
    
    120
    +        "end"
    
    121
    +      ],
    
    122
    +      "additionalProperties": false
    
    123
    +    },
    
    124
    +    "location": {
    
    125
    +      "description": "A location in a text file",
    
    126
    +      "type": "object",
    
    127
    +      "properties": {
    
    128
    +        "line": {
    
    129
    +          "description": "The line number",
    
    130
    +          "type": "integer"
    
    131
    +        },
    
    132
    +        "column": {
    
    133
    +          "description": "The column number",
    
    134
    +          "type": "integer"
    
    135
    +        }
    
    136
    +      },
    
    137
    +      "required": [
    
    138
    +        "line",
    
    139
    +        "column"
    
    140
    +      ],
    
    141
    +      "additionalProperties": false
    
    142
    +    }
    
    143
    +  }
    
    144
    +}

  • docs/users_guide/using.rst
    ... ... @@ -1428,7 +1428,7 @@ messages and in GHCi:
    1428 1428
         a new line.
    
    1429 1429
     
    
    1430 1430
         The structure of the output is described by a `JSON Schema <https://json-schema.org/>`_.
    
    1431
    -    The schema can be downloaded :download:`here <diagnostics-as-json-schema-1_1.json>`.
    
    1431
    +    The schema can be downloaded :download:`here <diagnostics-as-json-schema-1_2.json>`.
    
    1432 1432
     
    
    1433 1433
     .. ghc-flag:: -fdiagnostics-color=⟨always|auto|never⟩
    
    1434 1434
         :shortdesc: Use colors in error messages
    

  • rts/Interpreter.c
    ... ... @@ -473,6 +473,72 @@ void interp_shutdown( void ){
    473 473
     
    
    474 474
     #endif
    
    475 475
     
    
    476
    +const StgPtr ctoi_tuple_infos[] = {
    
    477
    +    (StgPtr) &stg_ctoi_t0_info,
    
    478
    +    (StgPtr) &stg_ctoi_t1_info,
    
    479
    +    (StgPtr) &stg_ctoi_t2_info,
    
    480
    +    (StgPtr) &stg_ctoi_t3_info,
    
    481
    +    (StgPtr) &stg_ctoi_t4_info,
    
    482
    +    (StgPtr) &stg_ctoi_t5_info,
    
    483
    +    (StgPtr) &stg_ctoi_t6_info,
    
    484
    +    (StgPtr) &stg_ctoi_t7_info,
    
    485
    +    (StgPtr) &stg_ctoi_t8_info,
    
    486
    +    (StgPtr) &stg_ctoi_t9_info,
    
    487
    +    (StgPtr) &stg_ctoi_t10_info,
    
    488
    +    (StgPtr) &stg_ctoi_t11_info,
    
    489
    +    (StgPtr) &stg_ctoi_t12_info,
    
    490
    +    (StgPtr) &stg_ctoi_t13_info,
    
    491
    +    (StgPtr) &stg_ctoi_t14_info,
    
    492
    +    (StgPtr) &stg_ctoi_t15_info,
    
    493
    +    (StgPtr) &stg_ctoi_t16_info,
    
    494
    +    (StgPtr) &stg_ctoi_t17_info,
    
    495
    +    (StgPtr) &stg_ctoi_t18_info,
    
    496
    +    (StgPtr) &stg_ctoi_t19_info,
    
    497
    +    (StgPtr) &stg_ctoi_t20_info,
    
    498
    +    (StgPtr) &stg_ctoi_t21_info,
    
    499
    +    (StgPtr) &stg_ctoi_t22_info,
    
    500
    +    (StgPtr) &stg_ctoi_t23_info,
    
    501
    +    (StgPtr) &stg_ctoi_t24_info,
    
    502
    +    (StgPtr) &stg_ctoi_t25_info,
    
    503
    +    (StgPtr) &stg_ctoi_t26_info,
    
    504
    +    (StgPtr) &stg_ctoi_t27_info,
    
    505
    +    (StgPtr) &stg_ctoi_t28_info,
    
    506
    +    (StgPtr) &stg_ctoi_t29_info,
    
    507
    +    (StgPtr) &stg_ctoi_t30_info,
    
    508
    +    (StgPtr) &stg_ctoi_t31_info,
    
    509
    +    (StgPtr) &stg_ctoi_t32_info,
    
    510
    +    (StgPtr) &stg_ctoi_t33_info,
    
    511
    +    (StgPtr) &stg_ctoi_t34_info,
    
    512
    +    (StgPtr) &stg_ctoi_t35_info,
    
    513
    +    (StgPtr) &stg_ctoi_t36_info,
    
    514
    +    (StgPtr) &stg_ctoi_t37_info,
    
    515
    +    (StgPtr) &stg_ctoi_t38_info,
    
    516
    +    (StgPtr) &stg_ctoi_t39_info,
    
    517
    +    (StgPtr) &stg_ctoi_t40_info,
    
    518
    +    (StgPtr) &stg_ctoi_t41_info,
    
    519
    +    (StgPtr) &stg_ctoi_t42_info,
    
    520
    +    (StgPtr) &stg_ctoi_t43_info,
    
    521
    +    (StgPtr) &stg_ctoi_t44_info,
    
    522
    +    (StgPtr) &stg_ctoi_t45_info,
    
    523
    +    (StgPtr) &stg_ctoi_t46_info,
    
    524
    +    (StgPtr) &stg_ctoi_t47_info,
    
    525
    +    (StgPtr) &stg_ctoi_t48_info,
    
    526
    +    (StgPtr) &stg_ctoi_t49_info,
    
    527
    +    (StgPtr) &stg_ctoi_t50_info,
    
    528
    +    (StgPtr) &stg_ctoi_t51_info,
    
    529
    +    (StgPtr) &stg_ctoi_t52_info,
    
    530
    +    (StgPtr) &stg_ctoi_t53_info,
    
    531
    +    (StgPtr) &stg_ctoi_t54_info,
    
    532
    +    (StgPtr) &stg_ctoi_t55_info,
    
    533
    +    (StgPtr) &stg_ctoi_t56_info,
    
    534
    +    (StgPtr) &stg_ctoi_t57_info,
    
    535
    +    (StgPtr) &stg_ctoi_t58_info,
    
    536
    +    (StgPtr) &stg_ctoi_t59_info,
    
    537
    +    (StgPtr) &stg_ctoi_t60_info,
    
    538
    +    (StgPtr) &stg_ctoi_t61_info,
    
    539
    +    (StgPtr) &stg_ctoi_t62_info,
    
    540
    +};
    
    541
    +
    
    476 542
     #if defined(PROFILING)
    
    477 543
     
    
    478 544
     //
    
    ... ... @@ -1828,82 +1894,11 @@ run_BCO:
    1828 1894
                 SpW(-1) = BCO_PTR(o_tuple_bco);
    
    1829 1895
                 SpW(-2) = tuple_info;
    
    1830 1896
                 SpW(-3) = BCO_PTR(o_bco);
    
    1831
    -            W_ ctoi_t_offset;
    
    1832 1897
                 int tuple_stack_words = (tuple_info >> 24) & 0xff;
    
    1833
    -            switch(tuple_stack_words) {
    
    1834
    -                case 0:  ctoi_t_offset = (W_)&stg_ctoi_t0_info;  break;
    
    1835
    -                case 1:  ctoi_t_offset = (W_)&stg_ctoi_t1_info;  break;
    
    1836
    -                case 2:  ctoi_t_offset = (W_)&stg_ctoi_t2_info;  break;
    
    1837
    -                case 3:  ctoi_t_offset = (W_)&stg_ctoi_t3_info;  break;
    
    1838
    -                case 4:  ctoi_t_offset = (W_)&stg_ctoi_t4_info;  break;
    
    1839
    -                case 5:  ctoi_t_offset = (W_)&stg_ctoi_t5_info;  break;
    
    1840
    -                case 6:  ctoi_t_offset = (W_)&stg_ctoi_t6_info;  break;
    
    1841
    -                case 7:  ctoi_t_offset = (W_)&stg_ctoi_t7_info;  break;
    
    1842
    -                case 8:  ctoi_t_offset = (W_)&stg_ctoi_t8_info;  break;
    
    1843
    -                case 9:  ctoi_t_offset = (W_)&stg_ctoi_t9_info;  break;
    
    1844
    -
    
    1845
    -                case 10: ctoi_t_offset = (W_)&stg_ctoi_t10_info; break;
    
    1846
    -                case 11: ctoi_t_offset = (W_)&stg_ctoi_t11_info; break;
    
    1847
    -                case 12: ctoi_t_offset = (W_)&stg_ctoi_t12_info; break;
    
    1848
    -                case 13: ctoi_t_offset = (W_)&stg_ctoi_t13_info; break;
    
    1849
    -                case 14: ctoi_t_offset = (W_)&stg_ctoi_t14_info; break;
    
    1850
    -                case 15: ctoi_t_offset = (W_)&stg_ctoi_t15_info; break;
    
    1851
    -                case 16: ctoi_t_offset = (W_)&stg_ctoi_t16_info; break;
    
    1852
    -                case 17: ctoi_t_offset = (W_)&stg_ctoi_t17_info; break;
    
    1853
    -                case 18: ctoi_t_offset = (W_)&stg_ctoi_t18_info; break;
    
    1854
    -                case 19: ctoi_t_offset = (W_)&stg_ctoi_t19_info; break;
    
    1855
    -
    
    1856
    -                case 20: ctoi_t_offset = (W_)&stg_ctoi_t20_info; break;
    
    1857
    -                case 21: ctoi_t_offset = (W_)&stg_ctoi_t21_info; break;
    
    1858
    -                case 22: ctoi_t_offset = (W_)&stg_ctoi_t22_info; break;
    
    1859
    -                case 23: ctoi_t_offset = (W_)&stg_ctoi_t23_info; break;
    
    1860
    -                case 24: ctoi_t_offset = (W_)&stg_ctoi_t24_info; break;
    
    1861
    -                case 25: ctoi_t_offset = (W_)&stg_ctoi_t25_info; break;
    
    1862
    -                case 26: ctoi_t_offset = (W_)&stg_ctoi_t26_info; break;
    
    1863
    -                case 27: ctoi_t_offset = (W_)&stg_ctoi_t27_info; break;
    
    1864
    -                case 28: ctoi_t_offset = (W_)&stg_ctoi_t28_info; break;
    
    1865
    -                case 29: ctoi_t_offset = (W_)&stg_ctoi_t29_info; break;
    
    1866
    -
    
    1867
    -                case 30: ctoi_t_offset = (W_)&stg_ctoi_t30_info; break;
    
    1868
    -                case 31: ctoi_t_offset = (W_)&stg_ctoi_t31_info; break;
    
    1869
    -                case 32: ctoi_t_offset = (W_)&stg_ctoi_t32_info; break;
    
    1870
    -                case 33: ctoi_t_offset = (W_)&stg_ctoi_t33_info; break;
    
    1871
    -                case 34: ctoi_t_offset = (W_)&stg_ctoi_t34_info; break;
    
    1872
    -                case 35: ctoi_t_offset = (W_)&stg_ctoi_t35_info; break;
    
    1873
    -                case 36: ctoi_t_offset = (W_)&stg_ctoi_t36_info; break;
    
    1874
    -                case 37: ctoi_t_offset = (W_)&stg_ctoi_t37_info; break;
    
    1875
    -                case 38: ctoi_t_offset = (W_)&stg_ctoi_t38_info; break;
    
    1876
    -                case 39: ctoi_t_offset = (W_)&stg_ctoi_t39_info; break;
    
    1877
    -
    
    1878
    -                case 40: ctoi_t_offset = (W_)&stg_ctoi_t40_info; break;
    
    1879
    -                case 41: ctoi_t_offset = (W_)&stg_ctoi_t41_info; break;
    
    1880
    -                case 42: ctoi_t_offset = (W_)&stg_ctoi_t42_info; break;
    
    1881
    -                case 43: ctoi_t_offset = (W_)&stg_ctoi_t43_info; break;
    
    1882
    -                case 44: ctoi_t_offset = (W_)&stg_ctoi_t44_info; break;
    
    1883
    -                case 45: ctoi_t_offset = (W_)&stg_ctoi_t45_info; break;
    
    1884
    -                case 46: ctoi_t_offset = (W_)&stg_ctoi_t46_info; break;
    
    1885
    -                case 47: ctoi_t_offset = (W_)&stg_ctoi_t47_info; break;
    
    1886
    -                case 48: ctoi_t_offset = (W_)&stg_ctoi_t48_info; break;
    
    1887
    -                case 49: ctoi_t_offset = (W_)&stg_ctoi_t49_info; break;
    
    1888
    -
    
    1889
    -                case 50: ctoi_t_offset = (W_)&stg_ctoi_t50_info; break;
    
    1890
    -                case 51: ctoi_t_offset = (W_)&stg_ctoi_t51_info; break;
    
    1891
    -                case 52: ctoi_t_offset = (W_)&stg_ctoi_t52_info; break;
    
    1892
    -                case 53: ctoi_t_offset = (W_)&stg_ctoi_t53_info; break;
    
    1893
    -                case 54: ctoi_t_offset = (W_)&stg_ctoi_t54_info; break;
    
    1894
    -                case 55: ctoi_t_offset = (W_)&stg_ctoi_t55_info; break;
    
    1895
    -                case 56: ctoi_t_offset = (W_)&stg_ctoi_t56_info; break;
    
    1896
    -                case 57: ctoi_t_offset = (W_)&stg_ctoi_t57_info; break;
    
    1897
    -                case 58: ctoi_t_offset = (W_)&stg_ctoi_t58_info; break;
    
    1898
    -                case 59: ctoi_t_offset = (W_)&stg_ctoi_t59_info; break;
    
    1899
    -
    
    1900
    -                case 60: ctoi_t_offset = (W_)&stg_ctoi_t60_info; break;
    
    1901
    -                case 61: ctoi_t_offset = (W_)&stg_ctoi_t61_info; break;
    
    1902
    -                case 62: ctoi_t_offset = (W_)&stg_ctoi_t62_info; break;
    
    1903
    -
    
    1904
    -                default: barf("unsupported tuple size %d", tuple_stack_words);
    
    1898
    +            if (tuple_stack_words > 62) {
    
    1899
    +                barf("unsupported tuple size %d", tuple_stack_words);
    
    1905 1900
                 }
    
    1906
    -
    
    1901
    +            W_ ctoi_t_offset = (W_) ctoi_tuple_infos[tuple_stack_words];
    
    1907 1902
                 SpW(-4) = ctoi_t_offset;
    
    1908 1903
                 Sp_subW(4);
    
    1909 1904
                 goto nextInsn;
    

  • rts/PrimOps.cmm
    ... ... @@ -1211,16 +1211,27 @@ INFO_TABLE_RET(stg_catch_retry_frame, CATCH_RETRY_FRAME,
    1211 1211
         gcptr trec, outer, arg;
    
    1212 1212
     
    
    1213 1213
         trec = StgTSO_trec(CurrentTSO);
    
    1214
    -    outer  = StgTRecHeader_enclosing_trec(trec);
    
    1215
    -    (r) = ccall stmCommitNestedTransaction(MyCapability() "ptr", trec "ptr");
    
    1216
    -    if (r != 0) {
    
    1217
    -        // Succeeded (either first branch or second branch)
    
    1218
    -        StgTSO_trec(CurrentTSO) = outer;
    
    1219
    -        return (ret);
    
    1220
    -    } else {
    
    1221
    -        // Did not commit: abort and restart.
    
    1222
    -        StgTSO_trec(CurrentTSO) = outer;
    
    1223
    -        jump stg_abort();
    
    1214
    +    if (running_alt_code != 1) {
    
    1215
    +      // When exiting the lhs code of catchRetry# lhs rhs, we need to cleanup
    
    1216
    +      // the nested transaction.
    
    1217
    +      // See Note [catchRetry# implementation]
    
    1218
    +      outer  = StgTRecHeader_enclosing_trec(trec);
    
    1219
    +      (r) = ccall stmCommitNestedTransaction(MyCapability() "ptr", trec "ptr");
    
    1220
    +      if (r != 0) {
    
    1221
    +          // Succeeded in first branch
    
    1222
    +          StgTSO_trec(CurrentTSO) = outer;
    
    1223
    +          return (ret);
    
    1224
    +      } else {
    
    1225
    +          // Did not commit: abort and restart.
    
    1226
    +          StgTSO_trec(CurrentTSO) = outer;
    
    1227
    +          jump stg_abort();
    
    1228
    +      }
    
    1229
    +    }
    
    1230
    +    else {
    
    1231
    +      // nothing to do in the rhs code of catchRetry# lhs rhs, it's already
    
    1232
    +      // using the parent transaction (not a nested one).
    
    1233
    +      // See Note [catchRetry# implementation]
    
    1234
    +      return (ret);
    
    1224 1235
         }
    
    1225 1236
     }
    
    1226 1237
     
    
    ... ... @@ -1453,21 +1464,26 @@ retry_pop_stack:
    1453 1464
         outer  = StgTRecHeader_enclosing_trec(trec);
    
    1454 1465
     
    
    1455 1466
         if (frame_type == CATCH_RETRY_FRAME) {
    
    1456
    -        // The retry reaches a CATCH_RETRY_FRAME before the atomic frame
    
    1457
    -        ASSERT(outer != NO_TREC);
    
    1458
    -        // Abort the transaction attempting the current branch
    
    1459
    -        ccall stmAbortTransaction(MyCapability() "ptr", trec "ptr");
    
    1460
    -        ccall stmFreeAbortedTRec(MyCapability() "ptr", trec "ptr");
    
    1467
    +        // The retry reaches a CATCH_RETRY_FRAME before the ATOMICALLY_FRAME
    
    1468
    +
    
    1461 1469
             if (!StgCatchRetryFrame_running_alt_code(frame) != 0) {
    
    1462
    -            // Retry in the first branch: try the alternative
    
    1463
    -            ("ptr" trec) = ccall stmStartTransaction(MyCapability() "ptr", outer "ptr");
    
    1464
    -            StgTSO_trec(CurrentTSO) = trec;
    
    1470
    +            // Retrying in the lhs of catchRetry# lhs rhs, i.e. in a nested
    
    1471
    +            // transaction. See Note [catchRetry# implementation]
    
    1472
    +
    
    1473
    +            // check that we have a parent transaction
    
    1474
    +            ASSERT(outer != NO_TREC);
    
    1475
    +
    
    1476
    +            // Abort the nested transaction
    
    1477
    +            ccall stmAbortTransaction(MyCapability() "ptr", trec "ptr");
    
    1478
    +            ccall stmFreeAbortedTRec(MyCapability() "ptr", trec "ptr");
    
    1479
    +
    
    1480
    +            // As we are retrying in the lhs code, we must now try the rhs code
    
    1481
    +            StgTSO_trec(CurrentTSO) = outer;
    
    1465 1482
                 StgCatchRetryFrame_running_alt_code(frame) = 1 :: CInt; // true;
    
    1466 1483
                 R1 = StgCatchRetryFrame_alt_code(frame);
    
    1467 1484
                 jump stg_ap_v_fast [R1];
    
    1468 1485
             } else {
    
    1469
    -            // Retry in the alternative code: propagate the retry
    
    1470
    -            StgTSO_trec(CurrentTSO) = outer;
    
    1486
    +            // Retry in the rhs code: propagate the retry
    
    1471 1487
                 Sp = Sp + SIZEOF_StgCatchRetryFrame;
    
    1472 1488
                 goto retry_pop_stack;
    
    1473 1489
             }
    

  • rts/RaiseAsync.c
    ... ... @@ -1043,8 +1043,7 @@ raiseAsync(Capability *cap, StgTSO *tso, StgClosure *exception,
    1043 1043
                 }
    
    1044 1044
     
    
    1045 1045
             case CATCH_STM_FRAME:
    
    1046
    -        case CATCH_RETRY_FRAME:
    
    1047
    -            // CATCH frames within an atomically block: abort the
    
    1046
    +            // CATCH_STM frame within an atomically block: abort the
    
    1048 1047
                 // inner transaction and continue.  Eventually we will
    
    1049 1048
                 // hit the outer transaction that will get frozen (see
    
    1050 1049
                 // above).
    
    ... ... @@ -1056,14 +1055,40 @@ raiseAsync(Capability *cap, StgTSO *tso, StgClosure *exception,
    1056 1055
             {
    
    1057 1056
                 StgTRecHeader *trec = tso -> trec;
    
    1058 1057
                 StgTRecHeader *outer = trec -> enclosing_trec;
    
    1059
    -            debugTraceCap(DEBUG_stm, cap,
    
    1060
    -                          "found atomically block delivering async exception");
    
    1058
    +            debugTraceCap(DEBUG_stm, cap, "raiseAsync: traversing CATCH_STM frame");
    
    1061 1059
                 stmAbortTransaction(cap, trec);
    
    1062 1060
                 stmFreeAbortedTRec(cap, trec);
    
    1063 1061
                 tso -> trec = outer;
    
    1064 1062
                 break;
    
    1065 1063
             };
    
    1066 1064
     
    
    1065
    +        case CATCH_RETRY_FRAME:
    
    1066
    +            // CATCH_RETY frame within an atomically block: if we're executing
    
    1067
    +            // the lhs code, abort the inner transaction and continue; if we're
    
    1068
    +            // executing thr rhs, continue (no nested transaction to abort. See
    
    1069
    +            // Note [catchRetry# implementation]). Eventually we will hit the
    
    1070
    +            // outer transaction that will get frozen (see above).
    
    1071
    +            //
    
    1072
    +            // As for the CATCH_STM_FRAME case above, we do not care
    
    1073
    +            // whether the transaction is valid or not because its
    
    1074
    +            // possible validity cannot have caused the exception
    
    1075
    +            // and will not be visible after the abort.
    
    1076
    +        {
    
    1077
    +            if (!((StgCatchRetryFrame *)frame) -> running_alt_code) {
    
    1078
    +                debugTraceCap(DEBUG_stm, cap, "raiseAsync: traversing CATCH_RETRY frame (lhs)");
    
    1079
    +                StgTRecHeader *trec = tso -> trec;
    
    1080
    +                StgTRecHeader *outer = trec -> enclosing_trec;
    
    1081
    +                stmAbortTransaction(cap, trec);
    
    1082
    +                stmFreeAbortedTRec(cap, trec);
    
    1083
    +                tso -> trec = outer;
    
    1084
    +            }
    
    1085
    +            else
    
    1086
    +            {
    
    1087
    +                debugTraceCap(DEBUG_stm, cap, "raiseAsync: traversing CATCH_RETRY frame (rhs)");
    
    1088
    +            }
    
    1089
    +            break;
    
    1090
    +        };
    
    1091
    +
    
    1067 1092
             default:
    
    1068 1093
                 // see Note [Update async masking state on unwind] in Schedule.c
    
    1069 1094
                 if (*frame == (W_)&stg_unmaskAsyncExceptionszh_ret_info) {
    

  • rts/STM.c
    ... ... @@ -1505,3 +1505,30 @@ void stmWriteTVar(Capability *cap,
    1505 1505
     }
    
    1506 1506
     
    
    1507 1507
     /*......................................................................*/
    
    1508
    +
    
    1509
    +
    
    1510
    +
    
    1511
    +/*
    
    1512
    +
    
    1513
    +Note [catchRetry# implementation]
    
    1514
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    1515
    +catchRetry# creates a nested transaction for its lhs:
    
    1516
    +- if the lhs transaction succeeds:
    
    1517
    +    - the lhs transaction is committed
    
    1518
    +    - its read-variables are merged with those of the parent transaction
    
    1519
    +    - the rhs code is ignored
    
    1520
    +- if the lhs transaction retries:
    
    1521
    +    - the lhs transaction is aborted
    
    1522
    +    - its read-variables are merged with those of the parent transaction
    
    1523
    +    - the rhs code is executed directly in the parent transaction (see #26028).
    
    1524
    +
    
    1525
    +So note that:
    
    1526
    +- lhs code uses a nested transaction
    
    1527
    +- rhs code doesn't use a nested transaction
    
    1528
    +
    
    1529
    +We have to take which case we're in into account (using the running_alt_code
    
    1530
    +field of the catchRetry frame) in catchRetry's entry code, in retry#
    
    1531
    +implementation, and also when an async exception is received (to cleanup the
    
    1532
    +right number of transactions).
    
    1533
    +
    
    1534
    +*/

  • testsuite/tests/cpranal/sigs/T25944.hs
    1
    +{-# LANGUAGE UndecidableInstances, LambdaCase #-}
    
    2
    +
    
    3
    +-- | This file starts with a small reproducer for #25944 that is easy to debug
    
    4
    +-- and then continues with a much larger MWE that is faithful to the original
    
    5
    +-- issue.
    
    6
    +module T25944 (foo, bar, popMinOneT, popMinOne) where
    
    7
    +
    
    8
    +import Data.Functor.Identity ( Identity(..) )
    
    9
    +import Data.Coerce
    
    10
    +
    
    11
    +data ListCons a b = Nil | a :- !b
    
    12
    +newtype Fix f = Fix (f (Fix f)) -- Rec
    
    13
    +
    
    14
    +foo :: Fix (ListCons a) -> Fix (ListCons a) -> Fix (ListCons a)
    
    15
    +foo a b = go a
    
    16
    +  where
    
    17
    +    -- The outer loop arranges it so that the base case `go as` of `go2` is
    
    18
    +    -- bottom on the first iteration of the loop.
    
    19
    +    go (Fix Nil) = Fix Nil
    
    20
    +    go (Fix (a :- as)) = Fix (a :- go2 b)
    
    21
    +      where
    
    22
    +        go2 (Fix Nil) = go as
    
    23
    +        go2 (Fix (b :- bs)) = Fix (b :- go2 bs)
    
    24
    +
    
    25
    +bar :: Int -> (Fix (ListCons Int), Int)
    
    26
    +bar n = (foo (Fix Nil) (Fix Nil), n) -- should still have CPR property
    
    27
    +
    
    28
    +-- Now the actual reproducer from #25944:
    
    29
    +
    
    30
    +newtype ListT m a = ListT { runListT :: m (ListCons a (ListT m a)) }
    
    31
    +
    
    32
    +cons :: Applicative m => a -> ListT m a -> ListT m a
    
    33
    +cons x xs = ListT (pure (x :- xs))
    
    34
    +
    
    35
    +nil :: Applicative m => ListT m a
    
    36
    +nil = ListT (pure Nil)
    
    37
    +
    
    38
    +instance Functor m => Functor (ListT m) where
    
    39
    +  fmap f (ListT m) = ListT (go <$> m)
    
    40
    +     where
    
    41
    +       go Nil = Nil
    
    42
    +       go (a :- m) = f a :- (f <$> m)
    
    43
    +
    
    44
    +foldListT :: ((ListCons a (ListT m a) -> c) -> m (ListCons a (ListT m a)) -> b)
    
    45
    +          -> (a -> b -> c)
    
    46
    +          -> c
    
    47
    +          -> ListT m a -> b
    
    48
    +foldListT r c n = r h . runListT
    
    49
    +  where
    
    50
    +    h Nil = n
    
    51
    +    h (x :- ListT xs) = c x (r h xs)
    
    52
    +{-# INLINE foldListT #-}
    
    53
    +
    
    54
    +mapListT :: forall a m b. Monad m => (a -> ListT m b -> ListT m b) -> ListT m b -> ListT m a -> ListT m b
    
    55
    +mapListT =
    
    56
    +  foldListT
    
    57
    +  ((coerce ::
    
    58
    + ((ListCons a (ListT m a) -> m (ListCons b (ListT m b))) -> m (ListCons a (ListT m a)) -> m (ListCons b (ListT m b))) ->
    
    59
    + ((ListCons a (ListT m a) -> ListT m b) -> m (ListCons a (ListT m a)) -> ListT m b))
    
    60
    +  (=<<))
    
    61
    +{-# INLINE mapListT #-}
    
    62
    +
    
    63
    +instance Monad m => Applicative (ListT m) where
    
    64
    +  pure x = cons x nil
    
    65
    +  {-# INLINE pure #-}
    
    66
    +  liftA2 f xs ys = mapListT (\x zs -> mapListT (cons . f x) zs ys) nil xs
    
    67
    +  {-# INLINE liftA2 #-}
    
    68
    +
    
    69
    +instance Monad m => Monad (ListT m) where
    
    70
    +  xs >>= f = mapListT (flip (mapListT cons) . f) nil xs
    
    71
    +  {-# INLINE (>>=) #-}
    
    72
    +
    
    73
    +infixr 5 :<
    
    74
    +data Node w a b = Leaf a | !w :< b
    
    75
    +  deriving (Functor)
    
    76
    +
    
    77
    +bimapNode f g (Leaf x) = Leaf (f x)
    
    78
    +bimapNode f g (x :< xs) = x :< g xs
    
    79
    +
    
    80
    +newtype HeapT w m a = HeapT { runHeapT :: ListT m (Node w a (HeapT w m a)) }
    
    81
    +
    
    82
    +-- | The 'Heap' type, specialised to the 'Identity' monad.
    
    83
    +type Heap w = HeapT w Identity
    
    84
    +
    
    85
    +instance Functor m => Functor (HeapT w m) where
    
    86
    +  fmap f = HeapT . fmap (bimapNode f (fmap f)) . runHeapT
    
    87
    +
    
    88
    +instance Monad m => Applicative (HeapT w m) where
    
    89
    +  pure = HeapT . pure . Leaf
    
    90
    +  (<*>) = liftA2 id
    
    91
    +
    
    92
    +instance Monad m => Monad (HeapT w m) where
    
    93
    +  HeapT m >>= f = HeapT (m >>= g)
    
    94
    +    where
    
    95
    +      g (Leaf x) = runHeapT (f x)
    
    96
    +      g (w :< xs) = pure (w :< (xs >>= f))
    
    97
    +
    
    98
    +popMinOneT :: forall w m a. (Monoid w, Monad m) => HeapT w m a -> m (Maybe ((a, w), HeapT w m a))
    
    99
    +popMinOneT = go mempty [] . runHeapT
    
    100
    +  where
    
    101
    +    go' :: w -> Maybe (w, HeapT w m a) -> m (Maybe ((a, w), HeapT w m a))
    
    102
    +    go' a Nothing = pure Nothing
    
    103
    +    go' a (Just (w, HeapT xs)) = go (a <> w) [] xs
    
    104
    +
    
    105
    +    go :: w -> [(w, HeapT w m a)] -> ListT m (Node w a (HeapT w m a)) -> m (Maybe ((a, w), HeapT w m a))
    
    106
    +    go w a (ListT xs) = xs >>= \case
    
    107
    +      Nil -> go' w (undefined)
    
    108
    +      Leaf x :- xs -> pure (Just ((x, w), undefined >> HeapT (foldl (\ys (yw,y) -> ListT (pure ((yw :< y) :- ys))) xs a)))
    
    109
    +      (u :< x) :- xs -> go w ((u,x) : a) xs
    
    110
    +{-# INLINE popMinOneT #-}
    
    111
    +
    
    112
    +popMinOne :: Monoid w => Heap w a -> Maybe ((a, w), Heap w a)
    
    113
    +popMinOne = runIdentity . popMinOneT
    
    114
    +{-# INLINE popMinOne #-}

  • testsuite/tests/cpranal/sigs/T25944.stderr
    1
    +
    
    2
    +==================== Cpr signatures ====================
    
    3
    +T25944.$fApplicativeHeapT:
    
    4
    +T25944.$fApplicativeListT:
    
    5
    +T25944.$fFunctorHeapT:
    
    6
    +T25944.$fFunctorListT:
    
    7
    +T25944.$fFunctorNode:
    
    8
    +T25944.$fMonadHeapT:
    
    9
    +T25944.$fMonadListT:
    
    10
    +T25944.bar: 1
    
    11
    +T25944.foo:
    
    12
    +T25944.popMinOne: 2(1(1,))
    
    13
    +T25944.popMinOneT:
    
    14
    +T25944.runHeapT:
    
    15
    +T25944.runListT:
    
    16
    +
    
    17
    +

  • testsuite/tests/cpranal/sigs/all.T
    ... ... @@ -12,3 +12,4 @@ test('T16040', normal, compile, [''])
    12 12
     test('T19232', normal, compile, [''])
    
    13 13
     test('T19398', normal, compile, [''])
    
    14 14
     test('T19822', normal, compile, [''])
    
    15
    +test('T25944', normal, compile, [''])

  • testsuite/tests/driver/json.stderr
    1
    -{"version":"1.1","ghcVersion":"ghc-9.13.20250529","span":{"file":"json.hs","start":{"line":9,"column":11},"end":{"line":9,"column":21}},"severity":"Error","code":48010,"message":["Empty list of alternatives in case expression"],"hints":["Perhaps you intended to use the \u2018EmptyCase\u2019 extension"]}
    1
    +{"version":"1.2","ghcVersion":"ghc-9.13.20250627","span":{"file":"json.hs","start":{"line":9,"column":11},"end":{"line":9,"column":21}},"severity":"Error","code":48010,"rendered":"json.hs:9:11: error: [GHC-48010]\n    Empty list of alternatives in case expression\n    Suggested fix:\n      Perhaps you intended to use the \u2018EmptyCase\u2019 extension\n","message":["Empty list of alternatives in case expression"],"hints":["Perhaps you intended to use the \u2018EmptyCase\u2019 extension"]}

  • testsuite/tests/driver/json_warn.stderr
    1
    -{"version":"1.1","ghcVersion":"ghc-9.13.20250529","span":{"file":"json_warn.hs","start":{"line":4,"column":3},"end":{"line":4,"column":4}},"severity":"Warning","code":40910,"message":["Defined but not used: \u2018x\u2019"],"hints":[],"reason":{"flags":["unused-matches"]}}
    
    2
    -{"version":"1.1","ghcVersion":"ghc-9.13.20250529","span":{"file":"json_warn.hs","start":{"line":7,"column":5},"end":{"line":7,"column":9}},"severity":"Warning","code":63394,"message":["In the use of \u2018head\u2019\n(imported from Prelude, but defined in GHC.Internal.List):\n\"This is a partial function, it throws an error on empty lists. Use pattern matching, 'Data.List.uncons' or 'Data.Maybe.listToMaybe' instead. Consider refactoring to use \"Data.List.NonEmpty\".\""],"hints":[],"reason":{"category":"x-partial"}}
    1
    +{"version":"1.2","ghcVersion":"ghc-9.13.20250627","span":{"file":"json_warn.hs","start":{"line":4,"column":3},"end":{"line":4,"column":4}},"severity":"Warning","code":40910,"rendered":"json_warn.hs:4:3: warning: [GHC-40910] [-Wunused-matches (in -Wextra)]\n    Defined but not used: \u2018x\u2019\n","message":["Defined but not used: \u2018x\u2019"],"hints":[],"reason":{"flags":["unused-matches"]}}
    
    2
    +{"version":"1.2","ghcVersion":"ghc-9.13.20250627","span":{"file":"json_warn.hs","start":{"line":7,"column":5},"end":{"line":7,"column":9}},"severity":"Warning","code":63394,"rendered":"json_warn.hs:7:5: warning: [GHC-63394] [-Wx-partial (in -Wextended-warnings)]\n    In the use of \u2018head\u2019\n    (imported from Prelude, but defined in GHC.Internal.List):\n    \"This is a partial function, it throws an error on empty lists. Use pattern matching, 'Data.List.uncons' or 'Data.Maybe.listToMaybe' instead. Consider refactoring to use \"Data.List.NonEmpty\".\"\n","message":["In the use of \u2018head\u2019\n(imported from Prelude, but defined in GHC.Internal.List):\n\"This is a partial function, it throws an error on empty lists. Use pattern matching, 'Data.List.uncons' or 'Data.Maybe.listToMaybe' instead. Consider refactoring to use \"Data.List.NonEmpty\".\""],"hints":[],"reason":{"category":"x-partial"}}

  • testsuite/tests/lib/stm/T26028.hs
    1
    +module Main where
    
    2
    +
    
    3
    +import GHC.Conc
    
    4
    +
    
    5
    +forever :: IO String
    
    6
    +forever = delay 10 >> forever
    
    7
    +
    
    8
    +terminates :: IO String
    
    9
    +terminates = delay 1 >> pure "terminates"
    
    10
    +
    
    11
    +delay s = threadDelay (1000000 * s)
    
    12
    +
    
    13
    +async :: IO a -> IO (STM a)
    
    14
    +async a = do 
    
    15
    +  var <- atomically (newTVar Nothing)
    
    16
    +  forkIO (a >>= atomically . writeTVar var . Just)
    
    17
    +  pure (readTVar var >>= maybe retry pure)
    
    18
    +
    
    19
    +main :: IO ()
    
    20
    +main = do
    
    21
    +  x <- mapM async $ terminates : replicate 50000 forever
    
    22
    +  r <- atomically (foldr1 orElse x)
    
    23
    +  print r

  • testsuite/tests/lib/stm/T26028.stdout
    1
    +"terminates"

  • testsuite/tests/lib/stm/all.T
    1
    +test('T26028', only_ways(['threaded1']), compile_and_run, ['-O2'])