Backtrace info has vanished from GHC :-(
Today I got this from a stage-1 build of GHC
Panic: the impoossible happened
ASSERT failed!
That's all. No call stack information whatsoever. Boo!
I talked to @bgamari. Turned out to be a consequence of
commit 36cddd2ce1a3bc62ea8a1307d8bc6006d54109cf
Author: Rodrigo Mesquita <rodrigo.m.mesquita@gmail.com>
Date: Mon Sep 23 11:51:24 2024 +0100
Remove redundant CallStack from exceptions
Before the exception backtraces proposal was implemented, ErrorCall
accumulated its own callstack via HasCallStack constraints, but
ExceptionContext is now accumulated automatically.
The original ErrorCall mechanism is now redundant and we get a duplicate
CallStack
Updates Cabal submodule to fix their usage of ErrorCallWithLocation to ErrorCall
CLC proposal#285
Fixes #25283
in particular this change in GHC.Utils.Panic.Plain
assertPanic' :: HasCallStack => a
-assertPanic' =
- let doc = unlines $ fmap (" "++) $ lines (prettyCallStack callStack)
- in
- Exception.throw (Exception.AssertionFailed
- ("ASSERT failed!\n"
- ++ withFrozenCallStack doc))
+assertPanic' = Exception.throw (Exception.AssertionFailed "ASSERT failed!")
Notice that the patch removed the call-stack information! The reasoning was that in the Glorious Future, and perhaps in the stage-2 compiler, Exception.throw captures and displays the call-stack info.
But it is crippling for a stage-1 compiler. If the stage-1 compiler crashes with an ASSERT error, I can't build the stage-2 compiler, even if the latter had better backtrace info.
So, I beg please can we put back the backtrace info into assertPanic and friends so that we always get call-stack info in the stage-1 compiler.
Other points
In discussing this with Ben we realised that the error infrastructure is a bit of a mess:
- The main interface is
GHC.Utils.Panic.- It has functions like
assertPpr :: HasCallStack => Bool -> SDoc -> a -> a, and hence depends onGHC.Utils.Outputable. - It wrangles call-stack
SDocs and ultimately callsException.throw
- It has functions like
- There is another module
GHC.Utils.Plain.Panicwhich also exports assert-like functions.- This one does not depend on
Outputable.
- This one does not depend on
- Sadly
GHC.Utils.PanicimportsGHC.Utils.Plain.Panicand re-exports all of it.- The former does not really depend on the latter; rather, GHC.Utils.Panic calls
Exception.throwdirectly. - GHC.Utils.Panic does depend on a data type,
PlainGhcExceptioninGHC.Utils.Plain.Panic. - I thought that
GHC.Utils.Panic.assertwould be defined asGHC.Utils.Panic.assertPpr empty; i.e. same but with no user trace info. But no! It's just a re-export of GHC.Utils.Plain.Panic.assert, which really goes by a whole different route toException.throw`.
- The former does not really depend on the latter; rather, GHC.Utils.Panic calls
I propose that
- We "bless"
GHC.Utils.Panicas the primary interface, and importGHC.Utils.Plain.Paniconly in the very few places whereOutputableis not available. - Provide
GHC.Utils.Panic.assertdifferent fromGHC.Utils.Plain.Panic.assert; indeed rename the latter toplainAssertperhaps. - Document and describe the structure, including where call-stack docs are built and how they are displayed.