diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java index 2482368fad3..907171a701c 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Scope.java @@ -272,9 +272,9 @@ public static WriteableScope create(Symbol owner) { } private static class ScopeImpl extends WriteableScope { - /** The number of scopes that share this scope's hash table. + /** true if this scope's hash table is shared with a nested scope. */ - private int shared; + private boolean shared; /** Next enclosing scope (with whom this scope may share a hashtable) */ @@ -339,8 +339,10 @@ public ScopeImpl(Symbol owner) { * of fresh tables. */ public WriteableScope dup(Symbol newOwner) { + Assert.check(!shared); + ScopeImpl result = new ScopeImpl(this, newOwner, this.table, this.nelems); - shared++; + shared = true; // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode()); // new Error().printStackTrace(System.out); return result; @@ -351,7 +353,7 @@ public WriteableScope dup(Symbol newOwner) { * the table of its outer scope. */ public WriteableScope dupUnshared(Symbol newOwner) { - if (shared > 0) { + if (shared) { //The nested Scopes might have already added something to the table, so all items //that don't originate in this Scope or any of its outer Scopes need to be cleared: Set acceptScopes = Collections.newSetFromMap(new IdentityHashMap<>()); @@ -383,7 +385,7 @@ public WriteableScope dupUnshared(Symbol newOwner) { * with next. */ public WriteableScope leave() { - Assert.check(shared == 0); + Assert.check(!shared); if (table != next.table) return next; while (elems != null) { int hash = getIndex(elems.sym.name); @@ -392,8 +394,8 @@ public WriteableScope leave() { table[hash] = elems.shadowed; elems = elems.nextSibling; } - Assert.check(next.shared > 0); - next.shared--; + Assert.check(next.shared); + next.shared = false; next.nelems = nelems; // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode()); // new Error().printStackTrace(System.out); @@ -403,12 +405,12 @@ public WriteableScope leave() { /** Double size of hash table. */ private void dble() { - Assert.check(shared == 0); + Assert.check(!shared); Entry[] oldtable = table; Entry[] newtable = new Entry[oldtable.length * 2]; for (ScopeImpl s = this; s != null; s = s.next) { if (s.table == oldtable) { - Assert.check(s == this || s.shared != 0); + Assert.check(s == this || s.shared); s.table = newtable; s.hashMask = newtable.length - 1; } @@ -429,7 +431,7 @@ private void dble() { /** Enter symbol sym in this scope. */ public void enter(Symbol sym) { - Assert.check(shared == 0); + Assert.check(!shared); if (nelems * 3 >= hashMask * 2) dble(); int hash = getIndex(sym.name); @@ -449,7 +451,7 @@ public void enter(Symbol sym) { /** Remove symbol from this scope. */ public void remove(Symbol sym) { - Assert.check(shared == 0); + Assert.check(!shared); Entry e = lookup(sym.name, candidate -> candidate == sym); if (e.scope == null) return; @@ -487,7 +489,7 @@ else while (true) { /** Enter symbol sym in this scope if not already there. */ public void enterIfAbsent(Symbol sym) { - Assert.check(shared == 0); + Assert.check(!shared); Entry e = lookup(sym.name); while (e.scope == this && e.sym.kind != sym.kind) e = e.next(); if (e.scope != this) enter(sym); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 3e3c9f2bcea..21cc8e57e1f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -1787,7 +1787,7 @@ private void handleSwitch(JCTree switchTree, JCExpression guard = patternlabel.guard; if (guard != null) { MatchBindings afterPattern = matchBindings; - Env bodyEnv = bindingEnv(env, matchBindings.bindingsWhenTrue); + Env bodyEnv = bindingEnv(switchEnv, matchBindings.bindingsWhenTrue); try { attribExpr(guard, bodyEnv, syms.booleanType); } finally { diff --git a/test/langtools/tools/javac/patterns/ScopeResizeTest.java b/test/langtools/tools/javac/patterns/ScopeResizeTest.java new file mode 100644 index 00000000000..5eb01b8daac --- /dev/null +++ b/test/langtools/tools/javac/patterns/ScopeResizeTest.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022, 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 8292756 + * @summary Verify the Scope can be safely and correctly resized to accommodate pattern binding variables + * when the Scope for a guard is constructed. + * @library /tools/lib /tools/javac/lib + * @modules + * jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.file + * jdk.compiler/com.sun.tools.javac.main + * jdk.compiler/com.sun.tools.javac.util + * @build toolbox.ToolBox toolbox.JavacTask + * @build combo.ComboTestHelper + * @compile ScopeResizeTest.java + * @run main ScopeResizeTest + */ + +import combo.ComboInstance; +import combo.ComboParameter; +import combo.ComboTask; +import combo.ComboTestHelper; +import java.util.stream.Stream; +import toolbox.ToolBox; + +public class ScopeResizeTest extends ComboInstance { + protected ToolBox tb; + + ScopeResizeTest() { + super(); + tb = new ToolBox(); + } + + public static void main(String... args) throws Exception { + int variantsSize = 17; + PredefinedVariables[] variants = Stream.iterate(0, i -> i + 1) + .limit(variantsSize) + .map(s -> new PredefinedVariables(s)) + .toArray(s -> new PredefinedVariables[s]); + new ComboTestHelper() + .withDimension("PREDEFINED_VARIABLES", (x, predefinedVariables) -> x.predefinedVariables = predefinedVariables, variants) + .run(ScopeResizeTest::new); + } + + private PredefinedVariables predefinedVariables; + + private static final String MAIN_TEMPLATE = + """ + public class Test { + public static void test(Object o) { + #{PREDEFINED_VARIABLES} + switch (o) { + case String s when s.isEmpty() -> {} + default -> {} + } + } + } + """; + + @Override + protected void doWork() throws Throwable { + ComboTask task = newCompilationTask() + .withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) { + case "PREDEFINED_VARIABLES" -> predefinedVariables; + default -> throw new UnsupportedOperationException(pname); + }); + + task.analyze(result -> {}); + } + + public record PredefinedVariables(int size) implements ComboParameter { + @Override + public String expand(String optParameter) { + StringBuilder variables = new StringBuilder(); + for (int i = 0; i < size(); i++) { + variables.append("int i" + i + ";\n"); + } + return variables.toString(); + } + } + +}