Version 1.2 of ai05s/ai05-0165-1.txt

Unformatted version of ai05s/ai05-0165-1.txt version 1.2
Other versions for file ai05s/ai05-0165-1.txt

!standard 8.3(12.3/2)          09-10-21 AI05-0165-1/01
!standard 8.3(26/2)
!class ramification 09-10-21
!status work item 09-10-21
!status received 09-06-23
!priority Low
!difficulty Easy
!qualifier Omission
!subject Inheriting non-conformant homographs
!summary
It is possible to inherit non-conformant homographs, but they can't be called or overridden.
!question
Is Typ3 illegal in the following example? (No.)
package Pack1 is type Int1 is interface; procedure Op (X : in Int1) is null; end Pack1;
package Pack2 is type Int2 is interface; procedure Op (Y : in out Int2) is null; end Pack2;
with Pack1; with Pack2; package Pack3 is type Typ3 is new Pack1.Int1 and Pack2.Int2 with record F1 : Integer; end record; end Pack3;
The declaration of Typ3 results in two inherited operations being declared:
--procedure Op (X : in Typ3) is null; --procedure Op (X : in out Typ3) is null;
8.3(12.3/2) says that these declarations are hidden from all visibility. But there isn't any rule that appears to make them illegal until/unless someone tries to override them.
!response
The original questioner suggested making these homographs illegal by requiring subtype conformance in 8.3(12.1/2). But that would be wrong, because that part of 8.3 is about visibility, not legality, and all possible cases have to be covered here.
One could imagine making these homographs illegal by adding a separate legality rule:
If two or more homographs are implicitly declared at the same place, all shall be subtype conformant.
However, this would introduce incompatibilities in generic instances, as currently there is an exception allowing such homographs.
To fix that, we'd have to reintroduce a version of a rule that existed in reasonably late drafts of AI95-0251-1, replacing 8.3(26/2) with:
A non-overridable declaration is illegal if there is a homograph occurring immediately within the same declarative region that is visible at the place of the declaration, and is not hidden from all visibility by the non-overridable declaration. In addition, a type extension is illegal if somewhere within its immediate scope it has two visible components with the same name. Similarly, the context_clause for a subunit is illegal if it mentions (in a with_clause) some library unit, and there is a homograph of the library unit that is visible at the place of the corresponding stub, and the homograph and the mentioned library unit are both declared immediately within the same declarative region.
If two or more homographs are implicitly declared at the same place (and not overridden by a non-overridable declaration) then at most one shall be a non-null non-abstract subprogram. If all are null procedures or abstract subprograms, then all of the null procedures shall be subtype conformant with one another. If all are abstract subprograms, then all of the subprograms shall be subtype conformant with one another.
All of the above Legality Rules also apply to dispatching operations declared in the visible part of an instance of a generic unit. However, they do not apply to other overloadable declarations in an instance; such declarations may have type conformant profiles in the instance, so long as the corresponding declarations in the generic were not type conformant.
This rule was dropped (presumably as being unnecessary, unfortunately the reasons for dropping this were not recorded, even privately), after the "hidden from all visibility" rule was added very late in the development of Amendment 1 (after the June 2005 ARG meeting, during which a number of anomalies were discovered).
But adding a rule like this seems like killing a fly with a bazooka: there is no known semantic problem with the declaration of Typ3 -- Op cannot be called (because it is not visible) or overridden (because an overriding would violate 3.9.2(10) for one of the two homographs), and dispatching works properly. And there is the ever-present danger of banning something useful. So we make no change to the language.
!ACATS Test
One could add a C-Test to check that the example is allowed, but this example is a pathology. There is no known use for the example, so there is no reason to insist that compilers implement it correctly.
!appendix

!topic Implicit homographs that aren't mode conformant
!reference 8.3(12.3/2), 3.9.2(10)
!from Adam Beneschan 09-06-23
!discussion

I was under the impression that the declaration of Typ3 was illegal in this
example:

    package Pack1 is
        type Int1 is interface;
        procedure Op (X : in Int1) is null;
    end Pack1;

    package Pack2 is
        type Int2 is interface;
        procedure Op (Y : in out Int2) is null;
    end Pack2;

    with Pack1;
    with Pack2;
    package Pack3 is
        type Typ3 is new Pack1.Int1 and Pack2.Int2 with record
            F1 : Integer;
        end record;
    end Pack3;

The declaration of Typ3 results in two inherited operations being
declared:

    --procedure Op (X : in Typ3) is null;
    --procedure Op (X : in out Typ3) is null;

I thought this was illegal, and GNAT does reject the program.
However, I can't find anything that makes it a requirement that implicit
homographs be mode- or subtype-conformant.  The only thing I could find related
to this is 3.9.2(10):

    In the declaration of a dispatching operation of a tagged type,
    everywhere a subtype of the tagged type appears as a subtype of
    the profile (see 6.1), it shall statically match the first subtype
    of the tagged type.  If the dispatching operation overrides an
    inherited subprogram, it shall be subtype conformant with the
    inherited subprogram.

However, the way 8.3(12.3/2) is worded, there isn't any overriding:

12.1 If two or more homographs are implicitly declared at
     the same place: ...

    12.3 Otherwise (all are null procedures, abstract subprograms, or
         require overriding), then any null procedure overrides all
         abstract subprograms and all subprograms that require
         overriding; if more than one such homograph remains that is
         not thus overridden, then if they are all fully conformant
         with one another, one is chosen arbitrarily; if not, they
         are all hidden from all visibility.

Applying this paragraph, the null procedures don't override anything; then more
than one homograph remains, and since they are not fully conformant, they are
hidden from all visibility, but there's not a word that indicates that one
overrides the other.  So the second sentence of 3.9.2(10) wouldn't apply.

