JDK-6357966 : parameters containing nested generic types avoid recursive calls
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 5.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2005-12-01
  • Updated: 2014-08-01
  • Resolved: 2014-08-01
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05) Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
The following piece of code contains an error, but logically seems to be correct.
###
import java.util.Map;

class GenericsTest {
    <A> void a(Map<? super A, ? super A> map) {
        a(map);
    }
}
###
Logically, the method call should be fine. There is a generic type A defining the interdependency between the map's key and value type. But the argument used to call the method cannot be used to call it another time. javac produces the following output:
###
[...]>javac GenericsTest.java
GenericsTest.java:6: <A>a(java.util.Map<? super A,? super A>) in GenericsTest cannot be applied to (java.util.Map<capture of ? super A,capture of ? super A>)
                a(map);
                ^
1 error
###

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Simply try to compile the following piece of code
###
import java.util.Map;

class GenericsTest {
    <A> void a(Map<? super A, ? super A> map) {
        a(map);
    }
}
###


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Logically, the method call should be fine. There is a generic type A defining the interdependency between the map's key and value type.
ACTUAL -
The argument used to call the method cannot be used to call it another time. javac produces the following output:
###
[...]>javac GenericsTest.java
GenericsTest.java:6: <A>a(java.util.Map<? super A,? super A>) in GenericsTest cannot be applied to (java.util.Map<capture of ? super A,capture of ? super A>)
                a(map);
                ^
1 error
###

ERROR MESSAGES/STACK TRACES THAT OCCUR :
GenericsTest.java:6: <A>a(java.util.Map<? super A,? super A>) in GenericsTest cannot be applied to (java.util.Map<capture of ? super A,capture of ? super A>)
                a(map);
                ^
1 error

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.Map;

class GenericsTest {
    <A> void a(Map<? super A, ? super A> map) {
        a(map);
    }
}
---------- END SOURCE ----------

Comments
1) I can't reproduce the bug in javac 6, 7, or 8. So whatever javac problems prompted it have been fixed. 2) Here's the specified inference behavior: The type of 'map' is 'Map<CAP1 super A, CAP2 super A>'. So (using JLS 3 notation) we have 'Map<CAP1, CAP2> << Map<? super a, ? super a>'. This leads to 'a <: CAP1' and 'a <: CAP2'. Since these are both upper bounds, 15.12.2.7 does not produce a resolution for 'a'. There is some confusion in JLS 3 about whether these upper bounds are preserved for use in 15.12.2.8. JLS 7 resolves this (see JDK-6640435). In 15.12.2.8, the resolution of 'a' is 'glb(CAP1, CAP2)'. glb is poorly specified in this case -- see JDK-8039222. Probably we would want it to produce A (the common lower bound of the two variables). JLS 8 respecifies inference, but doesn't fundamentally change any of this. 3) Alex's above analysis, which makes no use of capture, is still valid if the types are nested -- e.g., 'Class<Map<? super A, ? super A>> --> Class<? extends Map<? super a, ? super a>>'. In that case, I think the conclusion -- that the upper bound goes unused -- is just a variant of JDK-6640435.
01-08-2014

Closing as duplicate of JDK-8039222. The only remaining spec problems are captured there.
01-08-2014

EVALUATION Generic type inference should probably be enhanced (see 6488666).
01-12-2006

EVALUATION This is a very interesting limitation in type inference. The only type that is appropriate for the generic invocation of map is A itself. Thus, this.<A>a(map); works. Here is some explanation. Given the actual argument type Map<? super X, ? super X>, we want to infer what Y must be in the formal Map<? super Y, ? super Y>. 15.12.2.2 thus sets up inference with the constraint: Map<? super X, ? super X> << Map<? super Y, ? super Y> --- Actual --- << --- Formal --- - The formal has the form G<? super U, ...> where U involves Y - The actual has a supertype of the form G<? super V, ...> - Thus the inference recurses with the constraint X>>Y - This constraint has the form ...>>... - F=Y so the constraint Y<:X is implied - Given Y:<X, no type for Y is inferred by the process starting on page 463. The compiler is right to complain. Now, any use of '? extends A' changes things, because the algorithm is applied recursively not to X>>X but to X<<X. This causes the constraint X:>X to be implied (which is not the same as X<:X) and hence X to be inferred as Object. Hence all of these compile: <A> void a(Map<? extends A, ? extends A> map) { a(map); } <A> void a(Map<? extends A, ? super A> map) { a(map); } <A> void a(Map<? super A, ? extends A> map) { a(map); }
23-11-2006

WORK AROUND import java.util.Map; class GenericsTest { <A> void a(Map<? super A, ? super A> map) { this.<A>a(map); } }
04-12-2005

EVALUATION The compiler is following the specification, reassigning to JLS.
04-12-2005