JDK-6707034 : javac accepts method invocation with actuals that are not subtypes of the formal
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: linux,solaris_8
  • CPU: x86
  • Submitted: 2008-05-26
  • Updated: 2011-02-16
  • Resolved: 2009-06-18
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)


A DESCRIPTION OF THE PROBLEM :
javac rejects the following apparently incorrect snippet of code

  <T> void sort(T[] a, Comparator<? super T> c) { return null; }
  Enum[] ea = null;
  Comparator<Enum<?>> comp = null;
  { sort(ea, comp); }

I tried the following to see which way the subtype relations run between Enum and Enum<?>

  List<? extends Enum<?>> t1 = null;
  List<? extends Enum> t2 = null;

  // { t1 = t2; } // error here indicates Enum is not a subtype of Enum<?>
  { t2 = t1; } // allowed, showing Enum<?> is a subtype of Enum

javac appears to infer Enum in the call to sort(...), but that should lead to an error based on the subtyping relations.  javac agrees, rejecting

  { this.<Enum>sort(ea, comp); }

The fact that javac does not check the call after inferring the type argument is a bug.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the enclosed test case, which should fail

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Bound.java:13: <T>sort(T[],java.util.Comparator<? super T>) in Bound cannot be applied to (java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>)
  { this.<Enum>sort(ea, comp); } // yet javac accepts this code, inferring T=Enum
    ^
1 error

ACTUAL -
compile without error

ERROR MESSAGES/STACK TRACES THAT OCCUR :
error messages missing

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.*;
class Bound {
  List<? extends Enum<?>> t1 = null;
  List<? extends Enum> t2 = null;

  // { t1 = t2; } // error here indicates Enum is not a subtype of Enum<?>
  { t2 = t1; }

  <T> T sort(T[] a, Comparator<? super T> c) { return null; }
  Enum[] ea = null;
  Comparator<Enum<?>> comp = null;

  { this.sort(ea, comp); } // yet javac accepts this code, inferring T=Enum
                           // suggesting Enum<?> is a supertype of Enum.
}

---------- END SOURCE ----------

Comments
EVALUATION As expected this is not reproducible after 6638712 has been fixed
18-06-2009

SUGGESTED FIX There are two solutions to the problem described in this CR: 1) Forcing extra sanitty checks when assigning upper/lower bounds of UndetVars. E.g. an undet var cannot be assigned an upper bound that is not a supertype of all the undetvar's lower bounds. 2) Implementing theglobal check as described in the JLS 15.12.2.2 Since we are experiencing a lot of bugs related to this issue, I think that 1 alone is not sufficient when dealing with the general problem of recovering from a bad inference guess. For example, fixing javac as described in 1) cannot provide any relief in situations where javac is inferring constraints from actuals that are inconsistent with respect to the type that gets inferred from the return-type. The proper solution is then 2). There is a problem, though, as implementing JLS will cause a regression failure. The failure is associated with CR 6476073. The fix for this CR hinges on javac not performing the check described in 15.12.2.2, which means that fixing this CR would result in invalidating that fix. Actually the code snippet in 6476073 shouldn't compile, accordingly to the JLS. A good new is that implementing propagation of constraints from one stage (actuals) of type-inference to another (return type), it is possible to compile this code without problems (this technique requires the JLS extension described in 6640435). Thus, the fix for this bug should be applied toghether with the fixes for 6638712 and 6650759, not to break any existing regression test.
28-05-2008

EVALUATION The problem is similar to the one described in CR 6638712. due to a missing subtyping test after type-inference has been applied javac might select a method that is *not* applicable by subtyping (as described in JLS 15.12.2.2). In this case javac infers T=Enum, which makes the signature of the method unapplicable to the specified actual arguments. This specific problem is caused by the fact that UndetVars generated by javac during type inference are being assigned non-consistent upper/ower bounds. UndetVars are the entities that javac generates in order to keep track of subtyping constraints when type-inference is apllied. In this case, since the generic method declares just one type variable, javac replaces this type-variable with an undet var which we might refer to as T? (note the question mark). The signature of the method becomes: <T?> T sort(T?[] a, Comparator<? super T?> c) In order to derive constraints on the undet var, javac executes a subtyping test for each actual/formal pair of a given invocation site. In this case the following tests are generated: 1) Enum[] <: T?[] 2) Comparator<Enum<?>> <: Comparator<? super T?> The underlying idea is that javac can derive upper/lower bounds on undet vars as a side-effect of the subtyping test execution. Here's how: 1) Enum[] <: T?[] Enum <: T? this is true if the lower bound of T? is Enum --> javac store this info in the 'lobounds' field of the undet var T? 2) Comparator<Enum<?>> <: Comparator<? super T?> ? super T? <= Enum<?> T? <: Enum<?> this is true if the upper bound of T? is Enum<?> --> javac then stores this info in the 'hibounds' field of the undetvar T? After having executed both tests, we have the following erroneous situation: *) upper bound of T? is Enum<?> --> T? <: Enum<?> *) lower bound of T? is Enum --> Enum <: T? This is erroneous since, for transitivity of subtyping you also have that Enum <: Enum<?> which is clearly wrong. However, javac does not detect the error (and I think that JLS do the same) since inference based on actuals only takes into consideration lower bounds. Since here we have a lower bound for T?, javac then instantiate T? to Enum. The JLS have, however, a mechanism for recovering from such erroneous guesses; JLS 15.12.2.2 (applicability by subtyping) mandates an extra, global subtyping test between actuals and (inferred) formals. In this case we would have the following tests: a) Enum[] <: Enum[] - OK b) Comparator<Enum<?>> <: Comparator<? super Enum> - KO!!! Since b) fails, the instantiated method is *not* applicable. Unfortunately enough, javac does not implement this kind of global, post-inference check (for the reasons described in 6638712). As such, javac cannot detect the error.
26-05-2008