CVS difference for ais/ai-00318.txt

Differences between 1.2 and version 1.3
Log of other versions for file ais/ai-00318.txt

--- ais/ai-00318.txt	2003/01/24 04:14:27	1.2
+++ ais/ai-00318.txt	2003/06/18 00:12:24	1.3
@@ -1,32 +1,462 @@
-!standard 7.5 (00)                                 02-10-09  AI95-00318/00
+!standard 03.03.01  (02)                              03-06-23  AI95-00318/01
+!standard 06.05.00  (17)
+!standard 06.05.00  (18)
 !class amendment 02-10-09
+!status work item 03-05-23
 !status received 02-10-09
 !priority Medium
 !difficulty Medium
-!subject Limited types considered limited
+!subject Returning [limited] objects without copying
 
 !summary
 
+New syntax is proposed for identifying the object that
+will be returned from a function, allowing the
+object to be built in the context of the caller,
+without further copying required.
+
+This could be used to support returning limited objects from a
+function, to support returning objects of an anonymous access type,
+and more generally to reduce the copying that might be required
+when a function returns a complex object, a controlled object, etc.
 
 !problem
 
-(* Editor's note - this was split from AI-287. *)
+We already have a proposal for allowing aggregates of a limited type,
+by requiring that the aggregate be built directly in the target object.
+rather than being copied into the target.
+
+But aggregates can only be used with non-private types. Limited
+private types could not be initializable at their declaration point.
+It would be natural to allow functions to return limited objects,
+so long as the object could be built directly in the "target"
+of the function call, which could be a newly created object being
+initialized, or simply a parameter to another subprogram call.
+
+We have also considered allowing functions to return anonymous
+access types. In this case, if the function returned an allocator,
+it would be natural for the caller context to determine the storage
+pool to be used by the allocator.
+
+Whether returning a limited type or an anonymous access type, in both
+cases, it may be desirable to perform some other initialization
+to the object after it has been created, but before returning
+from the function. This is difficult to do while still creating
+the object directly in its "final" location.
 
 !proposal
 
+When declaring a local variable inside a function (not including
+within a nested program unit), the variable may be declared to
+be a "return" object, using the following syntax (analagous to
+the syntax used for constants):
+
+    identifier : [ALIASED] RETURN subtype_indication [:= expression];
+
+Within the scope of a return object (except within nested program units), no
+other return objects may be declared, and all return statements
+must have the name of the return object as their returned expression.
+
+[Possible alternative: return statements in the scope of a "return" object
+must omit the returned expression, and be like the return statements
+of a procedure. One possible down side of omitting the name
+of the return object is that it makes the reader's job a bit
+harder; they have to look back to find the object being returned.
+One possible up side -- it is perhaps clearer that no copying is happening
+at the return statement.]
+
+The return object would not be finalized prior to leaving the function.
+The caller would be responsible for its finalization.
+
+This syntax would not be restricted to limited types. It could
+also be used for non-limited types. The implementation advice would
+be that the amount of copying, finalization, etc. should be reduced,
+if possible, as part of returning from the function. This could be
+particularly useful for functions that return large objects, or objects
+with controlled parts.
+
+A call of a function with a limited result type could be used in the
+same contexts where we have proposed to allow aggregates of a limited
+type, namely contexts where a new object is being created (or can be).
+
+  1) Initializing a newly declared object (including a "return" object)
+  2) Default initialization of a record component
+  3) Initialized allocator
+  4) Component of an aggregate
+  5) IN formal object in a generic instantiation (including as a default)
+  6) Expression of a return statement
+  7) IN parameter in a function call (including as a default expression)
+
+In addition, since the result of a function call is a name in Ada 95,
+the following contexts would be permitted, with the same semantics
+as creating a new temporary constant object, and then creating a
+reference to it:
+
+  8) Declaring an object that is the renaming of a function call.
+  9) Use of the function call as a prefix to 'Address
+
+If we permit function result types to be anonymous access types
+(e.g. "function Blah return access T"), then we likely will want such
+functions, if they return the result of an allocator, to be able to use the
+context of the call to determine the storage pool for the allocator.
+This proposed syntax would allow the function to do the allocator in the
+"caller" context, but still be able to perform further initialization
+of the allocated object after the allocator. Essentially the "return"
+object would inherit the storage pool determined by the calling context,
+so that allocators that are used to initialize it, or that are assigned
+to it later, would use the caller-determined storage pool.
 
 !wording
 
