Version 1.13 of ais/ai-00228.txt
!standard 3.09.3 (6) 05-09-20 AI95-00228/07
!standard 8.05.4 (5/1)
!class binding interpretation 03-01-07
!status Amendment 200Y 03-02-20
!status WG9 approved 03-06-20
!status ARG Approved 8-0-0 03-02-09
!status work item 02-01-23
!status received 00-04-10
!priority Low
!difficulty Medium
!qualifier Clarification
!subject Premature use of "shall be overridden" subprograms
!summary
A subprogram which "shall be overridden" in the sense of 3.9.3(6) is not
subject to the restrictions that apply to an abstract subprogram. It is
illegal to rename a "shall be overridden" subprogram.
It is illegal to rename-as-body an abstract subprogram.
!question
AI95-00211 states that "the 'shall be overridden' property of 3.9.3(6) applies
to a renamed view. Thus, any renaming of an inherited subprogram that must be
overridden is illegal."
It seems that this formulation is incomplete, and that AI95-00211 should have
covered some other cases. Consider the following example, derived from the
one in the AI:
package Types is
type T is tagged null record;
function F return T;
end Types;
with Types;
package Extensions is
type E is new Types.T with null record;
type A is access function return E;
procedure P (X : A := F'access); --
function F return E;
end Extensions;
Between the place where E is declared and the place where F is overridden,
there are a number of constructs that could be used to reference F. Are such
constructs legal? (Yes.)
Also, it appears that there is no rule preventing renaming-as-body an
abstract subprogram. But of course an abstract subprogram has no body, so
that seems silly. Should there be a rule? (Yes.)
!recommendation
(See summary.)
!wording
(See corrigendum.)
!discussion
The answer to the second question is easy: this is an obvious hole, and we
add the needed rule.
The essence of the first question revolves around the difference between
"abstract" and "shall be overridden". AI95-00211 seems to have been written
with the assumption that these terms were more or less synonymous, but in fact
they are not. By definition, an abstract subprogram has no implementation, so
we must prevent any construct that could lead to nondispatching calls to such a
subprogram. On the other hand, the rules of 3.9.3(6) ensure that an overrider
will be provided for each "shall be overridden" subprogram, and that this
overrider will not be abstract (assuming the type itself is not abstract). So
nondispatching calls to such subprograms are fine, because we can guarantee
that there exists a proper implementation.
To illustrate the difference, consider the following example, which is somewhat
similar to the one in the question, but shows several levels of derivation:
package P1 is
type T1 is tagged ...;
function F return T1;
end P1;
with P1;
package P2 is
type T2 is new P1.T1 with ...;
--
--
private
function F return T2;
end P2;
with P2;
package P3 is
type T3 is new P2.T2 with ...;
--
--
function F return T3;
end P3;
with P1;
package P4 is
type T4 is new abstract P1.T1 with ...;
--
--
end P4;
In package P2, T2 inherits a function F which shall be overridden. In this
instance the overriding subprogram appears in the private part, but at the
point of the type declaration we know that an overriding subprogram is coming.
So in the visible part of P2 it is legal to evaluate the attribute Access for
F in a default expression (note that most other usages of F are illegal because
they would freeze T2 before its full definition). This wouldn't be true for
abstract subprograms.
In package P3, when we derive from P2.T2, P2.F being a function with a
controlling result, it has to be overridden. Again, F'Access is legal in a
default expression in the specification of P3, even before the declaration
of the overriding function.
In package P4, T4 is declared to be an abstract type, so the inherited F is
abstract. It may or may not be overridden, but it is illegal, say, to
evaluate F'Access before a non-abstract override has been given.
A similar example can be constructed with an abstract root type and an
inherited procedure:
package P1 is
type T1 is abstract tagged ...;
procedure P (A : T1) is abstract;
end P1;
with P1;
package P2 is
type T2 is new P1.T1 with ...;
--
--
private
procedure P (A : T2);
end P2;
with P2;
package P3 is
type T3 is new P2.T2 with ...;
--
--
end P3;
with P1;
package P4 is
type T4 is new abstract P1.T1 with ...;
--
--
end P4;
The inheritance mechanisms are similar in this second example, except that
the procedure P inherited by P3.T3 does not have the "shall be overridden"
property. That's because the parent type, P2.T2, is guaranteed to have a
non-abstract primitive procedure P (even though the overriding procedure for
P is declared in the private part).
Based on this analysis, let's look again at AI95-00211. This AI deals with
renamings of "shall be overridden" subprograms, as shown in the following
example:
package Types is
type T is abstract tagged ...;
procedure P (F : T) is abstract;
end Types;
with Types;
package Extensions is
type E is new Types.T with ...;
--
procedure Pr (F : E) renames P;
procedure P (F : E);
end Extensions;
Pr denotes a new view of P, and we know that P will ultimately be overridden.
The overriding declaration of P will actually provide a body for Pr, too, so
there is no need for Pr to inherit the "shall be overridden" property. However,
an odd consequence of this analysis is that, although Pr occurs before the
declaration of the override, it is not technically a squirreling renaming in
the sense of AARM 8.5.4(8.g). This seems unnecessarily confusing.
In order to avoid this oddity, it seems appropriate to make the renaming
illegal (as stated in AI95-00211) but the logic for reaching that conclusion
in AI95-00211 was incorrect, because of the confusion between "abstract" and
"shall be overridden".
!corrigendum 8.5.4(5/1)
Insert after the paragraph:
The profile of a renaming-as-body shall be subtype-conformant with that of the
renamed callable entity, and shall conform fully to that of the declaration it
completes. If the renaming-as-body completes that declaration before the
subprogram it declares is frozen, the profile shall be mode-conformant with
that of the renamed callable entity and the subprogram it declares takes its
convention from the renamed subprogram; otherwise, the profile shall be
subtype-conformant with that of the renamed callable entity and the convention
of the renamed subprogram shall not be Intrinsic. A renaming-as-body is illegal
if the declaration occurs before the subprogram whose declaration it completes
is frozen, and the renaming renames the subprogram itself, through one or more
subprogram renaming declarations, none of whose subprograms has been frozen.
the new paragraph:
The callable_entity_name of a renaming shall not denote a
subprogram that requires overriding (see 3.9.3).
The callable_entity_name of a renaming-as-body shall not
denote an abstract subprogram.
!ACATS test
A B-Test should be created to test these rules, possibly added to an existing
test.
!appendix
From: Pascal Leroy
Sent: Friday, April 07, 2000 3:16 AM
I am trying to implement AI95-00211 (now that we have ACATS tests for AIs ;-)
and it seems to me that there is one case missing from this AI.
The interesting part of AI95-00211 is this:
"Can a subprogram which must be overridden in the sense of 3.9.3(6) be renamed?
(No.)"
The referenced paragraph, 3.9.3(6), states that:
"For a derived type, if the parent or ancestor type has an abstract primitive
subprogram, or a primitive function with a controlling result, then ... the
subprogram shall be overridden with a nonabstract subprogram..."
It seems to me that AI95-00211 should also forbid the use of such a subprogram
as the prefix of attributes 'Access and 'Unchecked_Access. Here is an example,
derived from the one in the AI, which shows that you can use 'Access to squirrel
away one of these "must be overridden subprogram":
package Types is
type T is tagged null record;
function F return T;
end Types;
with Types;
package Extensions is
type E is new Types.T with null record;
type A is access function return E;
X : A := F'Access; -- Legal? Hopefully not!
function F return E;
end Extensions;
Note that an abstract subprogram cannot be used as the prefix of 'Access
(3.9.3(11)) but this doesn't help because these "must be overridden" subprograms
are not abstract.
*************************************************************
From: Tucker Taft
Sent: Friday, April 07, 2000 7:44 AM
Pascal Leroy wrote:
> ...
> It seems to me that AI95-00211 should also forbid the use of such a subprogram
> as the prefix of attributes 'Access and 'Unchecked_Access.
And 'Address, and presumably any attribute.
Probably the simplest thing would be to say that the implicitly declared
(inherited) function is abstract. Why didn't we say that? Then presumably you
*could* rename them, and you would just get an abstract function.
*************************************************************
From: Pascal Leroy
Sent: Friday, April 07, 2000 7:56 AM
> Probably the simplest thing would be to say that the implicitly declared (inherited)
> function is abstract. Why didn't we say that? Then presumably you *could* rename
> them, and you would just get an abstract function.
That crossed my mind, but I don't think it works. Consider:
package P1 is
type T1 is abstract tagged null record;
procedure P (X : T1) is abstract;
end P1;
with P1;
package P2 is
type T2 is new P1.T1 with null record;
private
procedure P (X : T2);
end P2;
with P2;
package P3 is
type T3 is new P2.T2 with null record;
end P3;
If you say that P2.T2 inherits an abstract P which is later overridden by the
declaration in the private part, then presumably P3.T3 also inherits an abstract
P, which then would have to be overridden. This is substantially different from
what 3.9.3(6) is currently saying. These "must be overridden" subprograms are
very weird.
*************************************************************
From: Tucker Taft
Sent: Friday, April 07, 2000 2:21 PM
There are no functions in your example. What exactly are you
trying to illustrate? Can you try to be more explicit?
In general, we know that a non-abstract type has no abstract
primitives, even if the non-abstract ones are declared in the private part.
The same would hold true for functions with a controlling result,
though it wouldn't be as interesting, since they always become
abstract again, even if they are overridden with a non-abstract
function. Making the inherited function abstract seems to
simplify the description to me, so I guess you need to explain
the problem again.
*************************************************************
From: Pascal Leroy
Sent: Monday, April 10, 2000 5:10 AM
> In general, we know that a non-abstract type has no abstract
> primitives, even if the non-abstract ones are declared in the private part.
Good point. I withdraw my objection.
*************************************************************
To: Randy Brukardt
Sent: Monday, April 10, 2000 6:12 PM
> Probably the simplest thing would be to say that the implicitly declared
> (inherited) function is abstract. Why didn't we say that? Then presumably
> you *could* rename them, and you would just get an abstract function.
I agree here with Pascal(1) (and not Pascal(2)!) -- I don't think this works.
This rule appeared very late in the game; it appears to have been added to
RM9X;5.0 (it isn't in 4.0). Prior to that, these things *were* abstract. I would
be very cautious about trying to resurrect a rule that didn't work then.
The point of this rule is to insure that a non-abstract tagged type has no
abstract operations. (Tucker claims this as a statement of fact, but it is in
fact this rule (3.9.3(6)) that guarentees that property; any change to this rule
may very well void that property.)
You could, of course, make 3.9.3(5) true for all types, then add a "must be
overridden" rule for any abstract operation of a non-abstract, tagged type.
Since that is the obvious way to get from RM9X;4.0 to the current rules, I have
to think that it doesn't work for some reason.
It is important to note that you *can* rename a "must be overridden" subprogram,
the issue is that the resulting subprogram still needs to be overridden (so it
is illegal for that reason). (And trying to override it causes illegal
homographs, so it cannot be made legal.) Changing the subprograms to be
"abstract" doesn't change this at all.
---
The interesting question is whether there is a real problem here at all. A
renaming is illegal because the needed overridding can't be given. There is no
such problem with attributes (nor with generic actual subprograms, the other
case covered in 3.9.3(11), which also should be covered by any rule coming out
of this discussion). We know that there must be a concrete subprogram at some
point (because of the "must be overridden" rule). Using it as the prefix to an
attribute might annoy a [mostly] one-pass compiler, but I don't see how that is
different than using a subprogram whose body hasn't been seen yet.
That is, to extend Pascal's original example:
package Types is
type T is tagged null record;
function F return T;
end Types;
with Types;
package Extensions is
type E is new Types.T with null record;
type A is access function return E;
function F2 return E;
Y : A := F2'Access; -- Legal? Of course.
X : A := F'Access; -- Legal? Why not?
function F return E; -- (1).
end Extensions;
The use of F2 in 'Access is legal. So why shouldn't the use of F in 'Access be
legal? We know that the overridding at (1) exists; otherwise the unit Extensions
is illegal anyway. And assuming the the overridding exists, how is this
different than F2? I can't find any reason.
Have I missed something?
*************************************************************
From: Pascal Leroy
Sent: Tuesday, April 11, 2000 3:46 AM
Well, there are two problems here.
The first problem is that surely the implicit F (the one that must be
overridden) and the explicit F (the one that overrides) are different
subprograms (otherwise we wouldn't be talking about overriding, right?). Now we
have this rule that "two access-to-subprogram values are unequal if they
designate different subprograms" (4.5.2(13)). Your proposal violates this rule,
because you are saying that the 'Access attribute will return the same value for
both Fs. That's distinctly ugly.
The second problem is more serious, and that's what got me started. Once you
have built an access-to-subprogram value that designates one of these
must-be-overridden subprograms, you can use it in a renaming, as in:
X : A := F'Access; -- Legal? Randy says yes.
function G return E renames X.all; -- Legal? Randy is busy trying to solve the halting problem.
function F return E;
Now the interesting question is: is G one of these must-be-overridden
subprograms? Because the subprogram name is dynamic, you have no way to tell.
But of course, this directly affects the legality of the program, because you
have to either accept or reject the renaming. (Now you should see the
relationship with AI 211.)
Note that abstract-ness is another property that (1) influences legality and (2)
propagates through renamings, but in the case of abstract subprograms we know
that a dynamic name cannot designate an abstract subprogram (3.9.3(11)) so we
know that the function G above is never abstract. That's why I believe that the
only option is to make 'Access illegal for these must-be-overridden subprograms.
*************************************************************
From: Randy Brukardt
Sent: Tuesday, April 11, 2000 12:08 PM
> Well, there are two problems here.
>
> The first problem is that surely the implicit F (the one that
> must be overridden) and the explicit F (the one that
> overrides) are different subprograms (otherwise we wouldn't
> be talking about overriding, right?). Now we have this rule
> that "two access-to-subprogram values are unequal if they
> designate different subprograms" (4.5.2(13)). Your proposal
> violates this rule, because you are saying that the 'Access
> attribute will return the same value for both Fs. That's
> distinctly ugly.
Well, I'm saying that F'Access designates the concrete subprogram. So of course
it would have the same value. This is the same subprogram that a call to F would
execute. I don't find this weird; tagged types (which these must be) already
have that semantics for calls of subprograms overridden in the private part. So
I'm not suggesting anything new here.
> The second problem is more serious, and that's what got me
> started. Once you have built an access-to-subprogram value
> that designates one of these must-be-overridden subprograms,
> you can use it in a renaming, as in:
>
> X : A := F'Access; -- Legal? Randy says yes.
> function G return E renames X.all; -- Legal? Randy is
> busy trying to solve the halting problem.
> function F return E;
>
> Now the interesting question is: is G one of these
> must-be-overridden subprograms? Because the subprogram name
> is dynamic, you have no way to tell. But of course, this
> directly affects the legality of the program, because you
> have to either accept or reject the renaming. (Now you
> should see the relationship with AI 211.)
No, I don't actually see any relationship with AI 211; I don't agree with any of
this. The properties of G come from the "designated subprogram" of A, which
certainly is not a "must be renamed" entity. So G is always legal, and certainly
does not require any overriding. That is true for any renaming, I don't know why
you think that it ought to be different for this case.
Any such property is "lost" when the 'Access is taken. And clearly a 'Access can
only refer to a "concrete" subprogram - it either refers to the overriding
subprogram or it has to be illegal somehow. Since there is no support in the RM
for the latter, I was suggesting that the former was intended.
> Note that abstract-ness is another property that (1)
> influences legality and (2) propagates through renamings, but
> in the case of abstract subprograms we know that a dynamic
> name cannot designate an abstract subprogram (3.9.3(11)) so
> we know that the function G above is never abstract. That's
> why I believe that the only option is to make 'Access illegal
> for these must-be-overridden subprograms.
Well, I don't agree that F'Access references the "must-be-overridden"
subprogram. Certainly a call to F references the concrete routine (put this
before the declaration of F2 in the example, assume a default initialized object
of it in the body of Extensions):
type Rec is record
Comp : E := F; -- Must reference the concrete routine,
-- not the "must-be-overridden" one.
end record;
So why should 'Access be different?
In all honesty, I really don't care about this issue that much. I was just
trying to figure out why the manual was the way it was. Since RM9X;4.0 has
pretty much the semantics that Tucker is suggesting, I was trying to figure out
why it was changed, and in particular, why it was changed away from simply
requiring overriding of any inherited abstract routines. I have to assume that
there was some important reason for this change, so changing it back is
something we should not do without knowing the reasons for the initial change.
A shakier assumption is that the issues of premature use was considered when the
change was made. It seems hard to believe that an entire new concept was
introduced without any consideration of uses of the subprograms. Thus, I find
that there must have been an intent that these things work -- I'm just trying to
figure out what they mean. And it turns out that that meaning is not
inconsistent with the rest of the language.
I know that is a shaky argument (as arguments from intent usually are).
Hopefully, Bob or Tucker will help out here.
In any case, I am not adverse to adding 3.9.3(11) language to
"must-be-overridden" subprograms. "Before the subprogram is overridden, the
subprogram may not be used as a generic actual subprogram or as the prefix of an
attribute reference for the Access, Unchecked_Access, or Address attributes."
(But, does this cause problems for generic formal types, where the subprogram is
not required to be overridden until instantiation??) But I don't think it is
strictly necessary to have a consistent semantics.
*************************************************************
From: Pascal Leroy
Sent: Tuesday, April 11, 2000 3:30 PM
> No, I don't actually see any relationship with AI 211; I don't agree with
> any of this. The properties of G come from the "designated subprogram" of A,
> which certainly is not a "must be renamed" entity. So G is always legal, and
> certainly does not require any overriding. That is true for any renaming, I
> don't know why you think that it ought to be different for this case.
So you are telling me that the following ought to be legal:
X : A := F'Access;
function G return E renames X.all;
function F return E;
but obviously AI 211 says that the following is illegal:
function G return E renames F;
function F return E;
so much for consistent language design! If your program is rejected because
of AI 211, just add an extra access-to-subprogram variable, it makes it
legal (and so much more readable).
> Well, I don't agree that F'Access references the "must-be-overridden"
> subprogram. Certainly a call to F references the concrete routine (put this
> before the declaration of F2 in the example, assume a default initialized
> object of it in the body of Extensions):
>
> type Rec is record
> Comp : E := F; -- Must reference the concrete routine,
> -- not the "must-be-overridden" one.
> end record;
>
> So why should 'Access be different?
I don't care about dynamic semantics, I am talking name resolution and
legality rules. In the above expression, the name F designates the
subprogram that will be overridden; it happens that at execution you call
the overriding one, but in this language we try to precisely define the
meaning of a name as opposed to what happens at execution.
If you say that the name F in F'Access references the overriding subprogram,
you are introducing a case where a name references an entity which has not
been declared yet. This is something entirely new, and it's going to break
more that one compiler (for no good reason).
*************************************************************
From: Randy Brukardt
Sent: Tuesday, April 11, 2000 6:44 PM
> I don't care about dynamic semantics, I am talking name resolution and
> legality rules.
But it is only the dynamic semantics that matters. There is no name resolution
or legality problem here.
> In the above expression, the name F designates the
> subprogram that will be overridden; it happens that at execution you call
> the overriding one, but in this language we try to precisely define the
> meaning of a name as opposed to what happens at execution.
> If you say that the name F in F'Access references the overriding subprogram,
> you are introducing a case where a name references an entity which has not
> been declared yet. This is something entirely new, and it's going to break
> more that one compiler (for no good reason).
That is not what I meant at all. I said that F'Access returns the overriding
subprogram -- I suppose that is the dynamic semantics. F is the
must-be-overridden subprogram - that is the name resolution. That is precisely
the meaning of a call to F (as I demonstrated earlier) -- why should F'Access
not be usable when a call is?
*************************************************************
From the minutes of the Cupertino ARG meeting (February 2002):
Pascal explains the current version of the AI. We could reintroduce the notion
of these being abstract, but that was the way it was in draft 4 of Ada 95, and
it was changed for draft 5. So he introduced the notion of "insubstantial" to
handle this.
A long discussion about the meaning of abstract and "must be overridden"
ensues. After several false starts, we look at the following example:
package P1 is
type T1 is abstract tagged ...
function F return T1;
procedure P (A : T1) is abstract;
end P1;
with P1;
package P2 is
type T2 is new P1.T1 ...
-- Is P'access allowed here?? [Yes.]
private
function F return T2;
procedure P (A : T2);
end P2;
with P2;
package P3 is
type T3 is new P2.T2 ... -- Legal (P is not abstract)
function F return T3;
end P3;
with P1;
package P4 is
type T2 is new abstract P1.T1 ...
-- P'Access is not allowed.
end P4;
If the parent type has an abstract procedure, the inherited procedure is "must
be overridden", but not abstract, so we can do anything with the inherited
subprogram, and don't have to override it again when deriving from the derived
type. The function case works the same, except that it is "must be overridden"
when deriving from the derived type.
Bob Duff explains that "we" (the MRT) defined this so that if you "know a real
definition is coming", you can take advantage of it (that is, the abstract
restrictions do not apply to it). So it is intentional that you can use these
as if they are normally defined, because they must be before the end of a
scope. This is the reason that "must be overridden" items are not defined to be
abstract.
Someone asks if P'Access freezes P? No, it could be in a default expression.
Pascal expresses concern about name resolution, but is convinced that a call
from outside would need to use the real body, so this should work. Pascal will
make sure that AI-211 does not conflict.
Tucker says that this AI is a tempest in teapot. He suggests that it should be
a confirmation. Pascal says that we finally understand what these paragraphs
mean.
Pascal will rewrite the AI.
Approve intent of the AI: 7-0-0
[Editor's note: this is complete reversal from earlier discussions.]
*************************************************************
From the minutes of the Bedford ARG meeting (October 2002):
[Various typos omitted - ED]
Tucker: How does freezing apply here? In the example which has P'Access, P is
frozen, so it can't be overridden. It would have to be a default expression.
Pascal wants to know where this rule is. After much argument, 13.14(4/1) is
pointed out.
There is some discussion about what compilers currently do with these examples.
Through the wonders of modern technology (the laptop), several people try this
example on their favorite compiler. Gary reports GNAT says: "prefix must be
abstract". Tucker says that AdaMagic complains about freezing; as a default
expression, it works; the squirreling rename says "fatal internal memory access
error"!
The example in the question is wrong, it should not have "abstract" in package
Types. It would be useful to distinguish between the function example (non
abstract type) and the procedure example (abstract type).
The examples are screwed up, so the AI is sent back to Pascal to fix them.
We discussed squirreling renames, and some members of the group are
uncomfortable with the fact that this is not a squirreling rename, but it looks
identical to it. That is different from the normal case. Randy says that he
thinks the conclusion of AI-211 is correct (that the rename is illegal), but
the logic to get to that conclusion is wrong. We probably need a binding
interpretation to fix this.
The group concludes that the best solution is to make these illegal. That
should be done by inserting a new rule after 8.5.4(5): "if the
callable_entity_name of a renaming is a must-be-overridden subprogram, then the
renaming is illegal".
*************************************************************
!topic Derived types, private parts, abstract subprograms
!reference RM95 3.9.3(6-7), 7.3.1(6), 8.3(12)
!from Adam Beneschan 04-03-09
!discussion
This is from comp.lang.ada. Robert Eachus recommended copying it to
Ada-Comment.
Following is my original message:
===============================================================================
We believe we have found an error in some Ada source code that is
publicly available on the Internet, that isn't supposed to compile but
that some compilers apparently let slip through. However, I wanted to
ask to make sure my interpretation of the RM is correct.
package pak1 is
type T1 is abstract tagged null record;
procedure Operation (X : T1) is abstract;
end pak1;
with pak1;
package pak2 is
type T2 is new pak1.T1 with null record;
private
procedure Operation (X : T2);
end pak2;
package pak3 is
procedure Foo;
end pak3;
with pak2;
package body pak3 is
type T3 is new pak2.T2 with null record;
procedure Foo is
X : T3;
begin
Operation (X); --legal?
end Foo;
end pak3;
As I interpret the RM: When T2 is declared, Operaton is inherited, but
the inherited subprogram is still abstract. 3.9.3(6) says that
Operation must be overridden with a nonabstract procedure, but that
the overriding may be done in the private part, which is what's done
here.
When T3 is declared, T2's primitive subprograms are inherited. As I
understand it, this includes primitive subprograms of T2 that are
overridden, since those would reemerge in places where the overriding
subprogram isn't visible. Is this correct in general for derived
types? Thus, it would seem that T3 would inherit both the abstract
and the nonabstract versions of Operation from T2; but by 7.3.1(6),
the nonabstract version is not visible in places where pak2's private
part is not visible. In this instance, the nonabstract inherited
version of Operation that operates on T3 is, in the language of
7.3.1(6), "not declared at all" and thus "cannot be named in a call"
(but still could be called indirectly with a dispatching call). This
would seem to mean that the call with the "--legal?" comment cannot
name the nonabstract inherited Operation; however, the *abstract*
inherited Operation is still visible (reemerges?), and thus Operation
in this statement names this subprogram, but this makes the call
illegal because it's a nondispatching call on an abstract subprogram
(3.9.3(7)).
Is this all correct?
Is this the intent of the language? It seems like it should be,
because declaring pak2.Operation in the private part of pak2 should
mean that you can't call it directly when the private part of pak2
isn't visible, even via a derived type. But I want to make sure there
isn't any subtlety I've missed before I make a bug report.
Thanks in advance for any help,
-- Adam
===============================================================================
Additional comments: It seems to me that there are two possible ways
to interpret the RM that would make the call to Operation illegal.
One is to say that T3 inherits both the abstract and nonabstract
overriding Operations, and that at the point with the "--legal?"
comment only the abstract one is visible and thus cannot be called
with a non-dispatching call. Another is to say that only the
nonabstract Operation exists for T3, but it is not visible at the
point with the comment, so the statement is illegal because Operation
is undefined. It doesn't matter in the above example, but it would
matter if you declare an object of type T3'Class and call Operation on
it. But in either case, it would be a little surprising to me if the
call in my original example were legal, because it would mean that you
can directly call a subprogram that's supposed to be invisible.
Another comment: If indeed T3 inherits both the abstract and
nonabstract Operations, this would violate 3.9.3(6) unless it were the
case that the inherited nonabstract Operation overrides the inherited
abstract Operation. I don't have a problem with this, since the whole
point of 3.9.3(6) is to make sure that a subprogram exists that can be
dispatched to, and 7.3.1(6) makes it clear that even inherited
subprograms that are not declared anywhere can be dispatched to.
8.3(12), which makes it clear that one implicit declaration can
override an implicit declaration, still seems to require that the
subprogram actually be declared, while 7.3.1(6) indicates that in some
cases (including the above example) an inherited subprogram is not
declared at all. So can a nonabstract subprogram that is not declared
at all override an abstract subprogram and thus satisfy 3.9.3(6)? I
think it's clear that it should, but a minor wording change might be
required.
Robert Eachus' response:
===============================================================================
I would suggest you send a bug report to the authors of the code, plus
report this to Ada Comment.
Incidently, I think the code as written is correct. Type T2 is not
abstract, so types derived from it are not abstract types. But this is
one of those "interesting corner cases" which should at least be
documented...
===============================================================================
My comment: I agree that types derived from T2 are not abstract
(actually, abstractness is not an inherited property anyway), but the
question is whether the way things are declared makes "Operation" on
T3 invisible and thus unusable by pak3.
Christoph Grein's response:
===============================================================================
I amended your example a bit.
> package pak1 is
> type T1 is abstract tagged null record;
> procedure Operation (X : T1) is abstract;
> end pak1;
>
> with pak1;
> package pak2 is
> type T2 is new pak1.T1 with null record;
> private
> procedure Operation (X : T2);
> end pak2;
with pak1;
package pak2P is
type T2 is private;
private
type T2 is new pak1.T1 with null record;
procedure Operation (X : T2);
end pak2P;
> package pak3 is
> procedure Foo;
> end pak3;
>
> with pak2, pak2P;
> package body pak3 is
> type T3 is new pak2.T2 with null record;
>
> procedure Foo is
> X : T3; Y : pak2P.T;
> begin
> Operation (X); --legal? (yes, because T2 is visibly derived
-- from T1
Operation (Y); -- illegal (not visible, because T2 is not visibly
-- derived from T1
> end Foo;
>
> end pak3;
My interpretation is that it is irrelevant whether inherited primitives are
overridden visibly or not if only the type is visibly derived.
It matters of course if the type is invisibly derived.
package P is
type T is tagged ...
procedure Prim (X: T; I: Integer := 5);
end P;
with P;
package P1 is
type T1 is new P.T with ...
private
procedure Prim (Y: T1; I: Integer := -5);
end P1;
In this example, Prim applied to a value of type T1 uses different default
values for I depending on whether the overriding is visible or not. Also named
association for the first parameter has to use different names.
And IMHO it's irrelevant whether the ancestor of Prim is abstract or not.
Thus:
with P1;
procedure M is -- cannot see the private part
Ob: P1.T1;
begin
P1.Prim (X => Ob); -- uses I => 5
end M;
procedure P1.M is -- bodies of children see the private parts of parents
Ob: P1.T1;
begin
P1.Prim (Y => Ob); -- uses I => -5
end P1.M;
Gnat 3.16a compiles this OK and uses the correct default, alas Apex 3.2.0b
reports an error on parameter name Y and uses the wrong default.
===============================================================================
*************************************************************
From: Pascal Leroy
Sent: Monday, March 15, 2004 7:24 AM
> As I interpret the RM: When T2 is declared, Operaton is
> inherited, but the inherited subprogram is still abstract.
Nope, it "shall be overridden", which is a completely different thing.
See the !discussion of AI 228 for (excruciating) details. From the AI
you can deduce that the call is legal, because none of the restrictions
applicable to an abstract subprogram pertain to a "shall be overridden"
one. (Don't read the !appendix, because the ARG went back and forth on
this one.)
*************************************************************
From: Adam Beneschan
Sent: Monday, March 15, 2004 4:38 PM
> Nope, it "shall be overridden", which is a completely different thing.
> See the !discussion of AI 228 for (excruciating) details. From the AI
> you can deduce that the call is legal, because none of the restrictions
> applicable to an abstract subprogram pertain to a "shall be overridden"
> one. (Don't read the !appendix, because the ARG went back and forth on
> this one.)
OK, thanks for pointing me to this. This explains what I need to
know.
It turns out the biggest mistake I made was this part of my original
message:
>> When T3 is declared, T2's primitive subprograms are inherited. As I
>> understand it, this includes primitive subprograms of T2 that are
>> overridden, since those would reemerge in places where the overriding
>> subprogram isn't visible. Is this correct in general for derived
>> types?
I had forgotten that the situation is different for tagged and
untagged types. For untagged types, if an inherited subprogram is
overridden in a private part, and you call the inherited subprogram
from a place where the private part isn't visible, you get the
parent's subprogram because the overriding one isn't visible. For
tagged types, though, it still calls the body of the overriding one,
even though it isn't visible, because of 3.9.3(20). What I had missed
is that 3.9.3(20) applies to a "call on a dispatching operation", and
I mistakenly read it as applying to a "dispatching call", which is not
the same thing. :(
*************************************************************
From: Adam Beneschan
Sent: Monday, March 15, 2004 7:38 PM
I wrote:
> I had forgotten that the situation is different for tagged and
> untagged types. For untagged types, if an inherited subprogram is
> overridden in a private part, and you call the inherited subprogram
> from a place where the private part isn't visible, you get the
> parent's subprogram because the overriding one isn't visible. For
> tagged types, though, it still calls the body of the overriding one,
> even though it isn't visible, because of 3.9.3(20). What I had missed
^^^^^^^^^
> is that 3.9.3(20) applies to a "call on a dispatching operation", and
^^^^^^^^^
> I mistakenly read it as applying to a "dispatching call", which is not
> the same thing. :(
I not only can't read, I can't type. That should be 3.9.2(20).
*************************************************************
Questions? Ask the ACAA Technical Agent