From ea5eb74a65f20ce28fa0a94ea851915d4a6f83da Mon Sep 17 00:00:00 2001 From: Aggelos Biboudis Date: Tue, 14 May 2024 06:41:58 +0000 Subject: [PATCH] 8326404: Assertion error when trying to compile switch with fallthrough with pattern Co-authored-by: Jan Lahoda Reviewed-by: vromero --- .../sun/tools/javac/comp/TransPatterns.java | 18 +- .../tools/javac/patterns/T8326404.java | 157 ++++++++++++++++++ 2 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 test/langtools/tools/javac/patterns/T8326404.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java index 5dc85d5f2c535..aed7b3265f807 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransPatterns.java @@ -458,7 +458,9 @@ private void handleSwitch(JCTree tree, newCases.add(c.head); appendBreakIfNeeded(tree, cases, c.head); } - cases = processCases(tree, newCases.toList()); + cases = newCases.toList(); + patchCompletingNormallyCases(cases); + cases = processCases(tree, cases); ListBuffer statements = new ListBuffer<>(); VarSymbol temp = new VarSymbol(Flags.SYNTHETIC, names.fromString("selector" + variableIndex++ + target.syntheticNameChar() + "temp"), @@ -523,8 +525,6 @@ private void handleSwitch(JCTree tree, boolean previousCompletesNormally = false; boolean hasDefault = false; - patchCompletingNormallyCases(cases); - for (var c : cases) { List clearedPatterns = c.labels; boolean hasJoinedNull = @@ -685,7 +685,7 @@ private static void patchCompletingNormallyCases(List cases) { if (currentCase.caseKind == CaseKind.STATEMENT && currentCase.completesNormally && cases.tail.nonEmpty() && - cases.tail.head.guard != null) { + (cases.tail.head.guard != null || cases.tail.head.labels.stream().anyMatch(cl -> cl instanceof JCPatternCaseLabel p && p.syntheticGuard != null))) { ListBuffer newStatements = new ListBuffer<>(); List copyFrom = cases; @@ -700,6 +700,7 @@ private static void patchCompletingNormallyCases(List cases) { }; currentCase.stats = newStatements.toList(); + currentCase.completesNormally = false; } cases = cases.tail; @@ -948,10 +949,12 @@ public void resolve(VarSymbol commonBinding, JCExpression commonNestedExpression = null; VarSymbol commonNestedBinding = null; boolean previousNullable = false; + boolean previousCompletesNormally = false; for (List c = inputCases; c.nonEmpty(); c = c.tail) { VarSymbol currentBinding = null; boolean currentNullable = false; + boolean currentCompletesNormally = c.head.completesNormally; JCExpression currentNestedExpression = null; VarSymbol currentNestedBinding = null; @@ -986,6 +989,8 @@ public void resolve(VarSymbol commonBinding, commonBinding.isUnnamedVariable() == currentBinding.isUnnamedVariable() && !previousNullable && !currentNullable && + !previousCompletesNormally && + !currentCompletesNormally && new TreeDiffer(List.of(commonBinding), List.of(currentBinding)) .scan(commonNestedExpression, currentNestedExpression)) { accummulator.add(c.head); @@ -1001,6 +1006,7 @@ public void resolve(VarSymbol commonBinding, commonNestedBinding = currentNestedBinding; } previousNullable = currentNullable; + previousCompletesNormally = currentCompletesNormally; } resolveAccummulator.resolve(commonBinding, commonNestedExpression, commonNestedBinding); return result.toList(); @@ -1473,8 +1479,8 @@ List bindingVars(int diagPos) { ListBuffer stats = new ListBuffer<>(); for (Entry e : hoistedVarMap.entrySet()) { JCVariableDecl decl = makeHoistedVarDecl(diagPos, e.getValue()); - if (!e.getKey().isPreserved() || - !parent.tryPrepend(e.getKey(), decl)) { + if (!e.getValue().isUnnamedVariable() && + (!e.getKey().isPreserved() || !parent.tryPrepend(e.getKey(), decl))) { stats.add(decl); } } diff --git a/test/langtools/tools/javac/patterns/T8326404.java b/test/langtools/tools/javac/patterns/T8326404.java new file mode 100644 index 0000000000000..aafaf97a5e1cb --- /dev/null +++ b/test/langtools/tools/javac/patterns/T8326404.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2024, 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 8326404 + * @summary Assertion error when trying to compile switch with fallthrough with pattern + * @compile T8326404.java + * @run main T8326404 + */ +public class T8326404 { + private static final record R(T a) {} + + public static void main(String[] args) { + assertEquals(4, run1("")); + assertEquals(3, run1(new R(""))); + assertEquals(2, run1(new R(42))); + + assertEquals(2, run1_break1("")); + assertEquals(1, run1_break1(new R(""))); + assertEquals(2, run1_break1(new R(42))); + + assertEquals(3, run2("")); + assertEquals(4, run2(new R(""))); + assertEquals(2, run2(new R(42))); + + assertEquals(1, run2_break1("")); + assertEquals(2, run2_break1(new R(""))); + assertEquals(2, run2_break1(new R(42))); + + assertEquals(2, run3("")); + assertEquals(4, run3(new R(""))); + assertEquals(3, run3(new R(42))); + + assertEquals(2, run3_break1("")); + assertEquals(2, run3_break1(new R(""))); + assertEquals(1, run3_break1(new R(42))); + } + + private static int run1(Object o) { + int i = 0; + switch (o) { + case String _: + i++; + case R(String _): + i++; + case R(Integer _): + i++; + default: + i++; + } + return i; + } + + private static int run1_break1(Object o) { + int i = 0; + switch (o) { + case String s: + i++; + case R(String _): + i++; + break; + case R(Integer _): + i++; + default: + i++; + } + return i; + } + + private static int run2(Object o) { + int i = 0; + switch (o) { + case R(String _): + i++; + case String _: + i++; + case R(Integer _): + i++; + default: + i++; + } + return i; + } + + private static int run2_break1(Object o) { + int i = 0; + switch (o) { + case R(String _): + i++; + case String _: + i++; + break; + case R(Integer _): + i++; + default: + i++; + } + return i; + } + + private static int run3(Object o) { + int i = 0; + switch (o) { + case R(String _): + i++; + case R(Integer _): + i++; + case String _: + i++; + default: + i++; + } + return i; + } + + private static int run3_break1(Object o) { + int i = 0; + switch (o) { + case R(String _): + i++; + case R(Integer _): + i++; + break; + case String _: + i++; + default: + i++; + } + return i; + } + + static void assertEquals(int expected, int actual) { + if (expected != actual) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + } +} \ No newline at end of file