Uncertainty about fix for JDK-5059679

Archie Cobbs archie.cobbs at gmail.com
Mon Feb 13 18:28:52 UTC 2023


I've got a PR submitted <https://github.com/openjdk/jdk/pull/12503> to fix
JDK-5059679 but I'm having second thoughts and would appreciate some advice
from the spec experts on the list.

The bug essentially asks whether a program like this should compile
(currently, it does):

public class NarrowingNameClash2 {

    public abstract class Upper<T> {
        abstract void method(T param);
    }

    public abstract class Lower<R> extends Upper<Class<R>> {
        abstract void method(Class<?> param);   // name clash here???
    }
}

§8.4.8.3 "Requirements in Overriding and Hiding" says:

It is a compile-time error if a class or interface C has a member method m1
and there exists a method m2 declared in C or a superclass or
superinterface of C, A, such that all of the following are true:

   - m1 and m2 have the same name.
   - m2 is accessible (§6.6) from C.
   - The signature of m1 is not a subsignature (§8.4.2) of the signature of
   m2 as a member of the supertype of C that names A.
   - The declared signature of m1 or some method m1 overrides (directly or
   indirectly) has the same erasure as the declared signature of m2 or some
   method m2 overrides (directly or indirectly).

OK so in this case let:

   - C = A = Lower
   - m1 = Lower.method(Class<R>)
   - m2 = Lower.method(Class<?>)

Note that m1 is a member of C by virtue of §8.2
<https://docs.oracle.com/javase/specs/jls/se19/html/jls-8.html#jls-8.2>
which states "The members of a class are... members inherited from its
direct superclass type (§8.1.4)".

Side note/question: Presumably when Upper.method(T) is inherited by Lower,
its type changes from void method(T) to void method(Class<R>) - correct? Is
this actually stated anywhere?

The first two criteria are easy:

   - m1 and m2 have the same name ✅
   - m2 is accessible (§6.6) from C ✅

Third criterion:

   - The signature of m1 is not a subsignature (§8.4.2) of the signature of
   m2 as a member of the supertype of C that names A.

For the signature of m1 to be a subsignature of the signature of m2, it
would have to equal the erasure of the signature of m2 as a member of C,
which would be void (Class). But the signature of m1 is void
method(Class<R>). So criterion #3 is met. ✅

Fourth criterion:

   - The declared signature of m1 or some method m1 overrides (directly or
   indirectly) has the same erasure as the declared signature of m2 or some
   method m2 overrides (directly or indirectly).

Both methods erase to void method(Class). ✅

OK so far so good (I think) and the above program should NOT compile and
therefore there is a bug.

Now consider the same thing except using interfaces:

public class NarrowingNameClash {

    public interface Upper<T> {
        void method(T param);
    }

    public interface Lower<R> extends Upper<Class<R>> {
        void method(Class<?> param);        // name clash here?
    }
}

According to my reading, interfaces do NOT inherit methods from
superinterfaces. So the above analysis fails, because the only candidates
would be m1 = Lower.method(Class<?>) and m2 = Upper.method(T), and those
don't erase to the same type.

So the second class using interfaces SHOULD compile successfully.

Does the above analysis sound correct?

Thanks,
-Archie

-- 
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230213/834c3538/attachment-0001.htm>


More information about the compiler-dev mailing list