Version 1.16 of ais/ai-00083.txt

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

!standard 07.06 (17)          00-07-13 AI95-00083/07
!standard 07.06 (21)
!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 by an assignment or return statement, the aggregate is built "in place". No anonymous object is created and Adjust is not called on the target object.
!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. 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):
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 work portably 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 (4.3(5) and AARM 4.3(5.b)). AARM 7.6(21.a) 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
<<Start_Example>> 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 exempt 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(17)
Insert after the paragraph:
For an assignment_statement, after the name and expression have been evaluated, and any conversion (including constraint checking) has been done, an anonymous object is created, and the value is assigned into it; that is, the assignment operation is applied. (Assignment includes value adjustment.) The target of the assignment_statement is then finalized. The value of the anonymous object is then assigned into the target of the assignment_statement. Finally, the anonymous object is finalized. As explained below, the implementation may eliminate the intermediate anonymous object, so this description subsumes the one given in 5.2, ‘‘Assignment Statements’’.
the new paragraph:
Implementation Requirements

For an aggregate of a controlled type whose value is assigned, other than by an assignment_statement or a 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 work portably 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.

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

From: John Barnes
Sent: Sunday, March 26, 2000 2:20 AM

I really want to see this initialization of aggregates discussed
sometime. I really hate it.

It is AI-197 and 83.

Tucker was supposed to be giving us a lecture on the topic but
unfortunately he will not be with us. But I note that the situation
has Robert's disapproval as well.

>Robert Dewar wrote:
>
>> I really dislike AI95-0083 for just the reasons given in the
>> Kallberg note

Maybe we have to put up with something (because of "null" constants in
certain predefined packages) but we don't have to put up with exactly
what we have at the moment.

I had hoped to write something last week but we had an unexpected
elderly relative in and out of hospital and a funeral. Well it was the
hospitalization which was unexpected. So was the funeral. I will try
to write something later today or tomorrow.

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

From: Tucker Taft
Sent: Sunday, March 26, 2000 7:22 AM

Here is my "lecture" on initialization-by-aggregate:

> >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.

I really don't know what more I could say.

If those who don't "like" these AIs could be more specific, I might
be able to expand my lecture.  I can believe there was a confused
belief that finalize was paired with only Initialize or Adjust,
but that is wrong.  There are three ways to initialize a controlled
object, by default, by copy, and by component-by-component initialization.
The third is available only when within the scope of the full
type definition, as appropriate (*all* controlled types should be
private, or else the "control" is pointless).

Let me know if a longer lecture would be of use, and what area I
should elaborate further.

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

From: Robert Dewar
Sent: Sunday, March 26, 2000 7:42 AM

Well I must say I have lost the thread here.

Can you answer the following questions

1. Does the original wording in the RM reflect your lecture? If not how
not and why not?

2. Does the AI change things wrt your lecture?

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

From: Tucker Taft
Sent: Sunday, March 26, 2000 9:23 AM

> 1. Does the original wording in the RM reflect your lecture? If not how
> not and why not?

Yes.  However, the original wording (e.g. of 7.6(1)) does not clearly spell out
that there are three ways to initialize a controlled object, by default,
by assignment, and component-by-component.  You must glean it from
reading 4.3(5) which indicates that an object is created as part of
evaluating an aggregate.

> 2. Does the AI change things wrt your lecture?

No.  My lecture still holds.

What the AI says is that when a controlled object is initialized
by assignment from an aggregate, rather than:

  1) creating an anonymous object by evaluating the aggregate, and
  2) assigning that anonymous object into the object being initialized
      (with the attendant adjustment)
  3) finalizing the anonymous object.

This must be shortened to simply:

  1) initializing the object directly from the component values specified
     in the aggregate.

Essentially, the "object" in which the aggregate is evaluated
is the user specified object, rather than a separate anonymous object.
The properties of this user-specified object are exactly like the "normal"
anonymous objects created as part of evaluating an aggregate.
In particular, they undergo no adjustment after evaluation of
the aggregate, but they do need to be finalized when their lifetime
ends.

The reason for the AI is that we do not want Adjust or Finalize
called in connection with using an anonymous object as a temporary
holder for the aggregate value, since neither operation has necessarily
been elaborated yet.

To reemphasize the critical point, this AI does *not* create a new
class of objects that get finalized without first having Adjust or
Initialize called.  That class of objects already existed -- the
anonymous objects created as part of evaluating an aggregate.
That class of objects will continue to exist independent of how we
end up wording this AI.  We most definitiely do not want to change
the way aggregate objects are created and finalized.

