Version 1.11 of ai05s/ai05-0125-1.txt

Unformatted version of ai05s/ai05-0125-1.txt version 1.11
Other versions for file ai05s/ai05-0125-1.txt

!standard 7.3.1(6)          11-02-09 AI05-0125-1/03
!standard 8.3(12.3/2)
!standard 8.3.1(5/2)
!class Amendment 08-10-20
!status work item 08-10-20
!status received 08-10-03
!priority Low
!difficulty Easy
!subject Nonoverridable operations of an ancestor
!summary
(See proposal.)
!problem
Consider:
package A1 is type Base is abstract tagged private; procedure P1 (X : Base); private type Base is tagged null record; procedure P2 (X : Base); end A1;
package A1.A2 is type Child is new Base with private; procedure P1 (X : Child); private type Child is new Base with null record; procedure P2 (X : Child); end A1.A2;
with A1.A2; package A1.A3 is type Grandchild is new A1.A2.Child with private; procedure P1 (X : Grandchild); private type Grandchild is new A1.A2.Child with null record; procedure P2 (X : Grandchild); -- not overriding!! end A1.A3;
The problem is that at the point where the full declaration of Grandchild occurs, the procedure P2 that operates on A1.A2.Child is not visible, and therefore the language does not treat the last declaration of P2 as overriding.
Note that, unlike other visibility issues, there is no simple workaround. For accessing components or calling subprograms of the grandparent type, a type conversion can be used. But an inability to override a routine is fatal; the only solution is to move the type somewhere where the operations are inherited.
For instance, to make a dispatching call to the original P2 with an object X of type Grandchild, a programmer could write:
A1.P2 (Base'Class (X));
But there is no way to write a body of that dispatching operation specifically for type Grandchild.
Since private dispatching operations are a convenient way to hide private information, this flaw forces O-O type trees to be placed in deep child package nesting.
This problem had quite an effect on the design of the graphic subsystem Claw; the designers would have used a flatter package structure than was ultimately used. Some of the private routines in Claw define raw message handlers, which should not be exposed to the user of Claw and surely should not be overridden by them.
!proposal
Add wording to ensure that an explicit declaration will override an inherited operation that is "known" to exist at the point of the explicit declaration. Require an overriding_indicator of OVERRIDING to ensure the overriding is intentional.
!wording
Modify 8.3(10.d):
As explained in 7.3.1, "Private Operations", some inherited primitive subprograms are never declared. Such subprograms cannot{, in general,} be overridden, although they can be reached by dispatching calls in the case of a tagged type.
Add after 8.3(12.3/2)
When a type extension inherits a subprogram that is never declared (see 7.3.1) and is not a homograph of an inherited subprogram that is declared, an explicit declaration of a dispatching operation with the same defining name and a type-conformant profile will nevertheless override the inherited subprogram (for the purposes of dispatching calls), so long as a corresponding dispatching operation of some ancestor type is visible at the point of the explicit declaration. Such an overriding requires an overriding_indicator, which shall be OVERRIDING (see 8.3.1).
Similarly, if a protected or task type inherits a subprogram that is never declared (and is not a homograph of an inherited subprogram that is declared), whose first parameter is of the synchronized type or is an access parameter designating the type, then if the type has a single entry or protected subprogram with the same defining name and a profile that is type-conformant with the prefixed view of the inherited subprogram, this entry or protected subprogram /implements/ the inherited subprogram (for the purposes of dispatching calls), so long as a corresponding dispatching operation of some progenitor is visible at the point of the entry or protected subprogram declaration. The declaration of such an entry or protected subprogram requires an overriding_indicator, which shall be OVERRIDING (see 8.3.1).
AARM NOTE: Such an inherited subprogram is necessarily a null procedure which was declared in the private part of the package where the associated interface was declared, since private dispatching operations may not be abstract, and null procedures are the only operations of an interface that are not abstract (other than the equality operators!).
Also note that a single overriding might override two null procedures that are private operations of two separate interfaces. This is fine, because as usual, if two interfaces have operations that are homographs, then it is presumed that these represent the "same" operation in some sense.
Modify 8.3.1(5/2) as follows:
* if the overriding_indicator is OVERRIDING, then the operation shall override a homograph {or an undeclared inherited subprogram} at the place of the declaration;
[Editor's note: 7.3.1(6/1) also requires changes, but AI05-0029-1 already made the needed changes.]
!discussion
In the case where a parent type of some derived type is "known" to have a dispatching operation through inheritance, but because of the visibility rules the operation is not declared for the derived type, we nevertheless want overriding to occur, for the purposes of dispatching. This only happens if the explicit declaration happens at a place where the corresponding operation of some ancestor type is visible. This is unlike the normal rule, where overriding happens even if the inherited operation is declared after the explicitly declared operation. To ensure that the overriding is intentional, we require that an overriding indicator of OVERRIDING be given in this special circumstance.
We considered allowing the user to specify NOT OVERRIDING at the point of the explicit declaration, but it was felt to be an abuse of the overriding indicator to have its presence or absence affect the semantics of the declaration. So instead we require a "confirming" OVERRIDING indication, and make it illegal to write NOT OVERRIDING in this case.
To minimize the incompatibility, if the explicit declaration happens at a place where the corresponding operation of the ancestor is not visible, no overriding occurs, and if an overriding indicator were given, it would have to indicate NOT OVERRIDING.
Note that if there is a visible inherited subprogram which is a homograph with the one that is never declared, then the visible inherited subprogram is the one being overridden, not the one that is never declared. This ensures compatibility with existing programs that override visible operations which happen to be homographs with private operations.
!ASIS
The overriding relationship is not currently represented in ASIS. If it were, it would have to properly reflect this new kind of overriding.
!example
!ACATS test
ACATS B and C tests are needed for this feature.
!appendix

!topic Proposed fix to problem of nonoverridable operations
!reference RM05 8.3, 8.3.1, 7.3.1(6)
!from Adam Beneschan 08-10-03
!discussion


This is in regard to an example recently posted on comp.lang.ada by Maxim Reznik.
I tend to agree with Dmitry Kazakov that the language is broken in this regard,
and I would like to discuss this and propose a possible language change.

The example looked essentially like this:

    package A1 is
       type Base is abstract tagged private;
       procedure P1 (X : Base);
    private
       type Base is tagged null record;
       procedure P2 (X : Base);
    end A1;

    package A1.A2 is
       type Child is new Base with private;
       procedure P1 (X : Child);
    private
       type Child is new Base with null record;
       procedure P2 (X : Child);
    end A1.A2;

    package A1.A3 is
       type Grandchild is new A1.A2.Child with private;
       procedure P1 (X : Grandchild);
    private
       type Grandchild is new A1.A2.Child with null record;
       procedure P2 (X : Grandchild);  -- not overriding!!
    end A1.A3;

The problem is that at the point where the full declaration of Grandchild occurs,
the procedure P2 that operates on A1.A2.Child is not visible, and therefore the
language does not treat the last declaration of P2 as overriding.

The Language Design Principle in 3.9(1.k/2) makes it clear that for a type extension,
if there is a primitive subprogram of the parent type that is not visible anywhere
where the type extension is declared, then any homograph is considered a separate,
non-overriding subprogram that is given its own slot in the "type descriptor"
(dispatch table, whatever).  Thus:

    package B1 is
       type Root is tagged private;
    private
       type Root is tagged null record;
       procedure P3 (X : Root);
    end B1;

    package B2 is
       type Child is new B1.Root with ... ;
       procedure P3 (X : Child);
    end B2;

At the point where Child is declared, B2 "doesn't know" that Child will have an
inherited operation named P3 (although the operation does exist and can be called
via dispatching), because that information is available only in the private part
of B1, which B2 can't see.  So it makes sense that P3 would be a separate
non-overriding declaration here.  But that doesn't apply in Maxim's example.
At the point in A1.A3 where Grandchild is declared, A1.A3 "knows", from information
that is visible to it, that Grandchild is descended from Child, that Child is
descended from Base, and that a primitive subprogram P2 is declared for Base
and will therefore exist for all types in Base'Class.  But it cannot override it,
because the declaration of the operation P2 on the *parent* is hidden from it.
This is a flaw in the language.

My proposed solution would be to add a paragraph to 8.3, probably between 8.3(23)
and 8.3(23.1/2).  I wouldn't add it to the list of bullet points starting with
8.3(9), because that discusses what happens when a declaration overrides a
homograph, which is another declaration; and this paragraph would discuss when
a declaration overrides an inherited subprogram that is not declared (see 7.3.1(6)),
so it would be inappropriate to add it to that list.  [Normally, it wouldn't
seem to make sense to talk about a declaration overriding something that isn't
declared, since part of the purpose of the overriding rules is to determine
what is meant by an identifier when there are two competing declarations that
it could mean.  But the semantics of calls on dispatching operations depend on
what inherited subprograms get overridden, whether those subprograms are
declared or not.  Furthermore, I would definitely want the "overriding"
keyword to be legal on such overriding subprograms, and "not overriding"
to be illegal.]

Add after 8.3(23):

A declaration that is a primitive operation of a type extension or private
extension can also override an inherited subprogram that is not declared, if
the inherited subprogram has the same defining name and has a type-conformant
profile, the original corresponding operation of the inherited subprogram is
an operation of a visible ancestor of the type extension or private extension
declaration, and the original corresponding operation is visible at the point
of the declaration.

[Note: I have avoided using the term "homograph" since a homograph is a
declaration and I don't want to change the definition to possibly refer to
something that is not declared.]

This depends on defining two terms, "original corresponding operation"
and "visible ancestor".  I would define them as follows:

* * * * * *

The "original corresponding operation" for a subprogram declaration S is
defined as follows: If S is an inherited subprogram S2, or overrides an
inherited subprogram S2, the original corresponding operation of S is the
original corresponding operation of S2.
Otherwise, the original corresponding operation of S is S itself.

[The idea is that this is the declaration that caused the slot in the
dispatch table to be allocated.]

* * * * * *

The "visible ancestors" of a type extension or private extension declaration D
are:

The parent and progenitor types of the declaration are visible ancestors of D;

If T is a visible ancestor of a declaration, and T is a type extension,
private extension, or interface type, then:

-- if T has a partial view, and the full view of T is not visible at
   the point of D, then the parent and progenitor types of the private
   extension declaration of T are visible ancestors of D;

-- otherwise, the parent and progenitor types of the type extension or
   interface type are visible ancestors of D.

* * * * * *

[These definitions may seem clumsy to some, because my personal preference
is for mathematically precise definitions.  I tried to define "original
corresponding operation" in a way that would avoid the possible problem of
a non-overriding homograph, such as P3 in the second example above.]

Basically, what this all means is that a declaration can override an inherited
operation P if the program can tell, looking at just the information visible
to it at that point, that an operation P must exist, even if (due to an
intermediate type declaration) the operation isn't actually declared.

Other changes that would need to be made:

In the last sentence of 7.3.1(6/1), the phrase "and cannot be overridden"
would have to be deleted or changed.

8.3.1(5/2-6/2) would need to be changed:

    * if the overriding_indicator is OVERRIDING, then the operation
      shall override a homograph at the place of the declaration or
      body, or it shall override an undeclared inherited subprogram
      with the same defining name and type-conformant profile,

    * if the overriding_indicator is NOT OVERRIDING, then the
      operation shall not override any homograph or inherited
      subprogram (at any place).

AARM 8.3(10.d) would need to be changed.

There may be other necessary changes.  I don't know.

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

From: Robert A. Duff
Sent: Friday, October 3, 2008  3:06 PM

I agree this is a flaw in the language.  I am quite surprised that it
works this way, but I checked the RM, and I think you're right.

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

From: Randy Brukardt
Sent: Monday, October 13, 2008  10:51 PM

I'm surprised that you feel that way. It was well-known (at least by us!),
as we repeatedly ran into it during the Claw development. We eventually
settled on the meta-rule to never derive from a sibling type, only from
child types (that is, types declared in child packages of their parent
type/package rather than in some other place). This wasn't necessary just
for overriding but also for components of types.

Note that in Claw, all of the types are declared in children of package
Claw (which contains the root types), so this case comes up for *every*
type. We placed various operations in the private part of Claw in order
that only the implementation could use and extend them.

However, I believe I'm against trying to "fix* this issue. The reason is
that the inheritance/overriding rules are already impossible to understand.
To make them subtly different would only increase the confusion. After all,
you can't directly call one of these grandparent operations directly for
the type, and you can't directly reference a grandparent component for the
type (even though you can do that with an appropriate type conversion).
To then say that overriding happens anyway seems dangerous, to say the
least.

Moreover, the "implemented by" rules would also need an analogous change
(they surely ought to work similarly to overriding), which would increase
the complexity several times. Possibly, we'd also need changes to the
generic inheritance rules - the imfamous 12.5.1(21/2). (I also had worried
about the abstract "require overriding" rules, but those aren't a problem
here because private abstract operations [and constructor functions] are
illegal.) Sounds like a mess to me.

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

From: Bob Duff
Sent: Friday, June 18, 2010  6:23 PM

It seems to me that AI05-0125-1 -- "Nonoverridable operations of an ancestor"
-- should be a binding interpretation if it is accepted at all.

To have a subtle difference like this between Ada 95/2005 and Ada 2012 is a
very bad idea.

If there's any support for this AI, we should probably first investigate whether
there are incompatibilities in practice.

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

From: Jean-Pierre Rosen
Sent: Monday, August 23, 2010  6:52 AM

The specification of A1.A3 misses a "with A1.A2;"

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

From: Tucker Taft
Sent: Friday, October 29, 2010  11:40 PM

[Version /02 of the AI was attached - Editor.]

Here is an AI attempting to address another thorny "Adam" question.  Basically,
we say that an explicit declaration of a dispatching operation on a type
extension overrides an inherited subprogram that is never declared, if the
explicit declaration occurs at a point where a corresponding dispatching
operation of some ancestor is visible.

In other words, if you "know" that you inherited a dispatching operation, you
can override it for the purposes of dispatching calls, even if the inherited
subprogram isn't "officially" declared.

We require an overriding_indicator of OVERRIDING in this special case, so there
is no ambiguity, and to prevent a "silent" inconsistency with Ada 95/2005.

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

From: Tucker Taft
Sent: Wednesday, February  9, 2011  11:13 PM

[Version /03 of the AI was attached - Editor.]

Here is a modest update to AI-125.  It now worries about entries and protected
subprograms overriding inherited subprograms which are not declared (which are
necessarily null procedures).  It also worries about the case where there is an
inherited subprogram that *is* declared which is a homograph of the one that is
*not* declared. Finally it makes mention of the possibility that a single
overriding might override two inherited subprograms, neither of which are
declared.  We consider that as OK, since we already allow a single declaration
to override two inherited subprograms from different interfaces.

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

From: Randy Brukardt
Sent: Wednesday, February  9, 2011  11:38 PM

> Here is a modest update to AI-125.

I must be dreaming. I actually thought I received some homework from Tucker. :-)

> It now worries about
> entries and protected subprograms overriding inherited subprograms
> which are not declared (which are necessarily null procedures).  It
> also worries about the case where there is an inherited subprogram
> that *is* declared which is a homograph of the one that is *not*
> declared.
> Finally it makes mention of the possibility that a single overriding
> might override two inherited subprograms, neither of which are
> declared.  We consider that as OK, since we already allow a single
> declaration to override two inherited subprograms from different
> interfaces.

I found the new AARM note midly confusing, since it seems to apply to both of
the new paragraphs, but it is really only talking about the second one. I was
trying to figure how it worked in the "normal" case and it didn't make much
sense.

Perhaps that won't be a problem in the actual AARM.

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

From: Tucker Taft
Sent: Thursday, February 10, 2011  8:02 AM

> I found the new AARM note midly confusing, since it seems to apply to
> both of the new paragraphs, but it is really only talking about the
> second one. I was trying to figure how it worked in the "normal" case
> and it didn't make much sense.

Actually the first paragraph of the AARM note applies only to the second
paragraph, while the second paragraph of the AARM note applies to both!
Probably should somehow make that a little clearer...

> Perhaps that won't be a problem in the actual AARM.

Perhaps a little clever formatting, or adding some wording to identify which
normative paragraphs each AARM note applies to.

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

From: Steve Baird
Sent: Thursday, February 10, 2011  4:27 PM

I have some corner-case questions about how this one interacts with interface
types.

In the first paragraph of the proposed wording , it says

     ... so long as a corresponding dispatching operation of some
     ancestor type is visible ....

Consider the following variation on the example given in the AI:

     package A1 is
        type Base is abstract tagged private;
        procedure P1 (X : Base);
     private
        type Base is tagged null record;
        procedure P2 (X : Base);
     end A1;

     package A1.A2 is
        type Child is new Base with private;
        procedure P1 (X : Child);
     private
        type Child is new Base with null record;
        procedure P2 (X : Child);
     end A1.A2;

     package Some_Other_Pkg is
        type Ifc is interface;
        procedure P2 (X : Ifc) is abstract;
     end Some_Other_Pkg;

     with A1.A2;
     with Some_Other_Pkg;
     package Foo is
        type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc with private;
        procedure P1 (X : Grandchild);
     private
        type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc
          with null record;
        procedure P2 (X : Grandchild);
     end Foo;

We replaced A1.A3 with Foo, so we lose all visibility there into A1's private
part. One might think that this would mean that the the new wording no longer
applies, but we have this interface type IFc which is a progenitor of Grandchild
and has a P2 with the right profile. So does the wording apply? Does this mean
that the AI introduces unwanted overriding? Or does the word "corresponding" (in
the cited RM wording) *obviously* eliminate the problem.

Perhaps there is no problem here, but it seems that we might want an AARM note
to clarify this situation.

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

From: Tucker Taft
Sent: Thursday, February 10, 2011  8:06 PM

I think the intent is pretty clear, but an AARM note could be used to minimize
the chance of an "unfriendly" reading.

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  9:52 PM

> I think the intent is pretty clear, but an AARM note could be used to
> minimize the chance of an "unfriendly"
> reading.

I've lost track of what the intent IS. Perhaps one of you can help out??

...
> Steve Baird wrote:
> > I have some corner-case questions about how this one interacts with
> > interface types.

What other kind of questions would you have? :-)

