GHC 9.10.1 is now available!

The GHC developers are very pleased to announce the release of GHC 9.10.1.
Binary distributions, source distributions, and documentation are available at
downloads.haskell.org and shortly via GHCup.

GHC 9.10 brings a number of new features and improvements, including:

  • The introduction of the GHC2024 language edition, building upon
    GHC2021 with the addition of a number of widely-used extensions.

  • Partial implementation of the GHC Proposal #281, allowing visible
    quantification to be used in the types of terms.

  • Extension of LinearTypes to allow linear let and where
    bindings

  • The implementation of the exception backtrace proposal, allowing the annotation of exceptions with backtraces, as well
    as other user-defined context

  • Further improvements in the info table provenance mechanism, reducing
    code size to allow IPE information to be enabled more widely

  • Javascript FFI support in the WebAssembly backend

  • Improvements in the fragmentation characteristics of the low-latency
    non-moving garbage collector.

  • ā€¦ and many more

A full accounting of changes can be found in the release notes.
As always, GHCā€™s release status, including planned future releases, can
be found on the GHC Wiki status.

We would like to thank GitHub, IOG, the Zw3rk stake pool,
Well-Typed, Tweag I/O, Serokell, Equinix, SimSpace, the Haskell
Foundation, and other anonymous contributors whose on-going financial
and in-kind support has facilitated GHC maintenance and release
management over the years. Finally, this release would not have been
possible without the hundreds of open-source contributors whose work
comprise this release.

As always, do give this release a try and open a ticket if you see
anything amiss.

44 Likes

Thanks to all for their hard work on the release, Iā€™m personally stoked about RequiredTypeArguments! No more need for Proxys or ambiguous types when reifying!!

4 Likes

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ć®ę–°ę©Ÿčƒ½

8 Likes

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 :slight_smile:

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.

1 Like

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.

4 Likes

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

4 Likes

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

7 Likes

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 .

1 Like

Thanks to all the GHC developers for this release. May I ask a couple of questions?

  1. 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?

  2. 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?

3 Likes

I think some files may have been duplicated in the packaging of GHC 9.10.1 for Windows:

  • GHC 9.8.2 lib\bin is 67 MB on disk. For GHC 9.10.1 it is 581 MB.
  • GHC 9.8.2 lib\lib does not exist. For GHC 9.10.1 it exists and is 1.08 GB on disk.
  • GHC 9.8.2 lib\doc is 132 KB on disk. For GHC 9.10.1 it is 775 MB.
3 Likes

This backtrace stuff is phenomenal!

11 Likes
9 Likes

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?

5 Likes

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.

2 Likes

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).

3 Likes

setBacktraceMechanismState acts as a filter for what gets included in the exception annotation, but the backtrace mechanism itself might need additional setup to work.

  • For the HasCallStackBacktrace to work, all functions in your call stack need a HasCallStack constraint (in your example, g doesnā€™t have it).
  • For the CostCentreBacktrace to work, you need to build your program with profiling support: -prof.
  • For the ExecutionBacktrace to work, you need to be on Linux and build your program with DWARF support: -g3.
  • For the IPEBacktrace to work, you need to build your program with info-table provenance: -finfo-table-map.
7 Likes

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
1 Like

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.

18 Likes

Thank you very much for the comprehensive response Ben! This looks great. Iā€™m looking forward to your blog post.

1 Like