8019521: Enhanced rethrow disabled in lambdas
authorjlahoda
Mon, 09 Sep 2013 23:13:45 +0200
changeset 19933 5182bc65845b
parent 19932 1386f12ad4b9
child 19934 1d8232b1aa18
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
langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
langtools/src/share/classes/com/sun/tools/javac/comp/Flow.java
langtools/src/share/classes/com/sun/tools/javac/tree/JCTree.java
langtools/test/tools/javac/lambda/EffectivelyFinalThrows.java
--- 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 {}
+}