The only issue is whether the anonymous object should be created, copied,
and then finalized for the cases when an aggregate is used to initialize
some user-specified object.  This AI says no -- such aggregates should
be evaluated directly into their target object, to avoid the problematic
"early" calls on adjust and finalize.

The overall "balance" equation should remain that for a given controlled type:

  Number of aggregate evaluations +
    Number of calls on Initialize + Number of calls on Adjust =
  Number of calls on Finalize

This AI does not destroy this balance.  It simply reduces the
value by one on each side of the equation.

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

From: Robert Dewar
Sent: Sunday, March 26, 2000 11:05 AM

OK, this sounds reasonable to me, I withdraw my objection ... I am still
a bit concerned by making this a required change, I don't see how the
RM justifies this requirement. I see it as desirable, but how can we
require this change to be made?

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

From: John Barnes
Sent: Sunday, March 26, 2000 3:16 PM

Folks

Here is a hastily written note on AI-83 as promised.

I hope it makes some sort of sense.

See you on Wednesday.

--------

AI-83 and counting objects

The background to Bjorn Kallberg's note on AI-83 and
aggregates of controlled types is that he had to give a
course on Ada 95 to some Ada 83 programmers. One topic was
of course controlled types. Looking for an exercise for them
to do he came across an example in my book, (page 302 of 2nd
edition, page 286 of 1st edition). This example declares a
controlled type and keeps track of how many objects of the
type are in existence and also keeps an identity number in
each object. Here is the relevant lump of book (sorry, it's
a bit long)

   ... begin extract

   As a simple example, suppose we wish to declare a type
and keep track of how many objects (values) of the type are
in existence and also record the identity number of each
object in the object itself. We could declare

with Ada.Finalization;  use Ada.Finalization;
package Tracked_Things is
   type Thing is new Controlled with
      record
         Identity_Number: Integer;
         ...   -- other data;
      end record;
   procedure Initialize(Object: in out Thing);
   procedure Adjust(Object: in out Thing);
   procedure Finalize(Object: in out Thing);
end Tracked_Things;

package body Tracked_Things is
   The_Count: Integer := 0;
   Next_One: Integer := 1;

   procedure Initialize(Object: in out Thing) is
   begin
      The_Count := The_Count + 1;
      Object.Identity_Number := Next_One;
      Next_One := Next_One + 1;
   end Initialize;

   procedure Adjust(Object: in out Thing)
       renames Initialize;

   procedure Finalize(Object: in out Thing) is
   begin
      The_Count := The_Count - 1;
   end Finalize;

end Tracked_Things;

   In this example we have considered each value of a thing
to be a new one and so Adjust is the same as Initialize and
we can conveniently use a renaming declaration to provide
the body as was mentioned in Section 12.6. An alternative
approach might be to consider new things to be created only
when an object is first declared (or allocated). This
variation is left as an exercise.

   The observant reader will note that the identity number
is visible to users of the package and thus liable to abuse.

We can overcome this by using a child package in which we
extend the type. This enables us to provide different views
of a type and effectively allows us to create a type with
some components visible to the user and some components
hidden.

   Consider

package Tracked_Things is
   type Identity_Controlled is abstract tagged private;
private
   type Identity_Controlled is abstract new Controlled with
      record
         Identity_Number: Integer;
      end record;
   procedure Initialize ...
   -- etc.
end Tracked Things;

package Tracked_Things.User_View is
   type Thing is new Identity_Controlled with
      record
         ...   -- visible data
      end record;
end Tracked_Things.User_View;

   In this arrangement we first declare a private type
Identity_Controlled just containing the component
Identity_Number (this component being hidden from the user)
and then in the child package we further extend the type
with the visible data which we wish the user to see. Note
carefully that the type Identity_Controlled is abstract so
objects of this type cannot be declared and moreover the
user cannot even see that it is actually a controlled type.
We also declare Initialize, Adjust and Finalize in the
private part and so they are also hidden from the user.

   ... end extract

Bjorn asked his students to write such a package and declare
some objects. For some students it worked and for others it
all went wrong. It went wrong of course for those who were
lazy and declared their objects in the same package as the
type and thus had Initialize called before its body had been
elaborated.

