javac infers a type bound where none should exist

XMLWordPrintable

    • generic
    • generic

      From an email discussion with Philippe:

      > Looking at the following paragraph on top of page 453, I was wondering why
      > no capture conversion is applied to V when recursively applying inference
      > to V << U.
      > ...
      > Otherwise, if the constraint has the form A <<F
      > ...
      > If F = U[], where the type U involves Tj, then if A is an array type
      > V[], or a type variable with an upper bound that is an array type V[], where V
      > is a reference type, this algorithm is applied recursively to the constraint
      > V <<U.

      Capture conversion was applied to A back in the 15.12.2.2 and 15.12.2.3, where the type of the actual argument expressions is taken. By 6.5.6.1, this type is after capture conversion. An array type won't be capture-converted, but this turns out not to be a problem...keep reading.

      > If not applying capture conversion, then the following two programs fail
      > for different reasons, which feels counter intuitive to me.
      > (NOTE: these programs are intended to fail to compile, but what is
      > interesting is the error message revealing different inference results).
      >
      > The first program behaves as expected. Inference is fed with 2 captured
      > type, and yields a List<Comparator<? extends Object>>.
      >
      > import java.util.*;
      > public class X {
      > public static <T> Comparator<T> compound(Comparator<? super T> a,
      > Comparator<? super T> b,
      > Comparator<? super T>... rest) {
      > int j = asList2(a, b);
      > }
      > public static <E> List<E> asList2(E a, E b) {
      > return null;
      > }
      > }

      The constraints set up by overload resolution are:
        Comparator<X> << E // X,Y are fresh type variables
        Comparator<Y> << E
        (X's upper bound is Object and lower bound is T. Similarly for Y.)
      which reduce to
        E >: Comparator<X>
        E >: Comparator<Y>

      javac is following the spec literally:
        lci(Comparator<X>, Comparator<Y>) = Comparator<lcta(X,Y)>
        lcta(X,Y) = ? extends lub(X,Y) = ? extends Object

      (A better lci of Comparator<X> and Comparator<Y> - given the lower bounds of X and Y - is Comparator<? super T>. The JLS cannot currently infer this type...keep reading.)

      > The second program fails with a different error message resulting from the
      > fact that no capture occured on array type (fair), and thus a wildcard is
      > directly injected into the inferred constraints thanks to the JLS
      > paragraph I pointed above.
      >
      > import java.util.*;
      > public class X {
      > public static <T> Comparator<T> compound(Comparator<? super T> a,
      > Comparator<? super T> b,
      > Comparator<? super T>... rest) {
      > int i = asList(a, b, rest);
      > }
      > public static <E> List<E> asList(E a, E b, E... rest) {
      > return null;
      > }
      > }

      Capture conversion does not apply to Comparator<? super T>[], but this is not a problem: the third actual argument can cause this constraint:
        Comparator<? super T>[] << E[]
      reducing to
        Comparator<? super T> << E

      and the lci/lcta functions are perfectly capable of dealing with Comparator<? super T> directly:
        lci(Comparator<X>, Comparator<Y>, Comparator<? super T>)
        lci(lci(Comparator<X>, Comparator<Y>), Comparator<? super T>)
      = lci( Comparator<? extends Object>, Comparator<? super T>)
      = Comparator<?>

      javac obtains Comparator<? super T> here, which is strictly speaking a compiler bug. The Eclipse compiler obtains Comparator<?>.

      (Sidebar: It's obvious that E should be Comparator<? super T> in both programs. The problem is that we forget the 'super T'-ness of X and Y, so inference for the first program gets a bad result. Even capture-converting the component type of the array type would not help, because we'd forget its T bound too. The right thing to do is improve lcta in the JLS to use the known bounds of type variables, whether they're captured or not.)

            Assignee:
            Dan Smith
            Reporter:
            Alex Buckley
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: