Ben Gamari pushed to branch wip/clc-430 at Glasgow Haskell Compiler / GHC

Commits:

8 changed files:

Changes:

  • changelog.d/clc-430
    1
    +section: base
    
    2
    +synopsis: Introduce `ThrownFrom` `ExceptionAnnotation`
    
    3
    +issues: #27453
    
    4
    +mrs: !16187
    
    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
    +}

  • libraries/base/changelog.md
    ... ... @@ -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
    

  • libraries/base/src/Control/Exception.hs
    ... ... @@ -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,
    

  • libraries/base/tests/T27453.hs
    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
    +

  • libraries/base/tests/all.T
    ... ... @@ -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, [''])

  • libraries/ghc-internal/src/GHC/Internal/Conc/Sync.hs
    ... ... @@ -478,8 +478,16 @@ target, the exception will be thrown even if the thread is currently
    478 478
     inside 'mask' or 'uninterruptibleMask'.
    
    479 479
       -}
    
    480 480
     throwTo :: Exception e => ThreadId -> e -> IO ()
    
    481
    -throwTo (ThreadId tid) ex = IO $ \ s ->
    
    482
    -   case (killThread# tid (toException ex) s) of s1 -> (# s1, () #)
    
    481
    +throwTo (ThreadId tid) ex
    
    482
    +  | backtraceDesired ex = do
    
    483
    +    bts <- ThrownFrom <$> collectBacktraces
    
    484
    +    throwTo_ $ addExceptionContext bts $ toException ex
    
    485
    +  | otherwise =
    
    486
    +    throwTo_ $ toException ex
    
    487
    +
    
    488
    +throwTo_ :: Exception e => ThreadId -> e -> IO ()
    
    489
    +throwTo_ (ThreadId tid) ex = IO $ \ s ->
    
    490
    +   case (killThread# tid ex' s) of s1 -> (# s1, () #)
    
    483 491
     
    
    484 492
     -- | Returns the 'ThreadId' of the calling thread (GHC only).
    
    485 493
     myThreadId :: IO ThreadId
    

  • libraries/ghc-internal/src/GHC/Internal/Control/Exception.hs
    ... ... @@ -115,6 +115,7 @@ module GHC.Internal.Control.Exception (
    115 115
             ExceptionContext(..),
    
    116 116
             annotateIO,
    
    117 117
             WhileHandling(..),
    
    118
    +        ThrownFrom(..),
    
    118 119
     
    
    119 120
       ) where
    
    120 121
     
    

  • libraries/ghc-internal/src/GHC/Internal/Exception/Type.hs
    ... ... @@ -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
    
    ... ... @@ -298,6 +300,19 @@ instance Exception a => Exception (ExceptionWithContext a) where
    298 300
         backtraceDesired (ExceptionWithContext _ e) = backtraceDesired e
    
    299 301
         displayException = displayException . toException
    
    300 302
     
    
    303
    +-- | 'ThrownBy' records the site from which an asynchronous exception was thrown (e.g. the call-site of @throwTo@).
    
    304
    +--
    
    305
    +-- @since 4.23.0.0
    
    306
    +newtype ThrownFrom = ThrownFrom Backtrace
    
    307
    +
    
    308
    +instance ExceptionAnnotation ThrownFrom where
    
    309
    +  displayExceptionAnnotation (ThrownFrom e) =
    
    310
    +    "Thrown asynchronously by " ++ case lines $ displayException e of
    
    311
    +      [] -> ""
    
    312
    +      (l1:ls) ->
    
    313
    +        unlines $ l1:[if null l then "  |" else "  | " ++ l | l <- ls]
    
    314
    +
    
    315
    +
    
    301 316
     -- |Arithmetic exceptions.
    
    302 317
     data ArithException
    
    303 318
       = Overflow