Version 1.5 of ai05s/ai05-0053-1.txt

Unformatted version of ai05s/ai05-0053-1.txt version 1.5
Other versions for file ai05s/ai05-0053-1.txt

!standard 3.10(9/2)          08-04-09 AI05-0053-1/05
!standard 6.5(2.1/2)
!class binding interpretation 07-05-15
!status ARG Approved 8-0-0 08-02-09
!status work item 07-05-15
!status received 07-05-07
!priority Medium
!difficulty Hard
!qualifier Omission
!subject Aliased views of unaliased objects
!summary
Remove the aliased reserved word from the syntax of extended_return_statement; make immutably limited return objects implicitly aliased.
!question
An extended return statement can be used to obtain an aliased view of an unaliased object.
This introduces a number of problems.
How are these problems to be resolved? (Eliminate aliased return objects.)
!recommendation
Eliminate aliased return objects (except for immutably limited types).
!wording
Note: This wording depends on the definition of "immutably limited" added in AI05-0052-1.
In 3.10(9/2), replace
The current instance of a limited tagged type, a protected type, a task type, or a type that has the reserved word limited in its full definition is also defined to be aliased.
with
The current instance of an immutably limited type is defined to be aliased, as is the return object of an extended_return_statement (see 6.5) that is of an immutably limited type.
In 6.5(2.1/2) (syntax for extended_return_statement), delete "[aliased]".
!discussion
Consider the case where an extended return statement includes the reserved word "aliased" and the function is called to initialize an unaliased object in place, either as required by 7.5(8.1/2) or as allowed by 7.6(21/2).
If an implementation wants to treat aliased objects differently than unaliased objects in some way, then an aliased view of an unaliased object can be problematic.
An optimizer might wish to assume that an assignment through an access value does not "kill" any information that is known about the value of an unaliased object.
If this were the only problem, then this might be resolved via a "clarification" stating that if an aliased view of an unaliased object is used as the prefix of an Unchecked_Access attribute, then the resulting access value is valid only for the lifetime of the aliased view (which will typically be shorter than the lifetime of the designated object).
This "clarification" seems like a good idea, but unfortunately it does not solve all of the problems in this area.
For example, the IBM/Rational Ada compiler implements an aliased object X whose nominal subtype is an unconstrained array subtype by storing a contiguous dope vector. This is needed in order to support the evaluation of X'Access in the case where the designated type of the access type is unconstrained.
In the case of an unaliased object, no storage is allocated for a contiguous dope vector.
This implementation model worked for Ada95 and it was not the intention of the Ada 2005 designers to invalidate this approach.
If it is possible to construct an aliased view of an object which lacks a contiguous dope vector, then things can go downhill rapidly.
The following example illustrates the situation:
procedure Aliased_Return_Object is type Outer; type Inner (Ref : access Outer) is limited null record; type Outer is limited record Self : Inner (Outer'access); Data : Integer := 0; end record;
type Vector is array (Positive range <>) of Outer; subtype S is Vector (1 .. 10);
function F return Vector is begin return X : aliased Vector := S'(others => <>) do declare type Vector_Ref is access all Vector; for Vector_Ref'Storage_Size use 0; Ptr : Vector_Ref := X'access; -- requires contiguous dope Xx : Vector renames Ptr.all; begin for I in Xx'range loop Xx (I).Data := I; end loop; end; end return; end F;
Y : S := F; -- no contiguous dope for Y begin null; end Aliased_Return_Object;
There is also a considerably more obscure problem involving object identity. In this example,
subtype Zero_Sized is String (1 .. Report.Ident_Int (0)); X1, Y1 : Zero_Sized := Some_Function; X2, Y2 : aliased Zero_Sized;
It is ok if X1'Address = Y1'Address but it is not ok if X2'Access = Y2'Access. If we can somehow construct X1'Access and Y1'Access, and if they must be distinct, then some implementations might have a problem.
This particular problem is not very important (and it would be solved by the Unchecked_Access "clarification" described above), but it does suggest that there may be other issues that have not been discovered yet.
How should these problems be addressed?
It has been decided to solve these problems by changing the syntax so that extended return objects cannot be declared with the reserved word "aliased".
Technically speaking, this isn't really much of a change because the keyword "aliased" really had no meaning here. 3.10 defines which objects are aliased, and that list was never updated to include return objects.
After general agreement that defining an extended return object to be aliased in the same cases where the current instance of a type is aliased would be useful and would pose no implementation difficulties, it has been decided to update 3.10(9/2) accordingly.
A little bit of rewording is then needed in order to avoid privacy-breaking. This was not an issue in the case of the current instance of a type because the current instance of a type can only be named in a context where the completion of the type is visible. With extended return statements, we we don't want to look through private types to see if the completion "has the reserved word limited". The rule is therefore expressed in terms of "an explicitly limited record type" instead.
We could have instead explicitly stated the Unchecked_Access "clarification" described above and added a runtime check which raises Program_Error when X'Access is evaluated during the execution of the preceding example.
However, it would have been hard to define what the predicate of that check should be.
Perhaps the check fails during the evaluation of Some_Object'Access (or Some_Object'Unchecked_Access) if Some_Object is an aliased extended return object whose nominal subtype is an unconstrained array subtype and the designated subtype of the access type is unconstrained.
Or perhaps for the check to fail it must also be the case that the function is called to initialize an object other than an aliased array object with an unconstrained nominal subtype (in implementation terms, an object which lacks a contiguous dope vector).
Or perhaps (if portability is not a concern) it must also be the case that the initialization of this dopeless object must be in place.
The first predicate, although the least precise, seems to be the best. The more precise alternatives would probably incur distributed overhead because a caller would have to pass in additional information (albeit only a boolean) on the off chance that a function's body might contain this problematic construct.
These complications tell us that aliased return objects are not worth the trouble; thus we selected the simple solution.
!corrigendum 3.10(9/2)
Replace the paragraph:
A view of an object is defined to be aliased if it is defined by an object_declaration or component_definition with the reserved word aliased, or by a renaming of an aliased view. In addition, the dereference of an access-to-object value denotes an aliased view, as does a view conversion (see 4.6) of an aliased view. The current instance of a limited tagged type, a protected type, a task type, or a type that has the reserved word limited in its full definition is also defined to be aliased. Finally, a formal parameter or generic formal object of a tagged type is defined to be aliased. Aliased views are the ones that can be designated by an access value.
by:
A view of an object is defined to be aliased if it is defined by an object_declaration or component_definition with the reserved word aliased, or by a renaming of an aliased view. In addition, the dereference of an access-to-object value denotes an aliased view, as does a view conversion (see 4.6) of an aliased view. The current instance of an immutably limited type is defined to be aliased, as is the return object of an extended_return_statement (see 6.5) that is of an immutably limited type. Finally, a formal parameter or generic formal object of a tagged type is defined to be aliased. Aliased views are the ones that can be designated by an access value.
!corrigendum 6.5(2.1/2)
Replace the paragraph:
extended_return_statement ::= return defining_identifier : [aliased] return_subtype_indication [:= expression] [do handled_sequence_of_statements end return];
by:
extended_return_statement ::= return defining_identifier : return_subtype_indication [:= expression] [do handled_sequence_of_statements end return];
!ACATS test
There probably would be value in a B-Test to ensure that 'aliased' is not allowed, as it is in the syntax of the Amendment.
!appendix

