United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: 6278587 Compiler fails to infer correct type for under-constrained type variables
6278587 : Compiler fails to infer correct type for under-constrained type variables

Details
Type:
Bug
Submit Date:
2005-06-01
Status:
Closed
Updated Date:
2010-08-18
Project Name:
JDK
Resolved Date:
2010-08-18
Component:
tools
OS:
generic
Sub-Component:
javac
CPU:
generic
Priority:
P3
Resolution:
Cannot Reproduce
Affected Versions:
5.0,6
Fixed Versions:
7

Related Reports
Backport:
Duplicate:
Duplicate:
Relates:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
This program doesn't compile

import java.util.*;

public class TestGenerics {
 
	/**Subclasses are parameterized by their own type*/
	private static abstract class SelfType<T extends SelfType<T>>{
		public abstract T getThis();
	}
 
	/**Supertype inherits directly from the parameterized SelfType*/
	private static class SuperType extends SelfType<SuperType>{
		@Override
		public SuperType getThis(){
			return this;
		}
	}
 
	/**Subtype inherits indirectly from the parameterized SelfType*/
	private static class SubType extends SuperType{}
 
	/**Creates a list containing a single SelfType*/
	public static <T extends SelfType<T>> List<T> makeSingletonList(T t){
		return Collections.singletonList(t);
	}
 
	/**
	 * Creates a list containing a single SelfType, allowing the list's
	 * element-type to be a supertype of the type of its single element
	 */
	public static <T extends SelfType<T>,S extends T> List<T> makeSingletonList2(S s){
		return Collections.singletonList((T)s);
	}
 
	public static void main(String[] args){
		/*making lists of super types works fine ...*/
		makeSingletonList(new SuperType());
		List<SuperType> lsup = makeSingletonList(new SuperType());
 
		/*but we can't make a list of sub types; seems weird ...*/
		List<SubType> lsub = makeSingletonList(new SubType()); //ERROR
		/*can't even call it w/o assigning the return value:*/
		makeSingletonList(new SubType()); //ERROR
 
 
		/*so instead, we should be able to make lists of super type containing sub type elements*/
		makeSingletonList2(new SubType()); //ERROR
		/*even if we assign the return value:*/
		lsup = makeSingletonList2(new SubType()); // ERROR (eclipse is okay with this though)
		/*this still doesn't work either:*/
		lsub = makeSingletonList2(new SubType()); // ERROR
 
		/*we can make lists of super type this way though*/
		makeSingletonList2(new SuperType()); // (eclipse doesn't like this though)
		/*also ok if we assign the return value*/
		lsup = makeSingletonList2(new SuperType());
	}
}

See http://forum.java.sun.com/thread.jspa?threadID=632009&tstart=0

###@###.### 2005-06-01 00:42:45 GMT

                                    

Comments
EVALUATION

A more general fix has been pushed in the 7 repository - see 6650759. As a result, the test case above now compiles w/o problems.
                                     
2010-08-18
EVALUATION

The fix of this bug caused a significant amount of compatibility issues (see 6650759, 6559165, 6536404 and related CRs).
                                     
2009-03-03
SUGGESTED FIX

Index: j2se/src/share/classes/com/sun/tools/javac/comp/Infer.java
--- /tmp/geta31570	2006-11-22 16:46:50.000000000 -0800
+++ /tmp/getb31570	2006-11-22 16:46:50.000000000 -0800
@@ -309,50 +309,38 @@
             }
         }
 
-	// repeatedly minimize undetvars, and check against bounds
-	// until all type variables are instantiated to non bottom
-	// types or no further progress is made.
-
-	//-System.err.println("undetvars="+undetvars);//DEBUG
-	//-System.err.println("targs="+targs);//DEBUG
-
-	ListBuffer<Type> restvars;        // type variables instantiated to bottom
-	ListBuffer<Type> insttypes;       // instance types.
-	int restlen = undetvars.length(); // length of restvars
-	int restlen1;                     // length of restvars in previous iteration
-
-	do {
 	    // minimize as yet undetermined type variables
-	    for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
-		minimizeInst((UndetVar) l.head, warn);
-	    }
+        for (Type t : undetvars)
+            minimizeInst((UndetVar) t, warn);
+
+        /** Type variables instantiated to bottom */
+        ListBuffer<Type> restvars = new ListBuffer<Type>();
+
+        /** Instantiated types or TypeVars if under-constrained */
+        ListBuffer<Type> insttypes = new ListBuffer<Type>();
+
+        /** Instantiated types or UndetVars if under-constrained */
+        ListBuffer<Type> undettypes = new ListBuffer<Type>();
 
