!standard 3.4(5.1/2) 08-10-18 AI05-0096-1/04 !standard 6.2(7) !class binding interpretation 08-05-23 !status Amendment 201Z 08-11-26 !status WG9 Approved 08-10-31 !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 unit 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 class-wide 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, consider 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 clearly 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) @drepl If the reserved word @b appears in a @fa, the parent type shall be a limited type. @dby If the reserved word @b appears in a @fa, 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) @drepl @xbullet in its declaration;> @dby @xbullet !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... ****************************************************************