Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[svn:parrot-pdd] r13151 - in trunk: . docs/pdds

1 view
Skip to first unread message

all...@cvs.perl.org

unread,
Jul 4, 2006, 1:43:19 PM7/4/06
to perl6-i...@perl.org
Author: allison
Date: Tue Jul 4 10:43:19 2006
New Revision: 13151

Modified:
trunk/docs/pdds/pdd23_exceptions.pod

Changes in other areas also in this revision:
Modified:
trunk/ (props changed)

Log:
[pdds]: Review and revise the Exceptions PDD.


Modified: trunk/docs/pdds/pdd23_exceptions.pod
==============================================================================
--- trunk/docs/pdds/pdd23_exceptions.pod (original)
+++ trunk/docs/pdds/pdd23_exceptions.pod Tue Jul 4 10:43:19 2006
@@ -42,9 +42,9 @@

=over

-=item B<< push_eh I<SUB_LABEL> >>
+=item B<push_eh I<SUB_LABEL>>

-=item B<< push_eh I<INVOCABLE_PMC> >>
+=item B<push_eh I<INVOCABLE_PMC>>

Push an invocable PMC -- usually a closure or, in rarer cases, a subroutine --
onto the exception handler stack.
@@ -58,15 +58,13 @@
exception (i.e. the call stack is I<not> unwound first). See below for more
detail.

-=item B<< pop_eh >>
+=item B<pop_eh>

Pop the most recently pushed exception handler off the control stack.

-{{ TODO: should we provide a "pop_eh $P0" variant returning the address, since
-it's easy and somebody might find it useful? Or is it more important to
-provide generic control stack introspection? Probably the latter. }}
+{{ TODO: Provide control stack introspection. }}

-=item B<< throw I<EXCEPTION> >>
+=item B<throw I<EXCEPTION>>

Throw an exception consisting of the given I<EXCEPTION> PMC. Active exception
handlers (if any) will be invoked with I<EXCEPTION> as the only parameter.
@@ -78,21 +76,22 @@
Any type of PMC can be thrown as an exception. However, if there's any chance
of cross-language calls -- and in a Parrot environment, cross-language
operations are kind of the point -- then you should be prepared to catch
-object of classes you would never have thrown yourself.
+exception classes you would never have thrown yourself.

That said, it is I<VERY STRONGLY RECOMMENDED> that any thrown PMC that can
possibly escape your private sandbox should meet the minimal interface
requirements of the C<parrot;exception> class, described below.

-=item B<< throwcc I<EXCEPTION> >>
+=item B<throwcc I<EXCEPTION> [ , I<CONTINUATION> ]>

-Throw an exception consisting of the given I<EXCEPTION> PMC after taking a
-continuation at the next opcode. Active exception handlers (if any) will be
+Throw an exception consisting of the given I<EXCEPTION> PMC after taking
+a continuation at the next opcode. When a I<CONTINUATION> is passed in,
+it will use that instead. Active exception handlers (if any) will be
invoked with I<EXCEPTION> and the given continuation as parameters.

-Except for its taking of a continuation which is then passed to exception
-handlers, C<throwcc> is just like C<throw>. This opcode is useful for
-exceptions that are more like warnings or notices than errors.
+Except for the continuation which is passed to exception handlers,
+C<throwcc> is just like C<throw>. This opcode is useful for exceptions
+that are more like warnings or notices than errors.

Exception handlers can resume execution immediately after the C<throwcc>
opcode by executing the C<handled> opcode, and then invoking the given
@@ -100,21 +99,26 @@
invoked with no parameters; in other words, C<throwcc> may I<not> return a
value.

-=item B<< die [ I<MESSAGE> ] >>
+{{ TODO: Resuming immediately after the C<throw> opcode isn't quite
+flexible enough. Perl 6, for example, resumes control flow after the end
+of the block in which the exception was thrown. }}
+
+=item B<die [ I<MESSAGE> ]>

