8168480: Speculative attribution of lambda causes NPE in Flow
authormcimadamore
Mon, 24 Oct 2016 14:47:48 +0100
changeset 41640 0c5bdda9ac56
parent 41639 90220025bd6c
child 41641 a628785b9dd9
8168480: Speculative attribution of lambda causes NPE in Flow Summary: Flow attempts to analyze too much of a lambda body during attribution Reviewed-by: vromero
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
langtools/test/tools/javac/lambda/8168480/T8168480.java
langtools/test/tools/javac/lambda/8168480/T8168480b.java
langtools/test/tools/javac/lambda/8168480/T8168480b.out
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Sat Oct 22 12:27:52 2016 -0700
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Mon Oct 24 14:47:48 2016 +0100
@@ -29,6 +29,7 @@
 
 import java.util.HashMap;
 
+import com.sun.source.tree.LambdaExpressionTree.BodyKind;
 import com.sun.tools.javac.code.*;
 import com.sun.tools.javac.code.Scope.WriteableScope;
 import com.sun.tools.javac.tree.*;
@@ -224,7 +225,7 @@
             diagHandler = new Log.DiscardDiagnosticHandler(log);
         }
         try {
-            new AliveAnalyzer().analyzeTree(env, that, make);
+            new LambdaAliveAnalyzer().analyzeTree(env, that, make);
         } finally {
             if (!speculative) {
                 log.popDiagnosticHandler(diagHandler);
@@ -241,19 +242,7 @@
         //related errors, which will allow for more errors to be detected
         Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log);
         try {
-            new AssignAnalyzer() {
-                WriteableScope enclosedSymbols = WriteableScope.create(env.enclClass.sym);
-                @Override
-                public void visitVarDef(JCVariableDecl tree) {
-                    enclosedSymbols.enter(tree.sym);
-                    super.visitVarDef(tree);
-                }
-                @Override
-                protected boolean trackable(VarSymbol sym) {
-                    return enclosedSymbols.includes(sym) &&
-                           sym.owner.kind == MTH;
-                }
-            }.analyzeTree(env, that);
+            new LambdaAssignAnalyzer(env).analyzeTree(env, that);
             LambdaFlowAnalyzer flowAnalyzer = new LambdaFlowAnalyzer();
             flowAnalyzer.analyzeTree(env, that, make);
             return flowAnalyzer.inferredThrownTypes;
@@ -1341,6 +1330,79 @@
     }
 
     /**
+     * Specialized pass that performs reachability analysis on a lambda
+     */
+    class LambdaAliveAnalyzer extends AliveAnalyzer {
+
+        boolean inLambda;
+
+        @Override
+        public void visitReturn(JCReturn tree) {
+            //ignore lambda return expression (which might not even be attributed)
+            recordExit(new PendingExit(tree));
+        }
+
+        @Override
+        public void visitLambda(JCLambda tree) {
+            if (inLambda || tree.getBodyKind() == BodyKind.EXPRESSION) {
+                return;
+            }
+            inLambda = true;
+            try {
+                super.visitLambda(tree);
+            } finally {
+                inLambda = false;
+            }
+        }
+
+        @Override
+        public void visitClassDef(JCClassDecl tree) {
+            //skip
+        }
+    }
+
+    /**
+     * Specialized pass that performs DA/DU on a lambda
+     */
+    class LambdaAssignAnalyzer extends AssignAnalyzer {
+        WriteableScope enclosedSymbols;
+        boolean inLambda;
+
+        LambdaAssignAnalyzer(Env<AttrContext> env) {
+            enclosedSymbols = WriteableScope.create(env.enclClass.sym);
+        }
+
+        @Override
+        public void visitLambda(JCLambda tree) {
+            if (inLambda) {
+                return;
+            }
+            inLambda = true;
+            try {
+                super.visitLambda(tree);
+            } finally {
+                inLambda = false;
+            }
+        }
+
+        @Override
+        public void visitVarDef(JCVariableDecl tree) {
+            enclosedSymbols.enter(tree.sym);
+            super.visitVarDef(tree);
+        }
+        @Override
+        protected boolean trackable(VarSymbol sym) {
+            return enclosedSymbols.includes(sym) &&
+                   sym.owner.kind == MTH;
+        }
+
+        @Override
+        public void visitClassDef(JCClassDecl tree) {
+            //skip
+        }
+    }
+
+    /**
      * Specialized pass that performs inference of thrown types for lambdas.
      */
     class LambdaFlowAnalyzer extends FlowAnalyzer {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8168480/T8168480.java	Mon Oct 24 14:47:48 2016 +0100
@@ -0,0 +1,125 @@
+/*
+ * @test
+ * @bug 8168480
+ * @summary Speculative attribution of lambda causes NPE in Flow
+ * @compile T8168480.java
+ */
+
+import java.util.function.Supplier;
+
+class T8168480 {
+    void f(Runnable r) { }
+    void s(Supplier<Runnable> r) { }
+
+    private void testVoid(boolean cond) {
+        f(() ->
+                new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                }.run());
+
+        f(() ->
+                f(() -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                }));
+
+        f(() -> {
+            if (cond) {
+                new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                }.run();
+            } else {
+                f(() -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                });
+            }
+        });
+    }
+
+    private void testReturn(boolean cond) {
+        s(() ->
+                new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                });
+
+        s(() ->
+                () -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                });
+
+        s(() -> {
+            if (cond) {
+                return new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                };
+            } else {
+                return () -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                };
+            }
+        });
+
+        s(() -> {
+            return cond ?
+                new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                } : () -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                };
+        });
+
+        s(() -> cond ?
+                new Runnable() {
+                    public void run() {
+                        switch (42) {
+                            default:
+                                break;
+                        }
+                    }
+                } : () -> {
+                    switch (42) {
+                        default:
+                            break;
+                    }
+                });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8168480/T8168480b.java	Mon Oct 24 14:47:48 2016 +0100
@@ -0,0 +1,12 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8168480
+ * @summary Speculative attribution of lambda causes NPE in Flow
+ * @compile/fail/ref=T8168480b.out -XDrawDiagnostics T8168480b.java
+ */
+
+import java.util.function.Supplier;
+
+class T8168480b {
+   Supplier<Runnable> ssr = () -> () -> { while (true); System.err.println("Hello"); };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/lambda/8168480/T8168480b.out	Mon Oct 24 14:47:48 2016 +0100
@@ -0,0 +1,2 @@
+T8168480b.java:11:57: compiler.err.unreachable.stmt
+1 error