AI-83 was designed so far as I am aware primarily to allow
constants to be declared in the package defining a type
provided they were initialized with an aggregate.  This
seemed necessary if users were to declare packages in the
same style for example as the type Character_Set in the
package Ada.Strings.Maps where the declaration of the type
is accompanied by that of a constant of the type named
Null_Set.

Personally I feel that the Ada style here is a bit dubious;
it seems bad practice to mix up declarations of a type with
those of objects of the type since in a sense the type is
not properly declared until the end of the package. I would
prefer to provide null values by declaring a function
returning a null value. This would avoid the problems.
(Maybe Ada is too free in its ability to do what you want
with a package (especially with tagged types) and the
building block approach lets us down by enabling us to build
inappropriate structures - maybe we should have had class
packages ...!)

Now given that we have to allow the wretched user to declare
a constant in the package defining the type then it seems we
have to allow these wretched in situ aggregates in that
package. However, there seems no reason to me to require
this optimization for objects initialized with aggregates in
other packages.

It seems to me to be quite reasonable to wish to monitor the
behaviour of a program by counting the number of times an
object of a type has been created and indeed to know how
many are in existence. Whether the type is private or not is
irrelevant since the count is not in the object but hidden
in the defining package where it is available to the author
of the package via various subprograms .

It seems that not only does the aggregate optimization
permit an object to be declared without it being counted but
moreover it will still get Finalized and so the count of
objects in existence could go negative which seems very
surprising.

However, if we restricted AI-83 to just the package defining
the type then provided the declarer of the package didn't
declare any objects, it would be fine because all objects
declared elsewhere would be counted properly.

Well that's my view of the story, I hope it aligns with
Bjorn's.

Incidentally when I wrote the example I think I hadn't
realized that if one declares an initialized object then
Initialize is not called anyway but only Adjust. This means
that one can't really count how many objects have been made
but just how many values have been made (maybe trying to
distinguish between values and objects is foolish and I
should remove the exercise that asks the reader to do that
from the book). However, it does seem reasonable to want to
be able to count how many are in existence at any time and
to be able to rely on the count going to zero at the end of
the relevant scope.

It's clearly an awkward topic.

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

From: Tucker Taft
Sent: Sunday, March 26, 2000 7:20 PM

> ...
> It seems that not only does the aggregate optimization
> permit an object to be declared without it being counted but
> moreover it will still get Finalized and so the count of
> objects in existence could go negative which seems very
> surprising.

As I tried to point out, with or without this optimization,
the proper equation is:
   Number of aggregate evaluations + Number of Inits + Number of Adjusts =
     Number of Finalizes.

If you don't count aggregates, but you do subtract one on each finalization,
you will get a negative count.  This optimization will not change that fact.

> However, if we restricted AI-83 to just the package defining
> the type then provided the declarer of the package didn't
> declare any objects, it would be fine because all objects
> declared elsewhere would be counted properly.

The whole notion of non-private controlled types seems like an obscure example,
and not worth making compilers more complicated.  Having different *run time*
semantics when inside or outside a package seems like a dangerous precedent, and
creates questions about inline expansion, macro-based generic instantiation,
child packages, etc.  All in all not a pretty situation.  Furthermore, as
pointed out above, you still need to count the "wretched" aggregates, whether or
not you perform this optimization, since they do require a balancing
finalization.

> ... However, it does seem reasonable to want to
> be able to count how many are in existence at any time and
> to be able to rely on the count going to zero at the end of
> the relevant scope.

And you will get the right answer if and only if you count aggregate evaluations
as well.

> ... It's clearly an awkward topic.

I think the example you have is a bit "awkward."  As you have discovered, it
really doesn't match the "normal" usage model for nonlimited controlled types.
Nonlimited controlled types are really about having conceptual "values" that may
involve levels of indirection.

If you shifted your example over to using limited controlled types, it would
probably make more sense, and you would only have to worry about Initialize and
Finalize -- no Adjusts or aggregates creating new values.

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

From: Robert Dewar
Sent: Sunday, March 26, 2000 9:16 PM

<<The whole notion of non-private controlled types seems like an obscure example
>>

I find this a bit of an extraordinary statement, non-private controlled
types are a valuable feature in my view.

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

From: Tucker Taft
Sent: Sunday, March 26, 2000 7:25 PM

Robert Dewar wrote:
>
> OK, this sounds reasonable to me, I withdraw my objection ... I am still
> a bit concerned by making this a required change, I don't see how the
> RM justifies this requirement. I see it as desirable, but how can we
> require this change to be made?

