Version 1.10 of ai05s/ai05-0028-1.txt

Unformatted version of ai05s/ai05-0028-1.txt version 1.10
Other versions for file ai05s/ai05-0028-1.txt

!standard 10.2.1(9/2)          07-10-01 AI05-0028-1/06
!standard 10.2.1(10.1/2)
!standard 10.2.1(11.1/2)
!standard 10.2.1(11.2/2)
!standard 10.2.1(11.4/2)
!standard 10.2.1(11.8/2)
!class binding interpretation 06-11-13
!status Amendment 201Z 08-11-26
!status WG9 Approved 07-11-08
!status ARG Approved 8-0-2 07-06-03
!status work item 06-11-13
!status received 06-10-11
!priority Medium
!difficulty Easy
!qualifier Omission
!subject Problems with preelaboration
!summary
The rules related to preelaborable initialization and to pragma Preelaborable_Initialization are fixed to eliminate a number of inconsistencies: in some cases they were too strict, disallowing perfectly legitimate constructs, and in other cases they were too lax, allowing constructs that could lead to problems.
!question
1 - 10.2.1(10.1/2) says that any formal private type or extension is assumed to not have preelaborable initialization. Surely this should not apply if the formal type has a pragma Preelaborable_Initialization.
2 - The same paragraph doesn't mention formal untagged derived types, but it seems like it should. These types may have a pragma P_I (see 10.2.1(11.8/2)) and since the actual type may have discriminants that differ from those of the ancestor of the formal, its preelaboration properties may differ too.
3 - I can't find a rule that would make the following illegal:
generic
type T is range <>; package G is
pragma Preelaborate;
end;
package body G is
C : constant T := T'First;
end G;
but it seems that an instance of G where T is not a static type cannot be preelaborable, so this case should be caught by the assume-the-worst rules in 10.2.1(10/2-10.4/2).
4 - 10.2.1(11.4/2) says that any override of the Initialize procedure is illegal. It would seem that an override with a null procedure should be legal. (The AI on pragma P_I was written long before we had null procedures.)
5 - More importantly, the property of having an override for the Initialize procedure violates privacy, as shown by the following example:
package P is
pragma Preelaborate; type T is new Ada.Finalization.Controlled with null record;
private procedure Initialize (X : in out T);
end P;
It is not possible to apply pragma P_I to type P.T, so how are preelaborable clients of P supposed to know if they can declare an object of type P.T?
6 - In the case where pragma P_I is applied to a generic formal type extension, it would seem reasonable to require (on the generic) that the ancestor type have preelaborable initialization. Otherwise, we are left with a generic that is legal but cannot be instantiated, which is obnoxious.
7 - Ada 2005 dropped the "default initialized object" part of 10.2.1(9/2), because it was thought that it couldn't matter. Consider this example from ACATS test BA21002.A (case k):
type Tag is tagged record C : My_Int := Func; D : Integer; end record; ...
Tag1 : constant BA21002_0.Tag := (0, 10); -- OK. -- Declaration of a constant (j) (k).
Type Tag does not have preelaborable initialization (because of the default expression). So 10.2.1(9/2) says that object Tag1 is illegal. But that is a nasty incompatibility with Ada 95, which (as the test shows) allowed this declaration.
Was this incompatibility intended? (No.)
!recommendation
(See Summary.)
!wording
For questions 1 and 2, change 10.2.1(10.1/2) as follows:
the actual for each {discriminated formal derived type, }formal private type{,} [(]or {formal private} extension[)] declared within the formal part of the generic unit is a [private] type [(or extension)] that does not have preelaborable initialization{, unless pragma Preelaborable_Initialization has been applied to the formal type};
For question 4, change the second sentence of 10.2.1(11.4/2) as follows:
However, a [user-defined] controlled type with an [overriding] Initialize procedure {that is not a null procedure} does not have preelaborable initialization.
For question 5, change 10.2.1(11.7/2) as follows:
If the pragma appears in the first list of basic_declarative_items of a package_specification, then the direct_name shall denote the first subtype of a [private type, private extension, or protected type that is not an interface type and is without entry_declarations] {composite type}, and the type shall be declared immediately within the same package as the pragma. If the pragma is applied to a private type or a private extension, the full view of the type shall have preelaborable initialization. If the pragma is applied to a protected type, {the protected type shall not have entries, and} each component of the protected type shall have preelaborable initialization. {For any other composite type, the type shall have preelaborable initialization.} In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
AARM Note: The reason why we need the pragma for private types, private extensions, and protected types is fairly clear: the properties of the full view determine whether the type has preelaborable initialization or not; in order to preserve privacy we need a way to express on the partial view that the full view is well-behaved. The reason why we need the pragma for other composite types is more subtle: a non-null override for Initialize might occur in the private part, even for a nonprivate type; in order to preserve privacy, we need a way to express on a type declared in a visible part that the private part does not contain any nasty override of Initialize.
Also change 10.2.1(11.2/2) to fix a typo: ...or a generic formal derived type, [have] {has} preelaborable initialization...
For question 7, replace the first sentence of 10.2.1(9/2) with:
The creation of an object (including a component) {that is initialized by default, if its} [of a] type [that] does not have preelaborable initialization.
!discussion
For question 1, it is clear that the purpose of allowing pragma Preelaborable_Initialization for a formal type was to ensure that the actual type would have preelaborable initialization, so it is silly for the assume-the-worst rules to not take the pragma into account. Note incidentally that the assume-the-worst rules only apply to generic bodies, not to generic specifications: the specification will be rechecked when the generic is instantiated, but we won't have a second chance for the body.
For question 2, we must be cautious to avoid creating incompatibilities. Disallowing any object of a formal derived type would be a disaster. We want to limit ourselves to the case where the formal type is discriminated because that is the only case where the actual and formal types may have different defaults. Incompatibilities should be rare, and can easily be fixed with pragma Preelaborable_Initialization.
For question 3, the petitioner seems confused, as the example is illegal by 10.2.1(10.2/2).
For question 4, it is clear that the case of a null Initialize procedure was overlooked. It seems useful to be able to "regain" preelaborable initialization by overriding Initialize with a null procedure. Furthermore, because Ada.Finalization.Initialize is a null procedure, the wording is simplified a bit by the new rule.
For question 5, we need to be able to apply pragma Preelaborable_Initialization to any type that may have a controlled part, because the Initialize procedure for that part may be overridden in a private part. This means that the pragma now applies to practically any composite type. We still reject the pragma on interfaces, tasks, and protected types with entries, because it doesn't make sense there.
For question 6, it appears that the petitioner is misguided. The rule he proposes would have weird effects: for the untagged case it is perfectly possible to have a parent without preelaborable initialization and a derived type with preelaborable initialization. There doesn't seem to be any good reason to reject a construct that is potentially useful.
For question 7, it is obvious that the mysterious disappearance of "initialized by default" was not intended, as the entire purpose of the pragma is to properly deal with objects that are initialized by default (it has no effect in other cases).
!corrigendum 10.2.1(9/2)
Replace the paragraph:
by:
!corrigendum 10.2.1(10.1/2)
Replace the paragraph:
by:
!corrigendum 10.2.1(11.2/2)
Replace the paragraph:
by:
!corrigendum 10.2.1(11.4/2)
Replace the paragraph:
by:
!corrigendum 10.2.1(11.7/2)
Replace the paragraph:
If the pragma appears in the first list of basic_declarative_items of a package_specification, then the direct_name shall denote the first subtype of a private type, private extension, or protected type that is not an interface type and is without entry_declarations, and the type shall be declared immediately within the same package as the pragma. If the pragma is applied to a private type or a private extension, the full view of the type shall have preelaborable initialization. If the pragma is applied to a protected type, each component of the protected type shall have preelaborable initialization. In addition to the places where Legality Rules normally apply, these rules apply also in the private part of an instance of a generic unit.
by:
If the pragma appears in the first list of basic_declarative_items of a package_specification, then the direct_name shall denote the first subtype of a composite type, and the type shall be declared immediately within the same package as the pragma. If the pragma is applied to a private type or a private extension, the full view of the type shall have preelaborable initialization. If the pragma is applied to a protected type, the protected type shall not have entries, and each component of the protected type shall have preelaborable initialization. For any other composite type, the type shall have preelaborable initialization. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit.
!ACATS test
Better be some.
!appendix

