exceptions

Matthew Fluet Matthew Fluet <fluet@CS.Cornell.EDU>
Thu, 15 Nov 2001 12:01:31 -0500 (EST)


Are we eventually planning on moving to the point where the SSA IL doesn't
need HandlerPush and HandlerPop, and implement handlers works entirely
from the information in  Transfer.Call {return, ...}?

I moved from mayRaise: bool to raises: Type.t vector option; it was an
easy exercise to make removeUnused flow raise/handle args/formals the way
it does with return/cont args/formals.  So, something like this:

fun fib 0 = 1
  | fib 1 = 1
  | fib n = fib (n - 1) + fib (n - 2)

val n = (fib 20) handle _ => 0

val _ = print (concat["fib(20) = ", Int.toString n, "\n"])

can be shown that while fib can raise, it doesn't matter what it raises.
So, all the overflows go to raises of the empty tuple.

But, we can get into situations like
 
 fun f () = ... raise Overflow ...
 fun g () = ... raise Overflow ...
 fun h () = ... ( ... f () ... g () ...) handle _ => exp1 ... 
 fun i () = ... ( ... f () ... ) handle Overflow => exp2 ...
 fun main () = ... h () ... i () ...

So, we can reasonably rewrite g to
 fun g () = ... raise () ...
But, we can't with f, because i () discriminates on the raised exception.
So, in h (), we really want a handler with no arguments, but we need a
wrapper handler for f () to compensate for the raised exception.  Easy
enough when I just rewrite the Transfer.Call's, but getting the
handlerPush and handlerPop right is going to be a pain.  And the type
checker rightfully complains loudly.

(Actually, this situation arises with the above fib example in
removeUnused1, which occurs before inlining, so all of the integer
arithmetic is still in their own functions, which obviously must raise
their argument, because various other uses make use of the exception.  The
recursive calls to fib don't require args for the handlers, but the
arithmetic does.)

Note, we can "fake" it, by requiring all handlers to accept one argument,
even if the raising function doesn't raise any arguments.  This will be
"safe" because the backend will still fetch from the global raise
location, but since that will be moved to an unused variable, it won't
matter that the raising function didn't move a value into it.  It's a
hack, and I don't really like it.