-	    restvars = new ListBuffer<Type>();
-	    insttypes = new ListBuffer<Type>();
-	    ListBuffer<Type> insttypes1 = new ListBuffer<Type>();
-            // same as insttypes, except that UndetVars replace TypeVars.
-	    for (List<Type> l = undetvars; l.nonEmpty(); l = l.tail) {
-		UndetVar uv = (UndetVar) l.head;
+        for (Type t : undetvars) {
+            UndetVar uv = (UndetVar)t;
                 if (uv.inst.tag == BOT) {
                     restvars.append(uv.qtype);
 		    insttypes.append(uv.qtype);
-		    insttypes1.append(uv);
+                undettypes.append(uv);
 		    uv.inst = null;
 		} else {
 		    insttypes.append(uv.inst);
-		    insttypes1.append(uv.inst);
+                undettypes.append(uv.inst);
 		}
 	    }
-            checkWithinBounds(tvars, insttypes1.toList(), warn);
-	    restlen1 = restlen;
-	    restlen = restvars.length();
-	} while (restlen != 0 && restlen != restlen1);
+        checkWithinBounds(tvars, undettypes.toList(), warn);
 
-        if (restlen != 0) {
+        if (!restvars.isEmpty()) {
             // if there are uninstantiated variables,
             // quantify result type with them
-	    mt = new MethodType(
-		mt.argtypes, new ForAll(restvars.toList(), mt.restype),
+            mt = new MethodType(mt.argtypes,
+                                new ForAll(restvars.toList(), mt.restype),
 		mt.thrown, syms.methodClass);
 	}
                                     
2006-11-23
EVALUATION

Let's examine each "error":

List<SubType> lsub = makeSingletonList(new SubType());

According to JLS 3rd Ed. (15.12.2.2 Phase 1: Identify
Matching Arity Methods Applicable by Subtyping) inference
is started with the initial constraint SubType << T.
This infers the type SubType for T.  In JLS lingo, this
gives U1 = SubType and S1 = SubType.  In order for the
method to be applicable, A1 must be a subtype of
S1 which is trivially true (SubType is a subtype of itself).

However, the inferred type U1 must also be a subtype
of B1[R1=U1, ...] which translates to:
SubType must be a subtype of SelfType<SubType> which
it isn't.

The same analysis holds for
makeSingletonList(new SubType());

Next is
makeSingletonList2(new SubType());

Here the inference is simpler, the type inferred
for S is SubType, but T is under constrained, so
Object is inferred for T.  However, Object is not
a subtype of SelfType<Object> why the method
invocation fails.

Next is
lsup = makeSingletonList2(new SubType());

Here again, T is under constrained but there is an
expected type, so the type inferred for T should
be SuperType which is indeed a subtype of SelfType<SuperType>.

This is a bug.

Lastly, consider
lsub = makeSingletonList2(new SubType());

Again, T is under constrained so the expected type
is considered and SubType is inferred for T.  However,
SubType is not a subtype of SelfType<SubType>.

In summary, there is one bug here and it can be reduced to this:

abstract class Simple {
    interface A<T extends A<T>> {}
    interface B extends A<B> {}
    interface C extends B {}
    interface D<T> {}
    abstract <T extends A<T>, S extends T> D<T> m(S s);
    {
	C c = null;
	D<B> d = m(c);
    }
}

###@###.### 2005-06-12 09:05:58 GMT
                                     
2005-06-12
WORK AROUND

Work around for the reduced case:

D<B> d = this.<B,C>m(c);

Work around for the original example:

makeSingletonList(new SuperType());
List<SuperType> lsup = makeSingletonList(new SuperType());

// This can't be made to work, List<SuperType> is not
// a subtype of List<SubType>
List<SubType> lsub = TestGenerics.<SuperType>makeSingletonList(new SubType());

TestGenerics.<SuperType>makeSingletonList(new SubType());
TestGenerics.<SuperType,SubType>makeSingletonList2(new SubType());
lsup = TestGenerics.<SuperType,SubType>makeSingletonList2(new SubType());

// Can't work, see above
lsub = TestGenerics.<SuperType,SubType>makeSingletonList2(new SubType());

###@###.### 2005-06-12 09:05:59 GMT
                                     
2005-06-12



Hardware and Software, Engineered to Work Together