The C<die> opcode throws an exception of type C<exception;death> with a
payload of I<MESSAGE>. If I<MESSAGE> is a string register, the exception
payload is a C<String> PMC containing I<MESSAGE>; if I<MESSAGE> is a PMC, it
is used directly as the exception payload.

-{{ TODO: What is the default when no I<MESSAGE> is given? }}
+{{ TODO: What is the default when no I<MESSAGE> is given? Something like
+"Fatal exception at LINE in FILE." followed by a backtrace. }}

If this exception is not handled, it results in Parrot returning an error
indication and the stringification of I<MESSAGE> to its embedding environment.
When running standalone, this means writing the stringification of I<MESSAGE>
-to the standard error and executing the standard C function C<exit(1)>.
+to standard error and executing the standard C function C<exit(1)>.

-=item B<< exit [ I<EXITCODE> ] >>
+=item B<exit [ I<EXITCODE> ]>

Throw an exception of type C<exception;exit> with a payload of I<EXITCODE>,
which defaults to zero, as an Integer PMC.
@@ -123,7 +127,7 @@
as a status to its embedded environment, or when running standalone,
to execute the C function C<exit(I<EXITCODE>)>.

-=item B<< handled >>
+=item B<handled I<EXCEPTION>>

While handling an exception, tell Parrot that the exception has been handled
and should be removed from the stack of active exceptions. This opcode is an
@@ -133,38 +137,65 @@

=head2 Order of Operations in Exception Handling

-=over 4
+When B<throw> or B<throwcc> is called, for all active exception
+handlers, in LIFO order:
+
+=over
+
+=item 1
+Find the topmost exception handler
+
+=item 2
+Push an exception record somewhere, presumably on the control stack.
+The exception record contains a pointer to an exception handler block
+and an exception PMC (and possibly a continuation)
+
+=item 3
+Invoke the handler (note: this is still in the thrower's dynamic
+context)
+
+=back
+
+If the handler returns normally:
+
+=over
+
+=item 1
+Find the "exception handling in progress" record

-=item B<throw> or B<throwcc>
+=item 2
+Find the next exception handler

- For all active exception handlers, in LIFO order:
- find the topmost exception handler
- push Exception Record somewhere,
- presumably on the control stack,
- containing pointer to exception handler block
- and exception PMC
- (and possibly a continuation)
- invoke the handler
- (note: this is still in the thrower's dynamic context)
-
-=item if the handler returns normally:
-
- find the "exception handling in progress" record
- find the next exception handler
- if found,
- invoke it
- else if there is a continuation in the Exception Record
- (because the throwing opcode was C<throwcc>),
- invoke the ER's continuation (i.e. resume execution)
- else,
- terminate program a la C<die>
+=item 3
+If the handler is found, invoke it

-=item C<handled> opcode
+=item 4
+Or if there is a continuation in the exception record
+(because the throwing opcode was C<throwcc>),
+invoke the ER's continuation (i.e. resume execution)

- pop and destroy Exception Record
+=item 5
+Otherwise terminate program a la C<die>

=back

+{{ TODO: this isn't right, another option is a) invoke the handler, b)
+the handler calls handled, and c) invoke the continuation to resume
+because the exception was handled. The question of whether to resume or
+die when an exception is never handled is determined by the severity of
+the exception, not by the simple fact of having a continuation. }}
+
+
+When the C<handled> opcode is called:
+
+=over
+
+=item 1
+Pop and destroy exception record
+
+=back
+
+
=head1 STANDARD EXCEPTIONS

=head2 Universal Exception Object Interface [Advisory]
@@ -220,7 +251,9 @@
C<set_inner_exception()> method to store that previous exception
as part of the exception object.

-{{ TODO: Should we use properties instead? }}
+{{ TODO: Should we use properties instead? ANR: I'm not sure what you
+mean by "an exception is a consequence of a previous exception".
+Example? }}

=back

@@ -252,6 +285,8 @@

=item B<exception;domain>

+{{ TODO: How about calling these B<exception;input>? }}
+
Generic base class for miscellaneous domain (input value) errors. Payload is
an array, the first element of which is the operation that failed (e.g. the
opcode name); subsequent elements depend on the value of the first element.
@@ -321,8 +356,8 @@
C<store_lex> is likely to succeed every time, as creating new lexicals at
runtime is OK in Tcl.)

