8182047: javac compile error on type-parameter-exceptions in lambda expressions
Reviewed-by: mcimadamore
This commit is contained in:
parent
c00b47cf6e
commit
b081062f1c
@ -2106,7 +2106,7 @@ public class Types {
|
||||
Type out = erasure.visit(t, recurse);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
// where
|
||||
private TypeMapping<Boolean> erasure = new StructuralTypeMapping<Boolean>() {
|
||||
private Type combineMetadata(final Type s,
|
||||
|
@ -2515,14 +2515,9 @@ public class Attr extends JCTree.Visitor {
|
||||
//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,17 +3106,72 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1568,6 +1568,10 @@ public class Check {
|
||||
exc.hasTag(BOT);
|
||||
}
|
||||
|
||||
boolean isChecked(Type exc) {
|
||||
return !isUnchecked(exc);
|
||||
}
|
||||
|
||||
/** Same, but handling completion failures.
|
||||
*/
|
||||
boolean isUnchecked(DiagnosticPosition pos, Type exc) {
|
||||
|
@ -808,7 +808,7 @@ compiler.err.lambda.body.neither.value.nor.void.compatible=\
|
||||
|
||||
# 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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user