CVS difference for ais/ai-10318.txt
--- ais/ai-10318.txt 2005/08/21 06:00:44 1.14
+++ ais/ai-10318.txt 2005/10/31 05:18:48 1.15
@@ -1,4 +1,4 @@
-!standard 03.10.02(10) 05-07-11 AI95-00318-02/10
+!standard 03.10.02(10) 05-09-20 AI95-00318-02/11
!standard 03.10.02(13)
!standard 03.08(14)
!standard 03.09(24)
@@ -402,7 +402,7 @@
The following should be added to the new rule added after 7.5(8) by AI-287:
For a function_call of a type with a part that is of a task, protected, or
-explictly limited record type that is used to initialize an object as allowed
+explicitly limited record type that is used to initialize an object as allowed
above, the implementation shall not create a separate return object (see 6.5)
for the function_call. The function_call shall be constructed directly in the
new object.
@@ -1021,7 +1021,7 @@
it applies to a function body. An @fa<extended_return_statement> shall apply to
a function body.
-For an extended_return_statement that applies to a function body:
+For an @fa<extended_return_statement> that applies to a function body:
@xbullet<If the result subtype of the function is defined by a
@fa<subtype_mark>, the @fa<return_subtype_indication> shall be a
@@ -1275,11 +1275,11 @@
@drepl
@xindent<@s9<The fact that the full view of File_Name is explicitly declared
-@key[limited] means that parameter passing and function return will always be
+@b<limited> means that parameter passing and function return will always be
by reference (see 6.2 and 6.5).>>
@dby
@xindent<@s9<The fact that the full view of File_Name is explicitly declared
-limited means that parameter passing will always be by reference and function
+@b<limited> means that parameter passing will always be by reference and function
results will always be built directly in the result object (see 6.2 and
6.5).>>
@@ -1913,6 +1913,1818 @@
all. However, if that is unacceptable to the majority, then (in the absence
of a better idea) I would rather drop the AI completely rather than give
users another (but different) crippled version of limited types.
+
+****************************************************************
+
+From: Stephen W. Baird
+Sent: Thursday, January 13, 2005 3:31 PM
+
+In the course of reviewing section 6, some questions came up about
+extended return statements (AI-318).
+
+Initially this discussion involved only Pascal, Randy, and the section 6
+reviewers (Steve B. and Jean-Pierre).
+
+It has become clear that a broader discussion is warranted.
+
+The discussion so far (with some minor editing) is attached.
+
+----
+
+Stephen W Baird/Cupertino/IBM wrote on 01/11/2005 03:09:16 PM:
+
+ If we get as far as entering the handled_sequence of statements of an
+ extended_return_statement (see AI-318), then clearly the object associated
+ with the return statement has been successfully initialized and it
+ will need to be finalized at some point.
+
+ If the return statement executes "normally" (i.e., if the final transfer
+ of control described in the dynamic semantics of a return statement is
+ executed), then the caller is responsible for this finalization.
+
+ Otherwise, the callee has to take care of this finalization (right?).
+ This could occur if the extended_return_statement is exited via a goto or exit
+ statement, or if an exception flies out of the handled_sequence_of_statements,
+ or if execution of the extended_return_statement is aborted (either by an
+ abort statement or via ATC).
+
+ Thus, the finalization rules for the return object are quite different
+ depending on how the extended_return_statement is exited.
+
+ It appears that the RM is missing a description of this distinction.
+
+ There also may be related problems in defining the master and
+ accessibility level of the function return object of an
+ extended_return_statement (see AI-162).
+
+ One approach would be to define an extended_return_statement to be
+ a master (in 7.6.1(3)), and therefore it would be the master of the
+ return object.
+
+ This would prohibit the following example
+
+ type R1 is record F : aliased Integer; end record;
+
+ function F1 return R1 is
+ type Ref is access all Integer;
+ Dangling : Ref;
+ begin
+ return Result : R1 do
+ Dangling := Result.F'Access; -- should not be legal
+ goto L;
+ end return
+ <<L>> Dangling.all := 123;
+ ... ;
+ end F1;
+
+ , and would result in the appropriate task-termination-awaiting in
+ the following variation
+
+ type R2 is record F : Some_Task_Type; end record;
+
+ function F2 return R2 is
+ begin
+ return Result : R2 do
+ goto L; -- must wait for Result.F to terminate
+ end return
+ <<L>> ... ;
+ end F2;
+
+ . On the other hand, this might introduce confusion (or worse) in the
+ case of a "normal" return and might require adding some
+ special rules to handle that case.
+
+ Another approach (the "black hole" model) would be to prohibit exiting
+ an extended_return statement without exiting the enclosing function. This
+ would require
+ - prohibiting goto statements and exit statements which would
+ transfer control out of an Extended_Return_Statement
+ - a dynamic semantics rule to the effect that an exception propagated
+ out of the Handled_Sequence_Of_Statements of an Extended_Return_Stmt
+ is propagated to the caller
+ . In this example,
+
+ function F return Integer is
+ E1, E2 : exception;
+ begin
+ return Result : Integer do
+ raise E1;
+ end return;
+ exception
+ when others => raise E2;
+ end F;
+
+ , this would mean that E1 would be propagated to the caller, not E2.
+ This seems unintuitive.
+
+ This approach has the advantage that there is no need to define what
+ happens if an extended_return_statement is exited without exiting
+ its enclosing function, but it does not eliminate the need for finalization
+ rules in the case where an extended_return_statement propagates an exception
+ to the caller.
+
+========
+
+Pascal Leroy/France/IBM wrote on 01/12/2005 03:52:45 AM:
+
+I don't like the "black hole" model
+because of its implication for exception handlers. On the other
+hand the other approach you mention is even less palatable as it
+seems to imply that the master of the return object might change
+during its lifetime.
+Now that I reread this, it also seems strange that initialization
+and finalization of the return object are done by different masters
+(in the normal case). Could this be the root of the problem? What
+if the caller always did initialization and finalization of the
+return object? Would that make any sense?
+
+========
+
+Jean-Pierre Rosen <rosen@adalog.fr> wrote on 01/12/2005 04:44:04 AM:
+
+Actually, the "black hole" model is what I had in mind when I read this
+remark. I don't like the idea of "canceling" a return statement in the
+middle, and if an exception is raised, I would expect it to be
+propagated to the caller, not inside the function.
+
+In short, I would expect these to be equivalent (assuming the return
+type is not limited):
+
+1)
+ declare
+ function F return T is
+ -- do something
+ end F;
+ begin
+ return F;
+ end;
+
+2)
+ return X : T do
+ -- Do the same thing
+ end return;
+
+(Note that in 1), there is no goto or exit issue, and that an exception
+is propagated).
+
+...
+
+If the model is that the caller creates the returned object before
+calling the function, yes, Pascal's idea of having the
+caller perform initialization and finalization of the return object
+would seem to make sense.
+
+========
+
+Stephen W Baird/Cupertino/IBM wrote on 01/12/2005 10:25:49 AM
+<responding to Pascal's message>:
+
+I don't like the black hole model either, mostly because it would be very
+confusing for users.
+However, I don't think it would be very difficult to implement:
+ Upon entering an extended_return_statement, a flag is set.
+ The handler for any handled_sequence_of_statements which encloses
+ an extended_return_statement would then query the flag and
+ do the right thing if the flag is set.
+Still, it is probably a bad idea.
+
+Your idea of having the caller perform default initialization is
+appealing, but it seems like it would be impossible
+to implement in case of an indefinite function result subtype.
+As a minor point, there is also the case where default initialization
+is not supposed to be performed:
+ return X : T := Explicit_Initial_Value do ... end return;
+Finally, there is the problem of modifying the function result object,
+exiting the extended_return_statement (e.g. via a goto) and then
+executing another extended_return_statement.
+
+========
+
+Pascal Leroy/France/IBM wrote on 01/12/2005 10:59:57 AM:
+
+> Your idea of having the caller perform default initialization is
+> appealing, but it seems like it would be impossible
+> to implement in case of an indefinite function result subtype.
+
+Yes, that part bothers me, although I haven't given it enough thought yet.
+
+> As a minor point, there is also the case where default initialization
+> is not supposed to be performed:
+> return X : T := Explicit_Initial_Value do ... end return;
+
+Good point. This is somewhat related to the previous issue, as the
+only case where this capability is important is for indefinite types
+(boxy discriminants or class-wide).
+
+> Finally, there is the problem of modifying the function result object,
+> exiting the extended_return_statement (e.g. via a goto) and then
+> executing another extended_return_statement.
+
+If the result object is initialized/finalized by the caller, this is
+not an issue. Exiting an extended_return_statement doesn't cause
+finalization of the result object, and re-entering another
+extended_return_statement (or the same one) doesn't cause it to be
+initialized. This is all well-defined. But again the indefinite
+case is problematic.
+
+========
+
+"Randy Brukardt" <randy@rrsoftware.com> wrote on 01/12/2005 11:19:29 AM:
+
+I completely agree with Jean-Pierre. I don't think any transfers out of an
+extended return statement should be allowed, and exceptions should be
+propagated directly to the caller. The master of the object is that of the
+caller (and yes, you may have to pass it in -- the initialization is done in
+the return statement, but it doesn't use the master that is textually
+there). I thought that was all obvious, it can't work any other way -- and I
+thought that the wording made that clear (I see I was wrong about that).
+
+Once you start a return statement, you have to return, not do other random
+junk. I don't see why that should be confusing to users (with the possible
+exception of the function's exception handler not working).
+
+========
+
+Stephen W Baird/Cupertino/IBM wrote on 01/12/2005 12:51:05 PM:
+
+> I don't see why that should be confusing to users (with the possible
+> exception of the function's exception handler not working).
+
+When I said that this approach would be confusing for users,
+I was talking about the behavior of exceptions.
+
+You say "The master of the object is that of the caller".
+
+I don't see how it can be that simple, even with the "black hole" model,
+because of the possibility that an extended_return_statement can propagate
+an exception (albeit directly to the caller).
+
+If an extended return statement propagates an exception, then
+
+ a) if the function result object has component tasks, who waits
+ for them to terminate?
+
+ b) if the function result object requires termination, when is
+ it performed?
+
+It would be very odd to require the caller to iterate over the components of
+the function result object (e.g. to perform finalization) in the case where
+the called function propagated an exception, particularly in the case where
+the function result subtype is indefinite.
+
+I suppose you could view this case as being a lot like an Unchecked_Deallocation
+of the function result object. That would mean that the callee would perform
+finalization but the caller would wait for tasks. We would also have to deal,
+one way or another, with the case of erroneous execution of "deallocated"
+discriminated tasks (see 13.11.2(11-15)).
+
+There is also the question of the static accessibility level of the function
+return object of an extended_return_statement. You certainly don't want to
+allow Local_Variable'Access as an access discriminant value for the function
+result object.
+
+========
+
+"Randy Brukardt" <randy@rrsoftware.com> wrote on 01/12/2005 01:24:59 PM:
+
+> You say "The master of the object is that of the caller".
+>
+> I don't see how it can be that simple, even with the "black hole" model,
+> because of the possibility that an extended_return_statement can propagate
+> an exception (albeit directly to the caller).
+
+Of course it's that simple. It's the same rules that you use for a "regular"
+function return that has finalizable components.
+
+> If an extended return statement propagates an exception, then
+>
+> a) if the function result object has component tasks, who waits
+> for them to terminate?
+
+The caller, of course.
+
+> b) if the function result object requires termination, when is it
+> performed?
+
+At the point that it would have happened if the function call had been
+successful.
+
+> It would be very odd to require the caller to iterate over the components
+> of the function result object (e.g. to perform finalization) in the case
+> where the called function propagated an exception, particularly in the
+> case where the function result subtype is indefinite.
+
+I don't think so. The only sensible rule is that the finalization/waiting
+(they are the same thing in my view) takes place at the same point whether
+the call returns normally or raises an exception during the return
+statement. Anything else would be madness - you'd have to move the object in
+the finalization chain and/or change its master partway through the
+evaluation of the return statement. That would be something that never
+happens in Ada 95, and it seems insane.
+
+> I suppose you could view this case as being a lot like an
+> Unchecked_Deallocation of the function result object. That would mean
+> that the callee would perform
+> finalization but the caller would wait for tasks. We would also have to
+> deal, one way or another, with the case of erroneous execution of
+> "deallocated" discriminated tasks (see 13.11.2(11-15)).
+
+No, finalization and task waiting take place at the same place.
+Unchecked_Deallocation is weird because the master is somewhere else, but
+that's a bug in my view - one that we can't change, of course. Task waiting
+and finalization are closely related things, and they should happen at the
+same place (even for Unchecked_Deallocation).
+
+> There is also the question of the static accessibility level of the
+> function return object of an extended_return_statement. You certainly don't
+> want to allow Local_Variable'Access as an access discriminant value for the
+> function result object.
+
+Right, but that should be obvious, and the rule needed is quite simple (the
+function object is less nested than the function).
+
+****************************************************************
+
+From: Gary Dismukes
+Sent: Thursday, January 13, 2005 4:20 PM
+
+> It has become clear that a broader discussion is warranted.
+
+After an initial reading of the exchange I agree with Randy's view that
+you can't exit out of an extended return statement (i.e., you have to
+return to the caller, whether normally or by exception) and that the
+caller has to perform all termination and finalization of the object.
+I haven't thought through all the implications of this, but it seems
+like the only reasonable model to me. Now, where are the gotchas?
+
+****************************************************************
+
+From: Stephen W. Baird
+Sent: Thursday, January 13, 2005 6:49 PM
+
+If the odd exception propagation rules don't bother you, then I don't
+think there are any big problems with the rule that you can't
+exit the handled_sequence_of_statements of an extended_return_statement
+without also exiting the enclosing function (i.e., the "black hole" rule).
+
+I just thought of another hole in this area that would need to be plugged,
+
+ function Bad_Transfer_Of_Control return T is
+ Return_Statement_Was_Entered : Boolean := False;
+ begin
+ select
+ delay 1.0;
+ then abort
+ return X : T do
+ Return_Statement_Was_Entered := True;
+ delay 10.0;
+ end return;
+ end select;
+
+ if Return_Statement_Was_Entered then
+ Put_Line ("Houston - we've got a problem");
+ end if;
+
+ ... ;
+ end Bad_Transfer_Of_Control;
+
+
+, but that's ok; we can ban extended_return_statements within the abortable
+part of an ATC statement (or make them abort-deferred, or ...).
+
+The "gotcha" as I see it is in the rule that "the
+caller has to perform all termination and finalization of the object".
+In the case where the function result subtype is, say, an unconstrained
+array subtype, and the function propagates an exception, this would mean
+that the callee would have to simultaneously propagate an exception and
+return an array for the caller to finalize. I don't see how to implement
+this without imposing distributed overhead on functions that don't use
+extended_return_statements.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, January 13, 2005 7:37 PM
+
+> The "gotcha" as I see it is in the rule that "the
+> caller has to perform all termination and finalization of the object".
+> In the case where the function result subtype is, say, an unconstrained
+> array subtype, and the function propagates an exception, this would mean
+> that the callee would have to simultaneously propagate an exception and
+> return an array for the caller to finalize. I don't see how to implement
+> this without imposing distributed overhead on functions that don't use
+> extended_return_statements.
+
+I don't see this as an issue with extended_return_statements, but rather
+with functions returning limited unconstrained subtypes. The rules require
+build-in-place, even for these sorts of functions. They have to, or limited
+functions don't work. The overhead has to be there even for a regular return
+statement, because they too are build-in-place. I view these to be more like
+procedures with a convenient syntax than functions, at lease in
+implementation.
+
+So, I think some sort of expensive special calling convention will be
+required for these things, so that the object can be created in the right
+place. That's going to be unpleasant, but its always necessary (even if a
+regular return is used to return an aggregate, for example -- you'll have
+precisely the same problems). I would expect that some implementations would
+have to pass a thunk to do that, or at least a package of storage pool/task
+master/finalization thumb.
+
+Anyway, I don't think that limited functions are going to be returning
+anything; the object (or a holder descriptor for it, for unconstrained
+subtypes) will be passed it, and the object will be constructed there.
+There's nothing to return (ever); the object gets finalized as for any other
+object constructed at that place. I could imagine an implementation
+returning a pointer to this object in the normal case (just so that it works
+like other functions), but that certainly wouldn't have anything to do with
+its finalization.
+
+
+
+I do think that there is an obvious transformation of such a function into
+constructs that we already understand. If LC is a limited controlled type,
+then:
+
+ type LC_Array is array (Positive range <>) of LC;
+
+ function Constructor return LC_Array is
+ begin
+ return (1..10 => <>);
+ end Constructor;
+
+ ...
+ Obj : LC_Array := Constructor;
+
+can be transformed into (with the same finalization meaning):
+
+ type LC_Array is array (Positive range <>) of LC;
+
+ type LC_Array_Access is access LC_Array;
+ type LC_Holder is new Ada.Finalization.Limited_Controlled with record
+ Item : LC_Array_Access;
+ end record;
+ procedure Constructor (Holder : in out LC_Holder) is
+ begin
+ Holder.Item := new LC_Array'(1..10 => <>);
+ end Constructor;
+ procedure Finalize (Holder : in out LC_Holder) is
+ begin
+ Free (Holder.Item);
+ end Finalize;
+
+ ...
+ Obj : Constructor;
+ Constructor (Obj);
+
+(Finalization of Obj forces the deallocation and finalization of the "Item"
+component.) Obviously, a compiler vendor can probably do better than this
+(without the explicit declarations, for instance), and probably would want
+to use a storage pool other than the default heap for this allocation.
+
+For an extended_return, the "body" of the return statement would be
+operating on the allocated item:
+
+ function Constructor return LC_Array is
+ begin
+ return D : LC_Array := (1..10 => <>) do
+ D(5) := ...;
+ end return;
+ end Constructor;
+
+would turn into:
+
+ procedure Constructor (Holder : in out LC_Holder) is
+ begin
+ Holder.Item := new LC_Array'(1..10 => <>);
+ begin
+ Holder.Item.all (5) := ...;
+ end;
+ end Constructor;
+
+Note that this transformation suggests that there is no real problem with
+transfers of control. But I still think it is weird to initiate a return
+statement and then goto out of it. So I think that should be illegal
+irrespective of any semantic issues. Exceptions seem to matter less, but I
+suspect that it would be easier to generate better code if you couldn't
+handle them locally.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, January 13, 2005 9:18 PM
+
+I think I have a somewhat different model. Here is what the AI
+says that is related to this issue:
+
+> DEALING WITH EXCEPTIONS
+>
+> There was some concern about what would happen if an exception were
+> propagated by an extended return statement, and then the same or some
+> other extended return statement were reentered. There doesn't seem to be
+> a real problem. The return object doesn't really exist outside the
+> function until the function returns, so it can be restored to its
+> initial state on call of the function if an exception is propagated from
+> an extended return statement. Once restored to its initial state, there
+> seems no harm in starting over in another extended_return_statement.
+
+In my view, the extended return statement should be treated
+like returning an aggregate, where all of the various statements
+after the "do" may be thought of as being squeezed into the middle
+of the aggregate. You can have arbitrary expressions in the
+middle of an aggregate, which can raise exceptions, etc., and
+these do *not* cause control to go to the caller. The exception
+is propagated to the enclosing exception handler, not directly
+to the caller. And until you hit the final right paren of the
+aggregate, or the "end" of the extended return, the object
+doesn't really "exist" as far as the outside world is concerned.
+The object can be returned to the initial state it had when
+the function was first called.
+
+I don't agree with Randy that finalization is performed by
+the caller in case of a failed extended return. I think
+a good model is a record that contains a controlled component
+and a regular component, where the regular component is
+initialized *after* the controlled component, and its initialization
+fails. In this case, we finalize the controlled component
+right away, even if, say, the record is being initialized
+as part of an allocator which wouldn't normally be finalized
+until much later.
+
+An interesting issue is how to handle task components.
+The question is when do they get added to the appropriate
+"activation list" which is presumably walked when the
+caller hits the point to activate the tasks. It seems
+simplest to let the caller take care of adding any task
+components to the activation list. This can presumably
+be done after the function returns, by walking the object
+to find all the task components and add them to the
+activation list. This would only happen if and when
+the function returns successfully. This saves having to
+pass in such an activation list, and avoids having to
+remove tasks from the list if the extended return
+statement fails in the middle.
+
+By the way, did we ever specify what happens if we have
+a limited aggregate as an actual parameter, and it
+has a task component? I would presume that all task
+components of such actual parameters are activated
+after evaluating all the parameters, immediately
+prior to invoking the body of the subprogram.
+It seems unwise to activate them piecemeal as the
+parameters are evaluated, as there is no defined
+order of parameter evaluation. It is sort of like
+the actual parameters are the components of a heap
+object, and the call represents the allocator.
+
+****************************************************************
+
+From: Robert I. Eachus
+Sent: Thursday, January 13, 2005 10:11 PM
+
+Randy Brukardt wrote:
+
+>I don't see this as an issue with extended_return_statements, but rather
+>with functions returning limited unconstrained subtypes. The rules require
+>build-in-place, even for these sorts of functions. They have to, or limited
+>functions don't work. The overhead has to be there even for a regular return
+>statement, because they too are build-in-place. I view these to be more like
+>procedures with a convenient syntax than functions, at lease in
+>implementation...
+
+I agree. We have to get this case right for any of this to make sense
+doing.
+
+>Note that this transformation suggests that there is no real problem with
+>transfers of control. But I still think it is weird to initiate a return
+>statement and then goto out of it. So I think that should be illegal
+>irrespective of any semantic issues. Exceptions seem to matter less, but I
+>suspect that it would be easier to generate better code if you couldn't
+>handle them locally.
+
+It is possible to make a transfer out of a return statement work, but I
+don't see much point to making implementors deal with it. The simplest
+rule seems to me to be that return statements create a scope for
+statement identifiers, and we fix the wording in 5.1(12&13) and 5.8(4)
+to match.
+
+As for exceptions, the rule should be similar, we don't care if an
+exception is raised AND handled inside the return statement. So the
+return statement should be outside the scope of exception handlers local
+to the function, but it may contain exception handlers. It is probably
+worth adding an example to the RM of a function call with nested
+exception handler just to show how to do the 'hard' case:
+
+ function Constructor return Limited_Array is
+ begin
+ return D: Limited_Array(1..10 => <>) do
+ for I in D'Range loop
+ begin
+ D(I) := ...;
+ exception when others =>
+ -- fix D(I) for some I.
+ end;
+ end loop.
+ end return;
+ end Constructor;
+
+with of course, an explanation pointing out that errors when allocating
+D will be handled by the caller, while errors in computing D(I) can be
+handled locally.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Thursday, January 13, 2005 10:24 PM
+
+Tucker wrote:
+
+...
+> In my view, the extended return statement should be treated
+> like returning an aggregate, where all of the various statements
+> after the "do" may be thought of as being squeezed into the middle
+> of the aggregate. You can have arbitrary expressions in the
+> middle of an aggregate, which can raise exceptions, etc., and
+> these do *not* cause control to go to the caller. The exception
+> is propagated to the enclosing exception handler, not directly
+> to the caller. And until you hit the final right paren of the
+> aggregate, or the "end" of the extended return, the object
+> doesn't really "exist" as far as the outside world is concerned.
+> The object can be returned to the initial state it had when
+> the function was first called.
+
+I certainly agree with you that the model is the same, but I don't agree
+with the conclusions that you draw from it. This model works in Ada 95
+because the aggregate is necessarily non-limited, so the aggregate is
+created into a temporary, and it isn't copied into the final object until
+after the function returns (or immediately before - in the context of the
+caller, in any case. A Program_Error raised by the Adjust copying into the
+final object will certainly not be caught by the function).
+
+The problem is, for limited functions, we have to build the aggregate (or
+extended_return_statement) in place. In *either* case, raising an exception
+in the middle is a problem, because the object is already partially
+constructed. And, at least in Janus/Ada, an object belongs to a particular
+master, and that is assumed to never change -- so the object gets finalized
+whenever that master goes away (unless it happens earlier, of course). So
+the object is constructed with an owner of the ultimate master - and thus
+won't get finalized until that master goes away.
+
+Your model would require changing the master of the object after it is
+created. While I suppose that there is some way to implement that, it would
+require a distributed overhead -- both adding extra nodes into the
+finalization chains to allow safe deletion from the main finalization chain,
+and of thunks that would have to be defined for all record types (or at
+least all limited record types).
+
+> I don't agree with Randy that finalization is performed by
+> the caller in case of a failed extended return. I think
+> a good model is a record that contains a controlled component
+> and a regular component, where the regular component is
+> initialized *after* the controlled component, and its initialization
+> fails. In this case, we finalize the controlled component
+> right away, even if, say, the record is being initialized
+> as part of an allocator which wouldn't normally be finalized
+> until much later.
+
+I think you're confused: there is no such rule in the Standard that I can
+find. The rules that exist pertain to a failed Adjust, and that rule (at the
+insistence of one S. Tucker Taft, as I recall) was relaxed to "might or
+might not be finalized". Finalization occurs when the object's master is
+left. In the case of a failed allocator, that could indeed be a long time in
+the future. I think the model you describe would be wrong, because it would
+finalize the object too soon.
+
+The overhead of the model that you espouse here would be severe -- it could
+only be implemented by putting an exception handler around *any*
+initialization that could possibly fail. Unless the implementation has
+zero-cost exception handlers, that's going to be a lot of expense. (We have
+to do that for Finalize calls, and it is by far the largest overhead of
+finalization in normal use. In the case of finalization, there really isn't
+a choice (because we can't let something failed poison an unrelated
+abstraction), but I don't see any special issue with initialization (as long
+as the finalization actually happens eventually).
+
+> An interesting issue is how to handle task components.
+> The question is when do they get added to the appropriate
+> "activation list" which is presumably walked when the
+> caller hits the point to activate the tasks. It seems
+> simplest to let the caller take care of adding any task
+> components to the activation list. This can presumably
+> be done after the function returns, by walking the object
+> to find all the task components and add them to the
+> activation list. This would only happen if and when
+> the function returns successfully. This saves having to
+> pass in such an activation list, and avoids having to
+> remove tasks from the list if the extended return
+> statement fails in the middle.
+
+You seem to be separating tasks and finalizable objects. I think that is
+serious mistake -- they should follow the same rules. Moreover, it is too
+late; tasks are created belonging to a master in Janus/Ada (so that they can
+be cleaned up if activation fails), so we'd have to pass in the master and
+activation list into the function. And, in any case, walking components of a
+record is that very expensive operation requiring a custom thunk for *all*
+record types (to avoid contract problems). You're telling us we need two of
+them? You're insane!
+
+> By the way, did we ever specify what happens if we have
+> a limited aggregate as an actual parameter, and it
+> has a task component?
+
+Yes, it's the AI-162 that you were the last person to rewrite. I seriously
+think that you are working too hard! (Now go review your sections of the
+AARM. :-) We redefined masters so that expressions and subprogram calls have
+them.
+
+> I would presume that all task
+> components of such actual parameters are activated
+> after evaluating all the parameters, immediately
+> prior to invoking the body of the subprogram.
+> It seems unwise to activate them piecemeal as the
+> parameters are evaluated, as there is no defined
+> order of parameter evaluation. It is sort of like
+> the actual parameters are the components of a heap
+> object, and the call represents the allocator.
+
+God, I hope not. Activating tasks is complicated enough without deciding to
+treat parameters specially. Why the heck would anyone care what order
+they're activated in anyway? It's usually unspecified, so you couldn't
+depend on anything about it anyway. And, as you say, the order of the
+parameters is unspecified; so how could one parameter even be able to
+determine if another parameter has it's tasks started? It certainly can't
+see them! I do agree that a single parameter would have to be activated like
+an allocator (it has to be activated somewhere). But we evaluate each
+parameter individually, and trying to tie them together would certainly add
+additional complexity for no benefit.
+
+
+I'm pretty much ready to give up on the entire idea of limited aggregates
+and functions, because it simply isn't worth the headaches that you guys
+keep coming up with. It would be tempting to disallow aggregates and
+functions that contain tasks, but we know that that sort of restrictions
+never work. You've certainly convinced me that it couldn't possibly be worth
+trying to implement these. (Probably ever.) Sigh.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Thursday, January 13, 2005 11:00 PM
+
+> ... Exceptions seem to matter less, but I
+> suspect that it would be easier to generate better code if you couldn't
+> handle them locally.
+
+I don't agree. I think we want things to work as similarly
+as possible between limited and non-limited, and between
+normal and extended returns. We know that in Ada 95,
+if you return an aggregate, and something fails in the
+middle of creating the aggregate, you can handle that
+inside the function. This should also be true if the
+type being returned happens to be limited, and should
+be true if the aggregate is turned into an extended
+return statement with statements initializing the components,
+and finally, it should be true if it is a limited extended
+return.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, January 14, 2005 12:05 AM
+
+I have to agree, but I come to the opposite conclusion: the way non-limited
+returns work currently is unacceptable, and if you really want them to be
+exactly the same, we'll need to change non-limited to match limited.
+
+The reason is that the current semantics essentially require a temporary;
+build-in-place is not allowed for non-limited types is not allowed. That's
+because build-in-place has to be undone if an exception occurs after the
+evaluation of the aggregate starts. We deal with that for regular
+assignments by checking the aggregate for the possibility of raising an
+exception before doing build-in-place, but for any type with controlled
+components, such a check must necessarily fail (we cannot know what happens
+in Adjust).
+
+My intent was to use build-in-place (that is, the new calling convention)
+for all record types. That would get rid of the obnoxious temporary that
+can't be optimized away, and which makes composite functions something to
+avoid whenever you care about performance. But you're saying that
+build-in-place can never be done for a non-limited type with controlled
+components, because you can tell if the target object is finalized before
+the function starts evaluating the aggregate or other expression (the
+function can handle the exception and then access the target). So you always
+have to use a temporary (backing out a user-defined Finalize is impossible).
+
+As someone who believes that the vast majority of types should be
+controlled, I cannot justify a significant required overhead that has
+virtually no user benefit. If a user really wants an explicit temporary,
+they can write one.
+
+In any case, we're pretty much required to use the same convention for
+limited and non-limited functions, because otherwise generics wouldn't work.
+And being forced to make a temporary at each call site is worse than the
+current situation, because the function might decide it has to make a copy
+too.
+
+In any case, the point of this exercise (from my perspective) was to get
+better performance for all controlled types. If the performance is going to
+be worse, I'd be better off forgetting I ever heard about AI-318 (because
+there is no reason to do a lot of work to end up with worse performance).
+Or, perhaps, forgetting about the Ada standard and doing it right would make
+the most sense. Neither would help Ada in the long run.
+
+Anyway, I've wasted far too much time on this discussion. I've made my
+position clear; I would rather drop AI-318 than be forced into Tucker's
+semantics.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, January 14, 2005 9:10 AM
+
+> I have to agree, but I come to the opposite conclusion: the way non-limited
+> returns work currently is unacceptable, and if you really want them to be
+> exactly the same, we'll need to change non-limited to match limited....
+
+That seems like a potentially serious incompatibility.
+I can imagine there is a non-trivial amount of code that
+looks like:
+
+ function Blah... is
+ begin
+ return Fum(...);
+ exception
+ when ... =>
+ raise Different_Exception;
+ end Blah;
+
+If exceptions propagated by Fum are not caught by the exception
+handler of Blah, we may break a lot of code which assumes
+that only "Different_Exception" is propagated from Blah.
+
+I understand your implementation concerns, but I think they
+are all manageable, though certainly not trivial. Yes, you
+may have to figure out how to finalize a partially initialized
+object sooner than normal, but unchecked-deallocation knows
+how to do that, and I believe most compilers clean up partially
+initialized allocated objects right away, rather than waiting
+until the heap as a whole is finalized.
+
+It would be significantly worse, in my view, to have
+to go back and change the way these things work now, while
+also subjecting existing code to an incompatible, inconsistent
+change in run-time semantics.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, January 14, 2005 9:57 AM
+
+Tucker wrote:
+
+> function Blah... is
+> begin
+> return Fum(...);
+> exception
+> when ... =>
+> raise Different_Exception;
+> end Blah;
+
+Or how about:
+
+ function Blah... is
+ begin
+ return Fum(...);
+ exception
+ when ... =>
+ return Alternate_Result(...);
+ end Blah;
+
+It does seem that a function should be able to handle an exception
+raised anywhere within it. (Well, ahem, cough... except in the handler
+itself.)
+
+****************************************************************
+
+From: Gary Dismukes
+Sent: Friday, January 14, 2005 11:48 AM
+
+> It does seem that a function should be able to handle an exception
+> raised anywhere within it. (Well, ahem, cough... except in the handler
+> itself.)
+
+And, ahem, cough, in the function's declarative part. ;)
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, January 14, 2005 3:22 PM
+
+> And, ahem, cough, in the function's declarative part. ;)
+
+Well, sure, but you can always put that code into a block statement.
+You can put the exception handlers in a block statement, too,
+but that leads to infinite regress.
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Friday, January 14, 2005 11:43 AM
+
+From the text of the AI:
+
+The syntax for extended return statements was initially proposed early
+on, but when this AI was first written up, we proposed instead a revised
+object declaration syntax where the word "return" was used almost like
+the word "constant," as a qualifier. This was somewhat more economical
+in terms of syntax and indenting, but was not felt to be as clear
+semantically as this current syntax.
+
+Given the current number of worms struggling to get out of the can,
+would it be appropriate to reconsider this solution?
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, January 14, 2005 1:59 PM
+
+I believe Randy has made a number of good arguments that
+indicate this has relatively little to do with the
+extended return statement, and even less to do with
+the specifics of its syntax. It is mostly related
+to returning limited objects, whether they be specified
+by an aggregate, function call, or extended return
+statement. In all cases you need to specify what happens
+if an exception is propagated before the return statement
+is completed.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, January 14, 2005 4:06 PM
+
+> Your model would require changing the master of the object after it is
+> created.
+
+I don't see that.
+
+Consider an uninit allocator of an array, in Ada 95:
+
+ type A is array(Positive range <>) of Some_Lim_Controlled;
+ type P is access A;
+
+ ... new A(1..10) ...
+
+If an exception is raised in the middle of initializing components, the
+implementation is required to finalize the ones for which Initialize
+completed successfully, and is forbidden from finalizing the others.
+
+I think the easiest way to implement this is to catch any exceptions
+that occur in the middle of initialization, finalize as necessary, and
+then reraise. This requires keeping track of how far through the array
+you got -- but that's necessary anyway -- this is just the loop index.
+
+Are you saying this implementation is wrong, and that finalization must
+wait until the collection is finalized? If so, I think we'd better
+change the rules to allow this, because it's the easiest and most
+efficient, and I believe many implementations already do it.
+
+(It's more efficient, because otherwise, you'd have to store the index
+of how far you got in the heap, for later use.)
+
+In other words, it's already the case (at least on many implementations)
+that finalization takes place earlier than the master, and this does not
+involve "changing masters". Therefore, the same could apply to these
+new kinds of returns.
+
+----------------
+
+I think your other concern was doing:
+
+ X := F(...);
+
+in the nonlimited controlled case. You don't want to have to make
+temporaries. I agree. Why don't we simply allow that? That is, an
+implementation is free to finalize X first, then pass its address to F,
+which can build-in-place -- if the implementation so chooses.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, January 14, 2005 4:47 PM
+
+> I think your other concern was doing:
+>
+> X := F(...);
+>
+> in the nonlimited controlled case. You don't want to have to make
+> temporaries. I agree. Why don't we simply allow that? That is, an
+> implementation is free to finalize X first, then pass its address to F,
+> which can build-in-place -- if the implementation so chooses.
+
+This seems like another potentially dangerous incompatibility.
+Suppose you have:
+
+ X := F(X(1..3));
+
+or X is visible up-level to F. You can't finalize X if
+F might be able to see part or all of X during its execution.
+
+Perhaps you meant we should allow pre-finalization of X
+if its value is not needed to evaluate the right hand side,
+and there is no chance it will be visible in an exception
+handler if F propagates an exception. The current language
+allows aborts to occur between the finalize and the
+copy-and-adjust steps. Allowing the evaluation of the
+right hand side to occur then is probably also OK, given
+the above provisos. But your compiler will have to do
+the analysis to determine it is safe.
+
+Our compiler essentially already does this analysis for
+*non* controlled types, I believe, to decide whether it
+is safe to pass in the address of the left hand side
+as the result temp for the function. We could certainly
+do this for controlled objects as well, if we could perform
+finalization before the call when safe.
+
+But I suspect Randy was looking for a single approach
+that was always allowed, without having to do any analysis
+at the call site. In that case, he is stuck for an assignment
+statement. Of course, if the function call is used to initialize a
+new object, then you can always safely pass in the address
+of the new object.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Friday, January 14, 2005 9:47 PM
+
+> If an exception is raised in the middle of initializing components, the
+> implementation is required to finalize the ones for which Initialize
+> completed successfully, and is forbidden from finalizing the others.
+
+Correct.
+
+> I think the easiest way to implement this is to catch any exceptions
+> that occur in the middle of initialization, finalize as necessary, and
+> then reraise. This requires keeping track of how far through the array
+> you got -- but that's necessary anyway -- this is just the loop index.
+
+This I totally disagree with. Certainly, you *could* try to implement it
+that way, but it would be a lousy implementation for the majority of
+compilers. First, it requires a "virtual" exception handler, and that has a
+significant cost on most systems. Second, figuring out where you are is
+possible in simple cases (like this), but it doesn't generalize in any
+sensible way. When you have discriminated types with variants and arrays,
+and multiple controlled components nested to several levels (which in fact
+happens in some Claw example programs), it just becomes a nightmare.
+
+It makes much more sense for each controlled part (with the technical
+meaning of part) to be initialized and finalized individually. Each gets
+registered when its initialization finishes successfully, so you can only
+finalize those that have finished.
+
+> Are you saying this implementation is wrong, and that finalization must
+> wait until the collection is finalized? If so, I think we'd better
+> change the rules to allow this, because it's the easiest and most
+> efficient, and I believe many implementations already do it.
+
+It's clearly wrong: the allocated object belongs to the collection, and
+shouldn't be finalized until the collection goes away. For a declared
+object, you can't tell the difference. And you can't tell the difference in
+Ada 95, either, because the type necessarily must be non-limited, and you
+can't tell the difference between the object being created in a temporary
+(which is finalized immediately, before the allocated object is even
+created) or this implementation.
+
+But for a limited object, the object has to be built-in-place, and thus the
+implementation is clearly wrong (*only* for a limited type). Whether that
+should be relaxed is an open question. If you want to require the above
+behavior, I'll fight you until the ends of the earth - it's clearly
+requiring a horrible implementation, and buys nothing for users. But if you
+just want to allow it, I don't particularly care. OTOH, we have generally
+specified finalization behavior of limited types in Ada without allowing
+much optimization, so I would tend to be conservative with these rules.
+
+> (It's more efficient, because otherwise, you'd have to store the index
+> of how far you got in the heap, for later use.)
+
+You have to store *something* in the heap in order to know to finalize these
+objects anyway (and usually that needs to be quite a bit - a subprogram
+address and static link and a chain, at a minimum), so I don't see much
+reason to worry about another word. For a chained implementation, there is
+no extra cost at all (just make sure its linked on the right chain).
+
+> In other words, it's already the case (at least on many implementations)
+> that finalization takes place earlier than the master, and this does not
+> involve "changing masters". Therefore, the same could apply to these
+> new kinds of returns.
+
+Such implementations are wrong for limited types (certainly with the rules
+as written). It's an "as-if" optimization for non-limited types, so it's
+fine to use it in Ada 95 and in Ada 2005.
+
+To even allow your implementation would require writing quite a bit of
+really tricky wording - I don't even know all of the places where it would
+have to be allowed.
+
+To implement something like what you describe in Janus/Ada certainly is
+possible (in the sense that anything is possible), but it would be quite a
+hit in performance, because you'd have to do everything twice. "Changing
+masters" would certainly be the cheapest way to do it, easiest would be a
+handler and explicit finalize in the sense of Unchecked_Deallocation (but
+that would be far more expensive because of exception handling overhead).
+But the latter only works on the "collections" created for access types,
+because those don't have pointers into them. The main finalization chain is
+full of pointers into it. The only way to allow early finalization of stack
+objects or changing a master oof a stack object would be to add additional
+dummy nodes for the pointers to point at. That would add overhead to all
+programs with finalization (with a lot of extra work, the extra overhead
+could be mitigated in blocks that don't do anything nasty, but that would be
+very expensive to check with any degree of accuracy).
+
+I prefer to keep the model simple, which is to finalize at the master of the
+object.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, January 14, 2005 9:59 PM
+
+...
+> This seems like another potentially dangerous incompatibility.
+> Suppose you have:
+>
+> X := F(X(1..3));
+>
+> or X is visible up-level to F. You can't finalize X if
+> F might be able to see part or all of X during its execution.
+
+Right. I thought about that on the way home last night. It would have to
+work like optimizing slices or (non-limited) aggregate assignments.
+
+...
+> Our compiler essentially already does this analysis for
+> *non* controlled types, I believe, to decide whether it
+> is safe to pass in the address of the left hand side
+> as the result temp for the function. We could certainly
+> do this for controlled objects as well, if we could perform
+> finalization before the call when safe.
+
+Exactly what I had in mind.
+
+> But I suspect Randy was looking for a single approach
+> that was always allowed, without having to do any analysis
+> at the call site. In that case, he is stuck for an assignment
+> statement. Of course, if the function call is used to initialize a
+> new object, then you can always safely pass in the address
+> of the new object.
+
+Not really. It's clearly going to be necessary to be able to use a temporary
+(for all types), because the function or aggregate could be directly passed
+as a parameter, thus there being no object to assign into. Once you have
+that, doing it for function calls if needed is fine. It's the "if needed"
+that matters; you don't want simple functions:
+
+ Ten := To_Unbounded_String ("Ten");
+
+to have to use temporaries soley to get the finalization "right". We already
+have lots of rules allowing optimizations in this area, so one more is
+unlikely to be harmful.
+
+It should never be necessary to use a temporary for:
+ Ten : Unbounded_String := To_Unbounded_String ("Ten");
+and I certainly want that be true for *all* types, not just limited types.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Saturday, January 15, 2005 9:35 AM
+
+Randy wrote:
+
+> But for a limited object, the object has to be built-in-place, and thus the
+> implementation is clearly wrong (*only* for a limited type).
+
+My example showed a limited type.
+
+What you seem to be saying is that the suggested implementation is wrong
+only for limited types, *and* only for heap objects. That seems insane.
+Why should heap objects be different from stack objects, here?
+(Again, I'm talking about the limited case.)
+
+The heap object in question is inaccessible (the allocator failed!),
+so *requiring* it to remain in existence until collection finalization
+seems to be of zero benefit to the user. [I realize there are sneaky
+ways to make this half-baked object accessible, but those are bugs
+waiting to happen. My point is that the pointer returned by "new" never
+arrives in this case.]
+
+>... Whether that
+> should be relaxed is an open question. If you want to require the above
+> behavior, I'll fight you until the ends of the earth - it's clearly
+> requiring a horrible implementation, and buys nothing for users. But if you
+> just want to allow it, I don't particularly care.
+
+I said "allow", not "require". Certainly, if an implementation has
+expensive exception handlers, that changes the trade-offs. We need not
+argue about which methods are "best" for all compilers.
+
+As I said, I'm surprised it's not already allowed, and I believe there
+are implementations that do this sort of thing in some or all cases.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Saturday, January 15, 2005 12:43 PM
+
+...
+>>I think the easiest way to implement this is to catch any exceptions
+>>that occur in the middle of initialization, finalize as necessary, and
+>>then reraise. This requires keeping track of how far through the array
+>>you got -- but that's necessary anyway -- this is just the loop index.
+>
+> This I totally disagree with. Certainly, you *could* try to implement it
+> that way, but it would be a lousy implementation for the majority of
+> compilers.
+
+Be that as it may, we implement it this way, and I believe so
+does Rational. I'm not sure about GNAT.
+
+> ...
+> It makes much more sense for each controlled part (with the technical
+> meaning of part) to be initialized and finalized individually. Each gets
+> registered when its initialization finishes successfully, so you can only
+> finalize those that have finished.
+
+I believe Rational keeps careful track of what components
+have been initialized, and then finalizes just those. I believe
+Bob was involved in implementing that. The AdaMagic approach is to
+have a "components master" which we use temporarily for registering
+components that need finalization, and then at some point we
+convert to a single registration for the whole object, throwing
+away the components master. If things get interrupted in the
+middle, then the components master is the "innermost" master,
+and the components on it get cleaned up. After we make the
+switch, the clean up of the components is embedded in a
+whole-object clean up routine generated for types with multiple
+finalizable components. The components master is unlinked
+from the chain of masters, and the cleanup routine for
+the object as a whole is linked onto the appropriate master.
+
+I'm sure there a million ways to implement this, and what makes
+the most sense will vary from one implementation to the next,
+depending on their run-time model, and on other tradeoffs they
+choose to make.
+
+> ...
+> It's clearly wrong: the allocated object belongs to the collection, and
+> shouldn't be finalized until the collection goes away....
+
+It might be hard to find "clear" wording saying this.
+And it seems undesirable. It is also interesting that
+"garbage collection" is permitted by the language, but
+never defined in detail nor is there a clear explanation
+of how garbage collection relates to finalization.
+
+> But for a limited object, the object has to be built-in-place, and thus the
+> implementation is clearly wrong (*only* for a limited type). Whether that
+> should be relaxed is an open question.
+
+As far as I know, AdaMagic (and hence Green Hills and Aonix) and
+Rational both attempt to finalize partially initialized objects
+right away. I can't imagine any value to the user to postpone
+this finalization, and by finalizing right away, we can reclaim
+the space that much sooner.
+
+> ...
+> If you want to require the above
+> behavior, I'll fight you until the ends of the earth - it's clearly
+> requiring a horrible implementation, and buys nothing for users.
+
+This seems a bit of an overstatement, and reclaiming storage
+sooner seems more than "nothing."
+
+> ...
+> But if you
+> just want to allow it, I don't particularly care. OTOH, we have generally
+> specified finalization behavior of limited types in Ada without allowing
+> much optimization, so I would tend to be conservative with these rules.
+
+It sounds like we need some clarification of this issue.
+If an allocator fails before creating the access value or
+activating component tasks, it seems difficult to justify
+requiring deferring finalization, and based on your strong statement,
+perhaps also difficult to justify requiring immediate
+finalization. Also, garbage collection needs to be factored
+into the finalization rules.
+
+> ...
+> Such implementations are wrong for limited types (certainly with the rules
+> as written).
+
+It would be interesting to identify these rules. We probably
+want to make them clearer, and given existing implementations,
+be permissive of either immediate or deferred finalization of
+partially initialized heap objects.
+
+****************************************************************
+
+From: Bob Duff
+Sent: Saturday, January 15, 2005 1:22 PM
+
+> "garbage collection" is permitted by the language, but
+> never defined in detail nor is there a clear explanation
+> of how garbage collection relates to finalization.
+
+There is some discussion of this in 13.11.3, much of which was banished
+to the AARM because we didn't think it was worth putting in the RM,
+given the lack of Ada implementations supporting GC. We figured, if
+somebody wants to implement GC, let *them* figure out all the
+interactions with finalization, with some hints about the issues in the
+AARM. But it's clear that the intent was that a GC'ed implementation
+would finalize the collected objects "prematurely".
+
+There are Ada implementations on top of the Java Virtual Machine and
+the .NET virtual machine. I presume they deal with these interactions
+by letting the virtual machine do its thing.
+
+I firmly believe that Ada should *allow* GC, just like I firmly believe
+that Ada should *allow* generic code sharing, despite the fact that
+(sadly) not too many implementations do these nice things.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Saturday, January 14, 2005 2:58 PM
+
+Here is a statement that I believe, at least in part,
+disallows premature finalization of heap objects (7.6.1(10)),
+and should be revised, probably:
+
+ ... If an instance of Unchecked_Deallocation is never
+ applied to an object created by an allocator, the object
+ will stilll exist when the corresponding master completes,
+ and it will be finalized then.
+
+****************************************************************
+
+From: Robert I. Eachus
+Sent: Saturday, January 15, 2005 9:07 PM
+
+I think that this argument is getting very far away from the original
+intent of this feature. The decision that has to be made IMHO is to
+keep the ability to initialize limited objects, or to let some notion of
+linguistic purity get in the way.
+
+In current Ada, if a function has an exception handler, that handler
+does not catch all exceptions raised between the call and return. If a
+user writing a function whose definition requires handling all
+exceptions that may be raised internally, then he knows how to use
+nested blocks and so forth to insure this. At first it looks like the
+necessary rule here would create undue hardship. But it won't. It will
+hardly even come up.
+
+Why? Because within the function returning a limited object, the object
+may not be limited. More often the case will be that the object will be
+partially limited. The function will be defined in a location that can
+"see into" the limited object. The parent part of the object may still
+be limited in this view, but that is fine. The initialization function
+called for the parent part will initialize that part of the object, and
+handle any exceptions it should and can internally.
+
+This leaves only two potential concerns. First an exception caused by
+the creation of the entire object being returned. But that is not a
+problem, at least as I see it. The object may be defined in the
+declarative part--and doesn't get handled locally anyway. More likely
+it gets defined before the keyword *do*. We can discuss that particular
+case at length, but I don't see the errors that may occur there (as
+opposed to being propagated there) as being all that important.
+*Storage_Error *may occur, but this is Dave Emery's parachute that opens
+on impact. Not when the object is too large for the heap--that case
+should work. But when the function is called with only a few words left
+on the stack, predicting where *Storage_Error* will occur and which
+handler might see it, is futile.
+
+If a programmer wants to handle Tucker's or Bob Duff's examples, he will
+write:
+
+<>Tucker wrote:
+
+ function Blah... is
+ begin
+ return Fum(...);
+ exception
+ when ... =>
+ raise Different_Exception;
+ end Blah;
+
+Or
+
+ function Blah... is
+ begin
+ return Fum(...);
+ exception
+ when ... =>
+ return Alternate_Result(...);
+ end Blah;
+
+Both are legal today, and AFAIK we are not talking about changing that,
+with the possible exception of limited objects being built in place.
+But the potential problem case as I see it, is when there is a sequence
+of statements within the return:
+
+ function Constructor return Limited_Array is
+ begin
+ return D: Limited_Array(1..10 => <>) do
+ for I in D'Range loop
+ begin
+ D(I) := ...;
+ exception when others =>
+ -- fix D(I) for some I.
+ end;
+ end loop.
+ end return;
+ end Constructor;
+
+And here there are no philosophical or other issues. The user can declare a
+handler inside the return, and it will do exactly what he wants. Of course, we
+do need to disallow gotos out of the scope and so on, but I hope we have
+already agreed on that already.
+
+This means that the *creation* of the object to be returned is the bone of
+contention. But I guess that I don't see that either. If the object is being
+built in place, how can it matter to the user whether it is the creation of the
+'temporary' or the 'target' that causes the exception? Certainly in:
+
+ Foo: Limited_Bar := Constructor(...);
+
+The user is not going to be surprised to get *Storage_Error* outside the
+constructor if there is not enough space to create Foo, or *Tasking_Error* if
+he has exceeded the limit on the number of tasks. So I just don't see a real
+problem here.
+
+Programmers will need to know that exceptions in the sequence_of_statements in
+a return statement occur after the enclosing scope has been left. But that is
+the sort of thing that is dealt with by an example in the RM. I could even see
+resolving the dispute between Tucker and Randy by allowing exceptions caused by
+creating and allocating the returned object to be handled in either the
+function or the caller as the implementation chooses. I don't really think it
+is as bad as all that though. There are some exceptions that will occur in the
+caller, others that can occur in the function, and a lot where either scope
+will be appropriate. I just can see destroying a useful new functionality by
+getting pedantic about potentially obscure cases. Yes, we have to define it,
+but no we don't have to over define it.
+
+****************************************************************
+
+From: Robert Dewar
+Sent: Saturday, January 15, 2005 9:11 PM
+
+I would hesitate to revise this. Allowing arbitrary early
+finalization can be disastrous to fundamental semantics of
+currently correct programs that use finalization e.g. to
+properly unlock something at the proper point. Garbage
+collection can occur early in such a case, since very
+likely we are using a dummy variable that no one references.
+
+In GNAT we have a pragma Finalize_Storage_Only, which says
+that the only reason for a finalization routine is to free
+storage. We had in mind two purposes
+
+1. Skip finalization at the outer level when program terminates
+(this is implemented in GNAT now).
+
+2. Allow early finalization in the garbage collected case.
+
+I really believe it is essential not to introduce a giant
+upwards incompatibility here!
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Sunday, January 16, 2005 9:30 AM
+
+This is *not* talking about local variables, even unreferenced
+local variables. In any case we have existing rules that disallow
+removing local variables of a limited controlled type.
+
+This is talking about objects created by an allocator, and
+most specifically, the issue at hand is components created
+by an allocator that *fails* due to an exception raised
+during its evaluation. I believe that we want to encourage
+implementations to recover the space for a failed allocator
+as soon as possible, which implies finalizing the components
+of the as-yet-incomplete-heap-object immediately, rather than
+at the point where the access type goes out of scope.
+
+****************************************************************
+
+From: Robert Dewar
+Sent: Sunday, January 16, 2005 9:42 AM
+
+It still worries me to assume that we are only talking about
+recovering space here. This would be a definite incompatibility.
+I just don't know how severe a one. If all the finalizer does
+is to deallocate, then that's not an issue, if it has unbounded
+interesting side effects, such as referencing something that
+does not exist until later on, then this seems a recipe for
+semantic confusion to me.
+
+****************************************************************
+
+From: Jean-Pierre Rosen
+Sent: Sunday, January 16, 2005 11:51 AM
+
+> I believe Randy has made a number of good arguments that
+> indicate this has relatively little to do with the
+> extended return statement, and even less to do with
+> the specifics of its syntax. It is mostly related
+> to returning limited objects, whether they be specified
+> by an aggregate, function call, or extended return
+> statement. In all cases you need to specify what happens
+> if an exception is propagated before the return statement
+> is completed.
+
+Of course, finalization issues are here to stay.
+I just thought that this would get rid of the exit/goto/exception issues.
+
+****************************************************************
+
+From: Robert I. Eachus
+Sent: Sunday, January 16, 2005 10:54 PM
+
+>It still worries me to assume that we are only talking about
+>recovering space here. This would be a definite incompatibility.
+>I just don't know how severe a one. If all the finalizer does
+>is to deallocate, then that's not an issue, if it has unbounded
+>interesting side effects, such as referencing something that
+>does not exist until later on, then this seems a recipe for
+>semantic confusion to me.
+
+I tend to agree with Tucker that the concern you have here is
+misplaced. There are some tough issues involving creation of a complex
+object containing tasks, but other rules seem to me to prevent those
+from ever surfacing. None of the tasks which are part of the object
+will ever be activated. A task designated by an access variable in the
+object could be created by an allocator, and be activated even though
+the object as a whole was never created. But such a task could never be
+referenced, and should be considered a program design error (or an ACVC*
+test case ;-) independent of what goes on here.
+
+I see the other issue that Robert raises as again, a possible problem,
+but one which is larger than what could arise in this case. If you
+create a finalization routine which assumes that all objects of the type
+have the same lifetime, then finalizing one of these objects early due
+to a failure during creation of a larger object is a potential problem.
+But that exception, which will occur in a declarative part (for any
+limited object). So *any* object created in that declarative part can
+be finalized early, or out of the expected order. Doesn't matter what
+is decided here. The program designer (or tester) is going to have to
+think about such things in every case where there is a complex
+initialization in a declarative part. This was true in Ada 83, and is
+true in Ada 95. Any potential exception in a declarative part has to be
+treated and tested as adding an additional path through the unit. (What
+I like about Ada compared to PL/I here is that each such case only adds
+one path. You don't get exponential explosions.)
+
+*If anyone reading this is too young to remember certain ACVC tests that
+didn't make it into the ACATS, consider yourself lucky.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Monday, January 147 2005 5:10 PM
+
+Tucker Taft wrote, replying to Robert Dewar:
+
+> Robert Dewar wrote:
+> > Tucker Taft wrote:
+> >
+> >> Here is a statement that I believe, at least in part,
+> >> disallows premature finalization of heap objects (7.6.1(10)),
+> >> and should be revised, probably:
+> >>
+> >> ... If an instance of Unchecked_Deallocation is never
+> >> applied to an object created by an allocator, the object
+> >> will stilll exist when the corresponding master completes,
+> >> and it will be finalized then.
+> >
+> > I would hesitate to revise this. Allowing arbitrary early
+> > finalization can be disastrous to fundamental semantics of
+> > currently correct programs that use finalization e.g. to
+> > properly unlock something at the proper point. Garbage
+> > collection can occur early in such a case, since very
+> > likely we are using a dummy variable that no one references...
+>
+> This is *not* talking about local variables, even unreferenced
+> local variables. In any case we have existing rules that disallow
+> removing local variables of a limited controlled type.
+>
+> This is talking about objects created by an allocator, and
+> most specifically, the issue at hand is components created
+> by an allocator that *fails* due to an exception raised
+> during its evaluation. I believe that we want to encourage
+> implementations to recover the space for a failed allocator
+> as soon as possible, which implies finalizing the components
+> of the as-yet-incomplete-heap-object immediately, rather than
+> at the point where the access type goes out of scope.
+
+I don't know what we want to do, but I thought I'd try to shed a bit of
+light on this topic. So I wrote an example program of the case that we're
+talking about.
+
+-----
+
+with Ada.Finalization;
+package Checkit2 is
+
+ Created_Yet : Boolean := False;
+
+ Finalized_Yet : Boolean := False;
+
+ type Check_Type is new Ada.Finalization.Limited_Controlled with null record;
+
+ procedure Initialize (Obj : in out Check_Type);
+
+ procedure Finalize (Obj : in out Check_Type);
+
+ function Raise_P_E return Integer;
+
+end Checkit2;
+
+package body Checkit2 is
+
+ procedure Finalize (Obj : in out Check_Type) is
+ begin
+ Finalized_Yet := True;
+ end Finalize;
+
+ procedure Initialize (Obj : in out Check_Type) is
+ begin
+ Created_Yet := True;
+ end Initialize;
+
+ function Raise_P_E return Integer is
+ begin
+ raise Program_Error;
+ return 10;
+ end Raise_P_E;
+
+end Checkit2;
+
+with Ada.Text_IO;
+with Checkit2;
+procedure Check2 is
+ -- Check when finalization of a failed limited allocator occurs.
+
+ type Record_Type is limited record
+ Cont : Checkit2.Check_Type;
+ Oops : Integer := Checkit2.Raise_P_E;
+ end record;
+
+
+begin
+ Ada.Text_IO.Put_Line ("--- Check when an allocated object is" &
+ " finalized when the initializer fails");
+ declare
+ Early : Boolean;
+ begin
+ declare
+ type Acc_Record_Type is access Record_Type;
+ Obj : Acc_Record_Type;
+
+ function Test return Acc_Record_Type is
+ begin
+ return (new Record_Type);
+ exception
+ when Program_Error =>
+ Early := Checkit2.Finalized_Yet;
+ if Checkit2.Finalized_Yet then
+ Ada.Text_IO.Put_Line ("%% Failed allocated object " &
+ "finalized inside of function");
+ elsif Checkit2.Created_Yet then
+ Ada.Text_IO.Put_Line ("%% Failed allocated object " &
+ "created but not finalized inside of function");
+ else
+ Ada.Text_IO.Put_Line ("%% Failed allocated object " &
+ "controlled component not created");
+ end if;
+ raise;
+ end Test;
+ begin
+ Obj := Test;
+ Ada.Text_IO.Put_Line ("** Test failed to raise exception.");
+ end;
+ exception
+ when Program_Error =>
+ if Checkit2.Created_Yet then
+ if Checkit2.Finalized_Yet then
+ if not Early then
+ Ada.Text_IO.Put_Line ("%% Allocated object finalized " &
+ "when type goes out of scope");
+ -- else already reported on finalization.
+ end if;
+ else
+ Ada.Text_IO.Put_Line ("** Allocated controlled component " &
+ "created but not finalized!");
+ end if;
+ else
+ Ada.Text_IO.Put_Line ("%% Allocated controlled component " &
+ "never created");
+ end if;
+ end;
+ Ada.Text_IO.Put_Line ("--- Check complete");
+end Check2;
+
+----
+
+Unfortunately, it didn't shed much light.
+Janus/Ada worked as I expected (printing "created but not finalized inside
+of function" and "finalized when type goes out of scope").
+The GNAT version I tried failed outright, printing "created but not
+finalized inside of function", then "component created but not finalized!".
+I didn't check if it just finalized the object too late, or never.
+I don't have ObjectAda installed on this OS right now, so I didn't try it.
+And I still haven't gotten around to trying to get the Rational Apex working
+again (the license manager just refuses to work on our network). I'm sure
+others will try those.
+
+----
+
+My personal opinion on this is rather split. Certainly, Initialize and
+Finalize routines can link objects into other data structures (Claw works
+this way), so it's important that we don't require any magic "going away".
+OTOH, any controlled type that allows allocation of its objects and can't
+handle an arbitrary Finalize call (from Unchecked_Deallocation, for example)
+is pretty dubious. OT3H, AI-179 decides not to decide on what happens with
+Unchecked_Deallocations that fail, and it would be odd to make similar
+requirements on allocations. Moreover, deallocation of storage is not
+semantically neutral -- especially if the storage comes from a user-defined
+pool.
+
+I do think it is crystal clear that the current model in the RM does not
+allow such early finalizations (7.6.1 defines when finalizations are done,
+and there is nothing about exceptions raised by allocators or return
+statements in 7.6.1!) I suspect implementers got caught making an "as-if"
+optimization that doesn't quite work.
+
+In any case, I'm not planning to run out and create an ACATS test for this
+case. It's hard to imagine a legitimate use for this, as raising an
+exception in an initializer is clearly a bug (not a feature!). If some
+programmers want to play Russian Roulette and leave those bugs in their
+production systems, that's their business, but I don't have a lot of
+sympathy.
+
+****************************************************************
+
+From: Pascal Leroy
+Sent: Tuesday, January 18, 2005 6:14 AM
+
+Apex produces:
+
+--- Check when an allocated object is finalized when the initializer fails
+%% Failed allocated object created but not finalized inside of function
+** Allocated controlled component created but not finalized!
+--- Check complete
+
+which surprises me and doesn't seem quite right. At least, it's
+GNAT-compatible ;-)
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 18, 2005 1:45 PM
+
+If an allocator's initialization fails, the user can't reclaim the space
+using Unchecked_Deallocation. For a long-running application,
+it would presumably be desirable if the implementation would
+recover the space. Perhaps for this and for garbage collection,
+we should permit an implementation to implicitly perform
+an "Unchecked_Deallocation" and the associated finalization
+as soon as the object is no longer accessible. This would
+tie into the wording in of 7.6.1(10), because it talks in
+terms of Unchecked_Deallocation.
+
+Of course if the pragma Controlled applies to the access type,
+then the garbage collection option would be disallowed, though
+implicitly deallocating the failed allocator might still be desirable
+to avoid a storage leak.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Tuesday, January 18, 2005 4:21 PM
+
+Interestingly, the second sentence of 7.6.1(10), which
+indicates that if Unchecked_Deallocation isn't used,
+the object is finalized at the end of the access type scope, is
+bracketed in the AARM, as though it is redundant.
+But later in the AARM, it says that if the implementation
+does garbage collection, then it "should" finalize the
+object before reclaiming its storage. These two seem
+to be inconsistent, unless we hypothesize that
+garbage collection is implicitly invoking Unchecked_Deallocation.
****************************************************************
Questions? Ask the ACAA Technical Agent