!standard 3.10(9/2) 08-02-25 AI05-0053-1/04 !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? (Ban aliased return objects) !recommendation Ban 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) (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 Ada05 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) @drepl A view of an object is defined to be @i if it is defined by an @fa or @fa with the reserved word @b, 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 @b 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. @dby A view of an object is defined to be @i if it is defined by an @fa or @fa with the reserved word @b, 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 @fa (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) @drepl @xcode<@fa@ft<@b>@fa< defining_identifier : [>@ft<@b>@fa<] return_subtype_indication [:= expression] [>@ft<@b>@fa< handled_sequence_of_statements >@ft<@b>@fa<];>> @dby @xcode<@fa@ft<@b>@fa< defining_identifier : return_subtype_indication [:= expression] [>@ft<@b>@fa< handled_sequence_of_statements >@ft<@b>@fa<];>> !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. ****************************************************************