...
> > with A1.A2;
> > with Some_Other_Pkg;
> > package Foo is
> > type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc with
> > private; procedure P1 (X : Grandchild); private type
> Grandchild is new
> > A1.A2.Child and Some_Other_Pkg.Ifc with null record;
> procedure P2 (X :
> > Grandchild); end Foo;

Well, *clearly* the new rules cannot apply here as there is no keyword
"overriding". In the interests of compatibility, we surely intend no change in
the absense of that keyword. So I think the question is irrelevant on this
example (although maybe I've missed something - perhaps that is not reflected in
the wording somehow??). OTOH, had the example included the keyword "overriding",
then it gets interesting.

with A1.A2;
with Some_Other_Pkg;
package Foo is
   type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc with private;
   procedure P1 (X : Grandchild);
private
   type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc with null record;
   overriding
   procedure P2 (X : Grandchild);
end Foo;

I don't know the answer here, because I don't really understand the purpose of
the phrase in question. "so long as a corresponding dispatching operation of
some ancestor type is visible at the point of the explicit declaration." Are we
trying to ensure that some version of this routine is visible at this point?

Presuming that is the intent, then I do not agree that this wording is good
enough. Both candidates are "corresponding" in some sense, and I'm not sure how
we can tell that one is and the other is not. Maybe an AARM note would be
convincing, but someone will have to write it.

