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

Commits:

9 changed files:

Changes:

  • changelog.d/T27453
    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
    +}

  • 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
    ... ... @@ -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
    

  • libraries/ghc-internal/src/GHC/Internal/Control/Exception.hs
    ... ... @@ -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)
    

  • libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs-boot
    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

  • 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
    
    ... ... @@ -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