391 lines
29 KiB
HTML
391 lines
29 KiB
HTML
<!-- Common Lisp HyperSpec (TM), version 7.0 generated by Kent M. Pitman on Mon, 11-Apr-2005 2:31am EDT -->
|
|
<HTML>
|
|
<HEAD>
|
|
<TITLE>CLHS: Issue EXIT-EXTENT Writeup</TITLE>
|
|
<LINK HREF="../Data/clhs.css" REL="stylesheet" TYPE="text/css" />
|
|
<META HTTP-EQUIV="Author" CONTENT="Kent M. Pitman">
|
|
<META HTTP-EQUIV="Organization" CONTENT="LispWorks Ltd.">
|
|
<LINK REL=TOP HREF="../Front/index.htm">
|
|
<LINK REL=COPYRIGHT HREF="../Front/Help.htm#Legal">
|
|
<LINK REL=DISCLAIMER HREF="../Front/Help.htm#Disclaimer">
|
|
<LINK REL=PREV HREF="../Issues/iss151_w.htm">
|
|
<LINK REL=UP HREF="../Issues/iss152.htm">
|
|
<LINK REL=NEXT HREF="../Issues/iss153_w.htm">
|
|
</HEAD>
|
|
<BODY>
|
|
<H1><A REV=MADE HREF="http://www.lispworks.com/"><IMG WIDTH=80 HEIGHT=65 ALT="[LISPWORKS]" SRC="../Graphics/LWSmall.gif" ALIGN=Bottom></A><A REL=TOP HREF="../Front/index.htm"><IMG WIDTH=237 HEIGHT=65 ALT="[Common Lisp HyperSpec (TM)]" SRC="../Graphics/CLHS_Sm.gif" ALIGN=Bottom></A> <A REL=PREV HREF="../Issues/iss151_w.htm"><IMG WIDTH=40 HEIGHT=40 ALT="[Previous]" SRC="../Graphics/Prev.gif" ALIGN=Bottom></A><A REL=UP HREF="../Issues/iss152.htm"><IMG WIDTH=40 HEIGHT=40 ALT="[Up]" SRC="../Graphics/Up.gif" ALIGN=Bottom></A><A REL=NEXT HREF="../Issues/iss153_w.htm"><IMG WIDTH=40 HEIGHT=40 ALT="[Next]" SRC="../Graphics/Next.gif" ALIGN=Bottom></A></H1>
|
|
|
|
<HR>
|
|
|
|
|
|
|
|
<H2>Issue EXIT-EXTENT Writeup</H2>
|
|
|
|
<PRE><B>Status:</B> proposal MINIMAL, as amended, passed Mar 89 X3J13 by vote of 11-5.<P>
|
|
<B>Issue:</B> <A HREF="iss152.htm">EXIT-EXTENT</A><P>
|
|
<B>References:</B> <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A>, <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> (p 142),<P>
|
|
<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A>, <A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>RETURN</B></A>, <A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>RETURN-FROM</B></A>,<P>
|
|
<A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>TAGBODY</B></A>, <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A>, <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A>,<P>
|
|
Dynamic extent (CLtL p.37),<P>
|
|
Nested dynamic extents (CLtL p.38),<P>
|
|
Blocks can only be exited once (CLtL p.120),<P>
|
|
Catch is disestablished just before the values <P>
|
|
are returned (CLtL p.139).<P>
|
|
Related issues: UNWIND-PROTECT-NON-LOCAL-EXIT is superseded<P>
|
|
by this one.<P>
|
|
<B>Category:</B> CLARIFICATION<P>
|
|
<B>Edit history:</B> ... Version 5 of UNWIND-PROTECT-NON-LOCAL-EXIT, 23-May-88 ...<P>
|
|
Version 1, 5-Sep-88, by Moon, for discussion<P>
|
|
Version 2, 1-Oct-88, by Masinter, minor edits<P>
|
|
Version 3, 7-Oct-88, by Moon, wording improvements<P>
|
|
Version 4, 7-Dec-88, by Masinter, add MEDIUM from<P>
|
|
UNWIND-PROTECT-NON-LOCAL-EXIT, discussion.<P>
|
|
Version 5, 12-Dec-88, Masinter, clarify MINIMAL allows MEDIUM<P>
|
|
Version 6, 8-Jan-89, Masinter, fix some bugs<P>
|
|
Version 7, 4-Apr-89, Moon, amend per X3J13 Mar-89, and make<P>
|
|
rationale and examples consistent with that<P>
|
|
<P>
|
|
<B>Problem description:<P>
|
|
</B><P>
|
|
CLtL does not specify precisely when the dynamic extent (lifetime)<P>
|
|
of a nonlocal exit such as a <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A>, <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A>, or <A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>TAGBODY</B></A> ends. <P>
|
|
For example, at what point is it no longer possible to <A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>RETURN-FROM</B></A><P>
|
|
a particular BLOCK?<P>
|
|
<P>
|
|
An "exit" refers to a point from which control can be transferred.<P>
|
|
For a <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> or <A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>RETURN-FROM</B></A>, the "exit" is the corresponding <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A><P>
|
|
or <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A> body. For a <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A>, the "exit" is the form within the <A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>TAGBODY</B></A><P>
|
|
which was being executed at the time the <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A> is performed.<P>
|
|
<P>
|
|
The extent of an exit is dynamic; it is not indefinite. The extent<P>
|
|
of an exit begins when the corresponding form (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A>, <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A> or <A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>TAGBODY</B></A><P>
|
|
clause) is entered. When the extent of an exit has ended, it is no<P>
|
|
longer legal to return from it.<P>
|
|
<P>
|
|
The extent of an exit is not the same thing as the scope of the<P>
|
|
designator by which the exit is identified. For example, a <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A><P>
|
|
name has lexical scope but the extent of its exit is dynamic; the<P>
|
|
scope of a <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> tag could differ from the extent of the <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A>'s<P>
|
|
return point. (That's part of what is at issue here.)<P>
|
|
<P>
|
|
The ambiguity at issue arises for the case where there are transfers<P>
|
|
of control from the cleanup clauses of an <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A>.<P>
|
|
<P>
|
|
When a transfer of control is initiated by <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A>, <A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>RETURN-FROM</B></A> or <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A>,<P>
|
|
a variety of events occur before the transfer of control is complete.<P>
|
|
In particular, <P>
|
|
<P>
|
|
(a) the cleanup clauses of any intervening <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> clauses<P>
|
|
are evaluated,<P>
|
|
<P>
|
|
(b) intervening dynamic bindings of special variables and catch tags<P>
|
|
are undone,<P>
|
|
<P>
|
|
(c) intervening exits are "abandoned", i.e., their extent ends and it<P>
|
|
is no longer legal to attempt to transfer control through them,<P>
|
|
<P>
|
|
(d) the extent of the exit being invoked ends,<P>
|
|
<P>
|
|
(e) control is finally passed to the target.<P>
|
|
<P>
|
|
The order of these events is not explicit in CLtL, however. The <P>
|
|
implementation note on p.142 gives a clue about the interweaving<P>
|
|
of (a) and (b), but there are differing opinions about the times<P>
|
|
at which (c) and (d) may occur. In particular,<P>
|
|
<P>
|
|
Is it legal for an implementation to end the extent of all <P>
|
|
intervening exits before processing the cleanup clauses of intervening <P>
|
|
UNWIND-PROTECTs?<P>
|
|
<P>
|
|
What is the dynamic context at the time <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> clauses are <P>
|
|
evaluated: how is the unwinding of dynamic bindings intertwined with <P>
|
|
evaluation of <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> cleanup clauses? <P>
|
|
<P>
|
|
<B>Proposal (EXIT-EXTENT:MINIMAL):<P>
|
|
</B><P>
|
|
The extent of an exit being "abandoned" because it is being passed over<P>
|
|
ends as soon as the transfer of control is initiated. That is, the<P>
|
|
event (c) occurs at the beginning of the initiation of the transfer of<P>
|
|
control. In the language of the implementation note on p.142, the<P>
|
|
extent ends at the beginning of the second pass. It is an error to<P>
|
|
attempt a transfer of control to an exit whose dynamic extent has<P>
|
|
ended.<P>
|
|
<P>
|
|
The event (d) occurs at the end of the transfer of control.<P>
|
|
<P>
|
|
Otherwise, events (a) and (b)--the undoing of dynamic binding of special<P>
|
|
variables and <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> tags, and the execution of <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> cleanup<P>
|
|
clauses--are performed in the order corresponding to the reverse order<P>
|
|
in which they were established, as implied by the implementation note<P>
|
|
on p.142. The effect of this is that the cleanup clauses of an <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A><P>
|
|
will see the same dynamic bindings of variables and <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> tags as were<P>
|
|
visible when the <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> was entered.<P>
|
|
<P>
|
|
This proposal is called "minimal" because it gives exits the smallest<P>
|
|
extent consistent with CLtL, except that event (d) occurs later than<P>
|
|
CLtL requires. A program that presumed a longer extent would be in<P>
|
|
error. Implementations may support longer extents for exits than is<P>
|
|
required by this proposal; in particular, an implementation which<P>
|
|
allowed the larger extent of the MEDIUM proposal below would still<P>
|
|
conform.<P>
|
|
<P>
|
|
<B>Proposal (EXIT-EXTENT:MEDIUM):<P>
|
|
</B><P>
|
|
The events of (a), (b), (c) and (d) are interwoven in the reverse <P>
|
|
order in which they were established. In particular, the extent of <P>
|
|
a passed-over exit ends when control reaches a frame that was <P>
|
|
established before the exit was established. <P>
|
|
<P>
|
|
In particular, it is legal, during the evaluation of an <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> <P>
|
|
cleanup form executed because of a non-local transfer of control, to<P>
|
|
initiate a new transfer of control to an exit intervening between the <P>
|
|
<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> and the original target; the original processing of <P>
|
|
transfer of control is abandoned. <P>
|
|
<P>
|
|
<B>Examples:<P>
|
|
</B><P>
|
|
;; Error under either proposal: <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A> exits normally before <A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>RETURN</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/f_funcal.htm#funcall"><B>funcall</B></A> (<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A> #'(<A REL=DEFINITION HREF="../Body/a_lambda.htm#lambda"><B>lambda</B></A> () (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A>))))<P>
|
|
<P>
|
|
;; Error under either proposal: normal exit before <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_let_l.htm#let"><B>let</B></A> ((a <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A>)) <P>
|
|
(<A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>tagbody</B></A> t (<A REL=DEFINITION HREF="../Body/s_setq.htm#setq"><B>setq</B></A> a #'(<A REL=DEFINITION HREF="../Body/a_lambda.htm#lambda"><B>lambda</B></A> () (<A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>go</B></A> t))))<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_funcal.htm#funcall"><B>funcall</B></A> a))<P>
|
|
<P>
|
|
;; Error under either proposal: <A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>TAGBODY</B></A> is passed over, before <A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/f_funcal.htm#funcall"><B>funcall</B></A> (<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_tagbod.htm#tagbody"><B>tagbody</B></A> a (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A> #'(<A REL=DEFINITION HREF="../Body/a_lambda.htm#lambda"><B>lambda</B></A> () (<A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>go</B></A> a))))))<P>
|
|
<P>
|
|
<P>
|
|
;;returns 2 under MEDIUM and MINIMAL, was error under MINIMAL version 6<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A> <P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A> 1)<P>
|
|
(<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A> 2)))<P>
|
|
<P>
|
|
;;returns 2 under MEDIUM, is error under MINIMAL<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> a <P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> b<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>return-from</B></A> a 1)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>return-from</B></A> b 2))))<P>
|
|
<P>
|
|
;; returns 2 under MEDIUM and MINIMAL, was error under MINIMAL version 6<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A> <P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A> 1)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A> 2)))<P>
|
|
<P>
|
|
;; returns 2 under MEDIUM, is error under MINIMAL<P>
|
|
;; because the catch of B is passed over by<P>
|
|
;; the first <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A>, hence portable programs must assume its dynamic extent<P>
|
|
;; is terminated. The binding of the catch tag is not yet disestablished<P>
|
|
;; and therefore it is the target of the second throw.<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'a<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'b<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'a 1)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'b 2))))<P>
|
|
<P>
|
|
<P>
|
|
;; the following was an error under MINIMAL version 6; the extent of<P>
|
|
;; the inner catch terminates as soon as the <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> commences, even<P>
|
|
;; though it remains in scope. Thus, the <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> of :SECOND-THROW<P>
|
|
;; sees the inner <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A>, but its extent has ended.<P>
|
|
;; under MEDIUM and MINIMAL version 7,<P>
|
|
;; it prints "The inner catch returns :SECOND-THROW"<P>
|
|
;; and then returns :OUTER-CATCH.<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'foo<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_format.htm#format"><B>format</B></A> t "The inner <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> returns ~s.~%"<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'foo<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'foo :first-throw)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'foo :second-throw))))<P>
|
|
:outer-catch))<P>
|
|
<P>
|
|
<P>
|
|
;; Following returns 10 under either proposal. The inner<P>
|
|
;; <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> of A is passed over, but because that <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A><P>
|
|
;; is disestablished before the <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> to A is executed,<P>
|
|
;; it isn't seen.<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'a<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'b<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/f_1pl_1_.htm#1PL"><B>1+</B></A> (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>catch</B></A> 'a (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'b 1)))<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>throw</B></A> 'a 10))))<P>
|
|
<P>
|
|
<P>
|
|
;; Following is an error under MINIMAL because the extent of<P>
|
|
;; the (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'BAR ...) exit ends when the (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'FOO ...)<P>
|
|
;; commences.<P>
|
|
;; Under MEDIUM, the pending exit to tag FOO is discarded by the<P>
|
|
;; second <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> to BAR and the value 4 is transferred to<P>
|
|
;; (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'BAR ...), which returns 4. The (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'FOO ...)<P>
|
|
;; then returns the 4 because its first argument has returned<P>
|
|
;; normally. XXX is not printed.<P>
|
|
<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'FOO<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'BAR<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'FOO 3)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'BAR 4)<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_wr_pr.htm#print"><B>PRINT</B></A> 'XXX))))<P>
|
|
<P>
|
|
<P>
|
|
;; Following returns 4 under either proposal; XXX is not printed.<P>
|
|
;; The (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'FOO ...) has no effect on the scope of the BAR<P>
|
|
;; catch tag or the extent of the (<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'BAR ...) exit.<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'BAR<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> 'FOO<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> (<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'FOO 3)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A> 'BAR 4)<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_wr_pr.htm#print"><B>PRINT</B></A> 'XXX))))<P>
|
|
<P>
|
|
<P>
|
|
;;The following are legal and print 5 under either proposal:<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_let_l.htm#let"><B>let</B></A> ((x 5))<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_declar.htm#declare"><B>declare</B></A> (<A REL=DEFINITION HREF="../Body/d_specia.htm#special"><B>special</B></A> x))<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A>)<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_wr_pr.htm#print"><B>print</B></A> x)))) <P>
|
|
<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_let_l.htm#let"><B>let</B></A> ((x 5))<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_declar.htm#declare"><B>declare</B></A> (<A REL=DEFINITION HREF="../Body/d_specia.htm#special"><B>special</B></A> x))<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_if.htm#if"><B>if</B></A> (test) (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A>))<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_wr_pr.htm#print"><B>print</B></A> x)))) <P>
|
|
<P>
|
|
<P>
|
|
<B>Rationale:<P>
|
|
</B><P>
|
|
For MINIMAL: Giving exits the smallest extent consistent with CLtL<P>
|
|
maximizes freedom for implementations; there are few applications,<P>
|
|
if any, that <A REL=DEFINITION HREF="../Body/f_provid.htm#require"><B>require</B></A> a longer extent. Delaying event (d) until<P>
|
|
the transfer of control is completed allows multiple attempts to<P>
|
|
exit from a single exit, if the first attempt is interrupted,<P>
|
|
possibly by an error.<P>
|
|
<P>
|
|
For MEDIUM: Giving exits a longer extent has cleaner semantics.<P>
|
|
<P>
|
|
<B>Current practice:<P>
|
|
</B><P>
|
|
Both implementations of Symbolics Genera (3600 and Ivory) end the extent<P>
|
|
of a target <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A> or <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> at the moment the values are returned, and<P>
|
|
end the extent of a passed-over exit at the moment the <A REL=DEFINITION HREF="../Body/s_throw.htm#throw"><B>THROW</B></A>, <A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>RETURN</B></A>, or<P>
|
|
<A REL=DEFINITION HREF="../Body/s_go.htm#go"><B>GO</B></A> commences. This choice of extent maximizes efficiency within the<P>
|
|
particular stack <A REL=DEFINITION HREF="../Body/f_docume.htm#structure"><B>structure</B></A> used by these implementations, by avoiding<P>
|
|
the need to retain the control information needed to use a passed over<P>
|
|
exit through the transfer of control. Genera signals an error if an<P>
|
|
attempt is made to use an exit that has been passed over.<P>
|
|
<P>
|
|
In some implementations, it is possible for a throw or non-local exit<P>
|
|
to be effectively "stopped" by an <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> cleanup clause that<P>
|
|
performs a non-local transfer of control to a passed-over exit.<P>
|
|
<P>
|
|
Some implementations crash or otherwise generate garbage code for<P>
|
|
non-local exits from cleanup clauses of <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A>.<P>
|
|
<P>
|
|
<B>Cost to Implementors:<P>
|
|
</B><P>
|
|
No currently valid implementation will be made invalid by the MINIMAL<P>
|
|
proposal. Some implementors may wish to add error checks if they<P>
|
|
do not already have them.<P>
|
|
<P>
|
|
MEDIUM would have a high cost for those implementations that currently<P>
|
|
have shorter extent.<P>
|
|
<P>
|
|
<B>Cost to Users:<P>
|
|
</B><P>
|
|
Most user programs don't do this, so there is likely little cost<P>
|
|
of converting existing code in any case. In any case, current implementations<P>
|
|
differ enough that this issue ostensibly does not<P>
|
|
affect current portable programs. Some users might have code that<P>
|
|
relies on the "unstoppable loops" that can be created with the MEDIUM<P>
|
|
proposal.<P>
|
|
<P>
|
|
<B>Benefits:<P>
|
|
</B><P>
|
|
Either proposal would make Common Lisp more precisely defined.<P>
|
|
<P>
|
|
<B>Cost of non-adoption :<P>
|
|
</B><P>
|
|
The semantics of exits will remain ambiguous.<P>
|
|
<P>
|
|
<B>Esthetics:<P>
|
|
</B><P>
|
|
Precisely specifying the meaning of dynamic extent improves the language.<P>
|
|
Leaving implementations free to implement a longer extent if they choose<P>
|
|
can be regarded as unesthetic, but consistent with Common Lisp philosophy.<P>
|
|
Having a <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> that is in scope even though its extent has ended may<P>
|
|
seem unesthetic, but it is consistent with how <A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>BLOCK</B></A> behaves.<P>
|
|
<P>
|
|
<B>Discussion:<P>
|
|
</B><P>
|
|
This issue is controversial. It was first discussed under the issue <P>
|
|
named UNWIND-PROTECT-CLEANUP-NON-LOCAL-EXIT. The issue was recast as<P>
|
|
the more global one of "extent of exits" rather than the specific <P>
|
|
one of "what happens if a cleanup in an <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> does a non-<P>
|
|
local exit", but the problem cases for both topics are the same.<P>
|
|
<P>
|
|
The goal of the MINIMAL proposal is to clarify the ambiguity in CLtL while<P>
|
|
minimizing changes to the current situation. The MEDIUM proposal<P>
|
|
defines the extent of an exit to end at the last moment possible<P>
|
|
within some particular reference implementation. It has<P>
|
|
a cost to implementors whose implementation is not identical to the<P>
|
|
reference implementation. Another alternative proposal, not considered<P>
|
|
here, would duck the issue by outlawing all non-local exits from <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A><P>
|
|
cleanup forms. That alternative would have a substantial cost to some users.<P>
|
|
<P>
|
|
Scheme is cleaner: it avoids this issue by specifying that the extent<P>
|
|
of an exit never ends.<P>
|
|
<P>
|
|
An argument for the MEDIUM proposal was made based on the example:<P>
|
|
<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> foo<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> bar<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>return-from</B></A> foo 'foo)<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_ret_fr.htm#return-from"><B>return-from</B></A> bar 'bar))))<P>
|
|
<P>
|
|
Since there is no reason for FOO and BAR not to be treated interchangeably,<P>
|
|
calling this an error would be inappropriate. <P>
|
|
<P>
|
|
It was argued that the MINIMAL proposal is equivalent to practically<P>
|
|
outlawing non-local exits from <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> cleanup clauses, because<P>
|
|
there is no general way to determine the target of the non-local exit<P>
|
|
that caused the cleanup clause to be invoked. <P>
|
|
<P>
|
|
The following example was offered as an argument against MINIMAL. Given:<P>
|
|
<P>
|
|
(<A REL=DEFINITION HREF="../Body/s_block.htm#block"><B>block</B></A> <A REL=DEFINITION HREF="../Body/a_nil.htm#nil"><B>nil</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/m_hand_1.htm#handler-case"><B>handler-case</B></A><P>
|
|
(<A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>unwind-protect</B></A> (<A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>return</B></A>)<P>
|
|
(<A REL=DEFINITION HREF="../Body/a_error.htm#error"><B>error</B></A> "foo")) ;probably an <A REL=DEFINITION HREF="../Body/a_error.htm#error"><B>error</B></A>, under <A REL=DEFINITION HREF="../Body/s_the.htm#the"><B>the</B></A> proposal<P>
|
|
(<A REL=DEFINITION HREF="../Body/a_error.htm#error"><B>error</B></A> ()<P>
|
|
(<A REL=DEFINITION HREF="../Body/f_wr_pr.htm#print"><B>print</B></A> "foo"))))<P>
|
|
<P>
|
|
If the <A REL=DEFINITION HREF="../Body/a_error.htm#error"><B>ERROR</B></A> handler has the same scope and extent a <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> in the same place<P>
|
|
would have (and that seems reasonable, though I'm not certain that the<P>
|
|
condition system specifically requires that interpretation), then the handler<P>
|
|
will be apparent to the call to <A REL=DEFINITION HREF="../Body/a_error.htm#error"><B>ERROR</B></A>, but will no longer be a valid target<P>
|
|
(its extent was exited by the <A REL=DEFINITION HREF="../Body/m_return.htm#return"><B>RETURN</B></A> in the <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A> body).<P>
|
|
<P>
|
|
The extent of an object with dynamic extent is the extent of the form <P>
|
|
which created it. Code which is executed "within" that form is within<P>
|
|
the extent of the object(s). This applies to all dynamic objects, such<P>
|
|
as special variable bindings, not just exits. Actually, I think the intent<P>
|
|
of the implementation note on p.142 is fairly clear and supports this<P>
|
|
interpretation. The supposedly ambiguous use of "frame" should be read<P>
|
|
as something like "form which establishes a dynamic extent". It might be<P>
|
|
clearer if the last sentence were changed to read something like:<P>
|
|
<P>
|
|
"On the second pass the stack is actually unwound. Each form which establishes<P>
|
|
a dynamic extent is undone in reverse order of creation until the matching<P>
|
|
<A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> is reached. The meaning of undoing a form depends on the type of form.<P>
|
|
For <A REL=DEFINITION HREF="../Body/s_unwind.htm#unwind-protect"><B>UNWIND-PROTECT</B></A>, it means executing the cleanup forms. For <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> it means<P>
|
|
removing the <A REL=DEFINITION HREF="../Body/s_catch.htm#catch"><B>CATCH</B></A> tag. For dynamic bindings it means undoing the binding,<P>
|
|
restoring the previous saved value. {This is not an exhaustive listing of the<P>
|
|
possibilities.}"<P>
|
|
<P>
|
|
</PRE>
|
|
<HR>
|
|
|
|
<A REL=NAVIGATOR HREF="../Front/StartPts.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Starting Points]" SRC="../Graphics/StartPts.gif" ALIGN=Bottom></A><A REL=TOC HREF="../Front/Contents.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Contents]" SRC="../Graphics/Contents.gif" ALIGN=Bottom></A><A REL=INDEX HREF="../Front/X_Master.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Index]" SRC="../Graphics/Index.gif" ALIGN=Bottom></A><A REL=INDEX HREF="../Front/X_Symbol.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Symbols]" SRC="../Graphics/Symbols.gif" ALIGN=Bottom></A><A REL=GLOSSARY HREF="../Body/26_a.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Glossary]" SRC="../Graphics/Glossary.gif" ALIGN=Bottom></A><A HREF="../Front/X3J13Iss.htm"><IMG WIDTH=80 HEIGHT=40 ALT="[Issues]" SRC="../Graphics/Issues.gif" ALIGN=Bottom></A><BR>
|
|
|
|
<A REL=COPYRIGHT HREF="../Front/Help.htm#Legal"><I>Copyright 1996-2005, LispWorks Ltd. All rights reserved.</I></A><P>
|
|
</BODY>
|
|
</HTML>
|