Note that one of the open AIs that we really ought to work on is that we don't
even have a working definition of dispatching in the case of interfaces. (See
AI05-0197-1). So this area is a complete mess.

I truly hate interfaces... (not quite to the level of coextensions, but pretty
close).

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

From: Randy Brukardt
Sent: Thursday, February 10, 2011  10:42 PM

> Perhaps a little clever formatting, or adding some wording to identify
> which normative paragraphs each AARM note applies to.

The problem here is that you put this wording in front of an existing 9
paragraph AARM note. I don't think putting this at the end is going to help
anything. :-)

Actually, I see a problem here: you have this added wording, but it is going
into a bulleted list immediately after an inner bulleted list. So does it belong
to the inner list or the outer one? Is this one bullet or two? If it goes after
the *inner* list (which seems to make more sense to me), then the preceding
bullet needs work as well (this doesn't work after an "otherwise" bullet). If it
*doesn't* go in the inner list, I don't see how it works in the interface case
(where there is two homographs already); in that case, we'd select the previous
bullet and never see this one.

So please tell me what you had in mind here.

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

From: Gary Dismukes
Sent: Tuesday, February 15, 2011  6:55 PM

A few days ago, Steve posted the following example:

> I have some corner-case questions about how this one interacts with
> interface types.
>
> In the first paragraph of the proposed wording , it says
>
>      ... so long as a corresponding dispatching operation of some
>      ancestor type is visible ....
>
> Consider the following variation on the example given in the AI:
>
>      package A1 is
>         type Base is abstract tagged private;
>         procedure P1 (X : Base);
>      private
>         type Base is tagged null record;
>         procedure P2 (X : Base);
>      end A1;
>
>      package A1.A2 is
>         type Child is new Base with private;
>         procedure P1 (X : Child);
>      private
>         type Child is new Base with null record;
>         procedure P2 (X : Child);
>      end A1.A2;
>
>      package Some_Other_Pkg is
>         type Ifc is interface;
>         procedure P2 (X : Ifc) is abstract;
>      end Some_Other_Pkg;
>
>      with A1.A2;
>      with Some_Other_Pkg;
>      package Foo is
>         type Grandchild is new A1.A2.Child and A1.A2.Ifc with private;

A1.A2.Ifc => Some_Other_Pkg.Ifc

>         procedure P1 (X : Grandchild);
>      private
>         type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc
>           with null record;
>         procedure P2 (X : Grandchild);
>      end Foo;
>
> We replaced A1.A3 with Foo, so we lose all visibility there into A1's
> private part. One might think that this would mean that the the new
> wording no longer applies, but we have this interface type IFc which
> is a progenitor of Grandchild and has a P2 with the right profile. So
> does the wording apply? Does this mean that the AI introduces unwanted
> overriding? Or does the word "corresponding"
> (in the cited RM wording) *obviously* eliminate the problem.
>
> Perhaps there is no problem here, but it seems that we might want an
> AARM note to clarify this situation.

Like Randy, I'm also concerned about the use of "corresponding" in the rule,
since it doesn't clearly say what corresponds to what, though I think I know
what's meant.  If we stick with this basic wording, that should be clarified if
possible.

However, I think there's another problem with the proposed rule that kicks out
Steve's example before we even get to worrying about "corresponding".

(For reference, the wording is:

  When a type extension inherits a subprogram that is never declared
  (see 7.3.1) and is not a homograph of an inherited subprogram that is
  declared, an explicit declaration of a dispatching operation with the
  same defining name and a type-conformant profile will nevertheless
  override the inherited subprogram (for the purposes of dispatching
  calls), so long as a corresponding dispatching operation of some
  ancestor type is visible at the point of the explicit declaration.
  Such an overriding requires an overriding_indicator, which shall be
  OVERRIDING (see 8.3.1).
)

The way I read this, the example is legal because the never-declared subprogram
is "a homograph of an inherited subprogram that is declared", namely the P2
coming from the interface, so the rule doesn't apply. Fine, we don't want it to
apply (though perhaps not for that reason:-).

However... this does bring to mind a variation on the above where it seems that
the rule won't apply when it should.  What if we change Foo back to being a
child unit, so the ancestor subprogram *is* visible. The rule still won't apply
(again because the never-declared subprogram is a homograph of the
interface-inherited P2), but I certainly assume that it's intended to apply in
that case.  That is, overriding of the never-declared subprogram should occur.
After all, I don't think we want overriding of the ancestor operation to
suddenly be suppressed as a result of adding a dependence on an interface.

So it appears that at least the first part of the rule doesn't have quite the
right effect.

It seems that somehow the "and is not a homograph of an inherited subprogram"
wording needs to be changed so that it only means subprograms that are inherited
from ancestors in the chain of types that's the source of the never-declared
subprograms, though that might be difficult to phrase properly.

----

One other small comment on the AARM paragraph in 8.3(10.d):

  Modify 8.3(10.d):

    As explained in 7.3.1, "Private Operations", some inherited primitive
    subprograms are never declared. Such subprograms cannot{, in general,}
    be overridden, although they can be reached by dispatching calls in
    the case of a tagged type.

I think we should add a reference to the new rules in 8.3.  Something
like:

    "(For a case where such overriding can occur, see 8.3(12.xx).)"

----

BTW, the sentence in the second added 8.3 paragraph in !wording must be a strong
contender for the prize of longest RM sentence -- it goes on for ten lines!

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

From: Randy Brukardt
Sent: Tuesday, February 15, 2011  7:27 PM

...
> (For reference, the wording is:
>
>   When a type extension inherits a subprogram that is never declared
>   (see 7.3.1) and is not a homograph of an inherited subprogram that is
>   declared, an explicit declaration of a dispatching operation with the
>   same defining name and a type-conformant profile will nevertheless
>   override the inherited subprogram (for the purposes of dispatching
>   calls), so long as a corresponding dispatching operation of some
>   ancestor type is visible at the point of the explicit declaration.
>   Such an overriding requires an overriding_indicator, which shall be
>   OVERRIDING (see 8.3.1).
> )
>
> The way I read this, the example is legal because the never-declared
> subprogram is "a homograph of an inherited subprogram that is
> declared", namely the P2 coming from the interface, so the rule
> doesn't apply.
> Fine, we don't want it to apply (though perhaps not for that
> reason:-).
>
> However... this does bring to mind a variation on the above where it
> seems that the rule won't apply when it should.
> What if we change Foo back to being a child unit, so the ancestor
> subprogram *is* visible.
> The rule still won't apply (again because the never-declared
> subprogram is a homograph of the interface-inherited P2), but I
> certainly assume that it's intended to apply in that case.