-{{ FIXME - Is it true that more opcodes throw exceptions? If so, they should
-be listed here. }}
+{{ TODO: List any other opcodes that currently throw exceptions and
+general categories of opcodes that should throw exceptions. }}

Other opcodes respond to an C<errorson> setting to decide whether to
throw an exception or return an error value. C<find_global> throws an
@@ -331,8 +366,32 @@
name requested doesn't exist in a lexical, current, global, or built-in
namespace.

-{{ FIXME - "erroron" as specified is dynamically rather than lexically
-scoped; is this good? }}
+{{ FIXME - "errorson" as specified is dynamically rather than lexically
+scoped; is this good? ANR: There are a couple of different factors here.
+One is the ability to globally define the severity of certain exceptions
+or categories of exceptions without needing to define a handler for each
+one. (e.g. Perl 6 may have pragmas to set how severe type-checking
+errors are. A simple "incompatible type" error may be fatal under one
+pragma, a resumable warning under another pragma, and completely silent
+under a third pragma.) Another is the ability to "defang" opcodes so
+they return error codes instead of throwing exceptions. We might provide
+a very simple interface to catch an exception and capture its payload
+without the full complexity of manually defining exception handlers
+(though it would still be implemented as an exception handler
+internally). Something like:
+
+ .local pmc error_code
+ .capture_start error_code
+ $P1 = find_lex 'foo'
+ .capture_end
+
+ # error_code contains what would have been the "error" return value
+
+This could eliminate the need for "defanging" because it would be almost
+as easy to use as error codes. It could be implemented once for all
+exceptional opcodes, instead of needing to be defined for each one. And,
+it still keeps the error information out-of-band, instead of mixing the
+error in with normal return values. }}

It's a little odd that so few opcodes throw exceptions (these are the
ones that are documented, but a few others throw exceptions internally
@@ -344,7 +403,7 @@
for exception-based or non-exception-based implementations, rather than
forcing one or the other.]

-=head2
+=head2 Resuming after Exceptions

Exceptions thrown by standard Parrot opcodes (like the one thrown by
C<find_global> above or by the C<throw> opcode) are always resumable,
@@ -358,11 +417,20 @@
$P1 = new ['parrot';'exception'], $P0 # create new exception object
throw $P1 # throw it

+=head2 Consequenses
+
Exceptions are designed to work with the Parrot calling conventions.
Since the return addresses of C<bsr> subroutine calls and exception
handlers are both pushed onto the control stack, it's generally a bad
idea to combine the two.

+{{ TODO: If this continues to be true, then we need a way to turn off
+all exceptions. A number of built-in opcodes throw exceptions. If there
+is no way to prevent this, then it is never safe to use the control
+stack for anything other than exceptions. Alternatively, we leave the
+control stack safe for more primitive control flow, and pick another
+strategy for exceptions. }}
+
=head1 ATTACHMENTS

None.

Bob Rogers

unread,
Jul 4, 2006, 5:54:48 PM7/4/06
to Allison Randal, perl6-i...@perl.org
If, as seems likely, exception bookkeeping is moved to a separate
stack in the interpreter (with or without dynamic-wind actions), then
C<bsr>/C<ret> addresses can stay in the Parrot_Context, and all of
pdd23_exceptions.pod that is quoted below ceases to be problematic.
Does that seem reasonable?

-- Bob

------------------------------------------------------------------------
=head2 Consequenses

Exceptions are designed to work with the Parrot calling conventions.
Since the return addresses of C<bsr> subroutine calls and exception
handlers are both pushed onto the control stack, it's generally a bad
idea to combine the two.

{{ TODO: If this continues to be true, then we need a way to turn off


all exceptions. A number of built-in opcodes throw exceptions. If there

is no way to prevent this, then it is never safe to use the control

stack for anything other than exceptions. Alternatively, we leave the

control stack safe for more primitive control flow, and pick another

strategy for exceptions. }}

