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

Commits:

15 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/Monad.hs
    ... ... @@ -33,7 +33,7 @@ module GHC.Core.Opt.Monad (
    33 33
         getAnnotations, getFirstAnnotations,
    
    34 34
     
    
    35 35
         -- ** Screen output
    
    36
    -    putMsg, putMsgS, errorMsg, msg,
    
    36
    +    putMsg, putMsgS, errorMsg, msg, diagnostic,
    
    37 37
         fatalErrorMsg, fatalErrorMsgS,
    
    38 38
         debugTraceMsg, debugTraceMsgS,
    
    39 39
       ) where
    
    ... ... @@ -41,6 +41,8 @@ module GHC.Core.Opt.Monad (
    41 41
     import GHC.Prelude hiding ( read )
    
    42 42
     
    
    43 43
     import GHC.Driver.DynFlags
    
    44
    +import GHC.Driver.Errors ( reportDiagnostic, reportError )
    
    45
    +import GHC.Driver.Config.Diagnostic ( initDiagOpts )
    
    44 46
     import GHC.Driver.Env
    
    45 47
     
    
    46 48
     import GHC.Core.Rules     ( RuleBase, RuleEnv, mkRuleEnv )
    
    ... ... @@ -52,7 +54,6 @@ import GHC.Types.Name.Env
    52 54
     import GHC.Types.SrcLoc
    
    53 55
     import GHC.Types.Error
    
    54 56
     
    
    55
    -import GHC.Utils.Error ( errorDiagnostic )
    
    56 57
     import GHC.Utils.Outputable as Outputable
    
    57 58
     import GHC.Utils.Logger
    
    58 59
     import GHC.Utils.Monad
    
    ... ... @@ -383,9 +384,22 @@ putMsgS = putMsg . text
    383 384
     putMsg :: SDoc -> CoreM ()
    
    384 385
     putMsg = msg MCInfo
    
    385 386
     
    
    387
    +diagnostic :: DiagnosticReason -> SDoc -> CoreM ()
    
    388
    +diagnostic reason doc = do
    
    389
    +    logger <- getLogger
    
    390
    +    loc <- getSrcSpanM
    
    391
    +    name_ppr_ctx <- getNamePprCtx
    
    392
    +    diag_opts <- initDiagOpts <$> getDynFlags
    
    393
    +    liftIO $ reportDiagnostic logger name_ppr_ctx diag_opts loc reason doc
    
    394
    +
    
    386 395
     -- | Output an error to the screen. Does not cause the compiler to die.
    
    387 396
     errorMsg :: SDoc -> CoreM ()
    
    388
    -errorMsg doc = msg errorDiagnostic doc
    
    397
    +errorMsg doc = do
    
    398
    +    logger <- getLogger
    
    399
    +    loc <- getSrcSpanM
    
    400
    +    name_ppr_ctx <- getNamePprCtx
    
    401
    +    diag_opts <- initDiagOpts <$> getDynFlags
    
    402
    +    liftIO $ reportError logger name_ppr_ctx diag_opts loc doc
    
    389 403
     
    
    390 404
     -- | Output a fatal error to the screen. Does not cause the compiler to die.
    
    391 405
     fatalErrorMsgS :: String -> CoreM ()
    

  • compiler/GHC/Core/Opt/SpecConstr.hs
    ... ... @@ -45,7 +45,7 @@ import GHC.Core.Make ( mkImpossibleExpr )
    45 45
     import GHC.Unit.Module
    
    46 46
     import GHC.Unit.Module.ModGuts
    
    47 47
     
    
    48
    -import GHC.Types.Error (MessageClass(..), Severity(..), DiagnosticReason(WarningWithoutFlag), ResolvedDiagnosticReason (..))
    
    48
    +import GHC.Types.Error (DiagnosticReason(..))
    
    49 49
     import GHC.Types.Literal ( litIsLifted )
    
    50 50
     import GHC.Types.Id
    
    51 51
     import GHC.Types.Id.Info ( IdDetails(..) )
    
    ... ... @@ -783,12 +783,11 @@ specConstrProgram guts
    783 783
            ; let (_usg, binds', warnings) = initUs_ us $
    
    784 784
                                   scTopBinds env0 (mg_binds guts)
    
    785 785
     
    
    786
    -       ; when (not (null warnings)) $ msg specConstr_warn_class (warn_msg warnings)
    
    786
    +       ; when (not (null warnings)) $ diagnostic WarningWithoutFlag (warn_msg warnings)
    
    787 787
     
    
    788 788
            ; return (guts { mg_binds = binds' }) }
    
    789 789
     
    
    790 790
       where
    
    791
    -    specConstr_warn_class = MCDiagnostic SevWarning (ResolvedDiagnosticReason WarningWithoutFlag) Nothing
    
    792 791
         warn_msg :: SpecFailWarnings -> SDoc
    
    793 792
         warn_msg warnings = text "SpecConstr encountered one or more function(s) with a SPEC argument that resulted in too many arguments," $$
    
    794 793
                             text "which resulted in no specialization being generated for these functions:" $$
    

  • compiler/GHC/Core/Opt/Specialise.hs
    ... ... @@ -12,7 +12,6 @@ import GHC.Prelude
    12 12
     
    
    13 13
     import GHC.Driver.DynFlags
    
    14 14
     import GHC.Driver.Config
    
    15
    -import GHC.Driver.Config.Diagnostic
    
    16 15
     import GHC.Driver.Config.Core.Rules ( initRuleOpts )
    
    17 16
     
    
    18 17
     import GHC.Core.Type  hiding( substTy, substCo, extendTvSubst, zapSubst )
    
    ... ... @@ -55,7 +54,6 @@ import GHC.Types.Id
    55 54
     import GHC.Types.Id.Info
    
    56 55
     import GHC.Types.Error
    
    57 56
     
    
    58
    -import GHC.Utils.Error ( mkMCDiagnostic )
    
    59 57
     import GHC.Utils.Monad    ( foldlM )
    
    60 58
     import GHC.Utils.Misc
    
    61 59
     import GHC.Utils.Outputable
    
    ... ... @@ -938,10 +936,12 @@ tryWarnMissingSpecs dflags callers fn calls_for_fn
    938 936
       | wopt Opt_WarnAllMissedSpecs dflags    = doWarn $ WarningWithFlag Opt_WarnAllMissedSpecs
    
    939 937
       | otherwise                             = return ()
    
    940 938
       where
    
    939
    +    allCallersInlined :: Bool
    
    941 940
         allCallersInlined = all (isAnyInlinePragma . idInlinePragma) callers
    
    942
    -    diag_opts = initDiagOpts dflags
    
    941
    +
    
    942
    +    doWarn :: DiagnosticReason -> CoreM ()
    
    943 943
         doWarn reason =
    
    944
    -      msg (mkMCDiagnostic diag_opts reason Nothing)
    
    944
    +      diagnostic reason
    
    945 945
             (vcat [ hang (text ("Could not specialise imported function") <+> quotes (ppr fn))
    
    946 946
                     2 (vcat [ text "when specialising" <+> quotes (ppr caller)
    
    947 947
                             | caller <- callers])
    

  • 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)
    

  • libraries/base/src/Data/List/NonEmpty.hs
    ... ... @@ -449,6 +449,8 @@ filter p = List.filter p . toList
    449 449
     -- something of type @'Maybe' b@. If this is 'Nothing', no element
    
    450 450
     -- is added on to the result list. If it is @'Just' b@, then @b@ is
    
    451 451
     -- included in the result list.
    
    452
    +--
    
    453
    +-- @since 4.23.0.0
    
    452 454
     mapMaybe :: (a -> Maybe b) -> NonEmpty a -> [b]
    
    453 455
     mapMaybe f (x :| xs) = maybe id (:) (f x) $ List.mapMaybe f xs
    
    454 456
     
    

  • 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/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'])