Well, I asked a version of this question last week. Part of the problem is that
I have no idea into which bulleted list this wording is supposed to go (the
specified position is at the end of one inner list in the middle of an outer
list). And so far as I can tell, either choice is wrong.

If it goes into the inner list, it only applies where there are "two or more
homographs implicitly declared". That's clearly wrong - there might not be any
implicit declarations.

If it goes into the outer list, it only applies when there are *not* "two or
more homographs implicitly declared" (because there is already a bullet starting
that way, and we don't want bullets that overlap). But that means that if there
are homographs, the rule cannot apply (even if it should because of
progenitors).

Without some resolution of which list this goes into, it isn't even possible to
consider how to fix the wording. And in all honestly, I don't see any way to
make it work for the interface case. (Indeed, I've forgotten why we would want
it to work for that case...have to go back and look at the examples again.)

Anyway, this wording doesn't work, and we'll have to come up with something else
at the meeting.

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

From: Tucker Taft
Sent: Wednesday, March 16, 2011  5:05 PM

I am tempted to suggest we give up on AI-125, where we are trying to allow the
overriding of an operation that is inherited but never declared.  It will become
incompatible if we eliminate the "preference" rule for operations that *are*
declared, and the wording is already getting pretty baroque.

Comments?

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

From: Randy Brukardt
Sent: Wednesday, March 16, 2011  7:09 PM

