Ben Gamari pushed to branch wip/clc-430 at Glasgow Haskell Compiler / GHC
Commits:
-
04e8e9c4
by Ben Gamari at 2026-06-29T11:25:15-04:00
9 changed files:
- + changelog.d/T27453
- libraries/base/changelog.md
- libraries/base/src/Control/Exception.hs
- + libraries/base/tests/T27453.hs
- libraries/base/tests/all.T
- libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs
- libraries/ghc-internal/src/GHC/Internal/Control/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs-boot
- libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
Changes:
| 1 | +section: base
|
|
| 2 | +synopsis: Introduce `ThrownFrom` `ExceptionAnnotation`
|
|
| 3 | +issues: #27453
|
|
| 4 | +mrs: !16273
|
|
| 5 | +description: {
|
|
| 6 | + Introduce new ``ExceptionAnnotation``, ``Control.Exception.ThrownFrom``, to record a backtrace of the site from which an asynchronous exception was thrown. ``Control.Exception.throwTo`` now attaches such an annotation when throwing exceptions for which ``backtraceDesired=True``. :ref:`CLC Proposal #430 <https://github.com/haskell/core-libraries-committee/issues/430>`).
|
|
| 7 | +} |
| ... | ... | @@ -35,6 +35,7 @@ |
| 35 | 35 | * Change `hIsReadable` and `hIsWritable` such that they always throw a respective exception when encountering a closed or semi-closed handle, not just in the case of a file handle. ([CLC proposal #371](github.com/haskell/core-libraries-committee/issues/371))
|
| 36 | 36 | * Annotate `onException` continuation with `WhileHandling`. ([CLC Proposal #397](https://github.com/haskell/core-libraries-committee/issues/397))
|
| 37 | 37 | * Improve error message for `Data.Char.chr`. ([CLC Proposal #384](https://github.com/haskell/core-libraries-committee/issues/384))
|
| 38 | + * Introduce new `ExceptionAnnotation`, `Control.Exception.ThrownFrom`, to record a backtrace of the site from which an asynchronous exception was thrown. `Control.Exception.throwTo` now attaches such an annotation when throwing exceptions for which `backtraceDesired=True`. ([CLC Proposal #430](https://github.com/haskell/core-libraries-committee/issues/430)).
|
|
| 38 | 39 | |
| 39 | 40 | ## 4.22.0.0 *TBA*
|
| 40 | 41 | * Shipped with GHC 9.14.1
|
| ... | ... | @@ -40,6 +40,7 @@ module Control.Exception |
| 40 | 40 | NoBacktrace(..),
|
| 41 | 41 | ExceptionWithContext(..),
|
| 42 | 42 | WhileHandling(..),
|
| 43 | + ThrownFrom(..),
|
|
| 43 | 44 | |
| 44 | 45 | -- * Concrete exception types
|
| 45 | 46 | IOException,
|
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Control.Exception
|
|
| 4 | + |
|
| 5 | +data WorldExploded = WorldExploded
|
|
| 6 | + deriving (Show)
|
|
| 7 | + |
|
| 8 | +instance Exception WorldExploded
|
|
| 9 | + |
|
| 10 | +main :: IO ()
|
|
| 11 | +main = do
|
|
| 12 | + tid <- fork $ catch handler $ threadDelay 1000*1000*1000
|
|
| 13 | + throwTo WorldExploded tid
|
|
| 14 | + |
|
| 15 | +handler :: ExceptionWithContext WorldExploded -> IO ()
|
|
| 16 | +handler (ExceptionWithContext ann WorldExploded) = do
|
|
| 17 | + putStrLn "killed from"
|
|
| 18 | + print ann
|
|
| 19 | + |
| ... | ... | @@ -322,3 +322,4 @@ test('T23697', |
| 322 | 322 | test('stimesEndo', normal, compile_and_run, [''])
|
| 323 | 323 | test('T24807', exit_code(1), compile_and_run, [''])
|
| 324 | 324 | test('T25066', [only_ways(['optasm']), grep_errmsg('T25066.g')], compile, ['-ddump-dmd-signatures'])
|
| 325 | +test('T27453', normal, compile_and_run, ['']) |
| ... | ... | @@ -105,6 +105,8 @@ import GHC.Internal.Int |
| 105 | 105 | import GHC.Internal.IO
|
| 106 | 106 | import GHC.Internal.IO.Exception
|
| 107 | 107 | import GHC.Internal.Exception
|
| 108 | +import GHC.Internal.Exception.Type (ThrownFrom(..))
|
|
| 109 | +import {-# SOURCE #-} GHC.Internal.Exception.Backtrace (collectBacktraces)
|
|
| 108 | 110 | import GHC.Internal.IORef
|
| 109 | 111 | import GHC.Internal.Magic ( lazy )
|
| 110 | 112 | import GHC.Internal.Maybe ( Maybe(..) )
|
| ... | ... | @@ -478,8 +480,16 @@ target, the exception will be thrown even if the thread is currently |
| 478 | 480 | inside 'mask' or 'uninterruptibleMask'.
|
| 479 | 481 | -}
|
| 480 | 482 | throwTo :: Exception e => ThreadId -> e -> IO ()
|
| 481 | -throwTo (ThreadId tid) ex = IO $ \ s ->
|
|
| 482 | - case (killThread# tid (toException ex) s) of s1 -> (# s1, () #)
|
|
| 483 | +throwTo tid ex
|
|
| 484 | + | backtraceDesired ex = do
|
|
| 485 | + bts <- collectBacktraces
|
|
| 486 | + throwTo_ tid $ addExceptionContext (ThrownFrom bts) (toException ex)
|
|
| 487 | + | otherwise =
|
|
| 488 | + throwTo_ tid $ toException ex
|
|
| 489 | + |
|
| 490 | +throwTo_ :: Exception e => ThreadId -> e -> IO ()
|
|
| 491 | +throwTo_ (ThreadId tid) ex = IO $ \ s ->
|
|
| 492 | + case (killThread# tid ex s) of s1 -> (# s1, () #)
|
|
| 483 | 493 | |
| 484 | 494 | -- | Returns the 'ThreadId' of the calling thread (GHC only).
|
| 485 | 495 | myThreadId :: IO ThreadId
|
| ... | ... | @@ -115,11 +115,12 @@ module GHC.Internal.Control.Exception ( |
| 115 | 115 | ExceptionContext(..),
|
| 116 | 116 | annotateIO,
|
| 117 | 117 | WhileHandling(..),
|
| 118 | + ThrownFrom(..),
|
|
| 118 | 119 | |
| 119 | 120 | ) where
|
| 120 | 121 | |
| 121 | 122 | import GHC.Internal.Control.Exception.Base
|
| 122 | -import GHC.Internal.Exception.Type (ExceptionWithContext(..), whileHandling)
|
|
| 123 | +import GHC.Internal.Exception.Type (ExceptionWithContext(..), whileHandling, ThrownFrom(..))
|
|
| 123 | 124 | |
| 124 | 125 | import GHC.Internal.Base (Functor(..), foldr, return, ($), (.))
|
| 125 | 126 | import GHC.Internal.IO (IO, interruptible)
|
| 1 | 1 | {-# LANGUAGE NoImplicitPrelude #-}
|
| 2 | +{-# LANGUAGE ImplicitParams #-}
|
|
| 2 | 3 | {-# LANGUAGE RoleAnnotations #-}
|
| 3 | 4 | |
| 4 | 5 | module GHC.Internal.Exception.Backtrace where
|
| 5 | 6 | |
| 6 | 7 | import GHC.Internal.Stack.Types (HasCallStack)
|
| 7 | 8 | import GHC.Internal.Types (IO)
|
| 8 | -import GHC.Internal.Exception.Context (SomeExceptionAnnotation)
|
|
| 9 | +import GHC.Internal.Exception.Context (ExceptionAnnotation, SomeExceptionAnnotation)
|
|
| 9 | 10 | |
| 10 | --- For GHC.Exception
|
|
| 11 | +data Backtraces
|
|
| 12 | + |
|
| 13 | +instance ExceptionAnnotation Backtraces
|
|
| 14 | + |
|
| 15 | +-- For GHC.Internal.Conc.Sync
|
|
| 16 | +collectBacktraces :: HasCallStack => IO Backtraces
|
|
| 17 | + |
|
| 18 | +-- For GHC.Internal.Exception
|
|
| 11 | 19 | collectExceptionAnnotation :: HasCallStack => IO SomeExceptionAnnotation |
| ... | ... | @@ -43,6 +43,8 @@ module GHC.Internal.Exception.Type |
| 43 | 43 | -- * Exception propagation
|
| 44 | 44 | , WhileHandling(..)
|
| 45 | 45 | , whileHandling
|
| 46 | + -- * Asynchronous exception provenance
|
|
| 47 | + , ThrownFrom(..)
|
|
| 46 | 48 | -- * Arithmetic exceptions
|
| 47 | 49 | , ArithException(..)
|
| 48 | 50 | , divZeroException, overflowException, ratioZeroDenomException
|
| ... | ... | @@ -59,6 +61,7 @@ import GHC.Internal.Base (String, Void, fmap, return, ($), (.), (++)) |
| 59 | 61 | import GHC.Internal.Show
|
| 60 | 62 | import GHC.Internal.Types (Bool(..))
|
| 61 | 63 | import GHC.Internal.Exception.Context
|
| 64 | +import {-# SOURCE #-} GHC.Internal.Exception.Backtrace
|
|
| 62 | 65 | |
| 63 | 66 | {- |
|
| 64 | 67 | A constraint used to propagate 'ExceptionContext's.
|
| ... | ... | @@ -298,6 +301,19 @@ instance Exception a => Exception (ExceptionWithContext a) where |
| 298 | 301 | backtraceDesired (ExceptionWithContext _ e) = backtraceDesired e
|
| 299 | 302 | displayException = displayException . toException
|
| 300 | 303 | |
| 304 | +-- | 'ThrownBy' records the site from which an asynchronous exception was thrown (e.g. the call-site of @throwTo@).
|
|
| 305 | +--
|
|
| 306 | +-- @since 4.23.0.0
|
|
| 307 | +newtype ThrownFrom = ThrownFrom Backtraces
|
|
| 308 | + |
|
| 309 | +instance ExceptionAnnotation ThrownFrom where
|
|
| 310 | + displayExceptionAnnotation (ThrownFrom e) =
|
|
| 311 | + "Thrown asynchronously by " ++ case lines $ displayExceptionAnnotation e of
|
|
| 312 | + [] -> ""
|
|
| 313 | + (l1:ls) ->
|
|
| 314 | + unlines $ l1:[if null l then " |" else " | " ++ l | l <- ls]
|
|
| 315 | + |
|
| 316 | + |
|
| 301 | 317 | -- |Arithmetic exceptions.
|
| 302 | 318 | data ArithException
|
| 303 | 319 | = Overflow
|