+!example
+
+Here is an example of a function with a limited result type
+using a "return" object:
+
+    function Construct_Obj(Len : Natural) return Lim_Type is
+	Result : return Lim_Type(Discrim => Len);  -- the "return" object
+    begin
+	-- Finish the initialization of the "return" object.
+	for I in 1..Len loop
+	    Result.Data(I) := I;
+	end loop;
+
+	-- And now return it.
+	return Result;
+           -- [Alternative: omit "Result" (or entire return statement);
+	   --  "return Result;" would be implicit]
+    end Construct_Obj;
+
+Here is essentially the same function, but with an anonymous access
+type for its result type:
+
+    function Construct_Obj(Len : Natural) return access Lim_Type is
+	Result : return access Lim_Type; -- The "return" object
+    begin
+        Result := new Lim_Type(Discrim => Len);
+          -- this uses the storage pool determined by the caller context
+
+	-- Finish the initialization of the allocated object
+	for I in 1..Len loop
+	    Result.Data(I) := I;
+	end loop;
+
+	-- And now return it.
+	return Result;
+           -- [Alternative: omit "Result" (or entire return statement);
+	   --  "return Result;" would be implicit]
+    end Construct_Obj;
+
+By "caller context", we mean that the same rules as apply to an
+allocator would apply to calls on this function, where the
+expected (access) type would determine the storage pool:
+
+    type My_Acc_Type is access Lim_Type;
+    for My_Acc_Type'Storage_Pool use My_Amazing_Stg_Pool;
+
+    P : My_Acc_Type;
+  begin
+    P := Construct_Obj(3);
+     -- allocator inside Construct_Obj uses My_Amazing_Stg_Pool
 
 !discussion
 
+In meetings with Ada users, there has been a general sense
+that if limited aggregates are provided in Ada 200Y, it would be desirable
+to also provide limited function returns which could act
+as "constructor" functions.
+
+Just allowing a function whose whole body is a return statement
+returning an aggregate (or another function call) does not give the
+programmer much flexibility. What they would like is to be able
+to create the object and then initialize it further somehow, perhaps
+by calling a procedure, doing a loop (as in the examples above),
+etc. This requires a named object. However, to avoid copying,
+we need this object to be created in its final "resting place,"
+i.e. in the target of the function call. This might be in the
+"middle" of some enclosing composite object the caller is initializing,
+or it might be in the heap, or it might be a stand-alone local
+object.
+
+Because the implementation needs to create the returned object in a place
+or a storage pool determined by the caller, it is important that
+the declaration of the object be distinguished in some way.
+By using the keyword "return" in its declaration, we have
+a fairly intuitive way for the programmer to indicate that
+this is *the* object to be returned. Clearly we only want
+to allow one of these at a time, and to require that all
+return statements within its scope explicitly (or perhaps
+implicitly) return that object.
+
+Because it may be necessary to do some computing before deciding
+exactly how the return object should be declared, we permit
+the return object to be declared within nested blocks within
+the function so long as there is no return object
+for the function already in scope. So different
+branches of an if or case statement could declare their "own"
+return object if appropriate, for example.
+
+Note that we have allowed the user to declare the return object
+as "aliased."  This seems like a natural thing which might be
+wanted, so you could initialize a circularly-linked list header
+to point at itself, etc.
+
+We had considered a different syntax for this before, namely a
+new kind of return statement, analogous to an accept statement,
+e.g.:
+
+    return Result : T := blah do
+        Result.Data(3) := 77;
+        ...
+    end Result;
 
-!example
+However, Bob Duff pointed out that for simple cases you ended up
+with two levels of nesting which seemed excessive:
 