Well, the AI is already incompatible (in that not giving an indicator is not
allowed in some cases, while it would declare a new subprogram in Ada 95). What
we don't want is any inconsistencies (where the routine dispatched to changes
between Ada 95 and Ada 2012). So if there is a way to make the wording have that
effect, I'd like to see it. (Especially as the remaining problem involved
interfaces, which I (a) could care less about; and (b) are pretty uncommon
anyway. It's annoying to have to give up on useful functionality because of some
corner case with a rarely used feature.)

But having said that, I admit that I couldn't figure out any sensible solution.
Simply banning the declaration would seem to have a ripple effect:

     package A1 is
        type Base is abstract tagged private;
        procedure P1 (X : Base);
     private
        type Base is tagged null record;
        --procedure P2 (X : Child);
     end A1;

     package A1.A2 is
        type Child is new Base with private;
        procedure P1 (X : Child);
     private
        type Child is new Base with null record;
        --procedure P2 (X : Child);
     end A1.A2;

     package Some_Other_Pkg is
        type Ifc is interface;
        procedure P2 (X : Ifc) is abstract;
     end Some_Other_Pkg;

     with A1.A2;
     with Some_Other_Pkg;
     package A1.A3 is
        type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc with private;
        procedure P1 (X : Grandchild);
     private
        type Grandchild is new A1.A2.Child and Some_Other_Pkg.Ifc
          with null record;
        overriding
        procedure P2 (X : Grandchild);
     end A1.A3;

