Skip to content

Add an option to infer CallStack implicit parameters

Adam Foltzer mentioned to me at ICFP that it would be nice if GHC had a "debug" mode that automatically inserted CallStack IPs in every function signature. Then you could easily get nice, fairly complete, call-stacks during development. Of course you could already do this with judicious use of CPP macros, but that's quite tedious (and error prone).

As an example, consider

module A where

foo xs = head xs

head :: [a] -> a
head (x:xs) = x
head _ = error "bad"

Compiling A in this mode should result in the following signature

module A where

foo :: (?callStack :: CallStack) => [a] -> a
head :: (?callStack :: CallStack) => [a] -> a

I have a few concerns with this though:

  1. We're changing a type signature that the user wrote. It would be a well-defined and consistent change across the whole module, but it's still unsettling.
  2. How do we handle a function that already has a CallStack IP?
bar :: (?stk :: CallStack) => [a] -> a

If we're aiming for consistent insertion of CallStacks everywhere, it might make sense to remove the ?stk :: CallStack and insert a ?callStack :: CallStack, so they'll be appended correctly. But here again we'd be changing a user-written type signature. Furthermore, bar might call a function from a different module (or package) that hasn't been given this consistent debug treatment, and this other module might expect that the CallStack be named ?stk.

  1. If the CallStack is not used, e.g.
null :: (?callStack :: CallStack) => [a] -> Bool
null [] = True
null (_:_) = False

it will trigger a redundant constraint warning. I think we could address this by bubbling up CallStack constraints in much the same way that other constraints are propagated, so

module A where

foo xs = head xs

head :: [a] -> a
head (x:xs) = x
head _ = error "bad"

null [] = True
null (_:_) = False

would get the signature

module A where

foo :: (?callStack :: CallStack) => [a] -> a
head :: (?callStack :: CallStack) => [a] -> a
null :: [a] -> Bool

In other words, we would only infer a CallStack constraint for a function when the body induces a wanted CallStack constraint.

  1. How would this interact with cabal packages? If I accidentally install a package in debug mode, all downsteam packages will be compiled against the CallStack-laden signatures. Seems like we might need a new "debug" way..

It's pretty clear to me that a debug mode would be useful (in fact I manually added a bunch of CallStacks to my code the other day, only to remove them once I was done debugging), but the concerns (particularly 4.) are substantial. There's also the question of how all of this interacts with the ongoing DWARF work.

Trac metadata
Trac field Value
Version 7.11
Type FeatureRequest
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information