8182047: javac compile error on type-parameter-exceptions in lambda expressions

Reviewed-by: mcimadamore
This commit is contained in:
Vicente Romero 2017-06-30 05:47:35 -07:00
parent c00b47cf6e
commit b081062f1c
5 changed files with 147 additions and 16 deletions

View File

@ -2106,7 +2106,7 @@ public class Types {
Type out = erasure.visit(t, recurse); Type out = erasure.visit(t, recurse);
return out; return out;
} }
} }
// where // where
private TypeMapping<Boolean> erasure = new StructuralTypeMapping<Boolean>() { private TypeMapping<Boolean> erasure = new StructuralTypeMapping<Boolean>() {
private Type combineMetadata(final Type s, private Type combineMetadata(final Type s,

View File

@ -2515,14 +2515,9 @@ public class Attr extends JCTree.Visitor {
//add thrown types as bounds to the thrown types free variables if needed: //add thrown types as bounds to the thrown types free variables if needed:
if (resultInfo.checkContext.inferenceContext().free(lambdaType.getThrownTypes())) { if (resultInfo.checkContext.inferenceContext().free(lambdaType.getThrownTypes())) {
List<Type> inferredThrownTypes = flow.analyzeLambdaThrownTypes(env, that, make); List<Type> inferredThrownTypes = flow.analyzeLambdaThrownTypes(env, that, make);
List<Type> thrownTypes = resultInfo.checkContext.inferenceContext().asUndetVars(lambdaType.getThrownTypes()); if(!checkExConstraints(inferredThrownTypes, lambdaType.getThrownTypes(), resultInfo.checkContext.inferenceContext())) {
log.error(that, Errors.IncompatibleThrownTypesInMref(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());
} }
checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget); checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
@ -3111,17 +3106,72 @@ public class Attr extends JCTree.Visitor {
} }
if (!speculativeAttr) { if (!speculativeAttr) {
List<Type> thrownTypes = inferenceContext.asUndetVars(descriptor.getThrownTypes()); if (!checkExConstraints(refType.getThrownTypes(), descriptor.getThrownTypes(), inferenceContext)) {
if (chk.unhandled(refType.getThrownTypes(), thrownTypes).nonEmpty()) {
log.error(tree, Errors.IncompatibleThrownTypesInMref(refType.getThrownTypes())); 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;
}
/** /**
* Set functional type info on the underlying AST. Note: as the target descriptor * Set functional type info on the underlying AST. Note: as the target descriptor
* might contain inference variables, we might need to register an hook in the * might contain inference variables, we might need to register an hook in the

View File

@ -1568,6 +1568,10 @@ public class Check {
exc.hasTag(BOT); exc.hasTag(BOT);
} }
boolean isChecked(Type exc) {
return !isUnchecked(exc);
}
/** Same, but handling completion failures. /** Same, but handling completion failures.
*/ */
boolean isUnchecked(DiagnosticPosition pos, Type exc) { boolean isUnchecked(DiagnosticPosition pos, Type exc) {

View File

@ -808,7 +808,7 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=\
# 0: list of type # 0: list of type
compiler.err.incompatible.thrown.types.in.mref=\ 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=\ compiler.misc.incompatible.arg.types.in.lambda=\
incompatible parameter types in lambda expression incompatible parameter types in lambda expression

View File

@ -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();
}
}