This is legal; the overriding routine fufills the "shall be overridden"
requirement.

Now, imagine that A1 is something like Claw. And A3 is a user-written extension
of Claw. And the author of Claw decided to add the commented out P2s to the
private part of Claw. Having done that, unlucky routines like A1.A3 would
suddenly become illegal, even though nothing will have changed in the visible
part of Claw.

We can *almost* accept this, if we decide that making something a child of Claw
effectively makes it part of the subsystem, so dependence on the private part is
part of that. But it does seem to make interfaces less useful.

So I'm torn between making this rare case illegal (but there is no effective
workaround short of changing the name or profile of the routine in the interface
-- this is supposed to be a separate routine from the one in the private part)
or giving up on the AI. I don't really have a problem with this being illegal,
since it is effectively exporting routines from the parent's private part --
something we really don't want to do (especially this way).

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

From: Tucker Taft
Sent: Wednesday, March 16, 2011  7:48 PM

I will do other homework first.  As far as interfaces, they may not seem that
useful at first, but trust me, once you start getting familiar with them, they
begin to proliferate.

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

From: Randy Brukardt
Sent: Wednesday, March 16, 2011  8:14 PM

> I will do other homework first.

Yes, please.

You can tell from my message that my opinion on this one flops around like a
fish caught in a gill net. :-) So it isn't at all obvious that we want to do
this. OTOH, I think I've convinced myself that the legality rule is necessary to
prevent unintentional leakage of private information, and it isn't significantly
different than the incompatibility that the AI already has (which was deemed to
be OK).

