Version 1.6 of ais/ai-00083.txt

Unformatted version of ais/ai-00083.txt version 1.6
Other versions for file ais/ai-00083.txt

!standard 07.06 (21)          99-11-18 AI95-00083/05
!standard 07.06.01 (20)
!class binding interpretation 95-07-27
!status Corrigendum 2000 99-07-28
!status WG9 approved 95-06-14
!status work item (letter ballot was 10-1-0) 96-06-05
!status ARG approved (subject to letter ballot) 7-0-3 95-11-01
!status received 95-07-27
!qualifier Clarification
!subject Aggregates of a controlled type
!summary
When an (extension) aggregate of a controlled type is assigned other than an assignment or return statement, the aggregate is built "in place"; Finalize and Adjust are not called on the object as a whole.
!question
If an object of a controlled type is declared in the same package as the type, and initialized with an aggregate, is Program_Error raised? (No.)
!recommendation
When an aggregate of a controlled type is created and immediately assigned into an object other than in an assignment or return statement (that is in an initial expression, subaggregate, formal parameter, generic IN parameter, or allocator), the implementation must not create a separate anonymous object for the aggregate; it must create the value of the aggregate directly in the target object. Thus, there is no assignment from the anonymous object to the target object, so the Finalize and Adjust that would be done for that assignment are not done.
!wording
(See corrigendum.)
!discussion
Consider the following controlled type:
type Dyn_String is private; Null_String : constant Dyn_String;
... private
type Dyn_String is new Ada.Finalization.Controlled with record ... end record; procedure Finalize(X : in out Dyn_String); procedure Adjust(X : in out Dyn_String);
Null_String : constant Dyn_String := (Ada.Finalization.Controlled with ...);
Clearly, at the time when the full constant declaration for Null_String is elaborated, the bodies for Finalize and Adjust have not yet been elaborated. RM 7.6(21) gives the permission to build the aggregate directly in the target object, thereby eliminating the need for the assignment (and the associated calls on Adjust/Finalize):
21 For an aggregate or function call whose value is assigned into a
target object, the implementation need not create a separate anonymous object if it can safely create the value of the aggregate or function call directly in the target object.
However, it seems important to require this behavior, so that this kind of programming is portable (that is, it will portably work without raising Program_Error due to an access-before-elaboration from calling Adjust or Finalize before their bodies are elaborated).
In other words, the first sentence of 7.6(21) should be an Implementation Requirement in the case where a new object is being created.
Note that no Adjust ever takes place on an aggregate as a whole, since there is no assignment to the aggregate as a whole (AARM 4.3(5,5b)). AARM 7.6(21a) talks about this case, and says that "only one value adjustment is necessary". This is misleading. It should say that only one adjustment of each controlled subcomponent (if any) is necessary in this case. No adjustments of the object as a whole are necessary (and as suggested above, such adjustments should be disallowed).
Note that this interpretation applies to all object creations, not just to object_declarations. Thus, continuing the above example, if we have:
type Dyn_String_Ptr is access all Dyn_String; Null_String_Ptr: Dyn_String_Ptr := new Dyn_String'(Ada.Finalization.Controlled with ...);
The aggregate must be built directly in the newly-created heap object.
Similarly, if we have
function Is_Null (Value : in Dyn_String) return Boolean;
then the aggregate actual parameter in the call
if Is_Null ((Ada.Finalization.Controlled with ...)) then
is built directly in a temporary object, and Adjust is not called on the object as a whole.
We except assignment and return statements from this requirement as there is no compelling reason to burden implementations with this requirement in those cases.
Note that all aggregates of a controlled type are extension aggregates: Controlled and Limited_Controlled are private, so it is not possible to create a normal record aggregate for such a type.
!corrigendum 7.06.01(20)
Insert after the paragraph:
the new paragraph:
Implementation Requirements

For an aggregate of a controlled type whose value is assigned, other than assignment_statement or return_statement, the implementation shall not create a separate anonymous object for the aggregate. The aggregate value shall be constructed directly in the target of the assignment operation, and Adjust is not called on the target object.
!ACATS test
ACATS test C761010 was constructed to test this rule. The annex H ACATS test CXH3002 also requires these semantics.
!appendix

!section 7.6(21)
!subject Aggregates of a controlled type
!reference RM95-7.6(21)
!reference AARM-7.6(21.a);6.0
!from Tucker Taft 95-07-25
!reference as: 95-5234.a Tucker Taft 95-7-25>>
!discussion

