Keep in mind the trick to use required type arguments in class methods:
class Storable a where
sizeOf :: forall a' -> a ~ a' => Int
alignment :: forall a' -> a ~ a' => Int
From GHC 9.10の新機能
Keep in mind the trick to use required type arguments in class methods:
class Storable a where
sizeOf :: forall a' -> a ~ a' => Int
alignment :: forall a' -> a ~ a' => Int
From GHC 9.10の新機能
Oh, thanks. Why do we need to do that? (I don’t have GHC 9.10 downloaded to check what it error it throws.)
Edit: actually, that syntax confuses me a bit, since it seems we have an argument to the left of a constraint. I’ve got to play around with this
For a type class variable, the (invisible) quantification must occur before the class head:
class forall a. Storable a where
sizeOf :: Int
There is no way to quantify a
differently, and a
is already bound in the method. This trick gets around it by using a second (visible) quantifier, and then asserting their equality.
Yes, the use of forall a' -> a ~ a' => ...
is a workaround. Ideally we need a syntax to specify that some of class variables need to be quantified visibly in a method. I’ve known about this issue for a long time, here’s an old proposal of mine:
It needs to be refurbished and submitted to the committee. If you read the discussion there you can see the idea wasn’t received as well as I hoped it would, but I still believe it’s a solid approach.
First of all, thank you @bgamari and all GHC contribs for this release!
Do I understand correctly that GHCUP does not provide this version yet?
I made a quick attempt at running scotty
CI with 9.10.1 and GHCUp fails with a mysterious
Attempting to install ghc 9.10.1 using ghcup
/opt/hostedtoolcache/ghcup/0.1.22.0/x64/ghcup install ghc 9.10.1
[ Error ] [e
Also see full CI logs: Add 9.10.1 to CI · scotty-web/scotty@91b0bac · GitHub
It’s a nice trick. A lower technology approach would be to define a second function outside of the class:
class Storable a where
sizeOfImpl :: Int
sizeOf :: forall a -> Storable a => Int
sizeOf t = sizeOfImpl @t
That way it’s also marginally easier to define instances (you don’t have to explicitly ignore the type variable).
instance Storable Int where
sizeOfImpl = 8
EDIT: Oh, and I used something similar to the trick in withST
in TypeAbstractions in GHC 9.10 - #5 by tomjaguarpaw
I have updated Stack’s default setup-info
dictionary for these binary distributions, and added GHC 9.10.1’s global hints to global-hints.yaml
.
Thanks to all the GHC developers for this release. May I ask a couple of questions?
I see the GHC 9.10.1 archive for Windows is 586 MB, compared to ~ 325 MB for GHC 9.6.3 to 9.8.2. Is there a reason for the 80% increase?
I note the new GHC boot packages: ghc-experimental-0.1.0.0
, ghc-internal-9.1001.0
(which seems an unusual version number, compared to other GHC boot packages), ghc-platform-0.1.0.0
and ghc-toolchain-0.1.0.0
. None of them are documented at 2.1. Version 9.10.1 — Glasgow Haskell Compiler 9.10.1 User's Guide. Is that an oversight?
I think some files may have been duplicated in the packaging of GHC 9.10.1 for Windows:
lib\bin
is 67 MB on disk. For GHC 9.10.1 it is 581 MB.lib\lib
does not exist. For GHC 9.10.1 it exists and is 1.08 GB on disk.lib\doc
is 132 KB on disk. For GHC 9.10.1 it is 775 MB.This backtrace stuff is phenomenal!
BTW, I’ve just experimented a little with GHC 9.10.1 to see something interesting with the new exception backtraces, but I couldn’t see it in action in my toy examples. Here’s what I tried in the end:
Main.hs
import GHC.Internal.Exception.Backtrace
import GHC.Environment
import GHC.Stack
{-# NOINLINE f #-}
f :: HasCallStack => [String] -> [String]
f [x] = error "Some Pesky Error"
f xs = ("def" ++) <$> xs
{-# NOINLINE g #-}
g :: [String] -> IO [String]
g xs = return $ ("abc" ++) <$> f xs
main = do
setBacktraceMechanismState CostCentreBacktrace True
setBacktraceMechanismState HasCallStackBacktrace True
setBacktraceMechanismState ExecutionBacktrace True
setBacktraceMechanismState IPEBacktrace True
args <- getFullArgs
print =<< g args
Here’s the output I got from this program:
$ ghc-9.10 Main.hs && ./Main
[1 of 2] Compiling Main ( Main.hs, Main.o ) [Source file changed]
[2 of 2] Linking Main [Objects changed]
Main: Some Pesky Error
CallStack (from HasCallStack):
error, called at Main.hs:8:9 in main:Main
f, called at Main.hs:13:32 in main:Main
Cost-centre stack backtrace:
IPE backtrace:
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:128:3 in ghc-internal:GHC.Internal.Exception
What do I need to do in order to observe something interesting in the backtrace lines?
Just to clarify, I think what you’re saying is that you have set
setBacktraceMechanismState CostCentreBacktrace True
setBacktraceMechanismState IPEBacktrace True
but
Cost-centre stack backtrace:
IPE backtrace:
are empty.
That’s right, but I’d say HasCallStack backtrace
is also “empty”, because it only displays some stack entries internal to its implementation, it doesn’t even pick up on the HasCallStack
constraint of my f
(which I already see in the “old” output).
I’m interested in any way of observing the new backtraces feature in action (i.e. any output with some new information about my exception that we didn’t get pre 9.10).
setBacktraceMechanismState
acts as a filter for what gets included in the exception annotation, but the backtrace mechanism itself might need additional setup to work.
HasCallStackBacktrace
to work, all functions in your call stack need a HasCallStack
constraint (in your example, g
doesn’t have it).CostCentreBacktrace
to work, you need to build your program with profiling support: -prof
.ExecutionBacktrace
to work, you need to be on Linux and build your program with DWARF support: -g3
.IPEBacktrace
to work, you need to build your program with info-table provenance: -finfo-table-map
.I’ve added HasCallStack
to g
and main
and I’ve added -g3 -finfo-table-map -prof
arguments to ghc while building Main.hs
, but I’m still not getting any result:
$ ghc-9.10 Main.hs -g3 -finfo-table-map -prof && ./Main
[1 of 2] Compiling Main ( Main.hs, Main.o )
[2 of 2] Linking Main
Main: Some Pesky Error
CallStack (from HasCallStack):
error, called at Main.hs:9:9 in main:Main
f, called at Main.hs:14:32 in main:Main
g, called at Main.hs:23:13 in main:Main
main, called at Main.hs:17:1 in main:Main
Cost-centre stack backtrace:
IPE backtrace:
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:128:3 in ghc-internal:GHC.Internal.Exception
Indeed I hope to publish a blog post about this feature soon. I was hoping to have it ready for the release but unfortunately time has been very tight recently.
In short:
HasCallStackBacktrace
requires that all functions of interest in the failing branch of the call-graph have a HasCallStack
constraint. Unfortunately, it does look like something isn’t quite right in the behavior here. I’ve opened #24807 to track this (and have now fixed it)CostCentreBacktrace
requires not only -prof
but also that cost-centres are added to the program (e.g. via a {-# CCS ... #-}
pragma or -fprof-auto
ExecutionBacktrace
requires DWARF support and that all libraries of your program (e.g. including base
) are built with -g
. As we see here, if any function in the call-chain does not have debug information the backtrace will be truncated at that frame.IPEBacktrace
requires -finfo-prov-map
. In general the more packages that are built with IPE information, the more helpful this backtrace will be. However, unlike ExecutionBacktrace
, absence of IPE information for a frame will not truncate the backtrace but rather will simply contribute no useful information.With a GHC build with IPE and debug information enabled (and the fix noted above), I get the following output from your program:
$ _build-ipe/stage1/bin/ghc hi.hs -g3 -finfo-table-map -prof -fprof-auto -fforce-recomp
$ ./hi h
hi: Some Pesky Error
CallStack (from HasCallStack):
error, called at hi.hs:8:9 in main:Main
f, called at hi.hs:13:32 in main:Main
CallStack (from -prof):
Main.f (hi.hs:(8,1)-(9,24))
Main.g (hi.hs:13:1-35)
Main.main.\ (hi.hs:21:3-18)
Main.main (hi.hs:(15,1)-(21,18))
Cost-centre stack backtrace:
Main.main (hi.hs:(15,1)-(21,18))
Main.main.\ (hi.hs:21:3-18)
Main.g (hi.hs:13:1-35)
Main.f (hi.hs:(8,1)-(9,24))
Native stack backtrace:
set_initial_registers (rts/Libdw.c:294.5) in /mnt/data/exp/ghc/ghc-9.10/hi
dwfl_thread_getframes in
get_one_thread_cb in /nix/store/53nia34f1pqf89idqs4bbm86k0xfgydf-elfutils-0.189/lib/libdw-0.189.so
dwfl_getthreads in /nix/store/53nia34f1pqf89idqs4bbm86k0xfgydf-elfutils-0.189/lib/libdw-0.189.so
dwfl_getthread_frames in /nix/store/53nia34f1pqf89idqs4bbm86k0xfgydf-elfutils-0.189/lib/libdw-0.189.so
libdwGetBacktrace (rts/Libdw.c:263.15) in /mnt/data/exp/ghc/ghc-9.10/hi
_ghczminternal_GHCziInternalziExecutionStackziInternal_sat_s2QY_entry (libraries/ghc-internal/src/GHC/Internal/ExecutionStack/Internal.hsc:217.31) in /mnt/data/exp/ghc/ghc-9.10/hi
stg_keepAlive_frame_info (rts/PrimOps.cmm:2954.1) in /mnt/data/exp/ghc/ghc-9.10/hi
ghczminternal_GHCziInternalziExecutionStackziInternal_collectStackTrace1_info (libraries/ghc-internal/src/GHC/Internal/ExecutionStack/Internal.hsc:86.10) in /mnt/data/exp/ghc/ghc-9.10/hi
ghczminternal_GHCziInternalziExceptionziBacktrace_zdwcollectBacktraces_info (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:145.54) in /mnt/data/exp/ghc/ghc-9.10/hi
ghczminternal_GHCziInternalziExceptionziBacktrace_collectBacktraces1_info (libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs:43.9) in /mnt/data/exp/ghc/ghc-9.10/hi
ghczminternal_GHCziInternalziException_errorCallWithCallStackException_info (libraries/ghc-internal/src/GHC/Internal/Exception.hs:122.1) in /mnt/data/exp/ghc/ghc-9.10/hi
stg_marked_upd_frame_info (rts/Updates.cmm:56.1) in /mnt/data/exp/ghc/ghc-9.10/hi
_ghczminternal_GHCziInternalziTopHandler_realzuhandler_r387_entry (libraries/ghc-internal/src/GHC/Internal/TopHandler.hs:188.1) in /mnt/data/exp/ghc/ghc-9.10/hi
stg_catch_frame_info (rts/Exception.cmm:405.1) in /mnt/data/exp/ghc/ghc-9.10/hi
stg_unmaskAsyncExceptionszh_ret_info (rts/Exception.cmm:87.1) in /mnt/data/exp/ghc/ghc-9.10/hi
stg_stop_thread_info (rts/StgStartup.cmm:47.1) in /mnt/data/exp/ghc/ghc-9.10/hi
StgRunIsImplementedInAssembler (rts/StgCRun.c:378.5) in /mnt/data/exp/ghc/ghc-9.10/hi
schedule (rts/Schedule.c:481.13) in /mnt/data/exp/ghc/ghc-9.10/hi
scheduleWaitThread (rts/Schedule.c:2675.11) in /mnt/data/exp/ghc/ghc-9.10/hi
rts_evalLazyIO (rts/RtsAPI.c:565.1) in /mnt/data/exp/ghc/ghc-9.10/hi
hs_main (rts/RtsMain.c:73.18) in /mnt/data/exp/ghc/ghc-9.10/hi
in /mnt/data/exp/ghc/ghc-9.10/hi
__libc_start_call_main in
__libc_start_main@@GLIBC_2.34 in /nix/store/46m4xx889wlhsdj72j38fnlyyvvvvbyb-glibc-2.37-8/lib/libc.so.6
_start in /mnt/data/exp/ghc/ghc-9.10/hi
IPE backtrace:
Cmm$rts/StgStartup.cmm. (:)
Cmm$rts/Exception.cmm. (:)
Cmm$rts/Exception.cmm. (:)
GHC.Internal.TopHandler. (:)
Cmm$rts/Updates.cmm. (:)
GHC.Internal.Exception.bindIO (libraries/ghc-internal/src/GHC/Internal/Base.hs:2246:1-76)
GHC.Internal.Exception.Backtrace. (:)
GHC.Internal.Exception.Backtrace.$ (libraries/ghc-internal/src/GHC/Internal/Base.hs:2180:1-9)
HasCallStack backtrace:
collectBacktraces, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:92:13 in ghc-internal:GHC.Internal.Exception
toExceptionWithBacktrace, called at libraries/ghc-internal/src/GHC/Internal/Exception.hs:127:5 in ghc-internal:GHC.Internal.Exception
error, called at hi.hs:8:9 in main:Main
f, called at hi.hs:13:32 in main:Main
In this case, the trivial nature of the test program means that the execution backtraces aren’t particularly helpful. It may be that IPE coverage needs improvement in this case or that we should improve the stack decoder’s treatment of primitive stack frame types (e.g. update frames, see #24811, #24812). I haven’t looked closely enough at this program to see why that is but I don’t doubt that there are improvements that could be made here.
An aside: When interpreting IPE and DWARF “backtraces” one must remember that the execution stack tracks continuations and not call-sites. In contrast to C, where there is a very strong correspondence between continuations and call-sites, in Haskell tail calls are quite common and therefore you will often see the entries from the “future” of your program in the backtrace.
Thank you very much for the comprehensive response Ben! This looks great. I’m looking forward to your blog post.
@mpilgrem GHC musl image glcr.b-data.ch/ghc/ghc-musl:9.10.1
is available now:
alpine:3.20
clang18
addedCross references:
I have created a pull request to update Stack’s Dev Containers.
Cross reference: Dev Container: Add 'GHC 9.10.1' by benz0li · Pull Request #6595 · commercialhaskell/stack · GitHub
Hi! Thank you for the awesome work on the exception backtraces. I have been playing around with them a bit and have encountered a few issues, so I went digging. Now that I did, I found that portions of the proposal seems to be missing, for example, I couldn’t find the rethrowing mechanisms (there seem to even be open CLC proposals on that topic) and while the toplevel handler of the RTS itself seem to be adjusted, this doesn’t seem to be the case for GHCi/ runghc. Is there some place where I can find out about the status of things?