+    function Fum() return T is
+    begin
+       return Result : T := blah do
+          Result.Data(3) := 77;
+          ...
+       end Result;
+    end Fum;
+
+Making a smaller change to the object declaration syntax seemed
+a simpler approach.
+
+POSSIBLE IMPLEMENTATION APPROACHES
+
+The implementation approach for anonymous access result types is very
+similar to that for limited result types. In the following,
+we will mostly talk about limited result types. Towards the end
+we will explain how it applies to anonymous access result types.
+
+Full accessibility level checking adds to the complexity.
+At the end we will show how to introduce restrictions
+that eliminate most of this complexity, in exchange for
+some loss in functionaliy.
+
+The implementation of this for limited result types is straightforward if
+the size of the result is known to the caller. It is essentially equivalent
+to a procedure with an OUT parameter -- the caller allocates
+space for the target object, and passes its address to the called
+routine, which uses it for the "return" object.
+
+If the size of the function result is not known to the caller (i.e.
+the function result subtype is unconstrained, and perhaps indefinite), then
+there are two basic possibilities:
+
+   1) The target object's (nominal) subtype is constrained (or at least
+      "definite"), even though the function result subtype is unconstrained;
+      the target object might be a component of a larger object.
+
+   2) The target object's nominal subtype is unconstrained, and its size
+      is to be determined by the result returned from the function;
+      the target object must be a stand-alone object, or an "entire"
+      heap object.
+
+In the first case, the caller determines the size of the target object and
+can allocate space for it; in the second, the caller cannot
+preallocate space for the target object, and must rely on the called
+routine allocating space for it in an "appropriate" place.
+
+The code for the called routine must handle both of these cases.
+One reasonable way to do so is for the caller to provide a
+"storage pool" for the result. In the first case, this storage
+"pool" has space for exactly one object of a given maximum size.
+It's allocate routine is trivial. It just checks to see if the
+size is no greater than the space available, and then returns the
+preallocated (target) address.
+
+In the second case, the storage pool is either the storage pool
+associated with the initialized allocator at the call site,
+or a storage pool that represents a secondary stack, or equivalent,
+used for returning objects of unknown size from a function.
+
+For upward compatibility, we would need to accommodate
+functions that return pre-existing objects by reference.
+One way to do this would be for the caller to provide an
+additional implicit boolean parameter which would indicate
+whether the called routine *must* create a new object, or
+could return a reference to an existing object.
+
+Of the nine places identified above where calls on functions with
+limited result type would be permitted, the cases where the called
+routine must create a new object are (1)-(5). Cases (6)-(9)
+allow the use of preexisting objects, so the storage pool provided
+would generally be the secondary stack if the size is unknown
+to the caller, or a preallocated primary stack area, if
+the size of the object returned is always the same. Case (6),
+where a return statement returns the result of a function call,
+is a bit of a halfway situation. For (6), the storage pool
+provided as part of the call in the return statement would be
+the same storage pool passed to the function.
+
+When the boolean flag indicates that a new object is not required,
+the called routine could return a reference to a preexisting object,
+and ignore the storage pool or target address provided.
+As a possible optimization, this case could be indicated by simply
+providing a null storage pool parameter, rather than a separate
+boolean flag. The called routine would take this to mean that
+the secondary stack, or equivalent, should be used if a new
+object is being created, but that it may return a reference to a
+preexisting object. For the simplest implementation model where
+the size of the result is always known to the caller, and no
+storage pool parameter is provided, a separate flag would probably
+be necessary. The net effect is that there would be one
+implicit parameter in both situations, a boolean flag for the
+known-size function result, and a possibly-null storage pool
+for the unknown-size function result.
+
+In all cases, the called routine would return the address of the result,
+whether newly created or preexisting. The caller would use
+this returned address in all cases where the function result might
+be a preexisting object (cases (6)-(9)), or in cases where the caller
+didn't preallocate space for the target.
+
+IMPLEMENTATION APPROACH FOR ANONYMOUS ACCESS RESULT TYPE
+
+For anonymous access result types, a very similar approach would
+be taken. In this case, however, a new object is never required.
+It would always be permissible to return an access value designating
+a preexisting object. The storage pool parameter would always
+be required, but the caller could always ignore it. An accessibility
+level would be needed associated with the storage pool, so the called
+routine would know the accessibility level of the result of an allocator
+that used the storage pool. An accessibility level would also need
+to be returned, so the caller would know the accessibility of the result.
+
+Although the RM talks about accessibility levels in terms of
+dynamic levels of nesting, most implementations use accessibility
+levels that correspond to static levels of nesting, but adjust
+the level when passing a given (formal) access parameter to a
+more nested subprogram with an access parameter as well, by collapsing
+deeper static levels into a level that corresponds to the static level of
+the given formal access parameter's declaration. This is explained in
+AARM 3.10.2(22.x-22.ee).
+
+Unfortunately, this "collapsing" of levels loses information.
+So when passing accessibility levels to and from a function with
+an anonymous access type result, it would be desirable to
+avoid "collapsing" such levels, and use the original accessibility
+levels. In some implementations it might be helpful for the
+caller to provide the called routine with a level for the called
+routine to use for its own locals, which is guaranteed to be deeper
+than any level number that the caller cares about.
+
+LIMITED TYPES AND ACCESSIBILITY ISSUES
+
+Because limited types can have access discriminants, and an
+accessibility check is required when an allocator for such
+a type is performed to be sure the allocated object doesn't
+outlive the object referenced by the access discriminant,
+some kind of accessibility level will also have to be provided
+to the called routine when a storage pool is provided,
+at least when the result type has access discriminants.
+Because the storage pool will often be local to the caller
+and the access discriminant might be specified via an
+access parameter to the function, the collapsing of accessibility
+levels mentioned above would have to be supressed in this
+case as well.
+
+Hence we end up with a general rule that when access parameters
+are passed to a function with a limited result type (with
+access discriminants), or with an anonymous access result type, no
+collapsing of accessibility levels is performed. The caller's accessibility
+levels are used in the access parameters, and in the storage
+pool. The called routine has to accommodate this somehow.
+Again, in some implementations it may be helpful for the
+caller to provide the called routine with an accessibility level
+it can use for its own locals that is certain to be deeper than
+any other level passed in from the caller.
+
+POSSIBLE SIMPLIFICATIONS OF ACCESSIBILITY CHECKING
+
+If we would like some of these capabilities, but would like
+to avoid dealing with uncollapsed accessibility levels,
+accessibility levels associated with storage pools, etc.,
+then we could make some restrictions that might
+simplify the implementation (though of course it would
+complexify the user's model a bit):
+
+  1) If a limited result type has access discriminants, then
+     the storage pool passed in must not outlive the function declaration.
+     This would imply that the function could safely set the access
+     discriminants to point to objects with an accessibility level
+     no deeper than the function declaration.  This is similar
+     to the test performed on return-by-reference now (6.5(17-20)).
+
+     With this restriction, no accessibilty level needs to be passed
+     in with the storage pool for limited result types.
+
+     Note also that with this restriction, calls on local functions
+     could not be used within initialized allocators for global access types,
+     if the function's result type is a limited type with access
+     discriminants (doesn't seem like much of a loss).
+
+  2) For anonymous access result types, if the storage pool were
+     not used inside the function, the accessibility of the returned access
+     value must be no deeper than that of the function declaration (e.g., it
+     could not return the value of an access parameter passed in,
+     unless the access parameter designated an object global to
+     the function). Again, this is essentially the check performed
+     now for return-by-reference types.
+
+     If the storage pool is used, then naturally the accessibility
+     is that of the storage pool, so the caller knows that the
+     maximum accessibility depth of the result is the depth of
+     the storage pool or the depth of the function declaration,
+     whichever is deeper.
+
+     If the designated type of the anonymous access type is
+     a limited type with access discriminants, then the same restriction
+     as (1) would apply to the storage pool, i.e. that the storage pool
+     depth must be no less than that of the function declaration.
+
+     With these restrictions, no accessibility level needs to be passed
+     in with the storage pool for anonymous access result types, and
+     in turn no level would be returned (without the storage pool level
+     passed in, it would be pretty much impossible to pass it back!).
+
+     Note that without the level being returned, a local function
+     could not be used to create a value assigned to variable of a
+     global access type, since the function might return a pointer
+     designating a local object, and it has no way of indicating that.
+
+CONCLUSION
+
+If we are willing to accept the restrictions of the above section,
+then the implementation burden is roughly the same for either
+return limited types or returning anonymous access types, namely
+that a storage pool may need to be passed in. The called routine
+needs to use that storage pool when creating a limited return object,
+or evaluating an allocator whose target is the anonymous access
+return object. If the return object is itself initialized
+by a function call, then the storage pool needs to be passed into
+that function as well, presuming that function also returns a limited
+type or an anonymous access type.
+
+If a user doesn't explicitly declare a return object, then each return
+statement is equivalent to a local block that declares a return object
+initialized from the return expression, and then returns it.
+
+If we don't want to accept the restrictions given above, then
+accessibility levels need to be passed with the storage pool,
+and the accessibility levels passed with access parameters
+should not be "collapsed."  An accessibility level would be
+returned from a function with an anonymous access result type.
+
+Note that an additional advantage of the restricted form is that
+more accessibility checking can be performed at compile-time, and
+it will generally involve less run-time overhead.
+
+Given this, it seems appropriate to consider the restricted
+(compile-time accessibility) form of the proposal first, and only if
+this is felt sufficiently valuable, to consider the unrestricted
+form of the proposal.
 
 !ACATS test
 
