Community
Participate
Working Groups
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.
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.
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.
Kent - pls look. Feels like we shouldn't pick the exact match if there is a more specific lesser match ? (at a quick glance)
Created attachment 93611 [details] Proposed patch with testcase
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); } }
Created attachment 93682 [details] Patch adding tagbit Then in exact method check, you could simply test: (method.tagBits & TagBits.HasRawArgumentType) != 0)
also see bug 206930
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
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 ?
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).
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.
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.
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; }
+1 for addressing in 3.4RC1. May also want to backport to 3.3 maintenance stream
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.
Released for 3.4RC1 Includes AmbiguousMethodTest test66 & 67
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).