Bug 150655 - [1.5][compiler] compiler does not find correct (parameterized) version of overloaded method
Summary: [1.5][compiler] compiler does not find correct (parameterized) version of ove...
Status: RESOLVED DUPLICATE of bug 207935
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 minor (vote)
Target Milestone: ---   Edit
Assignee: Kent Johnson CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 168230
Blocks:
  Show dependency tree
 
Reported: 2006-07-14 11:04 EDT by Markus Keller CLA
Modified: 2007-10-30 07:04 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Markus Keller CLA 2006-07-14 11:04:41 EDT
HEAD

Eclipse gives a compile error for the class below; JDK 1.5.0_07 accepts it.

import java.util.Random;
public class Utils {
	public static String choose(String one, String two) {
		return Utils.<String>choose(one, two);
// Error: "The method choose(String, String) of type Utils is not generic; it
// cannot be parameterized with arguments <String>"
	}
	
	public static <T> T choose(T one, T two) {
		return new Random().nextBoolean() ? one : two;
	}
}
Comment 1 Maxime Daniel CLA 2007-03-02 07:39:13 EST
Reproduced with I20070222-0951.

Released test case MethodVerifier#124.
public class X {
  public static String choose(String one, String two) {
    return one + X.<String>choose(one, two);
  }
  public static <T> T choose(T one, T two) {
    return two;
  }
  public static void main(String args[]) {
	System.out.println(choose("a", "b"));
  }
}

While javac 1.5.0_11, 6_01_b03 and 7_b06 all compile this code, the two former will select the second method at the calling site and print ab, while the latter will select the first method and overflow the stack.

