[MLton] cvs commit: improved exception history for Overflow

Matthew Fluet fluet@cs.cornell.edu
Fri, 20 May 2005 09:53:27 -0400 (EDT)


Here is another thought for improving exception history.

Consider a program like the following:

-------------------------------------------------------------------
fun loop n =
   if n > 12345
      then let 
	      val () = raise Fail "here"
	   in 2
	   end
   else 1 + (loop (4 * n))
        handle Overflow => 1

val _ = loop 1
-------------------------------------------------------------------

With -const 'Exn.keepHistory true' we get:

unhandled exception: Fail: here
with history:
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5

This is fine, but I find it a little disappointing that the actual line
where the exception is raised is not in the history.  If loop were a big
function with multiple occurences of raise Fail "here"  it is ambiguous as
to which one is being raised.  (As a more realistic example, consider a
large function with multiple non-exhaustive patterns.  Then a Bind
exception is very ambiguous.)  I note that if I change the program to the
following:

-------------------------------------------------------------------
fun loop n =
   if n > 12345
      then let 
	      val () = (fn e => raise e) (Fail "here")
	   in 2
	   end
   else 1 + (loop (4 * n))
        handle Overflow => 1

val _ = loop 1
-------------------------------------------------------------------

Then we get:

with history:
	loop.anon z.sml 4.17
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5
	loop z.sml 1.5

Which marks the exact point of the raised exception, along with the entire
call stack.  That eta expansion seems to be something that could easily be
done in implementExceptions.  Actually, since elaborate took care of
inserting the profile statements, the "eta expansion" would simply be to
wrap the raise expression with a new Enter/Leave pair.  Note, we don't
want to do the eta expansion in or before elaborate for a couple of
reasons:
 1) The compiler inserts Bind and Match exceptions after elaborate
 2) The default profile insertion of elaborate would name the function 
    "anon" or some such, whereas if we do it later, we could give it a 
    better name of "raise".

On the other hand, adding the profile expressions in implementExceptions 
is late enough in the game that we've lost the distinction between the 
basis and the user code.  So, that means that _all_ raised exceptions 
would report the origin, not just those that were raised in user code.  
This is different than the current behavior where an exception raised in 
the basis is only given its point of origin when -profile-basis true is a 
compile option.  On the other hand, it is exactly one more entry in the 
history, so it doesn't seem that bad.