From: Randy Brukardt
Sent: Never, authored May 15, 2007  8:30 PM

>If this were the only problem, then this might be resolved via a
>"clarification" stating that if an aliased view of an unaliased
>object is used as the prefix of an Unchecked_Access attribute,
>then the resulting access value is valid only for the lifetime of
>the aliased view (which will typically be shorter than the lifetime
>of the designated object).

This "clarification" makes no sense. Are you saying that a program
that has such a 'Access and saves it somewhere is erroneous? That's
completely unacceptable to me - the error should be detected somehow;
it would make more sense to ban the 'Access or (better) define the
accessibility of such a 'Access to be limited to the extended return statement.

Is there any actual value to aliased return objects in the first place?

The best solution I can think of would be to have "aliased" as part of the function
spec. We actually talked about that at one time but eventually dropped that.

> For example, the IBM/Rational Ada compiler implements an aliased
> object X whose nominal subtype is an unconstrained array subtype
> by storing a contiguous dope vector. This is needed in order to 
> support the evaluation of X'Access in the case where the designated
> type of the access type is unconstrained.

I have little sympathy for trying to support a contiguous dope vector; this is
an idea that I concluded wouldn't work (in all cases) back in 1983. It was the
original reason that we support/use non-contiguous objects. The struggle to
allow contiguous dope vectors reminds me of the struggle to support generic code
sharing. Arguably, implementers should get over it. ;-) In both cases, we add
rules to allow it, but those rules are both harmful to users and also don't
help that much. For instance, 3.10.2(27.1/2) requires static matching, while there
is no problem supporting *any* 'Access for an access-to-unconstrained array as long
as you don't require continguous array descriptors. Even slices would work fine.
(I vaguely recall that this rules helped in some generic code sharing case, so I'm
not being that serious about trying to repeal it...)

So, while I agree with

> This implementation model worked for Ada95 and it was not
> the intention of the Ada05 designers to invalidate this approach.

there are lots of other unintended consequences (like the incomparability of accessibility
in some cases) that are a lot harder to implement and have a lot more impact.

****************************************************************

Questions? Ask the ACAA Technical Agent