Skip to content

Improvements to exceptions#165

Merged
alt-romes merged 5 commits intomasterfrom
wip/exception-request
Jan 20, 2026
Merged

Improvements to exceptions#165
alt-romes merged 5 commits intomasterfrom
wip/exception-request

Conversation

@mpickering
Copy link
Copy Markdown
Contributor

Note, requires haskell-debugger/dap#26

@mpickering mpickering force-pushed the wip/exception-request branch 2 times, most recently from 3b5d8cd to cbee3a9 Compare January 14, 2026 12:04
We query the HasCallStack backtrace from an exception and if it's
present, use that to show a location to the user.
@mpickering mpickering force-pushed the wip/exception-request branch 2 times, most recently from 94d3fd9 to 6c7581f Compare January 14, 2026 16:10
@alt-romes
Copy link
Copy Markdown
Collaborator

Brilliant stuff. The MR looks great. Just a matter of fixing the CI

@mpickering
Copy link
Copy Markdown
Contributor Author

@alt-romes I introduced a new module into haskell-debugger-view which contains code used to get the ExceptionContext from an exception. I did this in order to avoid having to write a complicated inline expression. However, I am worrying this is not right the right place for such code, since the user may use an older version of haskell-debugger-view.

What should I do about these helper functions?

@alt-romes
Copy link
Copy Markdown
Collaborator

If the code is pertaining to implementing an exception view, I think it is fine to keep it in haskell-debugger-view.

If not, then you could either compile it remotely or we could introduce a new module in say extra-source-files which gets unconditionally loaded into the debuggee session and contains new utilities.

However, that new module would then need to be checked to contain the functions needed by the specific hdb version at runtime. We already do this for haskell-debugger-view, where we check at runtime that the version we loaded is a version supported by the hdb version running.

@mpickering mpickering force-pushed the wip/exception-request branch 3 times, most recently from dd1f48c to e36fb45 Compare January 19, 2026 12:26
The ExceptionInfo request is used by debuggers in order to display
information about the exception we are stopped at.
@mpickering
Copy link
Copy Markdown
Contributor Author

@alt-romes This is ready for review now.

Comment thread haskell-debugger/GHC/Debugger/Stopped.hs Outdated
Comment thread haskell-debugger-view/src/GHC/Debugger/View/Class.hs
Copy link
Copy Markdown
Collaborator

@alt-romes alt-romes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great.

I commented on some individual commits but I see that in the end some things ended up different, so you can ignore my previous comments in favor of the following two requested changes:

  • Move the whole new section "Exception context helpers" to a new module GHC.Debugger.Stopped.Exception
  • Use MultilineStrings for the long raw expressions (exceptionInfoExpr and exceptionLocationExpr)

Great work!

Comment on lines +369 to +444

--------------------------------------------------------------------------------
-- * Exception context helpers
--------------------------------------------------------------------------------

exceptionSourceSpanFromContext :: Debugger (Maybe SourceSpan)
exceptionSourceSpanFromContext = do
GHC.getResumeContext >>= \case
r:_ | resumeHistoryIx r == 0
, Nothing <- GHC.resumeBreakpointId r -> do
let excRef = resumeApStack r
evalRes <- Remote.eval
(Remote.raw exceptionLocationExpr `Remote.app` Remote.untypedRef excRef)
case evalRes of
Left err -> do
logSDoc Logger.Debug $
Ppr.text "Failed to evaluate exception context:" Ppr.<+> Ppr.text (show err)
return Nothing
Right fhv -> do
parsed <- obtainParsedTerm "Exception context location" 4 True anyTy (castForeignRef fhv)
(maybeParser exceptionLocationTupleParser)
case parsed of
Left errs -> do
logSDoc Logger.Debug $
Ppr.text "Failed to parse exception context location:"
Ppr.<+> Ppr.vcat (map (Ppr.text . getTermErrorMessage) errs)
return Nothing
Right Nothing -> return Nothing
Right (Just (file, line, col)) ->
return $ Just SourceSpan
{ file = file
, startLine = line
, startCol = col
, endLine = line
, endCol = col
}
_ -> return Nothing

exceptionLocationTupleParser :: TermParser (String, Int, Int)
exceptionLocationTupleParser =
(,,) <$> subtermWith 0 stringParser
<*> subtermWith 1 intParser
<*> subtermWith 2 intParser

exceptionLocationExpr :: String
exceptionLocationExpr = unlines
[ "let"
, " fromCallStack cs = case GHC.Internal.Data.Maybe.listToMaybe (GHC.Internal.Stack.getCallStack cs) of"
, " Just (_, loc) -> Just ( GHC.Internal.Stack.Types.srcLocFile loc"
, " , GHC.Internal.Stack.Types.srcLocStartLine loc"
, " , GHC.Internal.Stack.Types.srcLocStartCol loc)"
, " go exc ="
, " let ctx = GHC.Internal.Exception.Type.someExceptionContext exc"
, " bts :: [GHC.Internal.Exception.Backtrace.Backtraces]"
, " bts = Control.Exception.Context.getExceptionAnnotations ctx"
, " in case bts of"
, " bt : _ -> case GHC.Internal.Exception.Backtrace.btrHasCallStack bt of"
, " Just cs -> fromCallStack cs"
, " Nothing -> Nothing"
, " [] -> Nothing"
, " in go"
]

fallbackExceptionSourceSpan :: Maybe SrcSpan -> SourceSpan
fallbackExceptionSourceSpan mspan =
let fileLabel = maybe "<exception>" spanLabel mspan
in SourceSpan
{ file = fileLabel
, startLine = 0
, startCol = 0
, endLine = 0
, endCol = 0
}
where
spanLabel (RealSrcSpan rss _) = unpackFS (srcSpanFile rss)
spanLabel (UnhelpfulSpan reason) = unpackFS (unhelpfulSpanFS reason)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the busy work, but could you move this to a new module GHC.Debugger.Stopped.Exception

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread haskell-debugger/GHC/Debugger/Stopped.hs Outdated
@mpickering mpickering force-pushed the wip/exception-request branch from e36fb45 to 160fbf9 Compare January 20, 2026 10:22
@mpickering
Copy link
Copy Markdown
Contributor Author

I moved the helpers to a new module in a new commit.

Copy link
Copy Markdown
Collaborator

@alt-romes alt-romes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lovely! Let's land.

@alt-romes alt-romes merged commit dccb8e6 into master Jan 20, 2026
6 checks passed
@mpickering
Copy link
Copy Markdown
Contributor Author

For the record, Rodrigo says that the reason there are 5 exceptions thrown in the integration test is that..

  1. First call to error
  2. Second call to error
  3. Rethrow from using catch
  4. Rethrow from using bracket in withProgName called in evalWrapper
  5. Rethrow from using bracket in withArgs called in evalWrapper

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants