Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
5b5d9d47
by Ben Gamari at 2025-08-25T14:29:35-04:00
-
10f06163
by Cheng Shao at 2025-08-25T14:30:16-04:00
-
6dfa209e
by Cheng Shao at 2025-08-25T19:02:15-04:00
-
04fbc92c
by Ryan Scott at 2025-08-25T19:02:15-04:00
13 changed files:
- compiler/GHC/Cmm/Dataflow/Label.hs
- compiler/GHC/Rename/HsType.hs
- docs/users_guide/9.16.1-notes.rst
- rts/PrimOps.cmm
- rts/RaiseAsync.c
- rts/STM.c
- − testsuite/tests/lib/stm/T26028.hs
- − testsuite/tests/lib/stm/T26028.stdout
- − testsuite/tests/lib/stm/all.T
- + testsuite/tests/typecheck/should_fail/T26318.hs
- + testsuite/tests/typecheck/should_fail/T26318.stderr
- testsuite/tests/typecheck/should_fail/all.T
- utils/jsffi/dyld.mjs
Changes:
| ... | ... | @@ -83,6 +83,7 @@ import GHC.Data.Word64Map.Strict (Word64Map) |
| 83 | 83 | import qualified GHC.Data.Word64Map.Strict as M
|
| 84 | 84 | import GHC.Data.TrieMap
|
| 85 | 85 | |
| 86 | +import Data.Coerce
|
|
| 86 | 87 | import Data.Word (Word64)
|
| 87 | 88 | |
| 88 | 89 | |
| ... | ... | @@ -164,7 +165,7 @@ setFoldr k z (LS s) = S.foldr (\v a -> k (mkHooplLabel v) a) z s |
| 164 | 165 | |
| 165 | 166 | {-# INLINE setElems #-}
|
| 166 | 167 | setElems :: LabelSet -> [Label]
|
| 167 | -setElems (LS s) = map mkHooplLabel (S.elems s)
|
|
| 168 | +setElems (LS s) = coerce $ S.elems s
|
|
| 168 | 169 | |
| 169 | 170 | {-# INLINE setFromList #-}
|
| 170 | 171 | setFromList :: [Label] -> LabelSet
|
| ... | ... | @@ -272,7 +273,7 @@ mapKeys (LM m) = map (mkHooplLabel . fst) (M.toList m) |
| 272 | 273 | |
| 273 | 274 | {-# INLINE mapToList #-}
|
| 274 | 275 | mapToList :: LabelMap b -> [(Label, b)]
|
| 275 | -mapToList (LM m) = [(mkHooplLabel k, v) | (k, v) <- M.toList m]
|
|
| 276 | +mapToList (LM m) = coerce $ M.toList m
|
|
| 276 | 277 | |
| 277 | 278 | {-# INLINE mapFromList #-}
|
| 278 | 279 | mapFromList :: [(Label, v)] -> LabelMap v
|
| ... | ... | @@ -547,15 +547,7 @@ rnHsTyKi env tv@(HsTyVar _ ip (L loc rdr_name)) |
| 547 | 547 | ; this_mod <- getModule
|
| 548 | 548 | ; when (nameIsLocalOrFrom this_mod name) $
|
| 549 | 549 | checkThLocalTyName name
|
| 550 | - ; when (isDataConName name && not (isKindName name)) $
|
|
| 551 | - -- Any use of a promoted data constructor name (that is not
|
|
| 552 | - -- specifically exempted by isKindName) is illegal without the use
|
|
| 553 | - -- of DataKinds. See Note [Checking for DataKinds] in
|
|
| 554 | - -- GHC.Tc.Validity.
|
|
| 555 | - checkDataKinds env tv
|
|
| 556 | - ; when (isDataConName name && not (isPromoted ip)) $
|
|
| 557 | - -- NB: a prefix symbolic operator such as (:) is represented as HsTyVar.
|
|
| 558 | - addDiagnostic (TcRnUntickedPromotedThing $ UntickedConstructor Prefix name)
|
|
| 550 | + ; checkPromotedDataConName env tv Prefix ip name
|
|
| 559 | 551 | ; return (HsTyVar noAnn ip (L loc $ WithUserRdr rdr_name name), unitFV name) }
|
| 560 | 552 | |
| 561 | 553 | rnHsTyKi env ty@(HsOpTy _ prom ty1 l_op ty2)
|
| ... | ... | @@ -567,8 +559,7 @@ rnHsTyKi env ty@(HsOpTy _ prom ty1 l_op ty2) |
| 567 | 559 | ; (ty1', fvs2) <- rnLHsTyKi env ty1
|
| 568 | 560 | ; (ty2', fvs3) <- rnLHsTyKi env ty2
|
| 569 | 561 | ; res_ty <- mkHsOpTyRn prom (fmap (WithUserRdr op_rdr) l_op') fix ty1' ty2'
|
| 570 | - ; when (isDataConName op_name && not (isPromoted prom)) $
|
|
| 571 | - addDiagnostic (TcRnUntickedPromotedThing $ UntickedConstructor Infix op_name)
|
|
| 562 | + ; checkPromotedDataConName env ty Infix prom op_name
|
|
| 572 | 563 | ; return (res_ty, plusFVs [fvs1, fvs2, fvs3]) }
|
| 573 | 564 | |
| 574 | 565 | rnHsTyKi env (HsParTy _ ty)
|
| ... | ... | @@ -1670,6 +1661,30 @@ checkDataKinds env thing |
| 1670 | 1661 | type_or_kind | isRnKindLevel env = KindLevel
|
| 1671 | 1662 | | otherwise = TypeLevel
|
| 1672 | 1663 | |
| 1664 | +-- | If a 'Name' is that of a promoted data constructor, perform various
|
|
| 1665 | +-- validity checks on it.
|
|
| 1666 | +checkPromotedDataConName ::
|
|
| 1667 | + RnTyKiEnv ->
|
|
| 1668 | + -- | The type that the 'Name' belongs to. This will always be an 'HsTyVar'
|
|
| 1669 | + -- (for 'Prefix' names) or an 'HsOpTy' (for 'Infix' names).
|
|
| 1670 | + HsType GhcPs ->
|
|
| 1671 | + -- | Whether the type is written 'Prefix' or 'Infix'.
|
|
| 1672 | + LexicalFixity ->
|
|
| 1673 | + -- | Whether the name was written with an explicit promotion tick or not.
|
|
| 1674 | + PromotionFlag ->
|
|
| 1675 | + -- | The name to check.
|
|
| 1676 | + Name ->
|
|
| 1677 | + TcM ()
|
|
| 1678 | +checkPromotedDataConName env ty fixity ip name
|
|
| 1679 | + = do when (isDataConName name && not (isKindName name)) $
|
|
| 1680 | + -- Any use of a promoted data constructor name (that is not
|
|
| 1681 | + -- specifically exempted by isKindName) is illegal without the use
|
|
| 1682 | + -- of DataKinds. See Note [Checking for DataKinds] in
|
|
| 1683 | + -- GHC.Tc.Validity.
|
|
| 1684 | + checkDataKinds env ty
|
|
| 1685 | + when (isDataConName name && not (isPromoted ip)) $
|
|
| 1686 | + addDiagnostic (TcRnUntickedPromotedThing $ UntickedConstructor fixity name)
|
|
| 1687 | + |
|
| 1673 | 1688 | warnUnusedForAll :: OutputableBndrFlag flag 'Renamed
|
| 1674 | 1689 | => HsDocContext -> LHsTyVarBndr flag GhcRn -> FreeVars -> TcM ()
|
| 1675 | 1690 | warnUnusedForAll doc (L loc tvb) used_names =
|
| ... | ... | @@ -11,6 +11,11 @@ for specific guidance on migrating programs to this release. |
| 11 | 11 | Language
|
| 12 | 12 | ~~~~~~~~
|
| 13 | 13 | |
| 14 | +- Fix a bug introduced in GHC 9.10 where GHC would erroneously accept infix uses
|
|
| 15 | + of promoted data constructors without enabling :extension:`DataKinds`. As a
|
|
| 16 | + result, you may need to enable :extension:`DataKinds` in code that did not
|
|
| 17 | + previously require it.
|
|
| 18 | + |
|
| 14 | 19 | Compiler
|
| 15 | 20 | ~~~~~~~~
|
| 16 | 21 |
| ... | ... | @@ -1211,27 +1211,16 @@ 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 | - 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);
|
|
| 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();
|
|
| 1235 | 1224 | }
|
| 1236 | 1225 | }
|
| 1237 | 1226 | |
| ... | ... | @@ -1464,26 +1453,21 @@ retry_pop_stack: |
| 1464 | 1453 | outer = StgTRecHeader_enclosing_trec(trec);
|
| 1465 | 1454 | |
| 1466 | 1455 | if (frame_type == CATCH_RETRY_FRAME) {
|
| 1467 | - // The retry reaches a CATCH_RETRY_FRAME before the ATOMICALLY_FRAME
|
|
| 1468 | - |
|
| 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");
|
|
| 1469 | 1461 | if (!StgCatchRetryFrame_running_alt_code(frame) != 0) {
|
| 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;
|
|
| 1462 | + // Retry in the first branch: try the alternative
|
|
| 1463 | + ("ptr" trec) = ccall stmStartTransaction(MyCapability() "ptr", outer "ptr");
|
|
| 1464 | + StgTSO_trec(CurrentTSO) = trec;
|
|
| 1482 | 1465 | StgCatchRetryFrame_running_alt_code(frame) = 1 :: CInt; // true;
|
| 1483 | 1466 | R1 = StgCatchRetryFrame_alt_code(frame);
|
| 1484 | 1467 | jump stg_ap_v_fast [R1];
|
| 1485 | 1468 | } else {
|
| 1486 | - // Retry in the rhs code: propagate the retry
|
|
| 1469 | + // Retry in the alternative code: propagate the retry
|
|
| 1470 | + StgTSO_trec(CurrentTSO) = outer;
|
|
| 1487 | 1471 | Sp = Sp + SIZEOF_StgCatchRetryFrame;
|
| 1488 | 1472 | goto retry_pop_stack;
|
| 1489 | 1473 | }
|
| ... | ... | @@ -1043,7 +1043,8 @@ raiseAsync(Capability *cap, StgTSO *tso, StgClosure *exception, |
| 1043 | 1043 | }
|
| 1044 | 1044 | |
| 1045 | 1045 | case CATCH_STM_FRAME:
|
| 1046 | - // CATCH_STM frame within an atomically block: abort the
|
|
| 1046 | + case CATCH_RETRY_FRAME:
|
|
| 1047 | + // CATCH frames within an atomically block: abort the
|
|
| 1047 | 1048 | // inner transaction and continue. Eventually we will
|
| 1048 | 1049 | // hit the outer transaction that will get frozen (see
|
| 1049 | 1050 | // above).
|
| ... | ... | @@ -1055,40 +1056,14 @@ raiseAsync(Capability *cap, StgTSO *tso, StgClosure *exception, |
| 1055 | 1056 | {
|
| 1056 | 1057 | StgTRecHeader *trec = tso -> trec;
|
| 1057 | 1058 | StgTRecHeader *outer = trec -> enclosing_trec;
|
| 1058 | - debugTraceCap(DEBUG_stm, cap, "raiseAsync: traversing CATCH_STM frame");
|
|
| 1059 | + debugTraceCap(DEBUG_stm, cap,
|
|
| 1060 | + "found atomically block delivering async exception");
|
|
| 1059 | 1061 | stmAbortTransaction(cap, trec);
|
| 1060 | 1062 | stmFreeAbortedTRec(cap, trec);
|
| 1061 | 1063 | tso -> trec = outer;
|
| 1062 | 1064 | break;
|
| 1063 | 1065 | };
|
| 1064 | 1066 | |
| 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 | - |
|
| 1092 | 1067 | default:
|
| 1093 | 1068 | // see Note [Update async masking state on unwind] in Schedule.c
|
| 1094 | 1069 | if (*frame == (W_)&stg_unmaskAsyncExceptionszh_ret_info) {
|
| ... | ... | @@ -1505,30 +1505,3 @@ 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 | -*/ |
| 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 |
| 1 | -"terminates" |
| 1 | -test('T26028', only_ways(['threaded1']), compile_and_run, ['-O2']) |
| 1 | +{-# LANGUAGE GHC2021 #-}
|
|
| 2 | +{-# LANGUAGE NoDataKinds #-}
|
|
| 3 | +module T26318 where
|
|
| 4 | + |
|
| 5 | +class C1 l
|
|
| 6 | +instance C1 (x : xs)
|
|
| 7 | + |
|
| 8 | +class C2 l
|
|
| 9 | +instance C2 (x ': xs)
|
|
| 10 | + |
|
| 11 | +class C3 l
|
|
| 12 | +instance C3 ((:) x xs)
|
|
| 13 | + |
|
| 14 | +class C4 l
|
|
| 15 | +instance C4 ('(:) x xs) |
| 1 | +T26318.hs:6:16: error: [GHC-68567]
|
|
| 2 | + Illegal type: ‘x : xs’
|
|
| 3 | + Suggested fix:
|
|
| 4 | + Perhaps you intended to use the ‘DataKinds’ extension (implied by ‘UnliftedDatatypes’)
|
|
| 5 | + |
|
| 6 | +T26318.hs:9:16: error: [GHC-68567]
|
|
| 7 | + Illegal type: ‘x ': xs’
|
|
| 8 | + Suggested fix:
|
|
| 9 | + Perhaps you intended to use the ‘DataKinds’ extension (implied by ‘UnliftedDatatypes’)
|
|
| 10 | + |
|
| 11 | +T26318.hs:12:14: error: [GHC-68567]
|
|
| 12 | + Illegal type: ‘(:)’
|
|
| 13 | + Suggested fix:
|
|
| 14 | + Perhaps you intended to use the ‘DataKinds’ extension (implied by ‘UnliftedDatatypes’)
|
|
| 15 | + |
|
| 16 | +T26318.hs:15:14: error: [GHC-68567]
|
|
| 17 | + Illegal type: ‘'(:)’
|
|
| 18 | + Suggested fix:
|
|
| 19 | + Perhaps you intended to use the ‘DataKinds’ extension (implied by ‘UnliftedDatatypes’)
|
|
| 20 | + |
| ... | ... | @@ -741,3 +741,4 @@ test('T25325', normal, compile_fail, ['']) |
| 741 | 741 | test('T25004', normal, compile_fail, [''])
|
| 742 | 742 | test('T25004k', normal, compile_fail, [''])
|
| 743 | 743 | test('T26004', normal, compile_fail, [''])
|
| 744 | +test('T26318', normal, compile_fail, ['']) |
| ... | ... | @@ -1105,6 +1105,20 @@ class DyLD { |
| 1105 | 1105 | if (/libHSghc-internal-\d+(\.\d+)*/i.test(soname)) {
|
| 1106 | 1106 | this.rts_init();
|
| 1107 | 1107 | delete this.rts_init;
|
| 1108 | + |
|
| 1109 | + // At this point the RTS symbols in linear memory are fixed
|
|
| 1110 | + // and constructors are run, especially the one in JSFFI.c
|
|
| 1111 | + // that does GHC RTS initialization for any code that links
|
|
| 1112 | + // JSFFI.o. Luckily no Haskell computation or gc has taken
|
|
| 1113 | + // place yet, so we must set keepCAFs=1 right now! Otherwise,
|
|
| 1114 | + // any BCO created by later TH splice or ghci expression may
|
|
| 1115 | + // refer to any CAF that's not reachable from GC roots (here
|
|
| 1116 | + // our only entry point is defaultServer) and the CAF could
|
|
| 1117 | + // have been GC'ed! (#26106)
|
|
| 1118 | + //
|
|
| 1119 | + // We call it here instead of in RTS C code, since we only
|
|
| 1120 | + // want keepCAFs=1 for ghci, not user code.
|
|
| 1121 | + this.exportFuncs.setKeepCAFs();
|
|
| 1108 | 1122 | }
|
| 1109 | 1123 | init();
|
| 1110 | 1124 | }
|