Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 463770

Summary: Reference ambiguous for javac, not for JDT
Product: [Eclipse Project] JDT Reporter: Mauro Molinari <mauromol>
Component: CoreAssignee: Sasikanth Bharadwaj <sasikanth.bharadwaj>
Status: CLOSED WONTFIX QA Contact:
Severity: normal    
Priority: P3 CC: jarthana, sasikanth.bharadwaj, stephan.herrmann
Version: 4.4.2   
Target Milestone: ---   
Hardware: PC   
OS: Windows 7   
Whiteboard: stalebug

Description Mauro Molinari CLA 2015-04-02 05:35:06 EDT
Consider the following interfaces:

public interface InterfaceA<T> {
	void foo(T foo);
}

public interface InterfaceC {
	void foo(String foo);
}

public interface InterfaceB extends InterfaceA<String>, InterfaceC {
}

And the client class:

public class Test {
	public static InterfaceB factory() {
		return null; // irrelevant here
	}
	public Test() {
		InterfaceB ib = Test.factory();
		ib.foo("foo");
	}
}

Compiling with JDT is fine (using Java7 compliance).
Compiling with javac produces the following:

test\Test.java:12: error: reference to foo is ambiguous, both method foo(String)
 in InterfaceC and method foo(T) in InterfaceA match
                ib.foo("foo");
                  ^
  where T is a type-variable:
    T extends Object declared in interface InterfaceA
1 error
Comment 1 Stephan Herrmann CLA 2015-04-02 06:27:10 EDT
Same difference can also be observed when compiling for 1.8.

But, looking at InterfaceB, can you tell my, why 'T' would appear in any signature? InterfaceB does not extend InterfaceA, it extends InterfaceA<String> which doesn't have a method foo(T), only a method foo(String).

At first glance this looks like javac is failing to do a necessary type parameter substitution.

Am I missing anything? Could the above be expanded to create ambiguity between non-abstract methods?

I could imagine that javac's error message is borken, it may be trying to tell us something different (e.g., regarding erased signatures not being override compatible or such), but any reasonable error in this category should IMHO be reported against the definition of InterfaceB, not against its clients.
Comment 2 Mauro Molinari CLA 2015-04-02 07:00:39 EDT
There's a question/answer on StackOverflow that might be explaining why this happens: I said "might" because I admit I've not read it so carefully enough to determine it's the exact same problem, although the question seems to suggest this.
Here it is:
http://stackoverflow.com/questions/8966397/why-does-implementing-this-generic-interface-create-an-ambiguous-reference

Another hint: if InterfaceC is itself InterfaceC<T> and then InterfaceB implements both InterfaceA<String> and InterfaceB<String>, then no ambiguous reference error is given any more by javac.
The error is back if T has different bounds in InterfaceA and InterfaceC: if we use, say, JTree (any non-final class) instead of String and you have:
InterfaceA<T>
InterfaceC<T extends JTree>
InterfaceB extends InterfaceA<JTree>, InterfaceC<JTree>
javac complains again.
Comment 3 Stephan Herrmann CLA 2015-04-02 11:52:09 EDT
(In reply to Mauro Molinari from comment #2)
> There's a question/answer on StackOverflow that might be explaining why this
> happens: I said "might" because I admit I've not read it so carefully enough
> to determine it's the exact same problem, although the question seems to
> suggest this.
> Here it is:
> http://stackoverflow.com/questions/8966397/why-does-implementing-this-
> generic-interface-create-an-ambiguous-reference

Thanks, this may save us a lot of time weighing the letters of JLS against each other.

The part that I was fearfully suspecting is this:
"... and the signatures of all of the maximally specific methods have the same erasure ..."

In a parameterized world, Java can see that both methods are equivalent, but at the bare metal we have to give an erased signature for the "invoke" bytecode instruction, and here we cannot decide between foo(Ljava.lang.String;)V and foo(Ljava.lang.Object;)V.

Gee whizz, so it is explicitly legal to create a type which you cannot use.

We need to look for the missing comparison of erasures in Scope.mSMB(). Always missing? Missing only on some path? Consider 1.7- and 1.8+ modes ...

Anyone?
Comment 4 Jay Arthanareeswaran CLA 2015-04-06 05:15:30 EDT
Sasi, can you take this forward? Thanks!
Comment 5 Eclipse Genie CLA 2019-03-11 03:44:16 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 6 Mauro Molinari CLA 2019-03-11 04:51:33 EDT
I can still see no complain from JDT with 1.8 compliance in Eclipse 2018-12, but complain from javac 1.8.0.202.

I don't have the ability to test with newer javac versions right now though.