Version 1.7 of ais/ai-00083.txt

Unformatted version of ais/ai-00083.txt version 1.7
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.

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

!topic Controlled objects are created without any initialization nor adjust.
!reference RM95-7.6(10)
!reference RM95-7.6(21)
!reference AI95-0083
!from Bj=F6rn K=E4llberg 99-05-01
!keywords Initialize, Adjust, Controlled, Aggregates
!discussion

In 7.6 (1-2) it is stated, that every object is initialized and finalized,
and by the use of controlled types the user can gain additional control.
However, since the binding interpretation AI95-0083, this is not true. It
is possible to create controlled objects, that are neither initialized nor
adjusted. This is a serious break of the abstraction and makes it very hard
or impossible to use finalization for a number of purposes. One example is
the example in the book Ada95 by John Barnes, page 302, where an attempt is
made to keep a reference count.

Consider a declaration where the object is assigned an initial value.

Package A is
     Type C_t is new controlled with
     ...
end A;

with A; use A;
package B is
     O1 : C_t;
     O2 : C_t := O1;
     O3 : C_T := (C_t with .....)

Above O1 is initialized. O2 is not initialized by 7.6(10) but is instead
adjusted as part of the assignment operation. O3 is not initialized, but by
the AI83 it is not adjusted either.

The rationale behind AI95-0083 was to avoid program_error due to access of
the subprogram ?Adjust? before elaboration. However, the same argument
applies also to Initialize, so before AI83 it was never possibly to declare
controlled objects in the same package specification as the type, if there
are overriding subprograms for Initialize or Adjust. Now it is still
impossible to declare objects, except if they are initialized with an
aggregate expression. Apart from making Initialize less useful, it is also
a break against the uniformity goal of Ada95.

A possible solution would be to mandate that the objects subject to AI83
should be initialized instead.

A similar problem arises regarding aggregates. 7.6(17) states that in an
assignment statement an anonymous object is created, into which the values
are assigned. The target is finalized, this anonymous value is assigned
into the target, and the anonymous object is initialized. Also in this case
seems at least two very popular compilers use the AI83, and not adjust (nor
initialize) the anonymous object. Thus, also the anonymous object is
created without any initialization nor adjust.

In order to construct reliable abstractions in the presence of controlled
types some predicates like those in 7.6(1) are needed. However, as written,
the interpretation is not quite clear.

Is the intent that the following statements shall be true for every
controlled object? (YES!) (Initialize, Adjust, Finalize means, that the
corresponding primitive operations of the controlled object are called)

-    Every controlled object is Initialized or Adjusted when created
-    Every object is Finalized before being destroyed
-    In the absence of an abort, Initialize or Adjust is always paired with
a corresponding Finalize. In the presence of an abort, the corresponding
Finalize operation may be called several times.

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

From: Robert Dewar
Sent: Tuesday, December 07, 1999 10:01 PM

I really dislike AI95-0083 for just the reasons given in the K=E4llberg note

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

From: Tucker Taft
Sent: Wednesday, December 08, 1999 2:08 PM

Bjorn Kaelberg wrote:
> ...
> Is the intent that the following statements shall be true for every
> controlled object? (YES!)

No.  Aggregates (and objects initialized with an aggregate)
undergo neither Initialize nor Adjust.  Since you can only write
an aggregate when you have full visibility on the type, there
is no abstraction-breaking going on here, since you could arbitrarily
change any visible component anyway in that case.  By making the type
private (or a private extension) you ensure that no aggregates
will be written outside of the package region.

> ...(Initialize, Adjust, Finalize means, that the
> corresponding primitive operations of the controlled object are called)
>
> -    Every controlled object is Initialized or Adjusted when created
> -    Every object is Finalized before being destroyed
> -    In the absence of an abort, Initialize or Adjust is always paired with
> a corresponding Finalize. In the presence of an abort, the corresponding
> Finalize operation may be called several times.

Finalize is paired with an Initialize, an Adjust, or an aggregate
evaluation.  Initialize corresponds to initialization by default,
Adjust corresponds to initialization by copy (from an object with its
own independent finalize), and aggregate evaluation corresponds to
component-by-component initialization.

> /Bj=F6rn K=E4llberg

Robert Dewar wrote:

> I really dislike AI95-0083 for just the reasons given in the K=E4llberg note

I don't understand this complaint.  Are you suggesting that aggregates
should *not* be finalized, or that either Adjust or Initialize should
be called on an aggregate before it is finalized?  Or perhaps are
you simply complaining that we shouldn't "require" the optimization
that enables the definition of deferred constants in the same package
with a controlled type?

I really don't see any viable alternatives at this point, even if
some of us dislike AI-83 for aesthetic reasons.  There is existing code
that uses aggregates to define controlled values, and these
assume that Adjust is *not* performed on the aggregate before it
gets used or finalized.  Personally, I find it intuitive that when I
construct a value using an aggregate, I construct it exactly as I
want it, not in some way that will require an Adjust before it
gets used further or finalized.

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


Questions? Ask the ACAA Technical Agent