JDK-8066974 : Compiler doesn't infer method's generic type information in lambda body
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8u11
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: os_x
  • CPU: x86
  • Submitted: 2014-08-03
  • Updated: 2016-04-27
  • Resolved: 2014-12-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u101Fixed 9 b44Fixed
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows x64
OSX 10.9.4

A DESCRIPTION OF THE PROBLEM :
The compiler does not infer the correct type arguments for a method invocation that appears in a lambda body. The full example is on stackoverflow here: http://stackoverflow.com/questions/24986279/java-8-generics-exceptions-compile-time-error-when-using-a-lambda-expression/25101439#25101439



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the following class with javac

import java.util.ArrayList;
import java.util.List;


public class JavaBugTest
{
    interface AbleToThrowException<E extends Exception>
    {
    }

    interface Parameter {
        public <E extends Exception> Object execute(AbleToThrowException<E> algo) throws E;
    }

    interface ThrowsRuntimeException extends AbleToThrowException<RuntimeException>
    {
    }

    static ThrowsRuntimeException foo;


    public static Object manualLambda(Parameter p)
    {
        return p.execute(foo);
    }

    public static void main(String[] args)
    {
        List<Parameter> params = new ArrayList<>();
        params.stream().map(p -> p.execute(foo)); // Gives a compile time error.
        params.stream().map(JavaBugTest::manualLambda); // Works fine.
    }

}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The class should compile with 'p.execute(foo)` inferring 'RuntimeException' as its type argument.
ACTUAL -
We get a compilation error that is the result of the compiler failing to infer the correct type information.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
[~/test]$ javac JavaBugTest.java 
JavaBugTest.java:30: error: unreported exception E; must be caught or declared to be thrown
        params.stream().map((Parameter p) -> p.execute(foo)); // Gives a compile time error.
                                                      ^
  where E is a type-variable:
    E extends Exception declared in method <E>execute(AbleToThrowException<E>)
1 error

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.ArrayList;
import java.util.List;


public class JavaBugTest
{
    interface AbleToThrowException<E extends Exception>
    {
    }

    interface Parameter {
        public <E extends Exception> Object execute(AbleToThrowException<E> algo) throws E;
    }

    interface ThrowsRuntimeException extends AbleToThrowException<RuntimeException>
    {
    }

    static ThrowsRuntimeException foo;


    public static Object manualLambda(Parameter p)
    {
        return p.execute(foo);
    }

    public static void main(String[] args)
    {
        List<Parameter> params = new ArrayList<>();
        params.stream().map(p -> p.execute(foo)); // Gives a compile time error.
        params.stream().map(JavaBugTest::manualLambda); // Works fine.
    }

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
You can explicitly provide the type argument

params.stream().map(p -> p.<RuntimeException>execute(foo)); // Gives a compile time error.


Comments
Tested this with JDK 8u25, 8u40 and 9ea and issue is reproducible.
09-12-2014

Simpler test case: class Test { static class Throwing<E extends Throwable> { } static class RuntimeThrowing extends Throwing<RuntimeException> { } interface Parameter { <E extends Throwable> Object m(Throwing<E> tw) throws E; } interface Mapper<R> { R m(Parameter p); } <Z> Z map(Mapper<Z> mz) { return null; } //<-- if this is not generic, everything works void test(RuntimeThrowing rt) { map(p->p.m(rt)); } }
09-12-2014

Inference listerers are not set when checking method types; as a result some method types in the AST might contain inference variables which led to spurious flow analysis failures.
09-12-2014