Opened 11 years ago

Closed 23 months ago

#960 closed feature request (worksforme)

Lexical call site string

Reported by: paul@… Owned by:
Priority: normal Milestone:
Component: Compiler Version: 6.6
Keywords: Cc: claus.reinke@…, ndmitchell@…, id@…, pho@…, miffoljud@…
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: N/A
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:


A function that returns the lexical call site of the current function when an exception occurs.

I'm thinking primarily of "head []", but the same principle would apply to other functions. A dynamic traceback is not necessarily possible in a lazy language, but when "head" gets called on an empty list there must be somewhere in the program that actually said "head", even if it was not evaluated at the time or included in a closure or something. That way when I get a "head: empty list" I don't need to grep my program for "head" calls: I know where head was called from and can start looking there.


  • If I just have a function "foo = head" then it will report that the caller was "foo".
  • If I have a function "bar x y = ..." and subsequntly say "baz x = bar (x * 2)" then an exception in "bar" will report the caller as "baz".

I know there is a -x profiler option to get a sort of dynamic traceback, but I find it frequently just tells me the error is in "CAF.List", which isn't very informative.

I'm guessing at a difficulty of 1 week as I don't know the GHC internals. I'm guessing that the call site is going to have to be a hidden parameter passed in to each function call, or alternatively hard-coded when a function is in-lined.

Change History (19)

comment:1 Changed 11 years ago by simonpj

You're asking for "a function"; but what is this function called, and what is its type? In general it's not clear (to me) exactly what you are proposing; or whether it is implementable.

Clarification welcome!

comment:2 Changed 11 years ago by pauljohnson

My first thought was that the head function should include something like

head [] = error $ "head: empty string at " ++ callSite

So then if my file Foo.hs at line 35 says "head []" at run-time I get an error message saying

"head: empty string at Foo.hs line 35"

However this is not referentially transparent: "callSite" is not a constant, and its probably not useful for it to be valid outside exceptions. So instead how about

Exception -> String

returns the file and line where the function that triggered the exception was called from. Then the default exception handler can print the exception string followed by the output of callSite.

As Neil Mitchell pointed out in an email, Hat can answer the question of where the erroneous function was called from. But unless you are already using Hat its a lot of trouble to go to just to locate a simple error.


comment:3 Changed 11 years ago by simonpj

A neat way to do this might be to extend Template Haskell slightly, to provide

  currentLocation :: Q (Filepath, Int)

giving the filename and location of the current splice. (This is rather similar to the existing Template Haskell function currentModule.) Then you could define

  callSite :: ExpQ
  callSite = do { (f,n) <- currentLocation
                   ; return (LitE (StringL (f ++ ":" ++ show n))) }

And use it thus:

  error ("woggle at" ++ $callSite)

Since the Template Haskell splice $callSite is a TH splice, it's ok for it to grab contextual information.

comment:4 Changed 11 years ago by pauljohnson

I haven't studied Template Haskell, but won't that just give the location of the "error" call? I want the place the enclosing function was called from. So if "head" calls "error" I want to see what called "head".

comment:5 Changed 10 years ago by simonpj

Indeed ... and you want to see what called whatever called "head", and whatever called that, and so on.

Often asked for, but I don't know how to do that without runtime overhead, passing extra arguments. That's more or less what -xc does.

My suggestion is less ambitious but gets part way.

comment:6 Changed 10 years ago by malcolm.wallace@…

It occurs to me that there may be a relatively simple src->src transformation that could annotate every failing expression with its lexical callsite. Much in the way that hpc uses a src->src transformation to annotate expressions with information about whether (and how often) they get called. One of the cool things about the hpc transformation (in contrast to the more complex src->src transformation done by Hat) is that it does not change the types, so you can freely mix annotated code with plain unannotated code (e.g. from libraries).

I wonder whether you would need to identify "possibly failing" expressions - with something like the Catch analysis tool? Or would it be sufficient as a first cut to just assume that all expressions can fail?

comment:7 Changed 10 years ago by Neil Mitchell

Cc: ndmitchell@… added

I think the transformation would be simpler than Hat, but would require extra state to be passed round (the stack), so won't be as simple as HPC. I think you could probably write this as a Yhc.Core transformation in a few hours. I may give this a go sometime this week. It should also be pretty easy to add into the Yhc runtime. (I suspect things will be harder in GHC due to more aggressive optimisation, and more complex runtime)

I don't think something like Catch [1] would help narrow down the expressions - Catch identifies multiple separate calls to head, some of which might fail and some of which might pass - an implementation would have to pass failure information if any of them might fail. In addition Catch's transformations are quite heavy, so won't map directly onto the original Haskell. Of course Catch might solve the problem by formal verification, so might answer the question without actually running the program (once its finished...)


comment:8 Changed 10 years ago by igloo

Milestone: _|_
Test Case: N/A

comment:9 Changed 10 years ago by claus

Cc: claus.reinke@… added; ndmitchell@… removed

since hpc and ghci debugger are now live, does that help with the src->src transformations discussed above? see also

btw, i think the call-stack problems are rooted in the haskell report's translation of function definitions into case without regard to exhaustiveness of the patterns: the translation converts partial functions (which should fail at the call site) into pseudo-exhaustive functions (which can now fail in the callee's code, due to the translation of case).

in spite of these more involved considerations, i'd also like to have a simple way to get at the lexical call site, eg when simulating here-docs, i currently use a hack like this to avoid hard-coding the current file name:

thisFile = catchJust assertions (assert False undefined) getName
  where getName msg = let (_:_:x:_) = reverse $ findIndices (==':') msg
                      in return $ take x msg

comment:10 Changed 9 years ago by igloo

See also #1441

comment:11 Changed 9 years ago by Isaac Dupree

Cc: claus.reinke@… ndmitchell@… id@… added; claus.reinke@… removed

undefined could use a hack like assert currently does, but as soon as we want to start annotating things like fromJust, we'll need somewhat of a general mechanism. Potentially so worth it though.

comment:12 Changed 9 years ago by simonmar

Architecture: UnknownUnknown/Multiple

comment:13 Changed 9 years ago by simonmar

Operating System: UnknownUnknown/Multiple

comment:14 Changed 7 years ago by PHO

Cc: pho@… added

comment:15 Changed 6 years ago by batterseapower

Type of failure: None/Unknown

See also #3693

comment:16 Changed 3 years ago by Tarrasch

Cc: miffoljud@… added

I think this could be superseded by #3693. Only that it would require compilation with something like -g. There's also multiple overhead-full stack trace implementations today.

It might be time to close this whenever #3693 closes.

comment:17 Changed 2 years ago by gridaphobe

Can we consider this issue fixed by the implicit call-stacks introduced in

comment:18 in reply to:  17 Changed 2 years ago by PaulJohnson

Replying to gridaphobe:

Can we consider this issue fixed by the implicit call-stacks introduced in

Yes, I believe you can. Thanks.

comment:19 Changed 23 months ago by thomie

Resolution: worksforme
Status: newclosed
Note: See TracTickets for help on using tickets.