8182047: javac compile error on type-parameter-exceptions in lambda expressions
Reviewed-by: mcimadamore
--- 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();
+ }
+}