# HG changeset patch # User vromero # Date 1498826855 25200 # Node ID 67f4f8f4d34a7c06ffe056eb4fa9a1c6aeda54db # Parent 48b1fb3acdab03ba24b33d112c753dce89bb36ab 8182047: javac compile error on type-parameter-exceptions in lambda expressions Reviewed-by: mcimadamore diff -r 48b1fb3acdab -r 67f4f8f4d34a langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.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 erasure = new StructuralTypeMapping() { private Type combineMetadata(final Type s, diff -r 48b1fb3acdab -r 67f4f8f4d34a langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- 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 inferredThrownTypes = flow.analyzeLambdaThrownTypes(env, that, make); - List 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 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 thrownByFuncExpr, + List 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 nonProperList = thrownAtFuncType.stream() + .filter(e -> inferenceContext.free(e)).collect(List.collector()); + List 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 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 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), + */ + List 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; } /** diff -r 48b1fb3acdab -r 67f4f8f4d34a langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java --- 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) { diff -r 48b1fb3acdab -r 67f4f8f4d34a langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties --- 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 diff -r 48b1fb3acdab -r 67f4f8f4d34a langtools/test/tools/javac/T8182047/CorrectGenerationOfExConstraintsTest.java --- /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 { + void lambdaFun() throws E, MyEx2; + } + + public interface MyLambdaIF2 { + void lambdaFun() throws MyEx2, E; + } + + public void fun1(MyLambdaIF1 myLambda) throws E, MyEx2 { + myLambda.lambdaFun(); + } + + public void fun2(MyLambdaIF2 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(); + } +}