This is clearly a "binding interpretation."  The reason to require it is
to make the usage portable.  If we wanted to make things more complicated,
we could say that this "optimization" is only necessary prior to elaboration
of Adjust and Finalization, and everything they might call during their execution.
But that seems nasty to define.  If we have to make initialization-by-aggregate
special somewhere, we might as well give it a uniform semantics everywhere, so it can
never be the cause of an unexpected Program_Error due to an implicit call on
Adjust or Finalize.

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

From: Robert Dewar
Sent: Sunday, March 26, 2000 9:17 PM

I find this FAR too ambitious for a binding interpretation. Sure it is
desirable to have all compilers do the same thing, but I don't find the
justification for this AI in the RM. I would vote against it as a binding
interpretation, I don't see it can be supported.

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

From: Tucker Taft
Sent: Sunday, March 26, 2000 9:44 PM

> I find this FAR too ambitious for a binding interpretation. Sure it is
> desirable to have all compilers do the same thing, but I don't find the
> justification for this AI in the RM. I would vote against it as a binding
> interpretation, I don't see it can be supported.

I thought there was general agreement that there was a language
bug in this area, so a binding interpretation was appropriate
to fix the bug.

The complaint with the AI was that finalizations were not always balanced
by adjusts and initializes.  It turns out that this AI does not have any
effect on that complaint, so that particular complaint cannot
be fixed by fiddling with this AI.

If we want to debate whether the original problem
should be fixed, that's OK, but let's not get the
two discussions confused.

Also, if there are useful examples of non-private controlled types,
it would be helpful to see them, so we could evaluate alternative
solutions keeping them in mind.

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

From: Pascal Leroy
Sent: Monday, March 27, 2000 2:11 AM

> I find this FAR too ambitious for a binding interpretation. Sure it is
> desirable to have all compilers do the same thing, but I don't find the
> justification for this AI in the RM. I would vote against it as a binding
> interpretation, I don't see it can be supported.

I am confused now.  As I recall, this topic was discussed circa 1995, and I
don't remember seeing such a staunch opposition at the time.  Could you explain
what new piece of information has caused you to change your mind?

I don't think it is merely "desirable" for compilers to all do the same thing in
this instance, I think it's absolutely critical, because "null" constants of a
private type are such a common idiom, and numerous private types are controlled
(because they contain indirections).

Now maybe the resolution is to _mandate_ that Program_Error be raised in this
instance.  I could live with that (I believe that deferred constants should not
be in the language in the first place, so I am not going to fight for them).
But I couldn't live with the notion that the behavior is implementation-defined.

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

From: Randy Brukardt
Sent: Monday, March 27, 2000 11:12 AM

> And since there is no way I can count the aggregate evaluations since
> they occur silently, the whole quest is lost. Well that's a shame.

I really don't think so; I agree with Tuck here.

If your type is private, the only place that aggregates can occur is inside of
your package. So there is no particular problem to insure that they do the right
thing; and your users can't break that behavior.

Moreover, there is a distinct advantage to having them *not* call Initialize --
it provides a way to construct a value without having to worry about Initialize
coming along and screwing it up. That can be handy.

If you type is not private, it is not remotely bulletproof -- all that it takes
to mess up your count is for someone to change it explicitly. So what is the
concern? At most this gives you one more way to mess things up.

> It looks more and more as if types have to be limited in order to rely
> upon them and so user-defined assignment was a forlorn goal.
>
> RIP :=

Baloney. Claw is mostly constructed out of types that have user-defined
assignment with reference counting, and we have no problem with aggregates.
We've never had a user-reported bug having to do with this finalization (except
those related to compiler errors and the over-aggressive optimization covered by
AI-147).

We don't use aggregates in the bodies (although we could if we were careful with
the reference counts). It is not a problem in practice. It is more interesting
to deal with Adjust in a task-safe manner.

> Do extension aggregates have any bearing on this matter?

All aggregates of controlled types are extension aggregates by definition.
Controlled is private, so a non-extension aggregate is impossible.

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

From: Robert Dewar
Sent: Monday, March 27, 2000 7:33 AM

<<Now maybe the resolution is to _mandate_ that Program_Error be raised in
this instance.  I could live with that (I believe that deferred
constants should not be in the language in the first place, so I am not
going to fight for them).  But I couldn't live with the notion that the
behavior is implementation-defined.>>

There is an international standard for Ada.

Vendors implement this standard, and there is a validation suite for
this standard.

