[Git][ghc/ghc][wip/fendor/freeze-throw] 3 commits: Regression tests for backtraces
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 Regression tests for backtraces Document the stack traces of various exception throwing primitives. - - - - - 747ce42d by fendor at 2026-01-20T16:15:25+01:00 Hide implementation details of `throw` `throw` exposed implementation details as it doesn't freeze the `CallStack`: ``` HasCallStack backtrace: collectExceptionAnnotation, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:170:37 in ghc-internal:GHC.Internal.Exception toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:90:42 in ghc-internal:GHC.Internal.Exception throw, called at T26806b.hs:17:9 in main:Main ``` The functions `collectExceptionAnnotation` and `toExceptionWithBacktrace` are implementation details of `throw` that are noise to the end user. Thus, we freeze the `CallStack`, no longer exposing these details. Then the backtrace looks like: ``` HasCallStack backtrace: throw, called at T26806b.hs:17:9 in main:Main ``` - - - - - 721afc8e by fendor at 2026-01-20T16:17:49+01:00 Hide implementation details of `throwSTM` `throwSTM` exposed implementation details as it doesn't freeze the `CallStack`: ``` HasCallStack backtrace: collectExceptionAnnotation, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:175:37 in ghc-internal:GHC.Internal.Exception toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/STM.hs:190:26 in ghc-internal:GHC.Internal.STM throwSTM, called at T15395c.hs:8:5 in main:Main ``` The functions `collectExceptionAnnotation` and `toExceptionWithBacktrace` are implementation details of `throwSTM` that are noise to the end user. Thus, we freeze the `CallStack`, no longer exposing these details. Then the backtrace looks like: ``` HasCallStack backtrace: throwSTM, called at T15395c.hs:8:5 in main:Main ``` - - - - - 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: ===================================== libraries/ghc-internal/src/GHC/Internal/Exception.hs ===================================== @@ -87,7 +87,7 @@ throw e = -- Note also the absolutely crucial `noinine` in the RHS! -- See Note [Hiding precise exception signature in throw] let se :: SomeException - !se = noinline (unsafePerformIO (toExceptionWithBacktrace e)) + !se = noinline (unsafePerformIO (withFrozenCallStack $ toExceptionWithBacktrace e)) in raise# se -- Note [Capturing the backtrace in throw] @@ -162,7 +162,12 @@ throw e = -- primops which allow more precise guidance of the demand analyser's heuristic -- (e.g. #23847). --- | @since base-4.20.0.0 +-- | Collect a Backtrace and attach it to the 'Exception'. +-- +-- It is recommended to use 'withFrozenCallStack' when calling this function +-- in order to avoid leaking implementation details of 'toExceptionWithBacktrace'. +-- +-- @since base-4.20.0.0 toExceptionWithBacktrace :: (HasCallStack, Exception e) => e -> IO SomeException toExceptionWithBacktrace e ===================================== libraries/ghc-internal/src/GHC/Internal/STM.hs ===================================== @@ -28,7 +28,7 @@ import GHC.Internal.Base import GHC.Internal.Exception (Exception, toExceptionWithBacktrace, fromException, addExceptionContext) import GHC.Internal.Exception.Context (ExceptionAnnotation) import GHC.Internal.Exception.Type (WhileHandling(..)) -import GHC.Internal.Stack (HasCallStack) +import GHC.Internal.Stack (HasCallStack, withFrozenCallStack) -- TVars are shared memory locations which support atomic memory -- transactions. @@ -187,7 +187,7 @@ throwSTM e = do -- N.B. Typically use of unsafeIOToSTM is very much frowned upon as this -- is an easy way to end up with nested transactions. However, we can be -- certain that toExceptionWithBacktrace will not initiate a transaction. - se <- unsafeIOToSTM (toExceptionWithBacktrace e) + se <- unsafeIOToSTM (withFrozenCallStack $ toExceptionWithBacktrace e) STM $ raiseIO# se -- | Exception handling within STM actions. ===================================== libraries/ghc-internal/tests/backtraces/T15395a.hs ===================================== @@ -0,0 +1,5 @@ +import GHC.Internal.Control.Exception + +main :: IO () +main = + throw $ ErrorCall "throw error" ===================================== libraries/ghc-internal/tests/backtraces/T15395a.stderr ===================================== @@ -0,0 +1,7 @@ +T15395a: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall: + +throw error + +HasCallStack backtrace: + throw, called at T15395a.hs:5:3 in main:Main + ===================================== libraries/ghc-internal/tests/backtraces/T15395b.hs ===================================== @@ -0,0 +1,5 @@ +import GHC.Internal.Control.Exception + +main :: IO () +main = + throwIO $ ErrorCall "throwIO error" ===================================== libraries/ghc-internal/tests/backtraces/T15395b.stderr ===================================== @@ -0,0 +1,7 @@ +T15395b: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall: + +throwIO error + +HasCallStack backtrace: + throwIO, called at T15395b.hs:5:3 in main:Main + ===================================== libraries/ghc-internal/tests/backtraces/T15395c.hs ===================================== @@ -0,0 +1,8 @@ + +import GHC.Internal.STM +import GHC.Internal.Control.Exception + +main :: IO () +main = + atomically $ do + throwSTM $ ErrorCall "STM error" ===================================== libraries/ghc-internal/tests/backtraces/T15395c.stderr ===================================== @@ -0,0 +1,7 @@ +T15395c: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall: + +STM error + +HasCallStack backtrace: + throwSTM, called at T15395c.hs:8:5 in main:Main + ===================================== libraries/ghc-internal/tests/backtraces/T15395d.hs ===================================== @@ -0,0 +1,4 @@ + +main :: IO () +main = + undefined ===================================== libraries/ghc-internal/tests/backtraces/T15395d.stderr ===================================== @@ -0,0 +1,7 @@ +T15395d: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall: + +Prelude.undefined + +HasCallStack backtrace: + undefined, called at T15395d.hs:4:3 in main:Main + ===================================== libraries/ghc-internal/tests/backtraces/T15395e.hs ===================================== @@ -0,0 +1,4 @@ + +main :: IO () +main = + error "error" ===================================== libraries/ghc-internal/tests/backtraces/T15395e.stderr ===================================== @@ -0,0 +1,7 @@ +T15395e: Uncaught exception ghc-internal:GHC.Internal.Exception.ErrorCall: + +error + +HasCallStack backtrace: + error, called at T15395e.hs:4:3 in main:Main + ===================================== libraries/ghc-internal/tests/backtraces/all.T ===================================== @@ -1,6 +1,14 @@ +exc_opts = [ when(have_profiling(), extra_ways(['prof'])) + , when(js_arch(), skip) + , exit_code(1)] + test('T14532a', [], compile_and_run, ['']) test('T14532b', [], compile_and_run, ['']) -test('T26507', [ when(have_profiling(), extra_ways(['prof'])) - , when(js_arch(), skip) - , exit_code(1)], compile_and_run, ['']) +test('T26507', exc_opts, compile_and_run, ['']) +# Stack traces shouldn't expose implementation details +test('T15395a', exc_opts, compile_and_run, ['']) +test('T15395b', exc_opts, compile_and_run, ['']) +test('T15395c', exc_opts, compile_and_run, ['']) +test('T15395d', exc_opts, compile_and_run, ['']) +test('T15395e', exc_opts, compile_and_run, ['']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/231ab0acd2d9854c25f3574edd4a1a2... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/231ab0acd2d9854c25f3574edd4a1a2... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Hannes Siebenhandl (@fendor)