Version 1.8 of ai05s/ai05-0053-1.txt
!standard 3.10(9/2) 08-05-27 AI05-0053-1/06
!standard 6.5(2.1/2)
!class binding interpretation 07-05-15
!status ARG Approved 8-0-0 08-06-21
!status work item 08-05-21
!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 (see 7.5) 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; --
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; --
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 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 immutably 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
(see 7.5) 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.
****************************************************************
Editor's note, May 21, 2008
This AI was withdrawn from WG 9 consideration as AI05-0052-1, on which it depends,
was found to be seriously flawed. I do not believe that there is any actual problem
with this AI, only that it depends on a broken definition. (Indeed, this AI *would*
be broken with the original definition of AI-52, according to Pascal Leroy -- see
the discussion in the !appendix of AI-52.)
****************************************************************
Questions? Ask the ACAA Technical Agent