> As far as interfaces, they may not seem that useful at first, but
> trust me,
> once you start getting familiar with them, they begin to proliferate.

I don't think it is very likely that I'll be using them seriously anytime soon,
because I'd have to spend a man-year implementing them first. ;-) [There are
advantages and disadvantages to having your own compiler...]

But my experience with Ada OOP to date is that abstract types are only valuable
when you can share part of the data/implementation. And you can't do that with
interfaces. The value of the whole signature thing -- in any form -- I don't
get; I can count the number of cases where it would have been possible in some
sort of useful way in my real code on one hand. YMMV.

[To expand a bit: if you can't share the data and/or implementation, you end up
having to declare an overriding for each operation before you can do anything
with the new concrete type. Usually, the entire type has to be implemented
before you can do any testing at all. That takes *forever*, and is completely at
odds with the agile-like development process that I've always used (long before
someone started making money off of "agile development", BTW)-- if it takes more
than 1/2 day to get to working code, I get nervous.]

Anyway, this is fairly irrelevant to the question at hand.

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

From: Tucker Taft
Sent: Wednesday, March 16, 2011  8:50 PM

For what it is worth, useful interfaces often have only a small number of
operations, and are not a burden to implement.  But you can get significant
benefit from implementing those operations, because now your type can join a
"club" that makes it more useful.  For example, in model-view-controller GUIs,
there is often the notion of an "observer."  An observer gets notified when the
"model" changes.  Typically "observer" is an interface with just a couple of
operations.  You "register" yourself as an "observer" and wait to be called
back.

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

Questions? Ask the ACAA Technical Agent