I've had an idea. One way to ensure that a handler is not in scope when invoked (though possibly not the only way) is to keep the list of active handlers in a dynamic variable binding. The code for C<throw> can then *rebind* that variable, popping handlers off (in a way that is not destructive, i.e. leaves the value of old bindings untouched) before invoking them.
Here is pseudocode for a possible implementation: throw: maybe call a hook here for handler tracing. rebind the handlers list to a new copy of the value [1]. while there are handlers left { pop the next handler from the current binding. invoke it. } pop the (now empty) new handler list binding. if nonfatal (because there's a continuation?) { just return. } elsif debugging is appropriate (defined how?) { invoke the debugger. } else { die hard. } This has some advantages: 1. The set of bound handlers is easily introspected in PIR. (Not that introspecting closures is currently of much use, but that can change.) 2. The handlers are removed from the dynamic environment without affecting other aspects of the dynamic environment, and are automatically restored to the right thing when C<throw> is exited, either normally, or by calling a continuation, or whatever (but see drawback 3 below). 3. If the above pseudocode is implemented in PIR as a library routine (as it easily could be), then the C substrate doesn't have to keep track of exceptions that are in the process of being thrown. 4. In fact, and unless I have missed something additional that Perl 6 requires, this makes C<throw> completely re-entrant, in that the throwing of multiple exceptions could be nested, or even interleaved via coroutines and/or continuation calling. And also some drawbacks: 1. Exception handling might be slowed down somewhat wrt a pure C implementation. 2. It makes PDD23 depend on dynamic binding, which is even more in the "vapor" stage. 3. When a handler invokes a continuation, the the dynamic environment will be unwound to the continuation's scope, causing C<push_dynscope> thunks to be invoked *in the original handler context*. If an inner C<push_dynscope> causes the same class of error, the very same handler might be re-invoked. This is logically consistent, but wierd, and may not be acceptable [2]. If not, I'm not sure what to do about it. However, I believe this gives the best semantics for the long haul. Indeed, I can't think of another good way to implement this, though I may be blinkered by my Lisp experience: The pseudocode above could have been written for the CMUCL implementation of SIGNAL. Thoughts? -- Bob Rogers (trying not to be a vapor designer) http://rgrjr.dyndns.org/ [1] The copy can be avoided if the handlers are kept in a Pair list. [2] The ANSI CL spec is silent about this behavior (AFACS), but I believe that CMUCL does work this way.