If the language is modified by the ARG in a manner that is not reasonably
justified by the standard, then we have a situation that needs examination.

Remember what we are talking about here is just one thing

denial or approval of validation

Nothing else is important. In other words, the issue is not the label on the
AI, it is whether a test appears for this feature.

I think we are on very shaky ground if we start including mandatory tests
for features not reasonably derivable from the intent of the RM.

I just don't see this feature that the AI prescribes as being reasonably
derivable from the intent of the RM. Yes, it is a hole. Yes, the language
should have addressed this, but it didn't.

It is one thing for the ARG to be in the business of improving the language
by filling in the hole, it is quite another for the ARA to be in the business
of denying validation to a vendor who implements the language as defined in
the international standard.

Traditionally, the import of the "binding interpretation" designation was
precisely that it results in a mandatory test.

I don't see that here.

There are two situations

1. All vendors move to implement this AI anyway, in which case it does not
matter two hoots what designation it has.

2. Not all vendors implement this AI. Now I think in practice the market place
will put pressure on to implement it, but if the market place does not generate
such pressure, I think it is dubious for the ARG to substitute legislation
to replace market demand. The result of this can easily be for a vendor to
say:

  "we no longer bother with validation, since it is no longer being run by
   the AJPO, and so has no official status in the US. Furthermore the
   replacement validation has run amok, and is requiring compilers to
   do things that are not required by the standard."

I would far prefer this to be a non-binding interpretation, and then have
a set of tests that tests for adherence to these NBI's reporting the results.

Speaking for ACT, we will be happy to work hard on passing all these tests
(we like to pass 100% of all the tests as you know :-) and will be happy to
advertise that our compiler passes 100% of the NBI tests, showing that our
technology is advancing and keeping track of things. If a competitor fails
to implement all the ABI tests, we will be happy to point this out, but would
feel it inappropriate to deny validation on this basis.

I hope that makes my position clearer.

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

From: Pascal Leroy
Sent: Monday, March 27, 2000 8:11 AM

> I think we are on very shaky ground if we start including mandatory tests
> for features not reasonably derivable from the intent of the RM.
>
> I just don't see this feature that the AI prescribes as being reasonably
> derivable from the intent of the RM. Yes, it is a hole. Yes, the language
> should have addressed this, but it didn't.

Arguments from intent are always rather bogus but...

The RM states that calling an unelaborated subprogram raises Program_Error, and
in the case that we are discussing, Adjust is clearly not elaborated.  Now there
is this permission to optimize away some calls to Adjust, and this is what makes
the behavior implementation-defined.

It seems to me that the intent of this permission was to make it possible to
generate better code, not to allow the declaration of null objects of controlled
types.  Therefore, I would argue that the intent of the RM is rather clear: this
case should raise Program_Error.

In fact, I believe that the AI is not satisfactory as written: it doesn't
completely fix the problem.  We implemented it a few years ago, and I recently
received a complaint from a customer who was trying to write a null object for a
type that was _not_ controlled, but had controlled subcomponents.  This case has
exactly the same problem as the one covered by the AI (i.e., P_E is raised by a
call to an unelaborated Adjust), but it is _not_ covered by the AI.  So if you
have a controlled type, and for one reason or another need to wrap it into a
record, your application suddenly fails at elaboration.

> 2. Not all vendors implement this AI. Now I think in practice the market place
> will put pressure on to implement it, but if the market place does not generate
> such pressure, I think it is dubious for the ARG to substitute legislation
> to replace market demand.

I am not exactly happy with the notion that the semantics of Ada should be
governed by market pressures.  The problem we are discussing is not an obscure
cornercase of the language, it's very visible, in the sense that users are
likely to run into (and in fact they do).  I'd much prefer an ARG resolution,
even if that resolution is that it has to raised P_E.

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

From: Robert Dewar
Sent: Monday, March 27, 2000 9:03 AM

<<I am not exactly happy with the notion that the semantics of Ada should
be governed by market pressures.  The problem we are discussing is not
an obscure cornercase of the language, it's very visible, in the sense
that users are likely to run into (and in fact they do).  I'd much
prefer an ARG resolution, even if that resolution is that it has to
raised P_E.
>>

But the question is, do you think we should deny validation to a vendor
who conforms to the RM in this area!

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

From: Randy Brukardt
Sent: Monday, March 27, 2000 11:22 AM

