8019521: Enhanced rethrow disabled in lambdas
Summary: Fixing effectively final detection inside lambdas, small cleanup related to thrown types detection in lambdas
Reviewed-by: mcimadamore, jjg
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 09 17:36:23 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java Mon Sep 09 23:13:45 2013 +0200
@@ -2418,9 +2418,17 @@
preFlow(that);
flow.analyzeLambda(env, that, make, isSpeculativeRound);
- checkLambdaCompatible(that, lambdaType, resultInfo.checkContext, isSpeculativeRound);
+ checkLambdaCompatible(that, lambdaType, resultInfo.checkContext);
if (!isSpeculativeRound) {
+ //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().asFree(lambdaType.getThrownTypes());
+
+ chk.unhandled(inferredThrownTypes, thrownTypes);
+ }
+
checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
}
result = check(that, currentTarget, VAL, resultInfo);
@@ -2587,10 +2595,9 @@
* Lambda compatibility. Check that given return types, thrown types, parameter types
* are compatible with the expected functional interface descriptor. This means that:
* (i) parameter types must be identical to those of the target descriptor; (ii) return
- * types must be compatible with the return type of the expected descriptor;
- * (iii) finish inference of thrown types if required.
+ * types must be compatible with the return type of the expected descriptor.
*/
- private void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext, boolean speculativeAttr) {
+ private void checkLambdaCompatible(JCLambda tree, Type descriptor, CheckContext checkContext) {
Type returnType = checkContext.inferenceContext().asFree(descriptor.getReturnType());
//return values have already been checked - but if lambda has no return
@@ -2607,11 +2614,6 @@
if (!types.isSameTypes(argTypes, TreeInfo.types(tree.params))) {
checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
}
-
- if (!speculativeAttr) {
- List<Type> thrownTypes = checkContext.inferenceContext().asFree(descriptor.getThrownTypes());
- chk.unhandled(tree.inferredThrownTypes == null ? List.<Type>nil() : tree.inferredThrownTypes, thrownTypes);
- }
}
private Env<AttrContext> lambdaEnv(JCLambda that, Env<AttrContext> env) {
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Mon Sep 09 17:36:23 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java Mon Sep 09 23:13:45 2013 +0200
@@ -224,7 +224,6 @@
}
try {
new AliveAnalyzer().analyzeTree(env, that, make);
- new LambdaFlowAnalyzer().analyzeTree(env, that, make);
} finally {
if (!speculative) {
log.popDiagnosticHandler(diagHandler);
@@ -232,6 +231,23 @@
}
}
+ public List<Type> analyzeLambdaThrownTypes(Env<AttrContext> env, JCLambda that, TreeMaker make) {
+ //we need to disable diagnostics temporarily; the problem is that if
+ //a lambda expression contains e.g. an unreachable statement, an error
+ //message will be reported and will cause compilation to skip the flow analyis
+ //step - if we suppress diagnostics, we won't stop at Attr for flow-analysis
+ //related errors, which will allow for more errors to be detected
+ Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
+ try {
+ new AssignAnalyzer().analyzeTree(env, that, make);
+ LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
+ flowAnalyzer.analyzeTree(env, that, make);
+ return flowAnalyzer.inferredThrownTypes;
+ } finally {
+ log.popDiagnosticHandler(diagHandler);
+ }
+ }
+
/**
* Definite assignment scan mode
*/
@@ -1318,27 +1334,35 @@
* Specialized pass that performs inference of thrown types for lambdas.
*/
class LambdaFlowAnalyzer extends FlowAnalyzer {
+ List<Type> inferredThrownTypes;
+ boolean inLambda;
@Override
public void visitLambda(JCLambda tree) {
- if (tree.type != null &&
- tree.type.isErroneous()) {
+ if ((tree.type != null &&
+ tree.type.isErroneous()) || inLambda) {
return;
}
List<Type> prevCaught = caught;
List<Type> prevThrown = thrown;
ListBuffer<FlowPendingExit> prevPending = pendingExits;
+ inLambda = true;
try {
pendingExits = ListBuffer.lb();
caught = List.of(syms.throwableType);
thrown = List.nil();
scan(tree.body);
- tree.inferredThrownTypes = thrown;
+ inferredThrownTypes = thrown;
} finally {
pendingExits = prevPending;
caught = prevCaught;
thrown = prevThrown;
+ inLambda = false;
}
}
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ //skip
+ }
}
/**
--- a/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java Mon Sep 09 17:36:23 2013 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java Mon Sep 09 23:13:45 2013 +0200
@@ -1596,7 +1596,6 @@
public List<JCVariableDecl> params;
public JCTree body;
public boolean canCompleteNormally = true;
- public List<Type> inferredThrownTypes;
public ParameterKind paramKind;
public JCLambda(List<JCVariableDecl> params,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/EffectivelyFinalThrows.java Mon Sep 09 23:13:45 2013 +0200
@@ -0,0 +1,25 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8019521
+ * @summary Check that enhanced rethrow/effectivelly final works correctly inside lambdas
+ * @compile EffectivelyFinalThrows.java
+ */
+
+class EffectivelyFinalThrows {
+ interface SAM<E extends Throwable> {
+ public void t() throws E;
+ }
+ <E extends Throwable> void test(SAM<E> s) throws E {
+ s.t();
+ }
+ void test2(SAM<Checked> s) throws Checked {
+ test(() -> {
+ try {
+ s.t();
+ } catch (Throwable t) {
+ throw t;
+ }
+ });
+ }
+ static class Checked extends Exception {}
+}