-
 !appendix
 
 From: Robert Duff
@@ -1033,6 +1463,277 @@
 default initialization code where explicit initialization is
 provided later, and making sure the declaration is initialized
 before its first use.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, May 23, 2003,  8:05 AM
+
+I believe I floated a "trial balloon" a month or so
+ago about a syntax to support returning objects of
+a limited type from a function.  Bob Duff pointed
+out that it created yet another level of nesting in
+simple cases.  Also, it involved a completely new
+syntactic construct (return ... do ... end), which
+seems excessive.  So here is a revamped proposal, now
+structured as a "real" AI.
+
+[Editor's note: this is version /01 of the AI.]
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, May 23, 2003,  1:22 PM
+
+My gut reaction is that your trial balloon syntax is much preferred:
+-- Using different syntax for return means that it is clear that this is not
+the "normal" return with copying;
+-- There is no need to look all over the source code to find the return
+object;
+-- There is no set of complex rules to guarantee that only one return object
+is available in a given context.
+
+The 'weight' of the extra syntax is pretty similar either way (an entire new
+kind of object declaration doesn't seem "light" to me either).
+
+So, given the advantages of the original syntax, and since the only
+identified problem is an extra level of nesting (who cares?), I much prefer
+that alternative. (I'm unconvinced that we can afford the complexity of any
+of these proposals, but that's a different issue altogether. I don't like
+the idea that the compiler has to be able to determine at run-time whether a
+call is 'build-in-place' or 'existing object reference'; that seems like
+substantial overhead, and I would expect 'build-in-place' to be commonly
+used.)
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, May 23, 2003,  1:50 PM
+
+Can you explain this a bit more.  The called routine knows
+whether it is returning an existing object or a new object,
+so I don't see extra overhead there.  I suppose it has to
+check whether the caller allowed returning an existing object,
+but that is just a simple test, certainly cheaper than the
+average constraint check.
+
+The caller shouldn't care, since it can always use the address
+returned from the called function, whether or not it created
+a new object.
+
+What is the source of the overhead that I am missing?
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, May 23, 2003,  3:26 PM
+
+According to your write-up, the caller has to pass into the function some or
+all of:
+ -- A flag as to whether an object return is allowed;
+ -- The address of the memory to build the return in;
+ -- A storage pool;
+ -- An accessibility level.
+Obviously, there is going to be overhead to build and pass these things.
+(Parameter passing isn't free!) Even if these are all packed into a
+descriptor, initializing that descriptor is going to take a bunch of
+instructions. That's especially true if a storage pool is created on the fly
+for the call (which your writeup suggested in some cases).
+
+So, such function calls look quite a bit more expensive than the similar
+aggregates or the currently existing function calls. It's not likely to be
+hundreds of times worse, but it's pretty complicated and certainly will slow
+down these calls. That might matter for a few existing programs.
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Friday, May 23, 2003,  5:19 PM
+
+If we adopt the restriction that eliminates run-time accessibility issues
+for this proposal, then what the AI suggested was as follows:
+
+1) If the function had a known-size result, then the caller would
+   preallocate the space, and pass this address in the usual way for a
+   function that returned a "large" result.  In addition, a flag
+   would be passed to indicate whether the function was allowed
+   to simply return the address of a preexisting object.  If so,
+   then the caller would expect the function to return the address
+   of the result, which could be the preallocated space, or the
+   preexising object's address.
+
+   In this case, the only extra overhead for typical implementations
+   would be the extra boolean flag, and the test against it.
+
+2) If the function had an unknown-size result, and hence would normally
+   have to allocate the result on a secondary stack or heap, then the
+   caller would pass in a storage pool and a boolean flag (or a possibly-null
+   storage pool).  The storage pool is one of the following:
+     a) a "normal" storage pool, presuming the function call is used
+        as the expression of an initialized allocator
+     b) a special "secondary stack" storage pool, presumably which could
+        be precreated by the run-time system
+     c) an on-the-fly constructed "preallocated-space" storage pool, which
+        at a minimum would consist of:
+          i) a tag identifying it as one of these kinds of storage pools
+          ii) an address of the preallocated space
+          iii) the length of the preallocated space
+
+Case (2c) seems like the only one that involves measurable extra work at the
+call-site.  Presuming the storage pool is allocated on the primary stack
+then at the call-site you would have at least 3 instructions to initialize
+the storage pool (assignments of the tag, address, and length), probably
+more like 6 for the typical RISC machine.  Then you would have to pass
+the address of the storage pool as an implicit parameter.
+
+So I agree there would be some overhead, but by using the storage-pool
+"abstraction" for cases (2a,2b,2c) and a simple boolean flag for (1),
+the total amount seems pretty modest.
+
+Just to be more precise about the preallocate-space storage pool, here
+is a sample implementation of such a beast:
+
+   type Preallocated_Space_Storage_Pool(Addr : Integer_Address; Max_Size : Storage_Count) is
+     new Root_Storage_Pool with null record;
+
+   procedure Allocate(
+     Pool : in out Preallocated_Space_Storage_Pool;
+     Storage_Address : out Address;
+     Size_In_Storage_Elements : in Storage_Count;
+     Alignment : in Storage_Count) is
+   begin
+       if Size_In_Storage_Elements > Max_Size then
+           raise Storage_Error;
+       end if;
+       Storage_Address := To_Address(Addr);
+   end;
+
+   procedure Deallocate(...) is begin raise Program_Error; end;
+   function Storage_Size(Pool : ...) is begin return Pool.Max_Size; end;
+
+A local object of this type would need to be created at the call-site and
+passed as an implicit parameter when the space for the object is preallocated
+by the caller (case 2c above).
+
+> So, such function calls look quite a bit more expensive than the similar
+> aggregates or the currently existing function calls. It's not likely to be
+> hundreds of times worse, but it's pretty complicated and certainly will slow
+> down these calls. That might matter for a few existing programs.
+
+It doesn't look that expensive to me.  Calling functions with unknown-size
+results is relatively expensive anyway.  Presuming case (2c) above is relatively
+rare (limited type, unknown size result, caller preallocates), this doesn't
+seem like a show-stopper.
+
+****************************************************************
+
+From: Randy Brukardt
+Sent: Friday, May 23, 2003,  5:46 PM
+
+Tucker wrote:
+...
+> 1) If the function had a known-size result, then the caller would
+>    preallocate the space, and pass this address in the usual way for a
+>    function that returned a "large" result.  In addition, a flag
+>    would be passed to indicate whether the function was allowed
+>    to simply return the address of a preexisting object.  If so,
+>    then the caller would expect the function to return the address
+>    of the result, which could be the preallocated space, or the
+>    preexising object's address.
+>
+>    In this case, the only extra overhead for typical implementations
+>    would be the extra boolean flag, and the test against it.
+
+But then there is overhead to get rid of the extra 'preallocated' space if
+it isn't used. And the overhead of figuring out if that needs to be done. If
+the space is in a pool (because it's an anonymous access type, or an item
+with a non-stack size), this will require calling pool operation(s). In the
+stack case, this memory won't be recovered until the subprogram exits
+(Janus/Ada might reuse it, but it cannot recover it). If the object is
+controlled, it probably will have to be registered (not sure precisely when
+that would have to happen for this case, but it certainly can't happen
+inside the subprogram unless a finalization chain is passed into it, which
+would add even more overhead).
+
+In addition, this implementation means that you will end up allocating an
+'extra' copy of the object in the return existing object case. If the object
+is large (and certainly some of the objects we're talking about are), that
+could be a problem, as it could cause an existing program to raise
+Storage_Error.
+
+> Presuming case (2c) above is relatively
+> rare (limited type, unknown size result, caller preallocates), this doesn't
+> seem like a show-stopper.
+
+I don't think it's necessarily a show-stopper. But we have to do a
+cost/benefit analysis on new features. Certainly, there is a benefit here,
+but just like Interfaces, it is not at all clear to me that the benefit
+outweighs the cost, which is considerable (and growing).
+
+****************************************************************
+
+From: Tucker Taft
+Sent: Saturday, May 24, 2003,  12:34 PM
+
+> But then there is overhead to get rid of the extra 'preallocated' space if
+> it isn't used.
+
+I'm not sure I understand what this means.  It may be something
+about the way your compiler works.  In our compiler, if
+the caller preallocates temporary space for a function result,
+it is space that gets released automatically at the end of
+the enclosing scope, so there is no point (or sometimes, no
+way), to reclaim it earlier than then.
+
+> ... And the overhead of figuring out if that needs to be done.
+
+The caller would know at compile-time whether the function
+returns a known-size result, and whether the result is
+used in a context where a preexisting object would be
+permitted, so I don't see any run-time overhead there.
+
+ > ... If
+> the space is in a pool (because it's an anonymous access type, or an item
+> with a non-stack size), this will require calling pool operation(s). In the
+> stack case, this memory won't be recovered until the subprogram exits
+> (Janus/Ada might reuse it, but it cannot recover it). If the object is
+> controlled, it probably will have to be registered (not sure precisely when
+> that would have to happen for this case, but it certainly can't happen
+> inside the subprogram unless a finalization chain is passed into it, which
+> would add even more overhead).
+
+I am unclear now whether you are talking about overhead that is new
+to limited function return, or is the same as what you would face
+for non-limited function return.
+
+> In addition, this implementation means that you will end up allocating an
+> 'extra' copy of the object in the return existing object case. If the object
+> is large (and certainly some of the objects we're talking about are), that
+> could be a problem, as it could cause an existing program to raise
+> Storage_Error.
+
+You could set your upper limit for caller-preallocated space
+relatively low for these kinds of functions, if this is a
+significant concern.  That is, require use of the secondary
+stack or heap even if the result size is known, if the known
+size is so large as to be of concern.
+
+>>Presuming case (2c) above is relatively
+>>rare (limited type, unknown size result, caller preallocates), this doesn't
+>>seem like a show-stopper.
+>
+>
+> I don't think it's necessarily a show-stopper. But we have to do a
+> cost/benefit analysis on new features. Certainly, there is a benefit here,
+> but just like Interfaces, it is not at all clear to me that the benefit
+> outweighs the cost, which is considerable (and growing).
+
+Are you talking about implementation cost or run-time overhead?
+I don't see the run-time overhead as being much greater than
+function calls that return a non-limited type of similar complexity.
+If the result might be controlled, or large, or of unknown-size,
+then yes that adds to the run-time overhead, but that is true
+for non-limited functions as well.
 
 ****************************************************************
 

Questions? Ask the ACAA Technical Agent