> <<I am not exactly happy with the notion that the semantics of Ada should
> be governed by market pressures.  The problem we are discussing is not
> an obscure cornercase of the language, it's very visible, in the sense
> that users are likely to run into (and in fact they do).  I'd much
> prefer an ARG resolution, even if that resolution is that it has to
> raised P_E.> >>
>
> But the question is, do you think we should deny validation to a vendor
> who conforms to the RM in this area!

Well, for what it is worth, there is an Annex H test which requires
implementation of AI-83. So at least compilers which have validated Annex H have
indeed been held to this requirement. I've even rejected petitions on the test
based on approved AI-83; I think Dan Lehman did so as well. So we've been
requiring this behavior for validation for quite a while.

			Randy
			ACAA Technical Agent

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

From: Randy Brukardt [Randy@RRSoftware.Com]
Sent: Monday, March 27, 2000 11:28 AM

> In fact, I believe that the AI is not satisfactory as
> written: it doesn't completely fix the problem.  We
> implemented it a few years ago, and I recently received a
> complaint from a customer who was trying to write a null
> object for a type that was _not_ controlled, but had
> controlled subcomponents.  This case has exactly the same
> problem as the one covered by the AI (i.e., P_E is raised by
> a call to an unelaborated Adjust), but it is _not_ covered by
> the AI.  So if you have a controlled type, and for one reason
> or another need to wrap it into a record, your application
> suddenly fails at elaboration.

I thought that was what was covered by AI-197 (now folded into AI-83) and it
ought to be covered by the Corrigendum wording for AI-83. Are you sure it isn't?
Are you looking at the version I created last November, and not some old,
obsolete version??

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

From: Robert A Duff
Sent: Monday, March 27, 2000 12:06 PM

Robert,

> Remember what we are talking about here is just one thing
>
> denial or approval of validatoin
>
> Nothing else is important.

It seems to me that *users* of Ada are more important than validation
issues, and uniformity in the case of controlled aggregates is of
benefit to users.

I understand your point, but I think there is plenty of precedent for
making this sort of small change to fix what is clearly a bug in the
language design.  I don't see it as an indication that the ARG is
running amok.

I would like to see a (required-to-pass) ACATS test for this feature.
The "market" alone has not shown itself to be capable of forcing this
kind of uniformity.

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

From: John Barnes
Sent: Monday, March 27, 2000 1:15 PM

>> And since there is no way I can count the aggregate evaluations since
>> they occur silently, the whole quest is lost. Well that's a shame.

>If you type is not private, it is not remotely bulletproof -- all that it
>takes to mess up your count is for someone to change it explicitly. So what
>is the concern? At most this gives you one more way to mess things up.

Nonsense, the count is not in the object, it is in the package
defining the type and thus not visible to users. Privateness is a red
herring.

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

From: Pascal Leroy
Sent: Monday, March 27, 2000 2:26 PM

John, I have read this paragraph five times, and I still don't understand it.
Could you illustrate what you mean with a code example?

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

From: Tucker Taft
Sent: Monday, March 27, 2000 2:04 PM

In John's example, he only has one count, which is an overall
count of objects/values of the type.  In such a case, it is certainly
true that the count can be hidden even though the controlled type
is public.  I also agree with John that this shows that controlled
types are not particularly good for this kind of application.

However, I also will say that this is not the application for which
they were designed.  They were designed to support treating objects
with possible levels of indirection as a single conceptual object,
so assignment did the appropriate amount of copying or reference
counting of underlying levels of indirection, and storage was
automatically reclaimed when the object went away.  This application
works fine, and is probably the kind of application that should be
used in examples of non-limited controlled types.

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

From: Robert Dewar
Sent: Monday, March 27, 2000 2:22 PM

That seems far too limited a view of why they were designed. perhaps that
was Tuck's main purpose, but they have many other purposes (e.g. we use
them internally for protected types, to make sure that various
operating system resources such as locks, are released).

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

From: Robert A Duff
Sent: Monday, March 27, 2000 2:55 PM

That's what *limited* controlled types are for.  I think Tuck was
talking only about non-limited controlled types.

By the way, remember the history?  Non-limited controlled types very
nearly didn't make it into the language, which was not true of the
limited kind.

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

From: Tucker Taft
Sent: Monday, March 27, 2000 3:25 PM

Responding to Robert Dewar:

I am surprised that you would use *non-limited* controlled types
for this purpose.  I certainly understand the resource management
uses of *limited* controlled types, but non-limited controlled types
seem most naturally designed for treating objects with levels of
indirection as "first class citizens."

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

