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

javac is not correctly filtering non-members methods to obtain the function descriptor

    Details

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

      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 :
      Mac OS X 10.9.3

      A DESCRIPTION OF THE PROBLEM :
      Consider this snippet of java 8 code:

      public class Generics {
        public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
          return callable.call();
        }
        public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
          return callable.call();
        }
        public static void main(String[] args) throws Exception {
          f(() -> 1);
          g(() -> 1);
        }
      }

      interface Callable<V> {
        V call() throws Exception;
      }

      interface CheckedCallable1<V, E extends Exception> {
        V call() throws E;
      }

      interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
        @Override V call() throws E;
      }

      The lambda at the call to f compiles fine, whereas the lambda at the call to g does not compile, but rather gives this compile error:

      Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
        overridden method does not throw java.lang.Exception

      I believe this is a compiler bug, in particular since the identical code with Exception replaced with IOException compiles fine.

      Additional details are at http://stackoverflow.com/questions/24199148/lambdas-and-functional-interfaces-with-generic-throw-clauses

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Compile the following java code:

      public class Generics {
        public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
          return callable.call();
        }
        public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
          return callable.call();
        }
        public static void main(String[] args) throws Exception {
          f(() -> 1);
          g(() -> 1);
        }
      }

      interface Callable<V> {
        V call() throws Exception;
      }

      interface CheckedCallable1<V, E extends Exception> {
        V call() throws E;
      }

      interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
        @Override V call() throws E;
      }

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      I expect this code to compile.
      ACTUAL -
      The call to g results in the following compile error:

      Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
        overridden method does not throw java.lang.Exception

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      Error:(10, 7) java: call() in <anonymous Generics$> cannot implement call() in CheckedCallable2
        overridden method does not throw java.lang.Exception

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public class Generics {
        public static <V, E extends Exception> V f(CheckedCallable1<V, E> callable) throws E {
          return callable.call();
        }
        public static <V, E extends Exception> V g(CheckedCallable2<V, E> callable) throws E {
          return callable.call();
        }
        public static void main(String[] args) throws Exception {
          f(() -> 1);
          g(() -> 1);
        }
      }

      interface Callable<V> {
        V call() throws Exception;
      }

      interface CheckedCallable1<V, E extends Exception> {
        V call() throws E;
      }

      interface CheckedCallable2<V, E extends Exception> extends Callable<V> {
        @Override V call() throws E;
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      1) Don't extend a functional interface without a generic throws clause in a functional interface with a generic throws clause.

      or

      2) Use a subclass of Exception on the functional interface without generic throws clause.

        Issue Links

          Activity

          Hide
          vromero Vicente Arturo Romero Zaldivar added a comment -
          reduced test case:

          interface Callable<V> {
              V callFail(String s) throws Exception;
          }

          interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> {
              @Override
              V callFail(String s) throws Efail;
          }

          Related spec:

          At 9.9 Function types it says:

          ....
          • throws clause:
          The function type's throws clause is derived from the throws clauses of the
          methods in M. If the function type is generic, these clauses are first adapted to
          the type parameters of the function type (§8.4.4). If the function type is not
          generic but at least one method in M is generic, these clauses are first erased.

          in this case M = {CheckedCallableFail::callFail} and there should be no erasure as the function type is not generic but there are no generic methods in M
          Show
          vromero Vicente Arturo Romero Zaldivar added a comment - reduced test case: interface Callable<V> {     V callFail(String s) throws Exception; } interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> {     @Override     V callFail(String s) throws Efail; } Related spec: At 9.9 Function types it says: .... • throws clause: The function type's throws clause is derived from the throws clauses of the methods in M. If the function type is generic, these clauses are first adapted to the type parameters of the function type (§8.4.4). If the function type is not generic but at least one method in M is generic, these clauses are first erased. in this case M = {CheckedCallableFail::callFail} and there should be no erasure as the function type is not generic but there are no generic methods in M
          Hide
          vromero Vicente Arturo Romero Zaldivar added a comment -
          this bug was showing two problems in javac:
          - the one I commented in the comment above, which should be addressed as a separate issue.
          - a problem determining the right members when determining the function descriptor

          The second problem is more important and it's the one to be addressed first. Reusing this simpler code:


          interface Callable<V> {
              V callFail(String s) throws Exception;
          }

          interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> {
              @Override
              V callFail(String s) throws Efail;
          }


          For this specific test case it implies that when determining the function descriptor for interface CheckedCallableFail, javac considered as members of the interface the method CheckedCallableFail::callFail and method Callable::callFail, when only CheckedCallableFail::callFail is a member of the interface as it's a subsignature of the method defined in Callable
          Show
          vromero Vicente Arturo Romero Zaldivar added a comment - this bug was showing two problems in javac: - the one I commented in the comment above, which should be addressed as a separate issue. - a problem determining the right members when determining the function descriptor The second problem is more important and it's the one to be addressed first. Reusing this simpler code: interface Callable<V> {     V callFail(String s) throws Exception; } interface CheckedCallableFail<V, Efail extends Exception> extends Callable<V> {     @Override     V callFail(String s) throws Efail; } For this specific test case it implies that when determining the function descriptor for interface CheckedCallableFail, javac considered as members of the interface the method CheckedCallableFail::callFail and method Callable::callFail, when only CheckedCallableFail::callFail is a member of the interface as it's a subsignature of the method defined in Callable
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/dev/langtools/rev/c10b810f0685
          User: vromero
          Date: 2016-08-24 19:37:04 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/dev/langtools/rev/c10b810f0685 User: vromero Date: 2016-08-24 19:37:04 +0000
          Hide
          hgupdate HG Updates added a comment -
          URL: http://hg.openjdk.java.net/jdk9/jdk9/langtools/rev/c10b810f0685
          User: lana
          Date: 2016-08-31 20:17:47 +0000
          Show
          hgupdate HG Updates added a comment - URL: http://hg.openjdk.java.net/jdk9/jdk9/langtools/rev/c10b810f0685 User: lana Date: 2016-08-31 20:17:47 +0000

            People

            • Assignee:
              vromero Vicente Arturo Romero Zaldivar
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved: