Bug 214558 - [1.5][compiler] javac reports ambiguous method ref that Eclipse does not identify
Summary: [1.5][compiler] javac reports ambiguous method ref that Eclipse does not iden...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.3.1   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.4 RC1   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-01-07 18:23 EST by Ryan Fong CLA
Modified: 2008-05-14 05:46 EDT (History)
2 users (show)

See Also:
philippe_mulet: review+


Attachments
Proposed patch with testcase (3.28 KB, patch)
2008-03-26 10:58 EDT, Kent Johnson CLA
no flags Details | Diff
Patch adding tagbit (14.36 KB, patch)
2008-03-26 16:20 EDT, Philipe Mulet CLA
no flags Details | Diff
Proposed patch with testcase (5.04 KB, patch)
2008-04-30 14:32 EDT, Kent Johnson CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ryan Fong CLA 2008-01-07 18:23:38 EST
Build ID: M20071023-1652

Steps To Reproduce:
1.  Compile and run the code below in Eclipse using JDK 1.6.0_b03.  The output is "B: []".

Alpha.java
----------
import java.util.*;

public class Alpha {
    public void setData(Collection<Object[]> data) {
        System.out.println("A: " + data);
    }
    
    public static void main(String[] args) {
        ArrayList things = new ArrayList();
        Bravo b = new Bravo();
        b.setData(things);
    }
}

class Bravo extends Alpha {
    public void setData(ArrayList data) {
        System.out.println("B: " + data);
    }
}
----------

2.  Compile the above program using javac.  There is an error that Eclipse does not report:

C:\_eclipse\serenity\src>javac Alpha.java
Alpha.java:11: reference to setData is ambiguous, both method setData(java.util.
Collection<java.lang.Object[]>) in Alpha and method setData(java.util.ArrayList)
 in Bravo match
        b.setData(things);
         ^
Note: Alpha.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Expectation:
Eclipse JDT compiler should report the same error as javac and indicate that this code is not runnable.

More information:
We are using Eclipse to develop our Java program and we export Ant scripts (to compile via javac) for official builds.  We noticed this compiler output inconsistency and would like the outcome to be identical.  We expect Eclipse to flag the same error instead of resolving it.
Comment 1 Olivier Thomann CLA 2008-01-07 20:36:58 EST
Note that you could use the Eclipse compiler inside your ant script and then you would not have any differences between tested builds and official builds.
Comment 2 Ryan Fong CLA 2008-01-07 20:45:13 EST
Oliver, yes that is an option that we are considering as well as the wisdom of using both generics and non-generics in our code.  I'm just concerned that there is something deeper in the JDT compiler that we might need to investigate, or perhaps javac is broken.
Comment 3 Philipe Mulet CLA 2008-01-25 05:56:57 EST
Kent - pls look.

Feels like we shouldn't pick the exact match if there is a more specific lesser match ? (at a quick glance)
Comment 4 Kent Johnson CLA 2008-03-26 10:58:44 EDT
Created attachment 93611 [details]
Proposed patch with testcase
Comment 5 Philipe Mulet CLA 2008-03-26 15:54:20 EDT
The raw type check seems a bit expensive to me. We probably should introduce a tagbit for it.

Also you may add an extra test to prove the exact match case:

import java.util.*;
public class Alpha {
    public void setData(Collection<Object[]> data) {
        System.out.println("FAIL: " + data);
    }

    public static void main(String[] args) {
        ArrayList<Object> things = new ArrayList();
        Bravo b = new Bravo();
        b.setData(things);
    }
}
class Bravo extends Alpha {
    public void setData(ArrayList<Object> data) {
        System.out.println("SUCCESS: " + data);
    }
}
Comment 6 Philipe Mulet CLA 2008-03-26 16:20:39 EDT
Created attachment 93682 [details]
Patch adding tagbit

Then in exact method check, you could simply test:
(method.tagBits & TagBits.HasRawArgumentType) != 0)
Comment 7 Kent Johnson CLA 2008-03-27 16:33:48 EDT
also see bug 206930
Comment 8 Kent Johnson CLA 2008-04-28 16:12:50 EDT
If we move the raw type check to AFTER we have found an exact match, then its likely quicker to do it than expect a tag bit to be set on every possible matching method.