From: Robert Dewar
Sent: Monday, March 27, 2000 4:34 PM

OK, point taken, yes, indeed these are of course limited types ...

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

From: Robert Dewar
Sent: Monday, March 27, 2000 1:46 PM

<<Well, for what it is worth, there is an Annex H test which requires
implementation of AI-83. So at least compilers which have validated Annex H
have indeed been held to this requirement. I've even rejected petitions on
the test based on approved AI-83; I think Dan Lehman did so as well. So we've
been requiring this behavior for validation for quite a while.
>>

Sorry, I misread this, can you quote the test, since this claim is definitely
interesting.

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

From: Robert Dewar
Sent: Monday, March 27, 2000 1:45 PM

But validation is never withheld for failing an annex test, so this is not
nearly so critical. I must say I don't know what AI-83 is offhand, so I
don't know whether this is fully comparable.

I agree with Pascal that if we want to force uniformity, requiring
Program_Error is more appropriate.

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

From: Robert Dewar
Sent: Monday, March 27, 2000 1:44 PM

<<I would like to see a (required-to-pass) ACATS test for this feature.
The "market" alone has not shown itself to be capable of forcing this
kind of uniformity.
>>

OK, then the disagreement is clear enough. I object to such a test and I
think the ARG is overstepping its bounds by requiring such a test.

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

From: Randy Brukardt
Sent: Monday, March 27, 2000 5:28 PM

Robert asked:
> Sorry, I misread this, can you quote the test, since this claim is
> definitely interesting.

The test in question is CXH3002. I was incorrect, however, about Dan Lehman
having ruled on it (I actually went and looked it up).

I *did* rule on it (informally); the implementor certainly accepted the
explanation and was not particularly concerned about it. (There never was a
formal petition, and it was not run past the FRT).

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

From: Ada Rapporteur Group of WG9; managing the Ada issues [ARG@ACM.ORG]
on behalf of Randy Brukardt [Randy@RRSOFTWARE.COM]
Sent: Monday, March 27, 2000 8:27 PM
To: ARG@ACM.ORG
Subject: Re: Fw: [Ada-Comment] Controlled objects are created without
anyinitialization nor adjust.

> can you list the relevant section of CXH3002.

According to the petition I have, the types and subprograms are defined on
lines 118-125, and the object is declared at line 204.

It's not a very realistic example, but it appears whomever wrote it expected
AI-83 to be in force, as there is a comment next to the object:

    -- attempt to call Initialize here would P_E!

Stupid place to test a core language rule, if that was the intent.

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

From: Pascal Leroy
Sent: Tuesday, March 28, 2000 1:51 AM

> I thought that was what was covered by AI-197 (now folded into AI-83) and it
> ought to be covered by the Corrigendum wording for AI-83. Are you sure it
> isn't? Are you looking at the version I created last November, and not some
> old, obsolete version??

You're right, I was stupidly looking at an old version, the one that has been
approved by WG9 in 1995, and which we implemented.  The latest wording correctly
covers the case of a controlled subaggregate.

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

From: Tucker Taft
Sent: Monday, April 03, 2000 9:30 AM

Erhard Ploedereder wrote:
> ...I think it is absolutely counter-productive
> to say that, for now, we should mandate P_E to be raised here, when the
> official language fix is just around the corner.

Independent of the bigger issues, I would certainly find it silly
for us to start mandating Program_Error for the most simple of these
cases.

Furthermore, my impression as an implementor, is once you support the
simple case of "Obj : constant blah := (aggregate)" handling the
more general case is a relatively small step.  In fact, it could make
things more complicated for the compiler to disallow Program_Error
for the simple case, and then require it for the more complex cases.

I could see in a transitional period disallowing Program_Error in
the simple case, and making it implementation-defined for the more
complex cases, with the goal of shifting to disallowing Program_Error
in all cases eventually.  In other words, the initial ACATS test would
check only the simple cases for lack of P_E.  Eventually, ACATS tests for
the more complex cases would be created as well.

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

From: Robert Dewar
Sent: Wednesday, April 05, 2000 1:08 PM

Let's get back to the substantive issue here, which is what the
subject is about. Should this be an NBI or not.

My argument that it should not be binding is that it seems clearly
to be a change int he language. I have not seen a good argument
that this interpretation was intended or can be read into the RM.
On the contrary it seems pretty clear that the RM demands program error
here.