From: Pascal Leroy
Date: Wednesday, October 11, 2006  3:35 AM

I am implementing the new preelaboration and purity rules in 10.2.1, and I
believe that some of the rules are incorrect or incomplete.

1 - 10.2.1(10.1/2) says that any formal private type or extension is
assumed to not have preelaborable initialization.  Surely this should not
apply if the formal type has a pragma Preelaborable_Initialization.

2 - The same paragraph doesn't mention formal untagged derived types, but
it seems like it should.  These types may have a pragma P_I (see
10.2.1(11.8/2)) and since the actual type may have discriminants that
differ from those of the ancestor of the formal, its preelaboration
properties may differ too.

3 - I can't find a rule that would make the following illegal:

	generic
	   type T is range <>;
	package G is
	   pragma Preelaborate;
	end;

	package body G is
	   C : constant T := T'First;
	end G;

but it seems that an instance of G where T is not a static type cannot be
preelaborable, so this case should be caught by the assume-the-worst rules
in 10.2.1(10/2-10.4/2).

4 - 10.2.1(11.4/2) says that any override of the Initialize procedure is
illegal.  It would seem that an override with a null procedure should be
legal.  (The AI on pragma P_I was written long before we had null
procedures.)

5 - More importantly, the property of having an override for the
Initialize procedure violates privacy, as shown by the following example:

	package P is
	   pragma Preelaborate;
	   type T is new Ada.Finalization.Controlled with null record;
	private
	   procedure Initialize (X : in out T);
	end P;