If you consider the following variant (MethodVerifyTest#125): 
public class X {
  public static String choose(String one, String two) {
    return one + X.<String>choose(one, two);
  }
  public static <T> T choose(T one, T two) {
    return two;
  }
  public static void main(String args[]) {
	System.out.println(choose("a", "b"));
  }
}
we now report that the two methods are ambiguous, and all the javac-s above compile without error *but* recurse at runtime, which means that they select the first method.

I'll check the spec and report my findings here.
Comment 2 Maxime Daniel CLA 2007-03-02 09:14:36 EST
I would decompose the issue into two matters:
- decide when a non-generic method is applicable for a call that specify generic
  parameters;
- decide what to do when a non-applicable non-generic method competes with a
  generic method for a given call.

JLS 3 15.12.2 says that a non-generic method may be potentially applicable to an invocation that supplies explicit type parameters, and may even turn out to be applicable, and justifies this by compatibility needs. Unfortunately, it falls short on providing the list of cases where this can be true.

MethodVerifyTest#126 allows us to make focused tests for this issue:
public class X extends Y {
  public static String foo(String one, String two) {
    return X.<String>foo(one, two);
  }
  public String bar(String one, String two) {
    return this.<String>bar(one, two);
  }
  public String foobar(String one, String two) {
    return this.<String>foobar(one, two);
  }
}
class Y {
  public <T> String foobar(String one, String two) {
    return null;
  }
}

javac 1.5 and 6 consider that this is legit in the only case of an instance method that overrides a generic method (foobar), while javac 7 considers it as legit on all cases (foo, bar, foobar), and we consider this as non legit in all cases. 
I would hence contend that:
- javac 7 either has a bug or else implements forthcoming spec changes, while 
  javac 1.5 and 6 give us guidance for the shorter term;
- while our position is strong for foo and bar, we are distinct from javac and 
  not implementing the spirit of the spec for foobar (it should be possible to 
  override the generic Y#foobar method with a non-generic one).

Now to the second question. When a method is not applicable but another is, we
should select the applicable method. Hence in the case of MethodVerifyTest, we
should print 'ab' as javac 1.5 and 6 do. I would then consider that this bug is legit and raises specifically that situation.

MethodVerifyTest#125 illustrates another case where both methods are applicable and, in my understanding, ambiguous (hence javac would have a bug for that case, which is not the case initially reported in this bug anyway).

MethodVerifyTest#126 raises yet another case and deserves a bug of its own, which I opened as bug 176171.

Before I investigate any fix, could Philippe and Kent please comment on my analysis?
Comment 3 Kent Johnson CLA 2007-03-05 16:41:22 EST
The case described in comment #1 for MethodVerifyTest#125 is missing a type variable. The released test is:

  public static <T> String choose(String one, String two) {
    return one + X.<String>choose(one, two);
  }
  public static <T> T choose(T one, T two) {
    return two;
  }

It would appear that before reporting an ambiguous error, we should be breaking the tie between the methods using the presense of type parameters.

Need to think about this more but it definitely looks like you're on the right track, but the javac7 behaviour change is a problem.

Have you had any luck finding a reported bug for this case in their database?
Comment 4 Maxime Daniel CLA 2007-03-06 03:36:40 EST
(In reply to comment #3)
...
> 
> Have you had any luck finding a reported bug for this case in their database?
> 
The closer I could find is http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5081782 that clearly states (again) that calling a non-generic method with explicit type parameters should be legit, as far as the type parameters employed match types that exist and are accessible at the calling site. So no real help regarding javac 7 here. The bug points to two other bugs that I could not read at all (access rights issue?).

Comment 5 Kent Johnson CLA 2007-03-07 15:26:18 EST
This appears to be another special case to support backwards compatibility - life before generics.

Instead of a method invocation matching a method declaration, it appears that an unacceptable RAW match is valid IFF it overrides an acceptable match.

For example:

class X extends Y {
  String a(String s, A<Object> a) {
    return two;
  }
  String b(String s, A a) {
    return two;
  }
}
class Y {
  <T> String a(String s, A<Object> a) {
    return null;
  }
  <T> String b(String s, A<Object> a) {
    return null;
  }
}
class Test {
  public static void main(String[] s) {
    new X().<String>a("", new A<Object>());
    new X().<String>b("", new A<Object>());
  }
}
class A<T> {}

X.a() and Y.a() cause a name clash error, but X.b() is valid match to the message send in Test.

It would help if X.b() knew that it overrides a generic method Y.b().
Comment 6 Maxime Daniel CLA 2007-03-26 09:10:44 EDT
Bug 176171 has been detected to be a duplicate of bug 168230, which aims at clarifying how we may call non-generic methods with type arguments. I will wait for that bug to make progress before digging deeper.
Comment 7 Maxime Daniel CLA 2007-09-07 06:09:34 EDT
Philippe, is this to be reassigned to Kent?
Comment 8 Kent Johnson CLA 2007-09-11 13:28:16 EDT
The ambiguous method errors were fixed a month ago with the changes for bug 193265, etc.

This bug is now a duplicate of bug 168230, as the remaining issue deals with invoking non-generic methods with type arguments.

*** This bug has been marked as a duplicate of bug 168230 ***
Comment 9 Maxime Daniel CLA 2007-10-29 09:08:00 EDT
MethodVerifyTest#126 hasn't been reactivated. Doing so gives results that are markedly different from the ones I expected when creating the test.
Will consider the test cases of the other bugs and come back.
Comment 10 Maxime Daniel CLA 2007-10-29 13:35:54 EDT
Checked that we still bark at the following at 5.0 and 6.0 compliance levels, whereas javac does not:
public class X extends Y {
  public String foobar(String one, String two) {
    return this.<String>foobar(one, two);
  }
}
class Y {
  public <T> String foobar(String one, String two) {
    return null;
  }
}

(Note: isolated this subpart of test#126 following Philippe's suggestion, fearing that the two first errors would simply mask the last one for javac. To no avail.)
Comment 11 Maxime Daniel CLA 2007-10-30 07:04:44 EDT
Moving the dupe to new bug 207935 that focuses upon the remaining difference with javac 1.5 and 1.6.

*** This bug has been marked as a duplicate of bug 207935 ***