Allison Randal

unread,
Jul 5, 2006, 3:02:53 AM7/5/06
to Bob Rogers, perl6-i...@perl.org
Bob Rogers wrote:
> If, as seems likely, exception bookkeeping is moved to a separate
> stack in the interpreter (with or without dynamic-wind actions), then
> C<bsr>/C<ret> addresses can stay in the Parrot_Context, and all of
> pdd23_exceptions.pod that is quoted below ceases to be problematic.
> Does that seem reasonable?

Yeah, that's the current best solution (the last time Chip and I talked
on Saturday). I'd like avoid a stack entirely, but this is a
straightforward first attack on the problem. If we implement a separate
exception stack now and run into problems, we can refine and refactor later.

It's an encouraging sign that we've got hold of a good idea when the
same solution occurs to others independently. :)

Thanks,
Allison

Bob Rogers

unread,
Jul 8, 2006, 4:58:33 PM7/8/06
to Allison Randal, perl6-i...@perl.org
From: Allison Randal <all...@perl.org>
Date: Wed, 05 Jul 2006 00:02:53 -0700

Bob Rogers wrote:
> If, as seems likely, exception bookkeeping is moved to a separate
> stack in the interpreter (with or without dynamic-wind actions), then
> C<bsr>/C<ret> addresses can stay in the Parrot_Context, and all of
> pdd23_exceptions.pod that is quoted below ceases to be problematic.
> Does that seem reasonable?

Yeah, that's the current best solution (the last time Chip and I talked
on Saturday). I'd like avoid a stack entirely, but this is a
straightforward first attack on the problem. If we implement a separate
exception stack now and run into problems, we can refine and refactor later.

Were you thinking of using the same stack for dynamic binding context
and stack-winding actions as well? Or would these go on separate
stacks?

In what follows, I will speak of these collectively as the "dynamic
state stack", with the understanding that it may need to be pluralized.
(And the further understanding that continuations and coroutines can
make it branch, just like the Parrot_Context => RetContinuation "stack".
So maybe we need a better word?)

It's an encouraging sign that we've got hold of a good idea when the
same solution occurs to others independently. :)

Thanks,
Allison

Well, yesterday I thought of a problem with this. So maybe that just
means that we're all sharing the same delusion. ;-}

The snag is that calling a continuation would return to the
Parrot_Context with the return stack state as it was when the context
was last left, which could be different than when the continuation was
taken. What I would expect to happen is that the continuation also
captures the return stack state, as part of the dynamic context, so I
would argue that this is a bug.

This is currently not a problem with respect to error handling, since
the stack-unwinding done by C<throw> implicitly pops return addresses as
well, because it's the same stack. However, it does fail now with other
uses of continuations, as shown by the first new test case in the
attached patch. One could argue that maybe it is not worth fixing the
general case. However, separating the stacks as proposed would extend
this bug to cover the C<throw> case as well, and it is probably not
acceptable to say that you can't depend on the return stack after error
recovery. True?

Continuations also don't run actions (as shown by the second new test
case), and they don't preserve error handlers for the same reason --
these bugs are much more serious. Furthermore, I assume dynamic binding
would be similarly affected. (In fact, I've been itching to fix all of
this for almost six months now, but I think it needs to be done as part
of a general cleanup of dynamic state bookkeeping -- I now have more
tuits, but I don't yet understand the changes you are proposing well
enough to get started.)

The obvious solution is to save the current return stack TOS along
with that of the dynamic state stack(s) in the continuation, so that it
can be restored when the continuation is invoked. The drawback is that
doing so complicates the storage management of return stack entries.
This is the same complication that must be borne by dynamic state stack
entries, but it seems a shame to have to extend it to something that
otherwise would obviously belong strictly to the Parrot_Context in which
it was pushed.

So maybe there needs to be One Dynamic Stack (or whatever we call
it), so that we can unify the bookkeeping? This would also require that
all of these things nest properly, but that's no change from the current
implementation -- and nobody seems to have complained.

-- Bob

continuation-breakage.patch
0 new messages