It is not possible to apply pragma P_I to type P.T, so how are
preelaborable clients of P supposed to know if they can declare an object
of type P.T?

6 - In the case where pragma P_I is applied to a generic formal type
extension, it would seem reasonable to require (on the generic) that the
ancestor type have preelaborable initialization.  Otherwise, we are left
with a generic that is legal but cannot be instantiated, which is
obnoxious.

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

From: Tucker Taft
Date: Wednesday, October 11, 2006  7:01 AM

> 2 - The same paragraph doesn't mention formal untagged derived types, but
> it seems like it should.  These types may have a pragma P_I (see
> 10.2.1(11.8/2)) and since the actual type may have discriminants that
> differ from those of the ancestor of the formal, its preelaboration
> properties may differ too.

Can you give an example of this?  Untagged derived types
cannot add discriminants, only effectively rename them.
Is it that the *defaults* for the discriminants can change,
and that's the issue for P_I?

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

From: Pascal Leroy
Date: Wednesday, October 11, 2006  8:30 AM

Yes, absolutely.  For instance:

	type T1 (D1 : Integer := 3) is null record;
	type T2 (D2 : Integer := Some_Nasty_Function) is new T1(D1=>D2);

Type T1 has obviously preelaborable initialization, and type T2 doesn't
because the creation of a default-initialized object of type T2 calls
Some_Nasty_Function.

Now consider a formal type:

	generic
	   type Formal is new T1;
	package G is
	end G;

	package body G is
	   X : Formal;
	end G;

We want to reject the generic body because if G were instantiated with T2
as an actual type, the elaboration of X would call Some_Nasty_Function.

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

From: Greg Gicca
Date: Thursday, October 12, 2006  8:40 AM

A question from the side lines...

Can T1 and T2 be in different packages?

If so how do you reject the generic since T2 may not have been compiled
(or exist) when you compile G.  A bind or link time error seems unreasonable.

Maybe a novice question.

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

From: Randy Brukardt
Date: Thursday, October 12, 2006  11:48 AM

