Search code examples
javaeclipsegenericsjava-8javac

Lower-bounded wild card causes trouble in javac, but not Eclipse


This piece of code compiles in Eclipse but not in javac:

import java.util.function.Consumer;

public class Test {
    public static final void m1(Consumer<?> c) {
        m2(c);
    }
    private static final <T> void m2(Consumer<? super T> c) {
    }
}

javac output:

C:\Users\lukas\workspace>javac -version
javac 1.8.0_92

C:\Users\lukas\workspace>javac Test.java
Test.java:5: error: method m2 in class Test cannot be applied to given types;
        m2(c);
        ^
  required: Consumer<? super T>
  found: Consumer<CAP#1>
  reason: cannot infer type-variable(s) T
    (argument mismatch; Consumer<CAP#1> cannot be converted to Consumer<? super T>)
  where T is a type-variable:
    T extends Object declared in method <T>m2(Consumer<? super T>)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error
----------------------------------------------------------------

Which compiler is wrong and why? (Eclipse bug report and parts of discussion here)


Solution

  • This code is legal wrt JLS 8. javac version 8 and earlier had several bugs in how they handle wildcards and captures. Starting with version 9 (early access, I tried version ea-113 and newer) also javac accepts this code.

    To understand how a compiler analyzes this according to JLS, it is essential to tell apart what are wildcard captures, type variables, inference variables and such.

    The type of c is Consumer<capture#1-of ?> (javac would write Consumer<CAP#1>). This type is unknown, but fixed.

    The parameter of m2 has type Consumer<? super T>, where T is a type variable to be instantiated by type inference.

    During type inference an inference variable, denoted by ecj as T#0, is used to represent T.

    Type inference consists in determining, whether T#0 can be instantiated to any type without violating any given type constraints. In this particular case we start with this contraint:

    ⟨c → Consumer<? super T#0>⟩

    Which is stepwise reduced (by applying JLS 18.2):

    ⟨Consumer<capture#1-of ?> → Consumer<? super T#0>⟩

    ⟨capture#1-of ? <= ? super T#0⟩

    ⟨T#0 <: capture#1-of ?⟩

    T#0 <: capture#1-of ?

    The last line is a "type bound" and reduction is done. Since no further constraints are involved, resolution trivially instantiates T#0 to the type capture#1-of ?.

    By these steps, type inference has proven that m2 is applicable for this particular invocation. qed.

    Intuitively, the shown solution tells us: whatever type the capture may represent, if T is set to represent the exact same type, no type constraints are violated. This is possible, because the capture is fixed before starting type inference.