JDK-6531075 : Missing synthetic casts when accessing fields/methods of intersection types including type variables
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: generic
  • Submitted: 2007-03-05
  • Updated: 2012-03-22
  • Resolved: 2011-05-18
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 7
7 b27Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
JLS 4.4. says:
The members of a type variable X with bound T & I1 ... In are the members of the intersection type (��4.9) T & I1 ... In appearing at the point where the type variable is declared.

JLS 4.9 Intersection Types
The members of an intersection type T1 & ... & Tn are determined as follows:
   1. For each Ti, 1in, let Ci be the most specific class or array type such that Ti <: Ci Then there must be some Tk <: Ck such that Ck <: Ci for any i, 1in, or a compile-time error occurs. 
   2. For 1jn, if Tj is a type variable, then let ITj be an interface whose members are the same as the public members of Tj; otherwise, if Tj is an interface, then let ITj be Tj. 
   3. Then the intersection type has the same members as a class type (��8) with an empty body, direct superclass Ck and direct superinterfaces IT1 , ..., ITn, declared in the same package in which the intersection type appears.

-------------------------------------------------------------------------

The test below declares the generic method testMethod with the type parameters <W extends C & I & I1 & I2, T extends W>, where C is a class, I, I1, I2 - interfaces. W - type variable with the bound C & I & I1 & I2.

    static <W extends C & I & I1 & I2, T extends W> void testMethod(T t)

1. According to 4.4 the members of a type variable T with bound W are the members of the intersection type W.
2. According to 4.9.3 intersection type W has the same members as a class type with an empty body and direct superinterface IW.
3. According to 4.9.2  IW is an interface whose members are the same as the public members of W.
4. According to 4.4 the members of a type variable W with bound C & I & I1 & I2  are the members of the intersection type C & I & I1 & I2.
5. According to 4.9.1, 4.9.3 intersection type C & I & I1 & I2 has the same members as a class type with an empty body and direct superclass C and direct superinterfaces I & I1 & I2. 

So, according point 4,5, W has the same members as all members af C (except private members), I, I1 and I2.
So, according point 3, IW has the same members as all members af C (except private AND PROTECTED members), I, I1 and I2.
So, according point 1, the members of a type variable T are the same as PUBLIC members of C and all members of I, I1 and I2.

But in the test code below PROTECTED member of C is accessible from testMethod().
But methods of I, I1 and I2 are not accessible in runtime.

---------------------------Test.java------------------------------
package javasoft.sqe.tests.lang.TYPE.TESTS;

import java.io.PrintStream;

class C {
    public void mCPublic() {}
    public void mCProtected() {}
}

interface I {
    void mI();
}

interface I1 {
    void mI1();
}

interface I2 {
    void mI2();
}

class CI extends C implements I, I1, I2 {
    public void mI() {}
    public void mI1() {}
    public void mI2() {}
}

public class Test {
    public static int run(String argv[], PrintStream out) {
        testMethod(new CI());
        return 0/*STATUS_PASSED*/;
    }

    public static void main(String argv[]) {
        System.exit(run(argv, System.out) + 95/*STATUS_TEMP*/);
    }

    static <W extends C & I & I1 & I2, T extends W> void testMethod(T t) {
        t.mCPublic(); // OK
        t.mI();       // should be OK, but not (see OutPut1)
        t.mI1();      // should be OK, but not (see OutPut2)
        t.mI2();      // should be OK, but not (see OutPut3)
        t.mCProtected(); // should rise to a compile-time error, but don't
    }
}

------------------OutPut1---------------------------
Exception in thread "main" java.lang.NoSuchMethodError: javasoft.sqe.tests.lang.TYPE.TESTS.C.mI()V
	at javasoft.sqe.tests.lang.TYPE.TESTS.Test.testMethod(Test.java:40)
	at javasoft.sqe.tests.lang.TYPE.TESTS.Test.run(Test.java:30)
	at javasoft.sqe.tests.lang.TYPE.TESTS.Test.main(Test.java:35)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)

------------------OutPut2---------------------------
Exception in thread "main" java.lang.NoSuchMethodError: javasoft.sqe.tests.lang.TYPE.TESTS.C.mI1()V

------------------OutPut3---------------------------
Exception in thread "main" java.lang.NoSuchMethodError: javasoft.sqe.tests.lang.TYPE.TESTS.C.mI2()V

Comments
EVALUATION Following the evaluation of 6557960 JLS 4.9 should be updated so that each type Ti of an intersection type T1 & T2 ... & Tn where Ti is a type variable is translated into the type of an interface that has ALL members of Ti except private ones. This is in synch with the actual behaviour of javac, so no further action is required for tackling this issue.
26-02-2008

SUGGESTED FIX see http://sa.sfbay.sun.com/projects/langtools_data/7/6531075/
18-12-2007

EVALUATION When the qualifier of an expression is an intersection type like C & I & I1 & I2, during type-erasure javac should generate synthetic casts to either C, I, I1 or I2 depending on the method/field being accessed. This works without problem if the type of the qualifier is either (i) an intersection type as the one showed above, or (ii) a type variable (possibly a captured type) whose upper bound is such an intersection type. However javac does not emit any synthetic cast when the qualifier is a type-variable whose upper bound is not an intersection type. This can lead to a runtime error in the case in which the upper bound of that type-variable is itself a type-variable having an intersection type as upper bound. In this case (referring to the simplified example in 'Description') we have that: 1) Capture of Foo<?,?> is Foo<X,Y>, where ub(X) === Object & List<Integer> AND ub(Y) = X 2) arg.t has type Y (follows from 1) When translating the method call arg.t.add() we have that the qualifier type is Y. It can be seen that Y is a (captured) type-variable but Y's upper bound is not an intersection type (the upper bound of Y is X). For this reason no synthetic cast is generated. At a later point the type of the expression arg.t gets erased to Object so that javac generates a method call to add() with Object as the receiver type. This lead to the runtime error reported in both 'Description' and 'Evaluation'. The solution is to emit a synthetic cast when the type of the qualifier expression is either (i) an intersection type or (ii) a type variable (possibly a captured type) whose upper bound is either an intersection type or a type-variable for which (ii) applies (so that condition (ii) applies recursively to that new upper bound). As far as the call to mCProtected(), currently javac does not perform any access control when accessing fileds/methods on an intersection type.
17-12-2007