> Can T1 and T2 be in different packages?

Yes, of course.

> If so how do you reject the generic since T2 may not have been
> compiled (or exist) when you compile G.  A bind or link time
> error seems unreasonable.

Generally, in generic bodies Ada "assumes the worst". That is, if it is
*possible* for some actual to make the body illegal, then the body is
illegal even if there are many actuals that wouldn't make the body illegal.
This makes generic body sharing possible, and preserves the contract model,
among other things.

So, in this case, since it is possible for there to be a type like T2
(whether or not it actually exists), the generic body should be illegal.
Pascal's point is that we don't have such a rule, and we need one.

> Maybe a novice question.

No comment. ;-)

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

From: Randy Brukardt
Date: Friday, January 12, 2007  1:32 PM

We dropped the "default initialized object" part of 10.2.1(9/2), because we
thought that it couldn't matter. I think we were wrong. Consider this example
from test BA21002.A (case k):

   type Tag is tagged record
      C : My_Int := Func;
      D : Integer;
   end record;
   ...

   Tag1  : constant BA21002_0.Tag := (0, 10);                         -- OK.
                                       -- Declaration of a constant (j) (k).

Type Tag does not have preelaborable initialization (because of the default
expression). So 10.2.1(9/2) says that object Tag1 is illegal.

But that's a nasty incompatibility with Ada 95 (as shown by this example),
and there is no problem so long as the reason that type Tag does not have
PInit is simply because of its default expressions (or its component types
default expressions, ad nausum).

I think we have to put back the "default initialized" wording. That would also
cover <> in aggregates, the only way to write something other than default
initialization for these sorts of types as a whole. The only alternative to
that is to have two kinds of PInit, which sounds like heading down a rabbit hole
to me.

What do you think?

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

From: Tucker Taft
Date: Friday, January 12, 2007  2:27 PM

I agree, 10.2.1(9/2) should say:

   ... the creation of a default-initialized object (including
   a component) of a type that does not have preelaborable
   initialization.

Or something like that.  If you look at the wording in
10.2.1(11.3/2) there is similar wording, and perhaps we
should just use that as a model.  We clearly realized that
default initialization was critical when deciding whether
a component was preelaborable, so it makes sense to have
the same criteria for deciding whether an object is
preelaborable.

So this is a corrigendum bug, I guess.

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

From: Randy Brukardt
Date: Friday, January 12, 2007  2:54 PM

> I agree, 10.2.1(9/2) should say:
> 
>    ... the creation of a default-initialized object (including
>    a component) of a type that does not have preelaborable
>    initialization.
> 
> Or something like that.

Well, it should include the new technical term initialized by default
(which covers cases in allocators and aggregates as well). That wasn't done
in the past 'cause there was no such term. But we surely want this to apply
to aggregates and allocators.

I also don't think we need the component wording: it's covered by the
component rules for preelaborable initialization (PInit). That is, if
there is a component that doesn't have PInit, then the type containing it
cannot have PInit, either. That is part of the Ada 95 text that was moved
to (11.1-11.5/2), but was left here for no reason.

   ... the creation of an object that is initialized by default of a type
   that does not have preelaborable initialization.

> If you look at the wording in
> 10.2.1(11.3/2) there is similar wording, and perhaps we
> should just use that as a model.  We clearly realized that
> default initialization was critical when deciding whether
> a component was preelaborable, so it makes sense to have
> the same criteria for deciding whether an object is
> preelaborable.

Yes, and it makes even more sense because the wording was there in Ada 95, but
someone convinced us we didn't need it. That often happens because we didn't
understand all of the ramifications; it's always iffy to remove existing wording
"because we don't need it".

> So this is a corrigendum bug, I guess.

Yes. I think we should just add it to AI05-0028, which is already discussing
various preelaborable initialization bugs. I especially like that because that
AI is not assigned to me, so I'll just add the question and get out of the way. ;-)

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


Questions? Ask the ACAA Technical Agent