8204610: Compiler confused by parenthesized "this" in final fields assignments
authormcimadamore
Fri, 08 Jun 2018 16:33:40 +0100
changeset 50468 4a5fd709e286
parent 50467 e0a32b178e6f
child 50469 1476689320e0
8204610: Compiler confused by parenthesized "this" in final fields assignments Summary: parenthesis are not skipped consistently in DA/DU, forward reference analysis Reviewed-by: vromero
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java
src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java
test/langtools/tools/javac/DefiniteAssignment/T8204610.java
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 08 08:30:18 2018 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Fri Jun 08 16:33:40 2018 +0100
@@ -291,7 +291,7 @@
             ((v.flags() & HASINIT) != 0
              ||
              !((base == null ||
-               (base.hasTag(IDENT) && TreeInfo.name(base) == names._this)) &&
+               TreeInfo.isThisQualifier(base)) &&
                isAssignableAsBlankFinal(v, env)))) {
             if (v.isResourceVariable()) { //TWR resource
                 log.error(pos, Errors.TryResourceMayNotBeAssigned(v));
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jun 08 08:30:18 2018 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java	Fri Jun 08 16:33:40 2018 +0100
@@ -2391,31 +2391,18 @@
         }
 
         public void visitAssign(JCAssign tree) {
-            JCTree lhs = TreeInfo.skipParens(tree.lhs);
-            if (!isIdentOrThisDotIdent(lhs))
-                scanExpr(lhs);
+            if (!TreeInfo.isIdentOrThisDotIdent(tree.lhs))
+                scanExpr(tree.lhs);
             scanExpr(tree.rhs);
-            letInit(lhs);
-        }
-        private boolean isIdentOrThisDotIdent(JCTree lhs) {
-            if (lhs.hasTag(IDENT))
-                return true;
-            if (!lhs.hasTag(SELECT))
-                return false;
-
-            JCFieldAccess fa = (JCFieldAccess)lhs;
-            return fa.selected.hasTag(IDENT) &&
-                   ((JCIdent)fa.selected).name == names._this;
+            letInit(tree.lhs);
         }
 
         // check fields accessed through this.<field> are definitely
         // assigned before reading their value
         public void visitSelect(JCFieldAccess tree) {
             super.visitSelect(tree);
-            JCTree sel = TreeInfo.skipParens(tree.selected);
             if (enforceThisDotInit &&
-                    sel.hasTag(IDENT) &&
-                    ((JCIdent)sel).name == names._this &&
+                    TreeInfo.isThisQualifier(tree.selected) &&
                     tree.sym.kind == VAR) {
                 checkInit(tree.pos(), (VarSymbol)tree.sym);
             }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Fri Jun 08 08:30:18 2018 -0700
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java	Fri Jun 08 16:33:40 2018 +0100
@@ -148,6 +148,36 @@
         }
     }
 
+    /** Is this tree a 'this' identifier?
+     */
+    public static boolean isThisQualifier(JCTree tree) {
+        switch (tree.getTag()) {
+            case PARENS:
+                return isThisQualifier(skipParens(tree));
+            case IDENT: {
+                JCIdent id = (JCIdent)tree;
+                return id.name == id.name.table.names._this;
+            }
+            default:
+                return false;
+        }
+    }
+
+    /** Is this tree an identifier, possibly qualified by 'this'?
+     */
+    public static boolean isIdentOrThisDotIdent(JCTree tree) {
+        switch (tree.getTag()) {
+            case PARENS:
+                return isIdentOrThisDotIdent(skipParens(tree));
+            case IDENT:
+                return true;
+            case SELECT:
+                return isThisQualifier(((JCFieldAccess)tree).selected);
+            default:
+                return false;
+        }
+    }
+
     /** Is this a call to super?
      */
     public static boolean isSuperCall(JCTree tree) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/DefiniteAssignment/T8204610.java	Fri Jun 08 16:33:40 2018 +0100
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2018, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 8204610
+ * @summary Compiler confused by parenthesized "this" in final fields assignments
+ * @library /tools/javac/lib
+ * @modules jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.code
+ *          jdk.compiler/com.sun.tools.javac.comp
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.tree
+ *          jdk.compiler/com.sun.tools.javac.util
+ * @build combo.ComboTestHelper
+
+ * @run main T8204610
+ */
+
+import combo.ComboInstance;
+import combo.ComboParameter;
+import combo.ComboTask.Result;
+import combo.ComboTestHelper;
+
+public class T8204610 extends ComboInstance<T8204610> {
+
+    enum ParenKind implements ComboParameter {
+        NONE(""),
+        ONE("#P"),
+        TWO("#P#P"),
+        THREE("#P#P#P");
+
+        String parensTemplate;
+
+        ParenKind(String parensTemplate) {
+            this.parensTemplate = parensTemplate;
+        }
+
+        @Override
+        public String expand(String optParameter) {
+            return parensTemplate.replaceAll("#P", optParameter.equals("OPEN") ? "(" : ")");
+        }
+    }
+
+    public static void main(String... args) {
+        new ComboTestHelper<T8204610>()
+                .withArrayDimension("PAREN", (x, pk, idx) -> x.parenKinds[idx] = pk, 3, ParenKind.values())
+                .run(T8204610::new);
+    }
+
+    ParenKind[] parenKinds = new ParenKind[3];
+
+    @Override
+    public void doWork() {
+        newCompilationTask()
+                .withSourceFromTemplate(bodyTemplate)
+                .analyze(this::check);
+    }
+
+    String bodyTemplate = "class Test {\n" +
+                          "   final int x;\n" +
+                          "   Test() {\n" +
+                          "      #{PAREN[0].OPEN} #{PAREN[1].OPEN} this #{PAREN[1].CLOSE} . #{PAREN[2].OPEN} x #{PAREN[2].CLOSE} #{PAREN[0].CLOSE} = 1;\n" +
+                          "   } }";
+
+    void check(Result<?> res) {
+        boolean expectedFail = parenKinds[2] != ParenKind.NONE;
+        if (expectedFail != res.hasErrors()) {
+            fail("unexpected compilation result for source:\n" +
+                res.compilationInfo());
+        }
+    }
+}