JDK-8013846 : javac fails to reject semantically equivalent generic method declarations
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u37,8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2013-03-20
  • Updated: 2014-11-17
  • Resolved: 2013-09-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8 b108Fixed
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
javac 1.7.0_10

ADDITIONAL OS VERSION INFORMATION :
This issue is not OS dependent, but I tested on 64-bit Windows 7 SP1
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
The javac 1.7.0_10 compiler fails to reject code containing semantically equivalent generic method declarations.  Example source code is included with this bug report.  This code is correctly rejected by javac 1.6.0_37.

The example code contains two semantically equivalent declarations for a generic class method, but each has a distinct definition.  The compiler appears to always choose the first declaration when resolving method calls.

# Using javac 1.6.0_37:
$ javac -version
javac 1.6.0_37

$ javac t.java
t.java:10: <t1>smf(t1) is already defined in t
    static <t2 extends i2 & i1> Object smf(t2 x) {
                                       ^
1 error

# Using javac 1.7.0_10
$ javac -version
javac 1.7.0_10

$ javac t.java
<no errors>

$ java -version
java version  " 1.7.0_10 " 
Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)

$ java t
smf1

# Modify the source code to change the order of the smf() declarations.

$ javac t.java
<no errors>

$ java t
smf2

REGRESSION.  Last worked in version 6u31

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the example source code using javac 1.7.0_10.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The source code should be rejected with an error like that provided by javac 1.6.0_37.
ACTUAL -
No errors are produced and a .class file is generated by the compiler.  At run-time, the first declaration of the semantically equivalent generic method declaration is chosen when resolving method calls.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class t {
    interface i1 {}
    interface i2 {}

    static <t1 extends i1 & i2> Object smf(t1 x) {
        System.out.println( " smf1 " );
        return null;
    }

    static <t2 extends i2 & i1> Object smf(t2 x) {
        System.out.println( " smf2 " );
        return null;
    }

    public static void main(String[] s) {
        class c implements i1, i2 {}
        smf(new c());
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Correct the erroneous source code.  However, this requires recognizing a run-time problem and debugging the code to determine that the  " wrong "  method implementation is getting called.
Comments
A fix has been prepared which will cause javac to reject these kinds of declarations (bringing its behavior in line with the way it does overriding). Currently running tests and reviews.
11-09-2013

Well, it may be a bug and yet should not be fixed at this point, until we can resolve the spec issue.
10-09-2013

Code inspection agrees with Jon's comment. I'm wondering at this point whether this is actually a bug, and whether it should actually be fixed.
10-09-2013

While it may seem obvious and simple that the bounds should be unordered, currently, the bounds are ordered to the extent that the erasure takes the first bound. Therefore, there seems to be no simple solution at this point in time.
10-09-2013

Jon, for clarification on what the spec says in this area: see JDK-8024484. Also see my comment above about "two possibilities", which describes what the spec says should happen _depending on_ how the crucial bit of the spec is interpreted.
09-09-2013

I will prepare a patch that does what I suggested (intersections with the same elements are considered equal). If done right, it should be possible to change the behavior of javac on all counts with little effort. Again, I will put forward that differentiating the types T1 & T2 and T2 & T1 would be a huge mistake.
09-09-2013

Dan, can you clarify what currently does the spec say in this area, and what is the spec issue that needs to be resolved? -- I agree that in the face of spec issues, javac should be internally consistent.
09-09-2013

Note that the spec issue is unlikely to be resolved any time soon. In the mean time, javac should be made internally consistent (and probably undo the regression, unless there's a good reason for a change in behavior).
09-09-2013

I suggest you clarify the spec first, and then fix as appropriate. "Measure Twice, Cut Once" "Check Spec Twice, Fix Once"
09-09-2013

The fix for this will cause javac to consider T1 & T2 and T2 & T1 to be equal, since it does so elsewhere. If the spec ends up saying differently (which, I can't really see why it would), we'll fix that in a separate issue.
09-09-2013

The specification is unclear (see JDK-8024484). But javac should, at least, be internally consistent. Two possibilities: - If the bounds are the same, then this error applies: "It is a compile-time error to declare two methods with override-equivalent signatures in a class" (8.4.2). - If the bounds are different, then the invocation identifies two applicable methods (15.12.2.2), neither of which is most specific (15.12.2.5), and since they are not override-equivalent, the invocation is ambiguous (15.12.2.5). javac seems to prefer to treat bound lists as unordered, and may even support elimination of redundant elements. So the first condition should apply, at least until 8024484 is resolved. (Note that this logic is already implemented, I believe, and just needs to be used by the error check: as instance methods in different classes, javac would treat the second as if it overrode the first.)
09-09-2013

Work will resume once more critical bugs have been addressed.
03-09-2013

Why would types need to be ordered -- can't you do set-equivalence?
17-07-2013

The amount of work on this one could be considerable. Upper-bounds need to be normalized, which means there needs to be an order on types.
17-07-2013

This is possibly more complex than it seems. If we accept that list equality is an acceptable equivalence test, then the problem can be fixed by normalizing (ie sorting) the lists of bounds. However, the following case raises a question: class A {} class B extends A {} class C {} class T { static <T1 extends A & B & C> t() {} static <T2 extends B & C> t() {} } In essence, the intersection A & B & C is equivalent to B & C. This case would be considerably harder to detect. I will consult the JLS and post additional comments.
24-06-2013

Reproduced in jdk 1.8.0 b75.
02-05-2013