Consider the following controlled type:

    type Dyn_String is private;
    Null_String : constant Dyn_String;
 ...
 private
    type Dyn_String is new Ada.Finalization.Controlled
      with record
        ...
      end record;
    procedure Finalize(X : in out Dyn_String);
    procedure Adjust(X : in out Dyn_String);

    Null_String : constant Dyn_String :=
      (Ada.Finalization.Controlled with ...);

Clearly, at the time when the full constant declaration for
Null_String is elaborated, the bodies for Finalize and Adjust have
not yet been elaborated.  RM 7.6(21) gives the permission to
build the aggregate directly in the target object, thereby
eliminating the need for the assignment (and the associated calls on
Adjust/Finalize).   However, it seems important to *require*
this behavior, so that this kind of programming is portable
(that is, it will portably work without raising Program_Error due
to an access-before-elaboration from calling Adjust or Finalize
before their bodies are elaborated).

In other words, at least parts of 7.6(21) should be moved to
an Implementation Requirement (or the Dynamic Semantics).
In particular, when a constant is initialized from an aggregate
of a controlled type, the aggregate should be built in place,
and no Adjust or Finalize on the constant object as a whole
should take place.  Note that no Adjust ever takes place
on an aggregate as a whole, since there is no assignment
to the aggregate as a whole (AARM 4.3(5,5b)).

Note that AARM 7.6(21a) talks about this case, and says that
"only one value adjustment is necessary".  This is misleading.
It should say that only one adjustment of each controlled *subcomponent*
(if any) is necessary in this case.  *No* adjustments of the object as
a whole are necessary (and as suggested above, such adjustments
should be disallowed).

-Tuck

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

!section 7.6(21)
!subject Aggregates of a controlled type
!section 7.6
!reference AI95-00083/02
!reference RM95-7.6(21)
!from Gary Dismukes 97-07-11
!reference 1997-15794.a Gary Dismukes 1997-11-8>>
!discussion

AI-83 requires that a controlled object initialized by an
aggregate be built in place, without the use of the canonical
temporary object.  It seems that this requirement should also
exist for cases involving controlled components with default
initialization given by an aggregate, but it's not clear from
the wording of the AI whether it's intended to apply to such
cases.

For example, building on the example in the AI:

    type Dyn_String is private;
    Null_String : constant Dyn_String;
 ...
 private
    type Dyn_String is new Ada.Finalization.Controlled
      with record
        ...
      end record;
    procedure Finalize(X : in out Dyn_String);
    procedure Adjust(X : in out Dyn_String);

    type Rec_with_String is
      record
        Str : Dyn_String := (Ada.Finalization.Controlled with ...);
      end record;

    RS : Rec_with_String;

Is it intended that the component RS.Str be initialized in place,
without the use of a separate temporary object to hold the value
of the aggregate?  The fact that the summary says "explicitly
initialized" makes it unclear to me whether the AI is intended
to apply to this case (although the end of the !recommendation
does state that the interpretation applies to all object creations).
The question is whether the component's default initialization
is properly considered to be an "explicit initialization" for
purposes of this AI.

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

From: Randy Brukardt
Sent: October 7, 1999

At the recent ARG meeting, discussion centered around insuring that all
places where an extension aggregate can be used are explicitly covered
by the wording for this AI. (This discussion subsumes AI-00197, which now
will be deleted and merged into this one).

The meeting enumerated the places where an extension aggregate can be used:

Assignment, return statement, generic IN parameter, formal parameter,
allocator, initial expression, and subaggregate.

The meeting then discussed for each place whether or not this rule should
apply. Eventually, it was decided it should apply to all cases except
assignment and return statements. While return statements probably could be
implemented without a temporary for the aggregate, there did not seem to
be any compelling reason to burden implementations with such a requirement.

New wording was crafted to insure that all of the cases are covered.
Note that all aggregates of a controlled type are extension aggregates:
Controlled itself is private, so it is not possible to create a normal
aggregate for such a type.

------

The WG9 concerns with this AI were also discussed at the recent ARG meeting.
The following example was given:

   type T is new TP with ...
   X : T := T'(TP with ...);

If TP is controlled, then TP initialize gets called, but the finalize is T.

After discussion, it was determined that is in fact the expected behavior in
Ada 95, and indeed this AI doesn't change that fact. There are three ways to
create a controlled object in Ada 95: via Initialize (a default-initialized
object); Adjust (an explicitly initialized object); and via an aggregate
(for which neither Initialize nor Adjust is called).

It was pointed out that it is important that an overridding Finalize (for
instance) call the Finalize routine of its parent. If T's Finalize calls
TP Finalize in the above example, there probably aren't any problems.

Tucker volunteered to write an LSN document describing the Ada 95 controlled
type model, including the various surprises which are possible.

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


Questions? Ask the ACAA Technical Agent