Hannes Siebenhandl pushed to branch wip/fendor/freeze-throw at Glasgow Haskell Compiler / GHC
Commits:
-
43b811dc
by fendor at 2026-01-20T16:03:18+01:00
-
747ce42d
by fendor at 2026-01-20T16:15:25+01:00
-
721afc8e
by fendor at 2026-01-20T16:17:49+01:00
13 changed files:
- libraries/ghc-internal/src/GHC/Internal/Exception.hs
- libraries/ghc-internal/src/GHC/Internal/STM.hs
- + libraries/ghc-internal/tests/backtraces/T15395a.hs
- + libraries/ghc-internal/tests/backtraces/T15395a.stderr
- + libraries/ghc-internal/tests/backtraces/T15395b.hs
- + libraries/ghc-internal/tests/backtraces/T15395b.stderr
- + libraries/ghc-internal/tests/backtraces/T15395c.hs
- + libraries/ghc-internal/tests/backtraces/T15395c.stderr
- + libraries/ghc-internal/tests/backtraces/T15395d.hs
- + libraries/ghc-internal/tests/backtraces/T15395d.stderr
- + libraries/ghc-internal/tests/backtraces/T15395e.hs
- + libraries/ghc-internal/tests/backtraces/T15395e.stderr
- libraries/ghc-internal/tests/backtraces/all.T
Changes:
| ... | ... | @@ -87,7 +87,7 @@ throw e = |
| 87 | 87 | -- Note also the absolutely crucial `noinine` in the RHS!
|
| 88 | 88 | -- See Note [Hiding precise exception signature in throw]
|
| 89 | 89 | let se :: SomeException
|
| 90 | - !se = noinline (unsafePerformIO (toExceptionWithBacktrace e))
|
|
| 90 | + !se = noinline (unsafePerformIO (withFrozenCallStack $ toExceptionWithBacktrace e))
|
|
| 91 | 91 | in raise# se
|
| 92 | 92 | |
| 93 | 93 | -- Note [Capturing the backtrace in throw]
|
| ... | ... | @@ -162,7 +162,12 @@ throw e = |
| 162 | 162 | -- primops which allow more precise guidance of the demand analyser's heuristic
|
| 163 | 163 | -- (e.g. #23847).
|
| 164 | 164 | |
| 165 | --- | @since base-4.20.0.0
|
|
| 165 | +-- | Collect a Backtrace and attach it to the 'Exception'.
|
|
| 166 | +--
|
|
| 167 | +-- It is recommended to use 'withFrozenCallStack' when calling this function
|
|
| 168 | +-- in order to avoid leaking implementation details of 'toExceptionWithBacktrace'.
|
|
| 169 | +--
|
|
| 170 | +-- @since base-4.20.0.0
|
|
| 166 | 171 | toExceptionWithBacktrace :: (HasCallStack, Exception e)
|
| 167 | 172 | => e -> IO SomeException
|
| 168 | 173 | toExceptionWithBacktrace e
|
| ... | ... | @@ -28,7 +28,7 @@ import GHC.Internal.Base |
| 28 | 28 | import GHC.Internal.Exception (Exception, toExceptionWithBacktrace, fromException, addExceptionContext)
|
| 29 | 29 | import GHC.Internal.Exception.Context (ExceptionAnnotation)
|
| 30 | 30 | import GHC.Internal.Exception.Type (WhileHandling(..))
|
| 31 | -import GHC.Internal.Stack (HasCallStack)
|
|
| 31 | +import GHC.Internal.Stack (HasCallStack, withFrozenCallStack)
|
|
| 32 | 32 | |
| 33 | 33 | -- TVars are shared memory locations which support atomic memory
|
| 34 | 34 | -- transactions.
|
| ... | ... | @@ -187,7 +187,7 @@ throwSTM e = do |
| 187 | 187 | -- N.B. Typically use of unsafeIOToSTM is very much frowned upon as this
|
| 188 | 188 | -- is an easy way to end up with nested transactions. However, we can be
|
| 189 | 189 | -- certain that toExceptionWithBacktrace will not initiate a transaction.
|
| 190 | - se <- unsafeIOToSTM (toExceptionWithBacktrace e)
|
|
| 190 | + se <- unsafeIOToSTM (withFrozenCallStack $ toExceptionWithBacktrace e)
|
|
| 191 | 191 | STM $ raiseIO# se
|
| 192 | 192 | |
| 193 | 193 | -- | Exception handling within STM actions.
|
| 1 | +import GHC.Internal.Control.Exception
|
|
| 2 | + |
|
| 3 | +main :: IO ()
|
|
| 4 | +main =
|
|
| 5 | + throw $ ErrorCall "throw error" |
| 1 | +T15395a: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
|
|
| 2 | + |
|
| 3 | +throw error
|
|
| 4 | + |
|
| 5 | +HasCallStack backtrace:
|
|
| 6 | + throw, called at T15395a.hs:5:3 in main:Main
|
|
| 7 | + |
| 1 | +import GHC.Internal.Control.Exception
|
|
| 2 | + |
|
| 3 | +main :: IO ()
|
|
| 4 | +main =
|
|
| 5 | + throwIO $ ErrorCall "throwIO error" |
| 1 | +T15395b: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
|
|
| 2 | + |
|
| 3 | +throwIO error
|
|
| 4 | + |
|
| 5 | +HasCallStack backtrace:
|
|
| 6 | + throwIO, called at T15395b.hs:5:3 in main:Main
|
|
| 7 | + |
| 1 | + |
|
| 2 | +import GHC.Internal.STM
|
|
| 3 | +import GHC.Internal.Control.Exception
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main =
|
|
| 7 | + atomically $ do
|
|
| 8 | + throwSTM $ ErrorCall "STM error" |
| 1 | +T15395c: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
|
|
| 2 | + |
|
| 3 | +STM error
|
|
| 4 | + |
|
| 5 | +HasCallStack backtrace:
|
|
| 6 | + throwSTM, called at T15395c.hs:8:5 in main:Main
|
|
| 7 | + |
| 1 | + |
|
| 2 | +main :: IO ()
|
|
| 3 | +main =
|
|
| 4 | + undefined |
| 1 | +T15395d: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
|
|
| 2 | + |
|
| 3 | +Prelude.undefined
|
|
| 4 | + |
|
| 5 | +HasCallStack backtrace:
|
|
| 6 | + undefined, called at T15395d.hs:4:3 in main:Main
|
|
| 7 | + |
| 1 | + |
|
| 2 | +main :: IO ()
|
|
| 3 | +main =
|
|
| 4 | + error "error" |
| 1 | +T15395e: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall:
|
|
| 2 | + |
|
| 3 | +error
|
|
| 4 | + |
|
| 5 | +HasCallStack backtrace:
|
|
| 6 | + error, called at T15395e.hs:4:3 in main:Main
|
|
| 7 | + |
| 1 | +exc_opts = [ when(have_profiling(), extra_ways(['prof']))
|
|
| 2 | + , when(js_arch(), skip)
|
|
| 3 | + , exit_code(1)]
|
|
| 4 | + |
|
| 1 | 5 | test('T14532a', [], compile_and_run, [''])
|
| 2 | 6 | test('T14532b', [], compile_and_run, [''])
|
| 3 | -test('T26507', [ when(have_profiling(), extra_ways(['prof']))
|
|
| 4 | - , when(js_arch(), skip)
|
|
| 5 | - , exit_code(1)], compile_and_run, [''])
|
|
| 7 | +test('T26507', exc_opts, compile_and_run, [''])
|
|
| 6 | 8 | |
| 9 | +# Stack traces shouldn't expose implementation details
|
|
| 10 | +test('T15395a', exc_opts, compile_and_run, [''])
|
|
| 11 | +test('T15395b', exc_opts, compile_and_run, [''])
|
|
| 12 | +test('T15395c', exc_opts, compile_and_run, [''])
|
|
| 13 | +test('T15395d', exc_opts, compile_and_run, [''])
|
|
| 14 | +test('T15395e', exc_opts, compile_and_run, ['']) |