Version 1.11 of ai05s/ai05-0050-1.txt
!standard 6.5(24/2) 09-05-31 AI05-0050-1/07
!class binding interpretation 07-05-04
!status Amendment 201Z 08-11-26
!status WG9 Approved 09-06-12
!status ARG Approved 7-0-0 08-11-02
!status work item 07-05-04
!status received 07-04-20
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Return permissions are not enough for built-in-place return objects
!summary
If an object is being initialized by a function call and the implementation
can tell before the call that the object subtype and the function result
subtype are incompatible, then the implementation is permitted to raise
an exception before the call.
If the object declared in an extended return statement is unconstrained,
has discriminants, and is nonlimited, the existing permission does not apply.
(In this case, the discriminant values might change before the extended return
statement finishes, so raising an exception when the object is declared could
raise an exception that would not be raised by the full call). Note that
the permission still applies to simple return statements and constrained
return objects in extended return statements.
All of these permissions apply to recursive calls (that is, calls to a function
in a return statement).
!question
Should the implementation permissions of 6.5(24/2) be revised? The current
permission both requires inefficient implementations in some cases and also
allows an exception to be raised in cases where no exception would be raised
without the permission.
!recommendation
(See summary.)
!wording
Replace 6.5(24/2)
If the result subtype of a function is unconstrained, and a call on the
function is used to provide the initial value of an object with a
constrained nominal subtype, Constraint_Error may be raised at the point
of the call (after abandoning the execution of the function body) if,
while elaborating the return_subtype_indication or evaluating the expression
of a return statement that applies to the function body, it is determined
that the value of the result will violate the constraint of the subtype
of this object.
with
For a function call used to initialize a composite object with a constrained
nominal subtype or used to initialize a return object that is built in place
into such an object:
- If the result subtype of the function is constrained, and conversion of
an object of this subtype to the subtype of the object being initialized
would raise Constraint_Error, then Constraint_Error may be raised before
calling the function.
- If the result subtype of the function is unconstrained, and a return
statement is executed such that the return object is known to be
constrained, and conversion of the return object to the subtype of the
object being initialized would raise Constraint_Error, then
Constraint_Error may be raised at the point of the call (after abandoning
the execution of the function body).
[Editor's note: The definition of "built in place" was added by AI05-0067-1.]
AARM Reason:
Without such a permission, it would be very difficult to implement
"built-in-place" semantics. The intention is that the exception is raised at
same point that it would have been raised without the permission; it should
not change handlers if the implementation switches between return-by-copy and
built-in-place. This means that the exception is not handleable within the
function, because in the return-by-copy case, the constraint check to verify
that the result satisfies the constraints of the object being initialized
happens after the function returns. This implies further that upon detecting
such a situation, the implementation may need to simulate a goto to a point
outside any local exception handlers prior to raising the exception.
AARM Ramification:
These permissions do not apply in the case of an extended
return object with mutable discriminants. That's necessary because in that
case a return object can be created with the "wrong" discriminants and
then changed to the "right" discriminants later (but before returning).
We don't want this case raising an exception when the canonical semantics
will not do so.
It's still possible to write a program that will raise an exception using this
permission that would not in the canonical semantics. That could happen if a
return statement with the "wrong" discriminants or bounds is abandoned (via an
exception, or for an extended_return_statement, via an exit or goto
statement), and then a return statement with the "right" discrimiants or
bounds is executed. The only solution for this problem is to not have the
permission at all, but this is too unusual of a case to worry about the
effects of the permission, especially given the implementation difficulties
for built-in-place objects that this permission is intended to ease.
Note that the mutable-discriminant case only happens when
built-in-place initialization is optional. This means that any difficulties
associated with implementing built-in-place initialization without these
permissions can be sidestepped by not building in place.
!discussion
The implementation permissions of 6.5(24/2) do not go far enough.
This section states:
If the result subtype of a function is unconstrained, and a call on the
function is used to provide the initial value of an object with a
constrained nominal subtype, Constraint_Error may be raised at the point
of the call (after abandoning the execution of the function body) if,
while elaborating the return_subtype_indication or evaluating the
expression of a return statement that applies to the function body, it
is determined that the value of the result will violate the constraint
of the subtype of this object.
This is a fine start, but more is needed in order to efficiently
implement built-in-place initialization as required by AI95-00318-02.
First, the original permission does not cover a function with a constrained
result type. But such a function can be used to initialize an object
with the wrong constraints, and thus has the same implementation issues
that the original permission is intended to eliminate.
Consider:
declare
type T;
type Inner (D : access T) is limited null record;
type T is
limited record
F : Inner (Outer'access);
end record;
--
type Unconstrained is array (Positive range <>) of T;
subtype S1 is Unconstrained (1 .. Some_Func_Returning_20);
subtype S2 is Unconstrained (1 .. Some_Func_Returning_10);
function F return S1 is
begin
Text_Io.Put_Line ("F was called");
return (others => <>);
end F;
X : S2 := F;
begin null; end;
An implementation should be allowed to raise Constraint_Error before the
call to F (without invoking 11.6). Otherwise, F could try to build a large
object in place in a smaller object. That would not be good.
Thus the permission was extended to cover all types of function returns.
Second, built in place is only required for limited types, so it would
be OK for an implementation to ignore the implementation permissions of
6.5(24/2) for a nonlimited type. If an implementation chooses to implement
built in place for nonlimited types, it must take care with types with
mutable discriminants.
In particular, it must not modify discriminants of constrained objects.
Consider:
subtype Index is Integer range 1 .. 20;
type Rec (Length : Index := 10) is
record
F : String (1 .. Length);
end record;
function F return Rec is
begin
return Result : Rec do
Result := (5, "12345");
Text_Io.Put_Line ("Assignment succeeded");
end return;
end F;
X1 : Rec (10) := F;
In a case like this, the revised permission does not provide a permission to
raise Constraint_Error early (the return object is not "known to be
constrained", and thus the second bullet does not apply). That means that
implementations will typically have to use an anonymous temporary object
in this case and avoid building the result in place.
We considered a model which would be similar to an in-out or out mode parameter
with an unconstrained nominal subtype. In this model, Constraint_Error could be
raised anywhere that the return object was initialized or assigned, not just at
the point of the call.
Besides seeming rather non-specific, this model would allow an exception to be
raised in cases where it would not be raised in the absence of the permission.
That seems bad. For instance:
declare
type T (Is_Big : Boolean := True) is
record
case Is_Big is
when False => null;
when True => Big_Component : String (1..1024) := (others => 'X');
end case;
end record;
function F return T is
begin
return X : T do --
X.Big_Component := (others => 'Y');
X := (Is_Big => False);
end return;
end F;
Small_Object : T (Is_Big => False) := F;
begin
null;
end;
The permissive model would have allowed the declaration of X to raise
Constraint_Error. But that is bad, as the function ultimately returns an
object with the correct discriminant. Moreover, this is a common way to
declare objects where the constraint is not immediately known. Therefore,
we did not use this permissive model.
Finally, the implementation permissions of 6.5(24/2) should apply recursively in
the case where a function call returns the result of another function call. For
instance:
declare
type T;
type Inner (D : access T) is limited null record;
type T is
limited record
F : Inner (Outer'access);
end record;
--
type Unconstrained is array (Positive range <>) of T;
function Some_Function return T is ... ;
function F1 return Unconstrained is
begin
return (1 .. 10 => Some_Function);
end F1;
function F2 return Unconstrained is
begin
return F1;
exception
when Constraint_Error =>
return (1 .. 3 => <>);
end F2;
X : Unconstrained (1..3) := F2;
begin null; end;
In the execution of this example, it should be OK for the return statement
in F1 to raise Constraint_Error before executing any calls to Some_Function.
----
One could argue that these permissions are already implied by
11.6 and therefore do not need to be repeated. This is simply not the case.
For example, the permissions given in this section allow a constraint check
to be moved into an independent subprogram. 11.6 alone would not allow this.
Even if the permissions of this section were redundant, they should still
be stated explicitly here for the sake of the clarity of the language
definition. Reliance on these permissions is effectively required in the cases
where in-place initialization is required. Section 11.6 is about permissions
which enable optional optimizations - an implementer should have
the option of completely ignoring 11.6. Permissions which are
needed in order to correctly implement the language should be stated
outside of 11.6 .
----
The original wording of 6.5(24/2) applied in the case of a scalar result
type. We don't want this permission to apply (for example) in the case
of a function whose result subtype is Standard.Float. Thus, the revised
permission includes the phrase "of a composite type". This permission
refers to dynamic semantics, so there are no issues with privacy.
----
It is still possible for an implementation taking advantage of these permissions
to raise Constraint_Error in cases were the canonical semantics would not have.
That can happen when a return statement with the "wrong" discriminants is
abandoned and then a return statement with the "right" discriminants is executed
in its place. For instance:
type Rec (D : Integer) is record ...
function F return Rec is
begin
return O : Rec(4) do --
if <some condition> then goto Different_Object; end if;
end return;
<<Different_Object>>
return O : Rec(5) do --
...
end return;
end F;
O : Rec(5) := F;
The permission above would allow raising Constraint_Error at (1). But since that
object is never actually returned, and the object that is returned has the right
shape, the full function call which did not take advantage of the permission
would not raise any exception.
This cannot be avoided without dropping the permission completely; but that
would have an unacceptable implementation cost for built-in-place objects. (Rec
in the example above could be a type that must be built in place.)
!corrigendum 6.5(24/2)
Replace the paragraph:
If the result subtype of a function is unconstrained, and a call on the function
is used to provide the initial value of an object with a constrained nominal
subtype, Constraint_Error may be raised at the point of the call (after
abandoning the execution of the function body) if, while elaborating the
return_subtype_indication or evaluating the expression of a return
statement that applies to the function body, it is determined that the value of
the result will violate the constraint of the subtype of this object.
by:
For a function call used to initialize a composite object with a constrained
nominal subtype or used to initialize a return object that is built in place
into such an object:
- If the result subtype of the function is constrained, and conversion of
an object of this subtype to the subtype of the object being initialized would
raise Constraint_Error, then Constraint_Error may be raised before calling the
function.
- If the result subtype of the function is unconstrained, and a return
statement is executed such that the return object is known to be
constrained, and conversion of the return object to the subtype of the
object being initialized would raise Constraint_Error, then
Constraint_Error may be raised at the point of the call (after abandoning
the execution of the function body).
!ACATS test
As a permission, it is hard to write tests. But an ACATS C-Test should be
constructed to ensure that a case such as the one in the discussion where
mutable discriminants are changed from the "wrong" ones to the "right" ones
does not raise a premature exception.
!ASIS
No change needed.
!appendix
From: Stephen W. Baird
Sent: Friday, April 20, 2007 2:44 PM
!question
Should the implementation permissions of 6.5(24/2) be extended?
!discussion
The implementation permissions of 6.5(24/2) do not go far enough.
This section states:
If the result subtype of a function is unconstrained, and a call on the
function is used to provide the initial value of an object with a
constrained nominal subtype, Constraint_Error may be raised at the point
of the call (after abandoning the execution of the function body) if,
while elaborating the return_subtype_indication or evaluating the
expression of a return statement that applies to the function body, it
is determined that the value of the result will violate the constraint
of the subtype of this object.
This is a fine start, but more is needed in order to efficiently
implement initialization in place as required by AI95-00318-02 .
--------
Issue #1:
If an object is being initialized by a function call and the implementation
can tell before the call that the object subtype and the function result
subtype are incompatible, then the implementation should be permitted
to raise an exception before the call.
Consider:
declare
type T;
type Inner (D : access T) is limited null record;
type T is
limited record
F : Inner (Outer'Access);
end record;
-- requires initialization in place
type Unconstrained is array (Positive range <>) of T;
subtype S1 is Unconstrained (1 .. Ident_Int (10));
subtype S2 is Unconstrained (1 .. Ident_Int (11));
function F return S1 is
begin
Text_Io.Put_Line ("F was called");
return (others => <>);
end F;
X : S2 := F;
begin null; end;
An implementation should be allowed to raise Constraint_Error before the
call to F (without invoking 11.6).
In the case where the result subtype of a function is a constrained definite
structure, an implemention ought to be able to have a caller
simply pass in a buffer address; nothing more complicated than that should
be needed.
In order to implement initialization in place, F must be passed the address of
X. An implementation must verify before the call that S1 and S2 have compatible
constraints - it would not do to pass in, for example, the address of a small
object to a function which is going to fill in a large result.
The question is what to do if that test fails. The simple thing would be to
raise Constraint_Error at that point. The annoyingly complicated thing that
the language currently seems to require is to allocate a buffer of the size
the function expects, pass that buffer address into the function, finalize the
returned value, and then raise Constraint_Error (or something like that).
One could go further and handle cases involving qualified expressions
such as
Y : S1 := S2'(F);
, but this doesn't seem necessary.
--------
Issue #2:
Initialization in place is only required for limited types, so it would
be ok for an implementation to ignore the implementation permissions of
6.5(24/2) for a nonlimited type. If an implementation chooses not to
do this, however, there can be problems with extended return statements
which attempt to modify discriminants of the function result object after
it has been initialized.
Consider:
subtype Index is Integer range 1 .. 20;
type Rec (Length : Index := 10) is
record
F : String (1 .. Length);
end record;
function F return Rec is
begin
return Result : Rec do
Result := (5, "12345");
Text_Io.Put_Line ("Assignment succeeded");
end return;
end F;
X1 : Rec (10) := F;
For an in-out or out mode parameter with an unconstrained nominal subtype,
the formal inherits the constraints (if any) of the actual. The same
model should apply, at the discretion of the implementation, to return objects
in the case where the nominal subtype of the return object is unconstrained
and definite and "a call on the function is used to provide the initial value
of an object with a constrained nominal subtype".
This means that for the preceding example, execution of F could (at the
discretion of the implementation) be abandoned (and Constraint_Error raised at
the call site) at the point of the assignment statement. If the literal
"10" in the declaration of X1 were replaced with some other value, then the
default initialization of Result could raise Constraint_Error.
There is also a similar issue in the case of a function call which is
used to initialize an allocator when the designated subtype of the
access type is unconstrained but allocated objects themselves are constrained.
-------
Issue #3:
The implementation permissions of 6.5(24/2) should apply recursively in
the case where a function call returns the result of another function call.
Consider:
declare
type T;
type Inner (D : access T) is limited null record;
type T is
limited record
F : Inner (Outer'Access);
end record;
-- requires initialization in place
type Unconstrained is array (Positive range <>) of T;
function Some_Function return T is ... ;
function F1 return Unconstrained is
begin
return (1 .. 10 => Some_Function);
end F1;
function F2 return Unconstrained is
begin
return F1;
exception
when Constraint_Error =>
return (1 .. 3 => <>);
end F2;
X : Unconstrained (1..3) := F2;
begin null; end;
In the execution of this example, it should be ok for the return statement
in F1 to raise Constraint_Error before executing any calls to Some_Function.
One might argue that this freedom is already implicit in the current wording,
but this does not seem to be obvious.
--------
It seems that it would not make sense to propose RM wording for these issues
until we reach consensus on which, if any, of these issues require
any action at all.
One could argue that no action is required for any them:
#1 is essentially a complaint that the language requires an
implementation to do something that is silly and complex, but
not something that is undefined or impossible.
#2 is only a concern for implementations which choose to implement
initialization in place for functions with unconstrained definite
non-limited discriminated result subtypes. The language only
requires initialization in place in the limited case.
If an implementation doesn't want to deal with the complications of
implementing the given example, then maybe it shouldn't choose to
implement initialization in place for this case.
#3 could be viewed as requiring only a clarification of the current
wording.
****************************************************************
From: Tucker Taft
Sent: Friday, April 20, 2007 4:05 PM
...
> In the case where the result subtype of a function is a constrained definite
> structure, an implemention ought to be able to have a caller
> simply pass in a buffer address; nothing more complicated than that should
> be needed.
>
> In order to implement initialization in place, F must be passed the address of
> X. An implementation must verify before the call that S1 and S2 have compatible
> constraints - it would not do to pass in, for example, the address of a small
> object to a function which is going to fill in a large result.
I had presumed that for cases like this, one would use
an implementation based on the existing "OUT" parameter model,
where there is a "constrained" bit and an object whose
discriminants are of interest if the object is
constrained. The function would check the constrained bit,
and if True, check to be sure the discriminants match.
If they don't match, it would propagate an exception.
On the other hand, for OUT parameters, there is a check
before the function is called if the formal subtype is
constrained, so one could argue that that is a precedent
for raising the exception before the call occurs if
the return subtype is constrained and so is the target
object, and the subtypes don't match.
> The question is what to do if that test fails. The simple thing would be to
> raise Constraint_Error at that point. The annoyingly complicated thing that
> the language currently seems to require is to allocate a buffer of the size
> the function expects, pass that buffer address into the function, finalize the
> returned value, and then raise Constraint_Error (or something like that).
Where does the language require this? If the model is
"initialize in place," then allocating a buffer the size
the function "expects" is clearly inappropriate. I think
the language presumes you never allocate "extra" space
when calling these functions. You might be assigning
the function call into a component of another object,
so the space might already "exist" in that sense.
So the function can't "expect" a space of a particular
size, it must deal with what is the nominal subtype
of the target object.
In any case, I like your suggested generalization,
but in the absence of that, functions returning constrained
subtypes of limited types whose first subtype is unconstrained
must deal with the possibility that the target object has
the wrong constraints. Note that this applies to functions
returning arrays as well. It certainly seems annoying to
have to pass in a 'constrained bit when the result subtype
is constrained. There is no precedent for having to do
something like that for arrays.
> One could go further and handle cases involving qualified expressions
> such as
>
> Y : S1 := S2'(F);
>
> , but this doesn't seem necessary.
I would think if we allow the optimization, then we
should be fairly flexible about it.
,,,
> There is also a similar issue in the case of a function call which is
> used to initialize an allocator when the designated subtype of the
> access type is unconstrained but allocated objects themselves are constrained.
I think a nastier situation occurs when the default
value of the discriminant does *not* match that of the
target object, but the assignment produces a value
that *does* match. For example:
X1 : Rec(5) := F;
This is a case where if you do the optimization,
constraint_error is raised. If you don't do the
optimization, then the function call succeeds
and X1 is appropriately initialized. I think this
means that for nonlimited types, if the result
object is unconstrained, you must *not* do the
optimization on the off chance that the final
value of the discriminants at the point of the
actual return to the caller *do* match the target's
nominal subtype.
> -------
>
> Issue #3:
>
> The implementation permissions of 6.5(24/2) should apply recursively in
> the case where a function call returns the result of another function call.
I always presumed that they did. Clarification should
be provided if the words imply otherwise.
> ...
> It seems that it would not make sense to propose RM wording for these issues
> until we reach consensus on which, if any, of these issues require
> any action at all.
>
> One could argue that no action is required for any them:
> #1 is essentially a complaint that the language requires an
> implementation to do something that is silly and complex, but
> not something that is undefined or impossible.
It is pretty bad for functions returning arrays.
I think your generalization should be allowed.
> #2 is only a concern for implementations which choose to implement
> initialization in place for functions with unconstrained definite
> non-limited discriminated result subtypes. The language only
> requires initialization in place in the limited case.
> If an implementation doesn't want to deal with the complications of
> implementing the given example, then maybe it shouldn't choose to
> implement initialization in place for this case.
I think we need to *disallow* the optimization for nonlimited
types when the return object is unconstrained. It seems
very bad if the optimization results in a Constraint_Error
that wouldn't occur if you implemented the "canonical"
semantics.
> #3 could be viewed as requiring only a clarification of the current
> wording.
I hope it is just a clarification, but if we are going to have
to reword this section a bit anyway, we might as well be
explicit about nested calls.
****************************************************************
From: Randy Brukardt
Sent: Friday, April 20, 2007 5:08 PM
> I think a nastier situation occurs when the default
> value of the discriminant does *not* match that of the
> target object, but the assignment produces a value
> that *does* match. For example:
>
> X1 : Rec(5) := F;
>
> This is a case where if you do the optimization,
> constraint_error is raised. If you don't do the
> optimization, then the function call succeeds
> and X1 is appropriately initialized. I think this
> means that for nonlimited types, if the result
> object is unconstrained, you must *not* do the
> optimization on the off chance that the final
> value of the discriminants at the point of the
> actual return to the caller *do* match the target's
> nominal subtype.
I don't get it (although I didn't read this very carefully). If you have a
constrained object, you have to pass in those constraints with the object.
(That is, initialization of constraints and of the rest of the object might
be done separately, and in separate places.) Indeed, this example is pretty
much the *only* case where I'd even consider doing the optimization. (I'd
never consider it for unconstrained types, nor for assignments; it's just
too messy in those cases.) If it can't be done here, then it is worthless
for non-limited types (at least for those with any sort of discriminants or
bounds), and then why are we talking about it at all?
****************************************************************
From: Tucker Taft
Sent: Friday, April 20, 2007 5:35 PM
I would disallow the optimization if the object declared
in the extended return statement is *unconstrained* and
nonlimited. This means that its discriminant values might
change before the extended return statement finishes.
I would allow it if the return object is constrained by
its initial value, or if it is nominally constrained.
****************************************************************
From: Stephen W. Baird
Sent: Friday, April 20, 2007 6:05 PM
> > The question is what to do if that test fails. The simple thing would
> > be to raise Constraint_Error at that point. The annoyingly complicated
> > thing that the language currently seems to require is to allocate a
> > buffer of the size the function expects, pass that buffer address into
> > the function, finalize the returned value, and then raise Constraint_Error
> > (or something like that).
>
> Where does the language require this?
You're right, it really doesn't.
I expressed my opinion that
"In the case where the result subtype of a function is a constrained
definite structure, an implemention ought to be able to have a caller
simply pass in a buffer address; nothing more complicated than that
should be needed".
I think it follows from this premise that the language effectively
imposes this requirement because there really isn't any other reasonable
alternative available (since we are not currently allowed to raise
the exception before the call).
If you don't accept the premise, then I agree that this behavior is not
required.
> In any case, I like your suggested generalization, ...
... which would allow the simple implementation I described.
We agree on the important point.
> > Issue #2
>
> I think a nastier situation occurs when the default
> value of the discriminant does *not* match that of the
> target object, but the assignment produces a value
> that *does* match. For example:
>
> X1 : Rec(5) := F;
>
> This is a case where if you do the optimization,
> constraint_error is raised. If you don't do the
> optimization, then the function call succeeds
> and X1 is appropriately initialized. I think this
> means that for nonlimited types, if the result
> object is unconstrained, you must *not* do the
> optimization on the off chance that the final
> value of the discriminants at the point of the
> actual return to the caller *do* match the target's
> nominal subtype.
Good point.
I agree with your recommendations regarding all three issues.
****************************************************************
From: Randy Brukardt
Sent: Tuesday, October 30, 2007 7:10 PM
[Commenting on version /02 of the AI.]
I don't see much problem with allowing this permission for scalar types
(although I doubt it would be used much).
The issue with Deallocate is just the tip of the iceberg; any object that
needs finalization has similar issues if it gets constructed far enough to
"exist", as there is no permission to do the finalization early. That's
touched on in another AI (AI05-0066-1), although I suspect that we'll end up
with a solution that doesn't fix it. I think any object that is "inaccessible"
should be able to be finalized at the leaving of any master (so long as it
doesn't contain any tasks that haven't terminated -- waiting for tasks at
random points would be a no-no). The reason for restricting to the leaving
of any master prevents Finalize from being called in the middle of an operation
(it shouldn't be necessary to protect it against multitasking issues in an
otherwise single-tasking environment). A similar permission for Deallocate
is also needed. I don't see any reason to clutter this one spot with something
that needs to cover the entire language.
****************************************************************
From: Randy Brukardt
Sent: Saturday, June 14, 2008 1:30 AM
[Commenting on version /03 of the AI.]
...
> For a function call used to initialize a composite object with a constrained
> nominal subtype:
>
> - If the result subtype of the function is constrained, and does not match
> that of the object, Constraint_Error may be raised before calling the
> function.
>
> - Otherwise (the result subtype of the function is unconstrained), if a
> return statement is executed such that the return object is known to be
> constrained, and this constraint does not match that of the object,
> Constraint_Error may be raised at the point of the call (after abandoning
> the execution of the function body).
This is much better than the previous effort.
I think it would be good if the discussion pointed out that this permission
still could raise an exception that the canonical semantics would not:
type Rec (D : Integer) is record ...
function F return Rec is
begin
return O : Rec(4) do -- (1)
if <some condition> then goto Different_Object; end if;
end return;
<<Different_Object>>
return O : Rec(5) do -- (2)
...
end return;
end F;
O : Rec(5) := F;
The permission above would raise an exception at (1). But since that object is
never actually returned, and the object that is returned is the right shape, the
full function call without the permission would succeed.
I think this is OK (abandoning return statements is unusual and expensive
because of the finalization issues), but we probably ought to mention it
somewhere.
> If the function call is used to provide the initial value of an
> initialized allocator, then the call to System.Storage_Pools.Allocate
> may occur at any time during the execution of the call.
> If a return statement is left without a normal return, the result
> object may be deallocated.
I think it would make more sense to try to address this more generally, as
similar issues apply to failed allocators and probably any object declaration
with coextensions (even if the allocator of the coextension succeeds).
Admittedly, this case is easier (as finalization requires that the objects are
finalized here, while the allocator case currently requires that they are *not*
finalized), but it seems weird to fix a really unusual case without fixing the
*similar more likely cases.
> AARM Notes
It's pretty obvious that these are the original Steve Baird notes (I don't think
Bob starts sentences with commas!), they would benefit from a lot of Bob Duff
pruning. I'm certain that we don't want two pages of weird examples in the AARM,
particularly for a rule that seems fairly straigtforward.
> !discussion
The discussion section needs cleanup, too, as it still includes various
alternatives that aren't of interest and lots of Bairdian prose with
formatting that will not pass muster. If the AI authors don't keep these
sections up-to-date, the editorial reviewers will make your editor do that.
And I don't get paid enough to deal with the mountains of comments from the
reviewers!!
****************************************************************
From: Randy Brukardt
Sent: Saturday, June 14, 2008 1:49 AM
> > AARM Notes
>
> It's pretty obvious that these are the original Steve Baird notes (I
> don't think Bob starts sentences with commas!), they would benefit
> from a lot of Bob Duff pruning. I'm certain that we don't want two
> pages of weird examples in the AARM, particularly for a rule that
> seems fairly straigtforward.
To be a little more concrete, I think all that needs to be said in the AARM
Notes is:
These permissions apply transitively in the case of a function call
which returns the value of another function call.
[we don't need an example here, it's pretty obvious and there's one in the AI.]
These permissions do not apply in the case of an extended
return object with mutable discriminants. That's necessary because in that
case a return object can be created with the "wrong" discriminants and
then changed to the "right" discriminants later (but before returning).
We don't want this case raising an exception when the canonical semantics
will not do so.
[words are better here than just giving an example, and again the example will
be in the AI.]
It's still possible to write a program that will raise an exception using this
permission that would not in the canonical semantics. That could happen if a
return statement with the "wrong" discriminants or bounds is abandoned (via an
exception, or for an extended_return_statement, via an exit or goto
statement), and then a return statement with the "right" discrimiants or
bounds is executed. The only solution for this problem is not have the
permission at all, but this is too unusual of a case to worry about the
effects of the permission, especially given the implementation difficulties
for build-in-place objects that this permission is intended to ease.
[This is my question from the previous mail, fleshed out a bit. Seems important
to mention, as it is the counterpoint to the previous paragraph. There's
probably a better way to say this.]
Note that the mutable-discriminant case only happens when
build-in-place initialization is optional. This means that any difficulties
associated with implementing build-in-place initialization without these
permissions can be sidestepped by not building-in-place.
[From the original notes.]
And then the examples in the current AI should be moved to the discussion (and
probably lots of the discussion blown away).
****************************************************************
From: Bob Duff
Sent: Friday, October 10, 2008 3:21 PM
Here's a (belated) new version of AI-50. [This is version /04. - ED]
I tried to obey the stuff in the minutes. But I didn't define the term
"dynamically matching". I couldn't figure out any way to do so, other than the
attempts recorded in the minutes, which apparently folks didn't like. Anyway,
it seems better to just state the rule, rather than defining yet more
jaw-breaking terminology, especially since the term is used only for this one
rule.
I removed the stuff about Allocate and Deallocate, because that will be covered
by AI-107, as I understand it.
****************************************************************
Questions? Ask the ACAA Technical Agent