I'm not sure how serious a problem it is to allow the program, since the
declarations are both null subprograms, they can't be further inherited since
they're not visible, and you clearly could not declare an overriding Op later in
Pack3's spec (doing so would definitely violate 3.9.2(10)).  If the programmer
later tried to derive from Typ3:

    with Pack3;
    package Pack4 is
        type Typ4 is new Pack3.Typ3 with record ... end record;
        --overriding
        procedure Op (X : in Typ4);
    end Pack3;

he might be surprised to find that Op didn't override anything, and (I assume)
its body could not be called by any dispatching operations on objects of type
Int1'Class or Int2'Class.  Of course, if he had used the "overriding" keyword,
the error would have been caught at compile time since there is no inherited Op
to override.  If AI05-125 is adopted, so that Op *could* override the inherited
(but not declared) subprograms declared for the ancestor type (if I'm reading
the proposed wording correctly), then the declaration of Op in Pack4 would
probably become illegal by 3.9.2(10).

But was it the intent that Typ3 be allowed at all?  If not, then perhaps
8.3(12.1/2) should read:

   If two or more homographs are implicitly declared at
   the same place, they shall be subtype-conformant, and:


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

From: Randy Brukardt
Sent: Wednesday, June 24, 2009  10:48 AM

...
> The declaration of Typ3 results in two inherited operations being
> declared:
>
>     --procedure Op (X : in Typ3) is null;
>     --procedure Op (X : in out Typ3) is null;
>
> I thought this was illegal, and GNAT does reject the program.

Why would you think this is illegal?

...
> Applying this paragraph, the null procedures don't override anything;
> then more than one homograph remains, and since they are not fully
> conformant, they are hidden from all visibility, but there's not a
> word that indicates that one overrides the other.  So the second
> sentence of 3.9.2(10) wouldn't apply.

And since they're hidden from all visibility, you can't call them, you can't
override them, and you can't do anything else with them. Since they're null,
dispatching to one of them is harmless. So explain again why this should be
illegal??

...
> But was it the intent that Typ3 be allowed at all?

I don't see any problem with allowing Typ3. It's not particularly useful, but
the intent as I understood it was to allow anything that does not cause semantic
problems. Because making things illegal tends to introduce maintenance issues,
and we have a long history of having problems with "methodological
restrictions". And you haven't shown any semantic problems with Typ3.

If the subprograms required overriding, then Typ3 would be illegal because you
couldn't give a legal overriding for Op. But that has nothing whatsoever to do
with the inheritance of Op -- that would still be legal.

Perhaps there is some implementation issue that I don't know about that argues
for making this illegal, but in the absence of any real problem, I don't see the
point of changing the language (this is a particularly delicate part of Ada, as
you already know).

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

From: Adam Beneschan
Sent: Wednesday, June 24, 2009  11:40 AM

> Why would you think this is illegal?

It may be because of some part of the discussion in AI-251 back when I first
read it.  That was a while ago, and my recollection is vague, but I was pretty
sure that somebody at some point thought this would be illegal, so my thinking
was that there might be an omission in the RM wording that made something legal
that you all intended to be illegal.  But if I'm wrong and that wasn't the
intent, I guess that's fine.  I haven't been able to find a problem that would
cause even if any AI05-125 changes are made, and so far I haven't come up with a
case where there could be a problem at runtime when dispatching on an Int1'Class
or Int2'Class object.

OK, perhaps this is what I was thinking of, from the !recommendation section of
AI-251:

    A type that implements (directly or indirectly) an abstract
    interface inherits the interface's (abstract) primitive operations
    with the usual substitution of the new type for the abstract
    interface type in their profile.  If a type inherits multiple
    primitive subprograms that are homographs of one another, they
    must be subtype conformant with one another.  ...

Allowing the declaration of Typ3 violates the second sentence of the quoted
paragraph.  Probably that's why I thought it was "supposed" to be illegal.  But
yes, I realize that even if that was the designers' original intent, if they're
fine with this intention not being met and there aren't any potential problems
with the current situation, then there's no need to change anything.

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

From: Randy Brukardt
Sent: Thursday, October 22, 2009  12:22 AM

Unfortunately, AI95-00251/24 (the approved version) doesn't have a
!recommendation section. The paragraph you are reading is buried deep in the
!appendix. According to the message that contains it, that was the *very* first
draft of an AI that went through 24(!) drafts. Obviously, we dropped that
requirement somewhere along the way.

A bit more information: Draft 21 had a legality rule that all of the implicitly
declared null procedures were fully conformant. (That would of course make your
program illegal.) However, that was replaced by the current rules in Draft 22.
(You can see these changes in the differences between rev 1.33 and rev 1.34 in
the version control system.) [I didn't go back further than Draft 21; I recall
that this rule changed often as new problems were uncovered.]

It appears that those hiding rules were introduced to avoid anomalies when
homographs are inherited, called "early", and then overridden. (That's based on
the discussion recorded in the minutes of the York meeting (June 2005).)

I tried to find out why the legality rule had been dropped, but unfortunately
the new wording was crafted privately by Tucker and Pascal sitting in a cafe
after the York ARG meeting. So there isn't any Rationale explaining why they
chose that particular wording. (Which is why I referenced in the minutes as
"purports to address the above issues".)

Anyway, I'm pretty sure I don't want to reintroduce a legality rule that was
dropped because of semantic issues (even though the issues appear to be
peripheral to the actual legality rule). That seems like a recipe for restarting
an old headache.

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

Questions? Ask the ACAA Technical Agent