Adding the tag bit requires every method binding at creation time to do the raw type check. 1000 methods created = 1000 checks.

How many exact matches do we expect to find ?

Our other approach is to skip exact method matches when source level >= 1.5
Comment 9 Kent Johnson CLA 2008-04-28 16:24:49 EDT
We already skip looking at inherited types of a ParameterizedType

See ParameterizedTypeBinding.getExactMethod() :

if (foundNothing && (this.arguments == null || this.arguments.length <= 1))

So maybe we should only do exactMethod matches when there are no arguments & sourceLevel > 1.5

Philippe - what do you think ?
Comment 10 Philipe Mulet CLA 2008-04-29 06:57:02 EDT
What is the performance degradation then ?

Re: tagbit - I agree it would penalize more cases (I was thinking it could be made part of existing argument iterations though).
Comment 11 Kent Johnson CLA 2008-04-29 10:13:29 EDT
I'll get numbers today.

We can always do it when the arguments.length == 0

and when its == 1, it would be a fast check to see if its not a raw type.

I suspect that will represent the majority of exact matches.
Comment 12 Kent Johnson CLA 2008-04-30 14:32:56 EDT
Created attachment 98236 [details]
Proposed patch with testcase

This patch only does an exact match when the arg count is 2 or less, and then checks the args for raw types after a valid match is found.
Comment 13 Philipe Mulet CLA 2008-05-06 05:19:46 EDT
I would write it:

	public MethodBinding findExactMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) {
		switch (argumentTypes.length) {
			case 2 :
				if (argumentTypes[1].isRawType()) 
					return null; // skip find exact match since its less likely to find a match & raw type check is not worth it
				// fallthrough
			case 1 :
				if (argumentTypes[0].isRawType()) 
					return null; // skip find exact match since its less likely to find a match & raw type check is not worth it
				break;
			case 0 : 
				break;
			default :
				if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5)
					return null; // skip find exact match since its less likely to find a match & raw type check is not worth it
		}
		CompilationUnitScope unitScope = compilationUnitScope();
		unitScope.recordTypeReferences(argumentTypes);
		MethodBinding exactMethod = receiverType.getExactMethod(selector, argumentTypes, unitScope);
		if (exactMethod != null && exactMethod.typeVariables == Binding.NO_TYPE_VARIABLES && !exactMethod.isBridge()) {
			// must find both methods for this case: <S extends A> void foo() {}  and  <N extends B> N foo() { return null; }
			// or find an inherited method when the exact match is to a bridge method
			unitScope.recordTypeReferences(exactMethod.thrownExceptions);
			// special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
			if (receiverType.isInterface() || exactMethod.canBeSeenBy(receiverType, invocationSite, this)) {
				if (receiverType.id != T_JavaLangObject
					&& argumentTypes == Binding.NO_PARAMETERS
				    && CharOperation.equals(selector, GETCLASS)
				    && exactMethod.returnType.isParameterizedType()/*1.5*/) {
						return ParameterizedMethodBinding.instantiateGetClass(receiverType, exactMethod, this);
			    }
				// targeting a generic method could find an exact match with variable return type
				if (invocationSite.genericTypeArguments() != null) {
					exactMethod = computeCompatibleMethod(exactMethod, argumentTypes, invocationSite);
				}
				return exactMethod;
			}
		}
		return null;
	}
Comment 14 Philipe Mulet CLA 2008-05-06 05:20:19 EDT
+1 for addressing in 3.4RC1.
May also want to backport to 3.3 maintenance stream
Comment 15 Kent Johnson CLA 2008-05-06 10:37:49 EDT
But why spend the time doing the raw type checks before you have even found a match?

Once people have converted their code, raw types should no longer be used so we'll likely never fail because of one, and you're adding time to the case when no match is found.
Comment 16 Kent Johnson CLA 2008-05-06 11:25:34 EDT
Released for 3.4RC1

Includes AmbiguousMethodTest test66 & 67
Comment 17 Maxime Daniel CLA 2008-05-14 01:50:55 EDT
Verified for 3.4 RC1 using build I20080513-2000.

Philippe, Kent pls make a decision regarding the 3.3 backport (verification process forks depending on this).