Version 1.3 of ai05s/ai05-0096-1.txt

Unformatted version of ai05s/ai05-0096-1.txt version 1.3
Other versions for file ai05s/ai05-0096-1.txt

!standard 3.4(5.1/2)          08-07-11 AI05-0096-1/03
!standard 6.2(7)
!class binding interpretation 08-05-23
!status ARG Approved 7-0-1 08-06-22
!status work item 08-05-23
!status received 06-05-21
!priority Medium
!difficulty Medium
!qualifier Omission
!subject Deriving from formal limited types
!summary
An instance of a generic is illegal if a limited type extension is derived from a formal tagged limited private type and the actual type is nonlimited.
Derivation does not change the by-reference or by-copy status of a type.
!question
Consider the following:
generic type Lim is limited private; package Gen is ... private type New_Lim is limited new Lim; end Gen;
package New_Gen is new Gen (Integer);
The template is legal, but the instance seems to violate 3.4(5.1/2). However, that rule is not rechecked in the instance (it doesn't have the boilerplate wording for that), so this is legal. Is that intended? (Yes if untagged.)
!wording
Add to 3.4(5.1/2):
If the parent type is a tagged formal type, then in addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
AARM Note: If the parent type is an untagged limited formal type with an actual type that is nonlimited, we allow derivation as a limited type in the private part or body as no place could have visibility on the resulting type where it was known to be nonlimited (outside of the instance). (See the previous paragraph's annotations for an explanation of this.) However, if the parent type is a tagged limited formal type with an actual type that is nonlimited, it would be possible to pass a value of the limited type extension to a classwide type of the parent, which would be nonlimited. That's too weird to allow (even though all of the extension components would have to be nonlimited because the rules of 3.9.1 are rechecked), so we have a special rule to prevent that in the private part (type extension from a formal type is illegal in a generic package body).
Replace 6.2(7) by:
* an explicitly limited record type;
Delete the Ramification 6.2(7.a); it makes no sense here (there is no longer any mention of private here) and it essentially repeats the rule (just adding limited) if moved after 6.2(9).
!discussion
As explained by AARM 3.4(5.b-f/2), 3.4(5/2) is one of the (very few) rules where we intentionally don't recheck the rule in the private part. Specifically, a similar example to that in the question:
generic type Lim is limited private; package Gen is ... private type New_Lim is new Lim; end Gen;
package New_Gen is new Gen (Some_Tagged_Type); -- OK!
is allowed because nothing can have a view that "knows" that New_Lim is a tagged type.
So it seems that the example in the question should also be allowed for similar reasons. Note that the alternative would be to disallow most limited derivations (even for untagged types) in bodies, which seems too severe a restriction.
However, this is not true if the parent type is known to be a tagged type, because then we could pass a value of New_Lim to the class-wide type corresponding to the parent type. If that parent type is also nonlimited, we could then assign a limited tagged type.
An example should make this clearer:
generic type Lim is tagged limited private; with procedure Do_It (Obj : in Lim'Class); package Gen is ... private type New_Lim is limited new Lim with null record; end Gen;
type Non_Lim_Tagged is tagged record ...; Global : access Non_Lim_Tagged'Class;
procedure Copy_It (Obj : in Non_Lim_Tagged'Class) is begin Global := new Non_Lim_Tagged'Class'(Obj); end Copy_It;
package New_Gen is new Gen (Non_Lim_Tagged, Copy_It); -- Error:
Here, the template is clearly legal (the parent type is limited). However, the body of the generic could call Do_It with an object of type New_Lim. Moreover, the actual subprogram for that formal could copy the object, as in this example. That would be a weird thing to do with a type that was explicitly declared to be limited. Therefore, we recheck this rule in the private part for tagged types (a type extension from a formal type is illegal in a generic body anyway, so it doesn't pose a problem).
An objection was raised to this intepretation noting that a limited derived type is covered by 6.2(7), and thus such a type would be a by-reference type. That appears to be a problem if the actual type is by-copy; it surely cannot be both.
However, it is not specifically a problem with this case, but rather a general problem with the definition of by-reference types -- derived types should never change the parameter passing kind. For instance, consider:
package P is type LP is limited private; procedure Prim (Param : in LP); private type LP is new Integer; end P;
LP surely is a by-copy type (by 6.2(3)), so Param is passed by copy. Now make a non-generic package:
with P; package Q is type New_LP is limited new P.LP; end Q;
6.2(7) says that New_LP is a by-reference type; thus a call on the inherited Prim should pass Param by-reference. But the parent routine is by-copy, and it isn't possible to have the effect of pass-by-reference for Param. (Even a wrapper would have to copy Param; the only way to do it would be to duplicate the body of Prim, which is clear insanity.)
It's clear that this definition was not updated when limited derived types were introduced late in the Amendment process. An investigation of the standard shows that the reserved word "limited" is used only in a few places: private types and formal private types (already excluded by 6.2(7)), derived types and formal derived types (the incorrect case that we want to exclude from 6.2(7)), limited with clauses (irrelevant here), limited interfaces (covered by 6.2(5), so they don't need to be covered by 6.2(7)), and explicitly limited record types.
!corrigendum 3.4(5.1/2)
Replace the paragraph:
If the reserved word limited appears in a derived_type_definition, the parent type shall be a limited type.
by:
If the reserved word limited appears in a derived_type_definition, the parent type shall be a limited type. If the parent type is a tagged formal type, then in addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit.
!corrigendum 6.2(7)
Replace the paragraph:
by:
!ACATS Test
An appropriate example should be tested in an ACATS B-Test.
!appendix

From: Pascal Leroy
Sent: Thursday, May 22, 2008  12:24 AM

[From a private mail of an unrelated subject.]

Incidentally, I am wondering how 3.4(5.1/2) interacts with generics.  Shouldn't it
be rechecked in the private part of an instance?  Shouldn't it assume the worst in
a generic body?  Maybe it's a rule that ought to say "immutably limited".

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

From: Randy Brukardt
Sent: Friday, May 23, 2008  9:42 PM

I started to create an AI on this topic, but I've gotten less convinced that there is a
problem as I've looked at it. [And then more - read the whole message.]

First, let's discard the last sentence. Interfaces are never "immutably limited",
and the whole point of this feature was to allow the derivation of limited interfaces
to create new limited types. So if we required "immutably limited", these could never
be used for their intended purpose.

Second, rechecking in the private part. 3.4(5/2) does *not* require rechecking in
the private part. This one of the very, very few places where that was intentional.
(Indeed, itís the only one that comes to mind!) Consider:

   generic
       type Lim is limited private;
   package Gen is
       ...
   private
       type New_Lim is new Lim;
   end Gen;

   package New_Gen is new Gen (Some_Tagged_Type); -- OK!

This the only place in the language where it is legal to derive a tagged type without
a record extension. And that is considered OK because there is no place where New_Lim
could ever be viewed as a tagged type. (Similarly in the body of a generic.)
AARM 3.4(5.b/2 - 5.f/2) explains this.

3.4(5.1/2) is a very similar rule. Consider the very similar example:

   generic
       type Lim is limited private;
   package Gen is
       ...
   private
       type New_Lim is limited new Lim;
   end Gen;

   package New_Gen is new Gen (Integer); -- OK!

It seems that the same thinking should apply to this declaration: there is no location
where New_Lim could ever be seen as a non-limited type. So there doesn't seem to be
a problem. Similarly in the body.

However, there seems to be a possible problem with tagged types. 

   generic
       type Lim is tagged limited private;
   package Gen is
       ...
   private
       type New_Lim is limited new Lim with null record;
   end Gen;

   package New_Gen is new Gen (Some_Non_Limited_Tagged_Type); -- Ouch.

A declaration like New_Lim is illegal in a generic body by 3.9.1(4/2). And the
automatic recheck in the spec will make this illegal in the visible part. But
here in the private part, we have a case where where we have a limited tagged type
derived from a non-limited tagged type. Obviously, a class-wide operation of
Some_Non_Limited_Tagged_Type could assign a value of New_Lim.

However, even here I can't see a actual problem (although this is very weird).

   generic
       type Lim is tagged limited private;
   package Gen is
       ...
   private
       type New_Lim is limited new Lim with record
            Tsk : A_Task;
       end record;
   end Gen;

   package New_Gen is new Gen (Some_Non_Limited_Tagged_Type); -- Illegal.

3.9.1(3/2) says "if the parent type is nonlimited, then the components shall
be nonlimited", and is rechecked in the private part of the instance. Since
Lim is nonlimited in the instance, this declaration is illegal.

So the only extension components could be nonlimited, so assignment is available
for every component of New_Lim.

Still, this seems too weird for words. So I suggest adding a recheck rule for
tagged types only:

"If the parent type is a tagged type, in addition to the places where
Legality Rules normally apply (see 12.3), this rule applies also in the
private part of an instance of a generic unit."

AARM Note: If the parent type is a nonlimited untagged type, we allow derivation
as a limited type in the private part or body as no place could have visibility
on the resulting type where it was known to be nonlimited (outside of the instance).
(See the previous paragraph's AARM notes for an explanation of this.) However, if the
parent type is tagged, it would be possible to pass a value of the limited type
extension to a classwide type of the parent, which would be nonlimited. That's
too weird to allow (even though all of the extension components would have to be
nonlimited because the rules of 3.9.1 are rechecked), so we have a special rule
to prevent that in the private part (type extension from a formal type is illegal
in a generic package body).

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

From: Pascal Leroy
Sent: Saturday, May 24, 2008  3:13 AM

> It seems that the same thinking should apply to this declaration: there is
> no location where New_Lim could ever be seen as a non-limited type. So there
> doesn't seem to be a problem. Similarly in the body.

Not so fast.

First, it is very weird in this case too: reserved words should not lie.
One of the good things about "limited" in a type derivation is that it
improves readability and maintainability: if the parent type is changed to
become nonlimited, you hear about it.  This seems to apply in generics
as well as in non-generic code.

More importantly, you've got to explain what the parameter passing mode is
for New_Gen.New_Lim.  6.2(3) says that it's by-copy ('cause it's elementary)
and 6.2(7) says that it's by-reference ('cause it has "limited").  Are you
sure you want to force implementers to support by-reference integer types?
I don't think so. And tweaking the rules in 6.2 so prevent New_Gen.New_Lim
from being considered by-reference seems unwise to me: it would be a lot of
work and it could easily break things.

So again, I am arguing in favor of rechecking in the private part, and of
an assume-the-worst rule in the body.

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

From: Tucker Taft
Sent: Saturday, May 24, 2008  7:52 AM

Your point about parameter passing doesn't really fly, since the actual
could itself be a limited private type, but still be pass-by-copy.
I certainly don't understand the value of specifying assume-the-worst
in the body.

I don't see any harm in allowing this in the private part, presuming the type
is in fact treated like a limited private type whose full type might be
non-limited.

Or am I missing something fundamental?

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

From: Pascal Leroy
Sent: Sunday, May 235 2008  3:16 AM

> Or am I missing something fundamental?

I'm not sure if it's fundamental, but in Randy's example
New_Gen.New_Lim is certainly by-reference according to 6.2(7),
because it is "a nonprivate type with the reserved word limited
in its declaration". The definition of by-copy and by-reference was
written in such a way that the two categories were mutually exclusive.
Not anymore if Randy's example becomes legal. So at a minimum there is a
logical inconsistency that needs fixing.

But I am seriously concerned that there might be other places in the RM
where we assumed that elementary types were not limited. Someone will
have to look at all the usages of "limited" to see if making Randy's example
legal could cause trouble.

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

From: Tucker Taft
Sent: Sunday, May 25, 2008  6:58 AM

But from the point of view of the generic, New_Gen.New_Lim is a *private* type.
I don't think that changes when we instantiate, since the private part is not
visible outside the generic. Certainly if this were in the body that is the
position we would have to take.

So I think we have to make sure that 6.2(7) doesn't make such a type into a
by-reference type. It must remain a *private* type, even after instantiation,
just as it would if it were in the body.

We have had a rash of cases where we recheck legality rules in the private part,
but many of those have had to do with tagged types, where the compile-time and
run-time properties are all intertwined. With untagged types, I think we can
generally treat the private part pretty much like the body of the generic,
with no rechecking of legality rules, and with the "kind" of the type remaining
what it was from the point of view of the generic, rather than of the instance.
We'll have to be sure that the rules that matter (like 6.2(7)) are consistent
with this approach, of course.

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

From: Pascal Leroy
Sent: Monday, May 26, 2008  12:04 AM

> But from the point of view of the generic,
> New_Gen.New_Lim is a *private* type.  I don't
> think that changes when we instantiate, since
> the private part is not visible outside
> the generic.  Certainly if this were in the
> body that is the position we would have to
> take.

Hmm, that's an interesting viewpoint. I never saw it that way. I have always
thought that "In an instance, a generic_formal_parameter_declaration
declares a view whose properties are identical to those of the actual" (12.3(15)).
In this interpretation the actual is an integer type, so the formal is an
integer type too, and New_Lim is a type derived from an integer type.
Incidentally, that's the way I have implemented it (not that it proves anything).

I am curious to know what verse of the RM you think supports your viewpoint
that New_Lim is somehow private in an instance.
 
> We have had a rash of cases where we recheck legality
> rules in the private part, but many of those have
> had to do with tagged types, where the compile-time
> and run-time properties are all intertwined.  With
> untagged types, I think we can generally treat the
> private part pretty much like the body of the generic,
> with no rechecking of legality rules, and with
> the "kind" of the type remaining what it was from
> the point of view of the generic, rather than of
> the instance.

This would be a more convincing argument if there were more than one or
two rules that *don't* get rechecked in the private part.  As Randy mentioned,
3.4(5/2) if one of the few rules, and perhaps the only one, for which this
is true.  I have a hard time believing that all the other rules actually pertain
to tagged types.  It seems for instance that 4.9(37/2) pretty much contradicts
your statement (the private part is *not* treated like the body).

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

From: Tucker Taft
Sent: Monday, May 26, 2008  7:04 AM

One of the "language design principles" Bob and I had was that generic formal
privates and package privates were sort of "duals" of one another.
In a package private, the full type is given after the word "private."
In the generic formal private, the full type is the actual.

I'll see if I can track down the words in the AARM that discuss this.

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

From: Pascal Leroy
Sent: Monday, May 26, 2008  7:17 AM

I'm aware of the duality, and I remember reading the explanation in some
AARM note, but that still doesn't convince me that New_Lim is a private type
(note that 6.2(7) doesn't even say "descended from a private type").

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

From: Randy Brukardt
Sent: Tuesday, May 27, 2008  6:47 PM

>> Or am I missing something fundamental?
		
> I'm not sure if it's fundamental, but in Randy's example 
> New_Gen.New_Lim is certainly by-reference according to 6.2(7), because 
> it is "a nonprivate type with the reserved word limited in its 
> declaration".  The definition of by-copy and by-reference was written 
> in such a way that the two categories were mutually exclusive. Not anymore
> if Randy's example becomes legal.
> So at a minimum there is a logical inconsistency that needs fixing.

I think you are correct that there is something wrong with 6.2(7), but it
doesn't have anything to do specifically with my example. It's just that this
wording needs to take derived limited types into account. Consider the example
that Tucker implied earlier:

    package P is
       type LP is limited private;
       procedure Prim (Param : in LP);
    private
       type LP is new Integer;
    end P;

LP surely is a by-copy type (by 6.2(3)), so Param is passed by copy. So, now
make a non-generic package:

    with P;
    package Q is
       type New_LP is new P.LP;
    end Q;

Now, Pascal says 6.2(7) says that New_LP is a by-reference type. So the
inherited Prim's Param is supposed to be passed by-reference. How are we
going to do that?? The original Prim is pass-by-copy! You can fake
pass-by-copy for a pass-by-reference parameter, but not the reverse.

Obviously something is wrong with 6.2(7), it wasn't updated to take
explicitly limited derived types into account.

> But I am seriously concerned that there might be other places in the 
> RM where we assumed that elementary types were not limited.  Someone 
> will have to look at all the usages of "limited" to see if making 
> Randy's example legal could cause trouble.

I think this is FUD. We've had types like LP in the example above in Ada
since the beginning of time, and the planet hasn't stopped turning (and
implementations haven't crashed, well at least not most of the time).
I can buy the idea that there may be latent bugs in this area (there
aren't many areas where I'd be surprised by latent bugs!), but I think
we should worry about them when we find them, not sooner.

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

From: Randy Brukardt
Sent: Tuesday, May 27, 2008  6:57 PM

>> One of the "language design principles" Bob and I had was that 
>> generic formal privates and package privates were sort of "duals" of 
>> one another.
>> In a package private, the full type is given after the word 
>> "private."  In the generic formal private, the full type is the 
>> actual.

> I'm aware of the duality, and I remember reading the explanation in 
> some AARM note, but that still doesn't convince me that New_Lim is a 
> private type (note that 6.2(7) doesn't even say "descended from a private type").

I suspect the situation is similar to that for "constrained partial view",
where derivation does not eliminate any privateness.

But in any case, I think this is all irrelevant: 6.2(7) is badly
broken (limited derived types should not be included). Simple as
that. (And thanks for finding it, Pascal, we've managed to miss the
problem for far too long).

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

From: Tucker Taft
Sent: Tuesday, May 27, 2008  10:35 PM

How about if we change 6.2(7) to:

    * a record type with the reserved word limited in its definition;

Or:

    * an explicitly limited record type;

Doesn't that solve the problem?  I'm not sure what we had in mind when we
said "nonprivate" other than "record."

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

From: Pascal Leroy
Sent: Wednesday, May 28, 2008  12:05 AM

Yes I think it works.

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

From: Randy Brukardt
Sent: Wednesday, May 28, 2008  2:59 PM

> Doesn't that solve the problem?  I'm not sure what we had in mind when 
> we said "nonprivate"
> other than "record."

I think we (well, really you Ada 9x guys) just were trying to avoid defining
lots of new terms, and thus you didn't name "explicitly limited record type".
And this was a particularly bad way to describe explicitly limited record types
in Ada 95.

I just did a scan of the Ada syntax for uses of the word limited, and there
isn't any other types of interest that are covered by this wording: otherwise
there are various private and formal types (excluded already, or we surely want
them excluded), limited with (obviously irrelevant), derived types (new in the
Amendment), and explicitly limited records.

P.S. I never saw anyone actually agree or disagree with my analysis of the problem.
Besides wanting the reinforcement before I add this to the AI, the ego boost would
be nice, too. ;-) I was particularly interested how Pascal proposed to pass New_LP
parameters by reference to a routine that expects by-copy parameters. (Knowing the
answer to this would help implement shared generics!) I presume that you agree with
the existence of the problem since you're going on to solve it...

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


Questions? Ask the ACAA Technical Agent