!standard 3.9.3(3/2) 08-04-18 AI05-0068-1/03 !standard 3.9.3(4/2) !standard 3.9.3(5/2) !standard 3.9.3(6/2) !class ramification 07-11-10 !status WG9 Approved 08-06-20 !status ARG Approved 9-0-0 06-11-10 !status work item 07-10-23 !status received 07-10-11 !priority Medium !difficulty Hard !qualifier Clarification !subject Inherited subprograms may be both abstract and require overriding !summary Whether or not a subprogram is abstract when it is inherited depends on the view of the parent type. !question Consider the following: package P is type I is interface; procedure Op (X : I) is abstract; end P; with P; package Q is type T is abstract new P.I with private; -- Op inherited here. private type T is new P.I with null record; procedure Op (X : T) is null; end Q; with Q; package R is type T2 is new Q.T with null record; -- Legal? (No.) Op inherited here, but how? end R; 3.9.3(4/2) talks about types, not views. That would seem to imply that Q.Op "requires overriding". When that is inherited, it is inherited as a concrete routine and thus R is legal. However, this breaks privacy. If the full definition of type T had been abstract and no overriding provided, Q.Op would have been abstract and R would be illegal (because the inherited routine would require overriding, but no overriding is given). Is this privacy breakage intended?? (No.) !response Generally, when we say "type" (or "object") in the Standard, we really mean "view of the type" (or "view of the object"). That's especially true for Legality Rules, because the compiler can really only know about views. Anything else would break privacy. Therefore there is nothing wrong with the wording. 3.9.3(3/2) does not help in this case; it applies only to explicit abstract declarations. If it applied to inherited ones, then package Q would be illegal, and obviously we don't want that. Note that this implies that whether an inherited subprogram is abstract or concrete depends on the view of the type that it was inherited from. In the case of Q, Q.Op in the visible part is abstract, while Q.Op in the private part is concrete. That is, R is illegal since it is an unrelated unit, but if R was renamed to be a private child of Q, then it would be legal. This has other unusual effects. One rule that has been mentioned is 8.5.4(5.1/2): renaming a "requires overriding" subprogram is illegal (this is the so-called "squirreling rename"). Given the fact that abstractness depends on the view: with P; package S is type T is abstract new P.I with private; -- Op inherited here. procedure Org_Op1 (X : T) renames Op; -- Legal. private type T is new P.I with null record; procedure Org_Op2 (X : T) renames Op; -- Illegal. procedure Op (X : T) is null; end S; The first rename is legal because it is renaming an abstract subprogram, the second is illegal because it is renaming a "requires overriding" subprogram. !ACATS Test An ACATS B-test like the example in the question would be valuable. !appendix From: Randy Brukardt Sent: Wednesday, October 10, 2007 10:00 PM [Note: this is a condensed version of a private thread. - ED.] John asked if the following example is legal: package P is type T is abstract tagged private; procedure Op(X: T) is abstract; private type T is tagged record C: Integer; end record; end P; AARM 3.9.3(3.b) says it is not. What rule makes this illegal? The reply was: The full type is illegal because it violates 3.9.3(3): "If it [an abstract subprogram] is a primitive subprogram of a tagged type, then the tagged type shall be abstract." The partial view is legal, of course, but that doesn't absolve the full view from meeting this rule. ACATS test B393005 checks that this is so, so obviously all compilers enforce it. **************************************************************** From: Pascal Leroy Sent: Thursday, October 11, 2007 1:25 AM This is correct of course, but it is still a rather contrived way to express the rule. In particular, it is quite confusing to talk about abstract types, it would be clearer to say abstract view of a type: that would make it clear that both views must be considered when checking the rule. Also, the rule should not apply to abstract primitive subprograms that are overridden by non-abstract subprograms. We know that this is the intent, but it's not supported by the wording. In fact, 3.9.3(3) seems to apply only to explicitly-declared subprograms, but this is wrong, it has to apply to implicitly-declared abstract subprograms too. Lousy wording. Incidentally, a user note might be in order here: this is a case where the normative wording has rather non-intuitive consequences, and this should be spelled out. **************************************************************** From: Randy Brukardt Sent: Thursday, October 11, 2007 1:10 PM > This is correct of course, but it is still a rather contrived way to express > the rule. In particular, it is quite confusing to talk about abstract > types, it would be clearer to say abstract view of a type: that would make > it clear that both views must be considered when checking the rule. True enough, but insufficiently broken; this part of the wording is unchanged since Ada 95, and to my knowledge, this is the first time someone has been confused. Probably because there is an AARM note that gives essentially John's example... > Also, the rule should not apply to abstract primitive subprograms that are > overridden by non-abstract subprograms. We know that this is the intent, > but it's not supported by the wording. In fact, 3.9.3(3) seems to apply > only to explicitly-declared subprograms, but this is wrong, it > has to apply to implicitly-declared abstract subprograms too. No, that's wrong. Implicitly-declared inherited subprograms have their own rules (that is 3.9.3(4-6)); they become "shall-be-overridden" and are not necessarily illegal (and are *not* abstract). And I don't think there are any abstract predefined operations. Let me repeat that: inherited subprograms are *not* abstract. That continually confuses everyone because Bob and Tuck originally wrote it that way and could never remember that they changed it to a different; moreover, the comments in the ACATS tests made the same mistake until I fixed them in the recent test update (at least the ones I noticed; if anyone sees any more such comments, tell me and I'll fix them). So it is trivial for people to make that mistake. > Lousy wording. Not that bad, actually. These rules *are* confusing, and "view" probably would have been better, but there is nothing actually wrong. > Incidentally, a user note might be in order here: this is a case where the > normative wording has rather non-intuitive consequences, and this should be > spelled out. I guess I can't argue that because John was confused, but honestly I cannot see what is non-intuitive about this. There cannot be *any* non-abstract tagged type for which there is an abstract primitive. Anything else leads directly to madness, and I'm not sure what's surprising about that. What I find as non-intuitive is the way people try to end-run these rules that cannot be violated; what do they have to gain from calling an uncallable subprogram? **************************************************************** From: Pascal Leroy Sent: Thursday, October 11, 2007 1:25 AM > No, that's wrong. Implicitly-declared inherited subprograms > have their own rules (that is 3.9.3(4-6)); they become > "shall-be-overridden" and are not necessarily illegal (and > are *not* abstract). And I don't think there are any abstract > predefined operations. I am aware of this, but the confusing thing is that the inherited subprogram may be abstract (3.9.3(5/2)) or shall-be-overridden (3.9.3(6/2)). And because this section doesn't properly distinguish a type from a view of a type, it is unclear if (5/2) or (6/2) applies. Consider: package P is type I is interface; procedure Op (X : I) is abstract; end P; package Q is type T is abstract new P.I with private; -- Op inherited here. private type T is new P.I with null record; end Q; Surely Q.T inherits an implicit Op. But is it abstract (because the partial view of T is abstract and (5/2) applies) or shall-be-overridden (because the full view is concrete and (6/2) applies)? In this example it doesn't make a difference: the code is illegal either because of (3/2) (if the inherited subprogram is abstract) or because of (6/2) (if it is shall-be-overridden). But I wouldn't be surprised if Steve B. could come up with an example where the difference matters. I think that (5/2) should say "If the type has an abstract or untagged view...". **************************************************************** From: Randy Brukardt Sent: Thursday, October 11, 2007 2:56 PM Pascal writes: > > No, that's wrong. Implicitly-declared inherited subprograms > > have their own rules (that is 3.9.3(4-6)); they become > > "shall-be-overridden" and are not necessarily illegal (and > > are *not* abstract). And I don't think there are any abstract > > predefined operations. > > I am aware of this, but the confusing thing is that the inherited subprogram > may be abstract (3.9.3(5/2)) or shall-be-overridden (3.9.3(6/2)). And > because this section doesn't properly distinguish a type from a view of a > type, it is unclear if (5/2) or (6/2) applies. OK, I guess, but I was talking only about 3.9.3(3/2). I haven't even reread 3.9.3(4-6/2) in months (although I will soon, as it is literally the next paragraphs to do test objectives for). > Consider: > > package P is > type I is interface; > procedure Op (X : I) is abstract; > end P; > > package Q is > type T is abstract new P.I with private; > -- Op inherited here. > private > type T is new P.I with null record; > end Q; > > Surely Q.T inherits an implicit Op. But is it abstract (because the partial > view of T is abstract and (5/2) applies) or shall-be-overridden (because the > full view is concrete and (6/2) applies)? This cannot be related to a view, or you would have subprograms with abstract and concrete views. And so far as I know, there is no such thing -- a subprogram is always one or the other. (You might have an inherited abstract routine overridden by a concrete routine, but those are different routines that might have different visibility.) The *type* is concrete (because the full definition defines the type) and thus 3.9.3(6/2) applies. > In this example it doesn't make a difference: the code is illegal either > because of (3/2) (if the inherited subprogram is abstract) or because of > (6/2) (if it is shall-be-overridden). But I wouldn't be surprised if Steve > B. could come up with an example where the difference matters. 3.9.3(3/2) never applies to inherited subprograms, but it doesn't matter in this case because the type is concrete and 3.9.3(6/2) clearly applies and makes this illegal. > I think that (5/2) should say "If the type has an abstract or untagged > view...". Absolutely not. This example makes it clear to me that the reason this text says "type" is because it means "type" and not view. There might be an abstract partial view, but that is irrelevant: only the full type matters for these rules. Otherwise, you get bizarre anomalies like the one you give here. So the point is that the wording is *not* lousy; it's the readers who jump to the conclusion that the partial view has any bearing on these legality rules which are lousy. I do agree that a bit of clarification (most likely in the AARM) is needed. The problem is, that since we surely do *not* want the view to have anything to do with the rules 3.9.3(4-6/2), I cannot imagine wording or other changes. ------------------ Having written all of that, I realize that you are obviously slipping if you can't see how to make your example interesting. Just two weeks away from Ada... :-) First, we make the type legal: package P is type I is interface; procedure Op (X : I) is abstract; end P; with P; package Q is type T is abstract new P.I with private; -- Op inherited here. private type T is new P.I with null record; procedure Op (X : T) is null; end Q; Surely this is legal. Note that means that 3.9.3(3/2) had better not apply to the inherited subprogram if we decide it is abstract. Now we derive from it: with Q; package R is type T2 is new Q.T with null record; -- Op inherited here, but how? end R; If Op is abstract, then it is a "requires overriding" routine here, and R is illegal. If Op was "requires overriding" before, then it is just inherited, and R is legal. Despite the fact that the result makes my physically ill, it seems that we'd be breaking privacy by taking the second interpretation. That's because that interpretation would depend on know the full definition of type T; if it was declared abstract, then this would be illegal. Mr. Private (retired :-) would object. So we have to have subprogram views, and that means Janus/Ada is fundamentally hosed (subprograms and all of their properties sit in a global table; the "slot number" is the subprogram identifier in the code generator, so splitting them is not an option as it is for types. Maybe there would be some way to fake it for derivation purposes only (as there the visibility is known) - I don't think it can matter any other way. The net effect is that your question is wrong: it is not whether 3.9.3(5/2) or 3.9.3(6/2) applies, but rather where do they apply? I still think this is fundamentally new (subprogram views), so we'll need a full AI and rewrite of the wording. (I worry that there are cascading effects from this sort of change. You're welcome to reply "FUD".) Note that even with this interpretation, 3.9.3(3/2) still only applies to explicitly declared subprograms. Otherwise, you couldn't override abstract operations, and that surely is not what we want. In your original example, 3.9.3(5/2) applies to the visible view of subprogram Op (so it is legal), and 3.9.3(6/2) applies to the private view of subprogram Op (so it is illegal). No reference to 3.9.3(3/2) is needed. Anyway, no appeal to Steve Baird is needed to work this out. ;-) [I want to leave him alone to do his homework if at all possible.] Randy. P.S. I'll wait on writing an AI for this until I've done the test objectives for these paragraphs, because I may very find more problems when I do so. **************************************************************** From: Pascal Leroy Sent: Thursday, October 11, 2007 3:17 PM > Having written all of that, I realize that you are obviously > slipping if you can't see how to make your example > interesting. Just two weeks away from Ada... :-) Of course my brain has decayed in two weeks, but the fact that I don't have access to an Ada compiler anymore does not help. I was trying to play games with passing a subprogram to a generic but didn't go anywhere. In fact, I now see that what I was trying to do is fairly similar to what you ended up doing. > with Q; > package R is > type T2 is new Q.T with null record; > -- Op inherited here, but how? > end R; > > If Op is abstract, then it is a "requires overriding" routine > here, and R is illegal. If Op was "requires overriding" > before, then it is just inherited, and R is legal. Obviously the inherited Q.Op is abstract as seen from R, because that's the only way that you can preserve privacy. So R is illegal. > The net effect is that your question is wrong: it is not > whether 3.9.3(5/2) or 3.9.3(6/2) applies, but rather where do > they apply? It seems to me that the Op inherited by Q.T starts its life as an abstract subprogram and then later becomes requires overriding when the full type is known. Note that this has a funny consequence on squirreling renames: such a renaming is illegal for a requires overridding, but legal for an abstract subprogram. So if you have such a renaming in the visible part of Q, is it legal or not? Hmmm. > I still think this is fundamentally new (subprogram views), > so we'll need a full AI and rewrite of the wording. (I worry > that there are cascading effects from this sort of change. > You're welcome to reply "FUD".) I agree that an AI is needed (not that my opinion matters anymore). And I also agree that this seems scary. **************************************************************** From: Randy Brukardt Sent: Thursday, October 11, 2007 3:48 PM ... > > with Q; > > package R is > > type T2 is new Q.T with null record; > > -- Op inherited here, but how? > > end R; > > > > If Op is abstract, then it is a "requires overriding" routine > > here, and R is illegal. If Op was "requires overriding" > > before, then it is just inherited, and R is legal. > > Obviously the inherited Q.Op is abstract as seen from R, because that's the > only way that you can preserve privacy. So R is illegal. > > > The net effect is that your question is wrong: it is not > > whether 3.9.3(5/2) or 3.9.3(6/2) applies, but rather where do > > they apply? > > It seems to me that the Op inherited by Q.T starts its life as an abstract > subprogram and then later becomes requires overriding when the > full type is known. No, because that breaks privacy: we already "know" that it is "requires overriding", but we can't use that information when it is derived. It really is a first-class view. > Note that this has a funny consequence on squirreling renames: such a > renaming is illegal for a shall-be-overridden subprogram, but legal for an > abstract subprogram. So if you have such a renaming in the visible part of > Q, is it legal or not? Hmmm. Yuck. I would say that it is legal (because the renamed view is abstract), but this is not looking good. > > I still think this is fundamentally new (subprogram views), > > so we'll need a full AI and rewrite of the wording. (I worry > > that there are cascading effects from this sort of change. > > You're welcome to reply "FUD".) > > I agree that an AI is needed (not that my opinion matters anymore). And I > also agree that this seems scary. Well, I still value your opinion; I doubt you've lost all of your Ada standard sense in two weeks. Besides, you're still an ARG member (like Eachus and Cohen these days). ****************************************************************