8182047: javac compile error on type-parameter-exceptions in lambda expressions
authorvromero
Fri, 30 Jun 2017 05:47:35 -0700
changeset 45756 67f4f8f4d34a
parent 45755 48b1fb3acdab
child 45757 1017cd5d6506
8182047: javac compile error on type-parameter-exceptions in lambda expressions Reviewed-by: mcimadamore
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/test/tools/javac/T8182047/CorrectGenerationOfExConstraintsTest.java
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Thu Jun 29 14:32:39 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java	Fri Jun 30 05:47:35 2017 -0700
@@ -2106,7 +2106,7 @@
             Type out = erasure.visit(t, recurse);
             return out;
         }
-        }
+    }
     // where
         private TypeMapping<Boolean> erasure = new StructuralTypeMapping<Boolean>() {
             private Type combineMetadata(final Type s,
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Jun 29 14:32:39 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 30 05:47:35 2017 -0700
@@ -2515,14 +2515,9 @@
                 //add thrown types as bounds to the thrown types free variables if needed:
                 if (resultInfo.checkContext.inferenceContext().free(lambdaType.getThrownTypes())) {
                     List<Type> inferredThrownTypes = flow.analyzeLambdaThrownTypes(env, that, make);
-                    List<Type> thrownTypes = resultInfo.checkContext.inferenceContext().asUndetVars(lambdaType.getThrownTypes());
-
-                    chk.unhandled(inferredThrownTypes, thrownTypes);
-
-                    //18.2.5: "In addition, for all j (1 <= j <= n), the constraint reduces to the bound throws Ej"
-                    thrownTypes.stream()
-                            .filter(t -> t.hasTag(UNDETVAR))
-                            .forEach(t -> ((UndetVar)t).setThrow());
+                    if(!checkExConstraints(inferredThrownTypes, lambdaType.getThrownTypes(), resultInfo.checkContext.inferenceContext())) {
+                        log.error(that, Errors.IncompatibleThrownTypesInMref(lambdaType.getThrownTypes()));
+                    }
                 }
 
                 checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
@@ -3111,15 +3106,70 @@
         }
 
         if (!speculativeAttr) {
-            List<Type> thrownTypes = inferenceContext.asUndetVars(descriptor.getThrownTypes());
-            if (chk.unhandled(refType.getThrownTypes(), thrownTypes).nonEmpty()) {
+            if (!checkExConstraints(refType.getThrownTypes(), descriptor.getThrownTypes(), inferenceContext)) {
                 log.error(tree, Errors.IncompatibleThrownTypesInMref(refType.getThrownTypes()));
             }
-            //18.2.5: "In addition, for all j (1 <= j <= n), the constraint reduces to the bound throws Ej"
-            thrownTypes.stream()
-                    .filter(t -> t.hasTag(UNDETVAR))
-                    .forEach(t -> ((UndetVar)t).setThrow());
-        }
+        }
+    }
+
+    boolean checkExConstraints(
+            List<Type> thrownByFuncExpr,
+            List<Type> thrownAtFuncType,
+            InferenceContext inferenceContext) {
+        /** 18.2.5: Otherwise, let E1, ..., En be the types in the function type's throws clause that
+         *  are not proper types
+         */
+        List<Type> nonProperList = thrownAtFuncType.stream()
+                .filter(e -> inferenceContext.free(e)).collect(List.collector());
+        List<Type> properList = thrownAtFuncType.diff(nonProperList);
+
+        /** Let X1,...,Xm be the checked exception types that the lambda body can throw or
+         *  in the throws clause of the invocation type of the method reference's compile-time
+         *  declaration
+         */
+        List<Type> checkedList = thrownByFuncExpr.stream()
+                .filter(e -> chk.isChecked(e)).collect(List.collector());
+
+        /** If n = 0 (the function type's throws clause consists only of proper types), then
+         *  if there exists some i (1 <= i <= m) such that Xi is not a subtype of any proper type
+         *  in the throws clause, the constraint reduces to false; otherwise, the constraint
+         *  reduces to true
+         */
+        ListBuffer<Type> uncaughtByProperTypes = new ListBuffer<>();
+        for (Type checked : checkedList) {
+            boolean isSubtype = false;
+            for (Type proper : properList) {
+                if (types.isSubtype(checked, proper)) {
+                    isSubtype = true;
+                    break;
+                }
+            }
+            if (!isSubtype) {
+                uncaughtByProperTypes.add(checked);
+            }
+        }
+
+        if (nonProperList.isEmpty() && !uncaughtByProperTypes.isEmpty()) {
+            return false;
+        }
+
+        /** If n > 0, the constraint reduces to a set of subtyping constraints:
+         *  for all i (1 <= i <= m), if Xi is not a subtype of any proper type in the
+         *  throws clause, then the constraints include, for all j (1 <= j <= n), <Xi <: Ej>
+         */
+        List<Type> nonProperAsUndet = inferenceContext.asUndetVars(nonProperList);
+        uncaughtByProperTypes.forEach(checkedEx -> {
+            nonProperAsUndet.forEach(nonProper -> {
+                types.isSubtype(checkedEx, nonProper);
+            });
+        });
+
+        /** In addition, for all j (1 <= j <= n), the constraint reduces to the bound throws Ej
+         */
+        nonProperAsUndet.stream()
+                .filter(t -> t.hasTag(UNDETVAR))
+                .forEach(t -> ((UndetVar)t).setThrow());
+        return true;
     }
 
     /**
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Thu Jun 29 14:32:39 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Fri Jun 30 05:47:35 2017 -0700
@@ -1568,6 +1568,10 @@
             exc.hasTag(BOT);
     }
 
+    boolean isChecked(Type exc) {
+        return !isUnchecked(exc);
+    }
+
     /** Same, but handling completion failures.
      */
     boolean isUnchecked(DiagnosticPosition pos, Type exc) {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Jun 29 14:32:39 2017 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Jun 30 05:47:35 2017 -0700
@@ -808,7 +808,7 @@
 
 # 0: list of type
 compiler.err.incompatible.thrown.types.in.mref=\
-    incompatible thrown types {0} in method reference
+    incompatible thrown types {0} in functional expression
 
 compiler.misc.incompatible.arg.types.in.lambda=\
     incompatible parameter types in lambda expression
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T8182047/CorrectGenerationOfExConstraintsTest.java	Fri Jun 30 05:47:35 2017 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8182047
+ * @summary javac compile error on type-parameter-exceptions in lambda expressions
+ * @compile CorrectGenerationOfExConstraintsTest.java
+ */
+
+public class CorrectGenerationOfExConstraintsTest {
+    public static class MyExBase extends Exception {
+        private static final long serialVersionUID = 1L;
+    }
+
+    public static class MyEx1 extends MyExBase {
+        private static final long serialVersionUID = 1L;
+    }
+
+    public static class MyEx2 extends MyExBase {
+        private static final long serialVersionUID = 1L;
+    }
+
+    public interface MyLambdaIF1 <E extends Exception> {
+        void lambdaFun() throws E, MyEx2;
+    }
+
+    public interface MyLambdaIF2 <E extends Exception> {
+        void lambdaFun() throws MyEx2, E;
+    }
+
+    public <E extends Exception> void fun1(MyLambdaIF1<E> myLambda) throws E, MyEx2 {
+        myLambda.lambdaFun();
+    }
+
+    public <E extends Exception> void fun2(MyLambdaIF2<E> myLambda) throws E, MyEx2 {
+        myLambda.lambdaFun();
+    }
+
+    public void useIt1() throws MyEx1, MyEx2 {
+        fun1(this::lambda);
+    }
+
+    public void useIt1a() throws MyExBase {
+        fun1(this::lambda);
+    }
+
+    public void useIt2() throws MyEx1, MyEx2 {
+        fun2(this::lambda);
+    }
+
+    public void lambda() throws MyEx1, MyEx2 {
+        if (Math.random() > 0.5)
+        throw new MyEx2();
+        throw new MyEx1();
+    }
+}