Open sttaft opened 7 months ago
Thanks Tuck for the clear explanation of the issue. For consistency with the renaming of a non-dispatching primitive which "squirrels away" its designated operation, I think it makes more sense to squirrel away operations of the ancestor specified in the private extension.
Yannick Moy wrote:
Thanks Tuck for the clear explanation of the issue. For consistency with the renaming of a non-dispatching primitive which "squirrels away" its designated operation, I think it makes more sense to squirrel away operations of the ancestor specified in the private extension.
I was going to write something like this, but Yannick beat me to it. Suffice it to say that I agree with him. I don't see any compelling reason to break privacy here, and the programmer can squirrel the operation of the record extension by putting it into the private part after the record extension. Seems more flexible (if marginally useful either way).
I would have expected typical squirreling to occur in the private part anyway; there's no reason to make it available to clients of the package (at least for the usual use of being able to call it in the overridden operation). So in some sense this is a very unlikely case.
Randy.
I am glad to see some agreement on how to handle this. What you propose makes some sense, but it also means that we are holding onto operations that are pretty strange.
The "predefined" equality in particular seems weird, and I am not sure exactly what it would do. If each of the hidden ancestors of GrandChild adds some components, do these components participate in the equality test, even if the corresponding ancestor has overridden its own "=" operator?
I think in cases where it is unclear what are the semantics, my own instinct would be to make it illegal. It would be interesting to see how many uses of "early renaming" of private extension operations exist in the "wild", and what exactly the users of the feature expect to happen. If we base it on what GNAT does, it is a bit hard to know!
Yeah, making it illegal looks good too. With a nice explanation message in GNAT about the odd case at hand!
At the moment GNAT is all over the map on answering this question. It might be interesting to test other Ada 95+ compilers, since this feature has been present since Ada 95.
I tried a modified version of your test program (above) on Janus/Ada, GNAT, and ObjectAda. (I modified it to remove the expression functions that I never quite finished implementing, replacing them with explicit bodies.)
Janus/Ada displayed "2", then raised the exception found in the body of the Grandchild "=". GNAT displayed "0", then raised the exception found in the body of the Grandchild "=". ObjectAda got an "Fatal internal memory access error" when the test was compiled. Needless to say, I didn't get to run it. I didn't try to figure why that happened, either.
Not sure that proves anything.
I'm sort of hoping that someone (not me) is writing up an AI to clarify this area.
Both Janus/Ada and GNAT gave the same result, raising the exception in the Grandchild "=".
In the following program, it is not completely clear what the renamings of the inherited/predefined operations of GrandChild are supposed to do. In particular, Eq is defined as a rename of "=", and Save_Label is defined as a rename of "Label", at a point where we don't know what type is the immediate parent of GrandChild, since GrandChild is a private extension of Root, and only in the private part we learn it is an immediate descendant of Child.
In general, renaming of dispatching operations has some special rules, given in RM 8.5.4(8/3):
We are in the "otherwise" case of the above rule, but what is meant by "the inherited or predefined subprogram" is a bit ambiguous, since we haven't seen the full definition of GrandChild yet, so we don't know what are the properties of this inherited or predefined subprogram. Ultimately, GrandChild is defined as an immediate descendant of Child, and so presumably it is the version of Label inherited from Child, and the predefined "=" operator composed from the primitive "=" of Child and the "=" operators of any new components of GrandChild (there are none).
In this case, the current version of GNAT uses the "Root" version of "Label" and the GrandChild version of "=", so it seems to be wrong in both cases. On the other hand, it is a bit weird for a renaming that is supposed to be "squirreling away" something, to do it before that something even exists...
So the question is do we want to confirm that a renaming of an operation of a private extension before any overriding does in fact map at runtime to the operations inherited and/or constructed from those of its eventual immediate ancestor? Or do we want to make such a renaming illegal? Or do we want it to squirrel away operations of the ancestor specified in the private extension ("Root" in this example), or do we want it to simply map to the "final" operations of the full definition of the private extension ("GrandChild's operations)? Should the answer depend on whether or not the overridings happen in the visible part?
At the moment GNAT is all over the map on answering this question. It might be interesting to test other Ada 95+ compilers, since this feature has been present since Ada 95.