On Sat, 25 Feb 2023 03:57:30 GMT, Archie L. Cobbs <d...@openjdk.org> wrote:

>> This bug relates to the "potentially ambiguous overload" warning which is 
>> enabled by `-Xlint:overloads`.
>> 
>> The warning detects certain ambiguities that can cause problems for lambdas. 
>> For example, consider the interface `Spliterator.OfInt`, which declares 
>> these two methods:
>> 
>> void forEachRemaining(Consumer<? super Integer> action);
>> void forEachRemaining(IntConsumer action);
>> 
>> Both methods have the same name, same number of parameters, and take a 
>> lambda with the same "shape" in the same argument position. This causes an 
>> ambiguity in any code that wants to do this:
>> 
>>     spliterator.forEachRemaining(x -> { ... });
>> 
>> That code won't compile; instead, you'll get this error:
>> 
>> Ambiguity.java:4: error: reference to forEachRemaining is ambiguous
>>         spliterator.forEachRemaining(x -> { });
>>                    ^
>>   both method forEachRemaining(IntConsumer) in OfInt and method 
>> forEachRemaining(Consumer<? super Integer>) in OfInt match
>> 
>> 
>> The problem reported by the bug is that the warning fails to detect 
>> ambiguities which are created purely by inheritance, for example:
>> 
>> interface ConsumerOfInteger {
>>     void foo(Consumer<Integer> c);
>> }
>> 
>> interface IntegerConsumer {
>>     void foo(IntConsumer c);
>> }
>> 
>> // We should get a warning here...
>> interface Test extends ConsumerOfInteger, IntegerConsumer {
>> }
>> 
>> 
>> The cause of the bug is that ambiguities are detected on a per-method basis, 
>> by checking whether a method is part of an ambiguity pair when we visit that 
>> method. So if the methods in an ambiguity pair are inherited from two 
>> distinct supertypes, we'll miss the ambiguity.
>> 
>> To fix the problem, we need to look for ambiguities on a per-class level, 
>> checking all pairs of methods. However, it's not that simple - we only want 
>> to "blame" a class when that class itself, and not some supertype, is 
>> responsible for creating the ambiguity. For example, any interface extending 
>> `Spliterator.OfInt` will automatically inherit the two ambiguities mentioned 
>> above, but these are not the interface's fault so to speak so no warning 
>> should be generated. Making things more complicated is the fact that methods 
>> can be overridden and declared in generic classes so they only conflict in 
>> some subtypes, etc.
>> 
>> So we generate the warning when there are two methods m1 and m2 in a class C 
>> such that:
>> 
>> * m1 and m2 consitiute a "potentially ambiguous overload" (using the same 
>> definition as before)
>> * There is no direct supertype T of C such that m1 and m2, or some methods 
>> they override, both exist in T and constitute a "potentially ambiguous 
>> overload" as members of T
>> * We haven't already generated a warning for either m1 or m2 in class C
>> 
>> If either method is declared in C, we locate the warning there, but when 
>> both methods are inherited, there's no method declaration to point at so the 
>> warning is instead located at the class declaration.
>> 
>> I noticed a couple of other minor bugs; these are also being fixed here:
>> 
>> (1) For inherited methods, the method signatures were being reported as they 
>> are declared, rather than in the context of the class being visited. As a 
>> result, when a methods is inherited from a generic supertype, the ambiguity 
>> is less clear. Here's an example:
>> 
>> interface Upper<T> {
>>     void foo(T c);
>> }
>> 
>> interface Lower extends Upper<IntConsumer> {
>>     void foo(Consumer<Integer> c);
>> }
>> 
>> Currently, the error is reported as:
>> 
>> warning: [overloads] foo(Consumer<Integer>) in Lower is potentially 
>> ambiguous with foo(T) in Upper
>> 
>> Reporting the method signatures in the context of the class being visited 
>> makes the ambiguity clearer:
>> 
>> warning: [overloads] foo(Consumer<Integer>) in Lower is potentially 
>> ambiguous with foo(IntConsumer) in Upper
>> 
>> 
>> (2) When a method is identified as part of an ambiguous pair, we were 
>> setting a `POTENTIALLY_AMBIGUOUS` flag on it. This caused it to be forever 
>> excluded from future warnings. For methods that are declared in the class 
>> we're visiting, this makes sense, but it doesn't make sense for inherited 
>> methods, because it disqualifies them from participating in the analysis of 
>> any other class that also inherits them.
>> 
>> As a result, for a class like the one below, the compiler was only 
>> generating one warning instead of three:
>> 
>> public interface SuperIface {
>>     void foo(Consumer<Integer> c);
>> }
>> 
>> public interface I1 extends SuperIface { 
>>     void foo(IntConsumer c);        // warning was generated here
>> }   
>> 
>> public interface I2 extends SuperIface { 
>>     void foo(IntConsumer c);        // no warning was generated here
>> }   
>> 
>> public interface I3 extends SuperIface { 
>>     void foo(IntConsumer c);        // no warning was generated here
>> }   
>> 
>> 
>> With this patch the `POTENTIALLY_AMBIGUOUS` flag is no longer needed. I 
>> wasn't sure whether to renumber all the subsequent flags, or just leave an 
>> empty placeholder, so I chose the latter.
>> 
>> Finally, this fix uncovers new warnings in `java.base` and `java.desktop`, 
>> so these are now suppressed in the patch.
>
> Archie L. Cobbs has updated the pull request incrementally with one 
> additional commit since the last revision:
> 
>   Do some refactoring & cleanup suggested in reviews.

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java line 101:

> 99:     // Flag bits indicating which item(s) chosen from a pair of items
> 100:     private static final int FIRST = 0x01;
> 101:     private static final int SECOND = 0x02;

not sure about this flags, won't they clash with the existing flags?

-------------

PR: https://git.openjdk.org/jdk/pull/12645

Reply via email to