I am not at all convinced that a proper analysis of implementation
difficulty has been carried out.

The only time we can MANDATE what are clear changes are when we know
there is no implementation burden, e.g. making Bit_Order static.

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

From: Erhard Ploedereder
Sent: Wednesday, April 05, 2000 5:32 PM

The RM doesn't demand P_E. P_E is the canonical semantics, but 7.6(21)
explicitly gives the permission to do as the AI is now mandating.

Raising P_E implies for the user that the declaration of a non-limited
controlled types with user-defined Initialize and Adjust and an object
creation of that type must not be in the same package spec (or list of basic
decl. items).

It's a pretty nasty portability problem if one implementation allows
this for initialized objects and the other always raises P_E for this.

The change by the AI is upward-compatible for the user (except for extremely
pathological examples). And, it will be in the Technical Corrigendum.

All this argues for implementers to make the change ASAP.

Traditionally, "non-binding" was applied to situations, where
   - it was an upward-incompatible change,
   - it directly contradicted the RM, or
   - it was a severe implementation burden.
The first does not apply; the second (arguably) does not really apply,
given 7.6(21) and the derivative nature of the elaboration error that
arises; no strong argument was made so far for the third case.

So I don't think that a NBI rating is warranted.

Whether the ACATS includes a rated test for it at day zero or after some
suitable phasing-in period, is a different question. Needless to say, I'm
heavily against the day zero approach, which is unreasonable.

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

From: Robert Dewar
Sent: Wednesday, April 05, 2000 5:47 PM

<<no strong argument was made so far for the third case.>>

I hereby make that case, we have not done the analysis in GNAT to know how
difficult this is. It is by no means trivial, we did investigate this
some time ago, and decided that the difficulty did not justify the
technical gain at the time, and that there was no significant customer
demand for this feature.

I object to changes in the language which force vendors to deploy resources
in a manner that is not consistent with best serving our customers. I prefer
that *we* be the judge of how best to serve our customers when it comes to
improvements in our technology, not the ARG!

What the NBI designation does here is to say, "sorry, we were wrong,
this implementation permission should have been a requirement, and in
the next version of the language it will be a requirement."

The trouble with the ARG is that it has a VERY narrow range of vision.
When it comes to finding a bug in the language and fixing it, that very
narrow range of vision is fine.

When it comes to deciding what enhancements to mandate to the language,
this narrow vision is very damaging, because it leads the ARG to decide
that a particular feature is desirable in isolation. But such isolated
decisions never make sense. Every improvement you make damages the
Ada tecnologies out there by siphoning off effort from possibly more
important features, and the ARG is in no position to make this tradeoff.

The language definition is quite clear, I see no convincing argument in the
AI, or elsewhere to change the language here. Yes, I see an argument that
the language would be more useful if this change is made, but that same
argument can be made for hundreds of other possible changes!

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

From: Tucker Taft
Sent: Wednesday, April 05, 2000 6:09 PM

Robert Dewar wrote:
> ...
> The only time we can MANDATE what are clear changes are when we know
> there is no implementation burden, e.g. making Bit_Order static.

I think we should mandate changes when we think there is a clear bug
in the language.  To me this is a bug.  People bump into this frequently
when they try to do very "normal" things with controlled types
(e.g. define a Null_Unbounded_String).  We simply goofed when we wrote the RM.
This isn't an enhancement in my view, but rather an important bug fix.

In general, the burden of proof of implementation problems has to be on the implementor,
not on the ARG.  Clearly several compilers have already implemented some
version of this.  It is hopeless for the ARG to do implementation burden
analysis on compilers they didn't write.  If there is an ARG member who
knows of a serious implementation burden, then by all means speak up.
I haven't heard anything like that yet.

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

From: Robert Dewar
Sent: Wednesday, April 05, 2000 7:22 PM

We are doing an analysis right now of the implementation burden.

I must say that I don't think of this as a bug in the language. Yes, it was
a bit of bad design, but if we are allowed to fix bad design decisions, I
have MUCH more important candidates, for example, the horrible non-upward
compatible design of fixed-point, which continues to cause us grief, and
the goof on the definition of Size which ends up being THE major non
compatibility between Ada 83 and Ada 95. These are FAR more serious
mistakes in the RM, but I am NOT suggesting fixing them (well I think
we need to fix them, but I would not dream of fixing them by changing
the language, rather than extending it).

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


Questions? Ask the ACAA Technical Agent