Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8066974

Compiler doesn't infer method's generic type information in lambda body

    Details

    • Type: Bug
    • Status: Closed
    • Priority: P4
    • Resolution: Fixed
    • Affects Version/s: 8u11
    • Fix Version/s: 9
    • Component/s: tools
    • Subcomponent:
    • Resolved In Build:
      b44
    • CPU:
      x86
    • OS:
      os_x
    • Verification:
      Verified

      Backports

        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.

          Issue Links

            Activity

            Hide
            pardesha Pardeep Sharma added a comment -
            Tested this with JDK 8u25, 8u40 and 9ea and issue is reproducible.
            Show
            pardesha Pardeep Sharma added a comment - Tested this with JDK 8u25, 8u40 and 9ea and issue is reproducible.
            Hide
            mcimadamore Maurizio Cimadamore added a comment -
            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.
            Show
            mcimadamore Maurizio Cimadamore added a comment - 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.
            Hide
            mcimadamore Maurizio Cimadamore added a comment -
            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));
                }
            }
            Show
            mcimadamore Maurizio Cimadamore added a comment - 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));     } }
            Hide
            hgupdate HG Updates added a comment -
            URL: http://hg.openjdk.java.net/jdk9/dev/langtools/rev/c402b2856ed1
            User: mcimadamore
            Date: 2014-12-18 13:27:17 +0000
            Show
            hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/dev/langtools/rev/c402b2856ed1 User: mcimadamore Date: 2014-12-18 13:27:17 +0000
            Hide
            hgupdate HG Updates added a comment -
            URL: http://hg.openjdk.java.net/jdk9/jdk9/langtools/rev/c402b2856ed1
            User: lana
            Date: 2014-12-23 22:27:35 +0000
            Show
            hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/langtools/rev/c402b2856ed1 User: lana Date: 2014-12-23 22:27:35 +0000

              People

              • Assignee:
                mcimadamore Maurizio Cimadamore
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                6 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: