United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6369608 inference: Inferred types not substituted in bounds of unconstrained type variables
JDK-6369608 : inference: Inferred types not substituted in bounds of unconstrained type variables

Details
Type:
Bug
Submit Date:
2006-01-06
Status:
Closed
Updated Date:
2014-02-26
Project Name:
JDK
Resolved Date:
2011-07-21
Component:
specification
OS:
generic
Sub-Component:
language
CPU:
generic
Priority:
P4
Resolution:
Fixed
Affected Versions:
5.0
Fixed Versions:
7 (rc)

Related Reports
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
See https://bugs.eclipse.org/bugs/show_bug.cgi?id=121369

Apparently, the specification requires a compiler must infer
List<T>, not List<Object> for U in a call to Foo.foo() in this
example:

class Foo {
  static <T, U extends java.util.List<T>> void foo() {
    return;
  }
}
A further issue with uninferred type variables:

Can you explain how javac is accepting the following code if not doing what I am suggesting below?
  public <E, S extends java.util.Collection<E>> S test1(S param){ return null; }
  public void test2() { test1(new Vector<String>()); }

15.12.2.7 determines that: S = Vector<String>.
Then 15.12.2.8 kicks in to infer unconstrained E.
  * S >> R' means:   Object >> Vector<String>
  * Bi[T1 = B(T1) ... Tn = B(Tn)] >> Ti   means: Collection<E> >> S

Stepping back for an instant; what are we doing here? S already got inferred, so why would it try to infer it over again? Where is the fact that S = Vector<String> appearing then?

I think the spec should rather say
  Bi[T1 = B(T1) ... Tn = B(Tn)] >> B(Ti)   (note 'B(Ti)' instead of 'Ti')
but then the algorithm described in 15.12.2.7 wouldn't yield anything as:
  Collection<E> >> Vector<String>
doesn't yield any data (as no formal argument on right side).

This is why I would suggest injecting:
  Vector<String> << Collection<E>
If you don't do this, how can you infer E to be String instead of Object?
(E=Object would invalidate the bound contract for S, as Vector<String> is not <: Collection<Object>.)

If you change the line:
  test1(new Vector<String>());
into:
  String s = (String) test1(new Vector<String>());
you can see that javac did infer S to be Vector<String> as per error message tells.

To be clear my spec suggestion is that instead of 15.12.2.8 only saying:
  * additional constraints Bi[T1 = B(T1) ... Tn = B(Tn)] >> Ti,
    where Bi is the declared bound of Ti,
It would also hint from bounds of already inferred type variables, since
these may contain references to underconstrained variables.
  * additional constraints B(Ti) << Bi[T1 = B(T1) ... Tn = B(Tn)],
    where Bi is the declared bound of Ti,

                                    

Comments
EVALUATION

There's a recurrent pattern in the JDK that would be invalidated by inferring T as List<Object>:

enum MyEnum {}

class Test {
   Class<?> returnClass = null;

   void test() {
       Object value = Enum.valueOf((Class)Test.class, "");
   }
} 

Note that the signature of Enum.valueOf is:
<T extends Enum<T>> T valueOf(Class<T>, String) 

In this case 15.12.2.7 fails (as the first argument type is raw) - no type is inferred from actual arguments; 15.12.2.8 derives the following set of constraints for T:

T <: Enum<T> (from declared bound)
T <: Object (from expected return type)

This lead to T = glb(Enum<T>, Object) = Enum<T>. If we replace T by Object, then we end up with Enum<Object> which no longer respect the constraints on T (inferred type for T=X should be a subtype of Enum<X>). As a result this method call cannot be type-checked.

One way out is to replace any F-bounded type variable on the RHS with ? thus giving:
T <: Enum<?>
T <: Object

Another way out of this is to infer T = #1, where ub(#1) = Enum<#1>

This has the advantage of (i) not being a straight type-variable trying to escape its context and (ii) respecting the bound constraints; in fact we have that

#1 <: [T:=#1] Enum<T> = Enum<#1>
ub(#1) = Enum<#1> <: Enum<#1> -> OK!
                                     
2007-06-20
EVALUATION

Given:
  static <T, U extends java.util.List<T>> void foo() { return; }
  Foo.foo();
the JLS does infer U to be List<T>, and then T to be Object. As Philippe says, U should be List<Object>. 15.12.2.8 should say:

"Any remaining type variable Tr that has not yet been inferred is then inferred to have type Object. If a previously inferred type Tp uses Tr, then Tp is now inferred to be Tp[Tr=Object]."

Sidebar: Philippe seems to imply that the example above gives a different inference result than:
  static <T, U extends java.util.List<T>> U foo() { return null; }
  String s = (String) Foo.foo();
but javac 1.6 infers Object for both T and U in both examples. Here, we compute U=glb(String, List<T>), so Object is the only possibility.

In the F-bounded case of:
  static <T, U extends java.util.List<U>> U foo() { return null; }
  String s = (String) Foo.foo();
we have U<:String and U<:List<U>, and need to compute U=glb(String,List<U>), which is ill-formed. The worst case would be:
  static <T extends java.util.List<U>, U extends java.util.List<T>> ...
as we would have computations which rely on each other:
  T=glb(..., List<U>)
  U=glb(..., List<T>)

Ideally, T and U would both become List<Object>, or Object at worst. I propose this additional modification to 15.12.2.8:

"Any equality constraints are resolved, and then, for each remaining constraint of the form Ti<:Uk, the argument Ti is inferred to be glb(U'1..U'k) where U'i is Ui with any uninferred Tj (j<>i) replaced with Object."
                                     
2007-06-19



Hardware and Software, Engineered to Work Together