diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java index e229522659b..b5c006ff490 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java @@ -152,8 +152,12 @@ public void setFieldIndex(int depth) { * @param scope used to determine source level */ public boolean isUnnamed(BlockScope scope) { - return ((this.name.length == 1 && this.name[0] == '_') || ("\\u005F".equals(this.name.toString()))) //$NON-NLS-1$ + return isPotentiallyUnnamed() && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK21 && scope.compilerOptions().enablePreviewFeatures; } + + public boolean isPotentiallyUnnamed() { + return (this.name.length == 1 && this.name[0] == '_') || ("\\u005F".equals(this.name.toString())); //$NON-NLS-1$ + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java index a1ef3ba7c4c..b72d0287224 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java @@ -79,7 +79,8 @@ public FlowInfo analyseCode( for(int i=0; i < this.constantExpressions.length; i++) { Expression e = this.constantExpressions[i]; nullPatternCount += e instanceof NullLiteral ? 1 : 0; - if (i > 0 && (e instanceof Pattern)) { + if (i > 0 && (e instanceof Pattern) + && (currentScope.compilerOptions().sourceLevel < ClassFileConstants.JDK21 || !currentScope.compilerOptions().enablePreviewFeatures)) { if (!(i == nullPatternCount && e instanceof TypePattern)) currentScope.problemReporter().IllegalFallThroughToPattern(e); } @@ -170,14 +171,28 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { private void casePatternExpressionGenerateCode(BlockScope currentScope, CodeStream codeStream) { if (this.patternIndex != -1) { - Pattern pattern = ((Pattern) this.constantExpressions[this.patternIndex]); - if (containsPatternVariable()) { + BranchLabel thenTarget = new BranchLabel(codeStream); + for (int i = 0; i < this.constantExpressions.length - 1; i++) { + Pattern pattern = ((Pattern)this.constantExpressions[i]); + pattern.thenTarget = thenTarget; LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null); codeStream.load(local); pattern.generateCode(currentScope, codeStream); + pattern.fullWrapupGeneration(codeStream); + codeStream.goto_(pattern.thenTarget); + pattern.elseTarget.place(); + } + Pattern pattern = ((Pattern)this.constantExpressions[this.constantExpressions.length - 1]); + pattern.thenTarget = thenTarget; + LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null); + codeStream.load(local); + pattern.generateCode(currentScope, codeStream); + if (pattern.countNamedVariables(currentScope) > 0) { + pattern.wrapupGeneration(codeStream); } else { - pattern.setTargets(codeStream); + pattern.fullWrapupGeneration(codeStream); } + pattern.setTargets(codeStream); if (!(pattern instanceof GuardedPattern)) codeStream.goto_(pattern.thenTarget); @@ -285,9 +300,11 @@ private Expression getFirstValidExpression(BlockScope scope, SwitchStatement swi scope.problemReporter().validateJavaFeatureSupport(JavaFeature.PATTERN_MATCHING_IN_SWITCH, e.sourceStart, e.sourceEnd); if (this.constantExpressions.length > 1 || e instanceof GuardedPattern gp && gp.patterns.length > 1) { - PatternVariableCounter myVisitor = new PatternVariableCounter(); - this.traverse(myVisitor, scope); - if (myVisitor.numVars > 0) { + int count = 0; + for (Expression expr : this.constantExpressions) { + count += ((Pattern)expr).countNamedVariables(scope); + } + if (count > 0) { scope.problemReporter().illegalCaseLabelWithMultiplePatterns(this); } } @@ -563,24 +580,4 @@ public LocalDeclaration getLocalDeclaration() { return patternVariableIntroduced; } -private class PatternVariableCounter extends ASTVisitor { - - public int numVars = 0; - - @Override - public boolean visit(TypePattern pattern, BlockScope scope) { - if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ - this.numVars++; - } - return true; - } - - @Override - public boolean visit(RecordPattern pattern, BlockScope scope) { - if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ - this.numVars++; - } - return true; - } -} } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java index 8d7ee1a04dc..1b5c7c8a795 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java @@ -80,9 +80,29 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) { this.thenTarget = new BranchLabel(codeStream); this.elseTarget = new BranchLabel(codeStream); - for (Pattern pattern : this.patterns ) { - pattern.generateOptimizedBoolean(currentScope, codeStream, this.thenTarget, this.elseTarget); + + LocalVariableBinding local = currentScope.findVariable(SwitchStatement.SecretPatternVariableName, null); + BranchLabel toGuard = new BranchLabel(codeStream); + + for (int i = 0; i < this.patterns.length - 1; i++) { + Pattern pattern = this.patterns[i]; + pattern.thenTarget = toGuard; + pattern.generateCode(currentScope, codeStream); + pattern.fullWrapupGeneration(codeStream); + codeStream.goto_(pattern.thenTarget); + pattern.elseTarget.place(); + codeStream.load(local); + } + Pattern pattern = this.patterns[this.patterns.length - 1]; + pattern.thenTarget = toGuard; + pattern.elseTarget = this.elseTarget; + pattern.generateCode(currentScope, codeStream); + if (pattern.countNamedVariables(currentScope) > 0) { + pattern.wrapupGeneration(codeStream); + } else { + pattern.fullWrapupGeneration(codeStream); } + toGuard.place(); Constant cst = this.condition.optimizedBooleanConstant(); setGuardedElseTarget(currentScope, this.elseTarget); @@ -92,6 +112,9 @@ public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStr this.thenTarget, null, cst == Constant.NotAConstant); + if (cst != Constant.NotAConstant) { + codeStream.goto_(this.thenTarget); + } if (this.thenInitStateIndex2 != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2); codeStream.addDefinitelyAssignedVariables(currentScope, this.thenInitStateIndex2); @@ -251,11 +274,32 @@ public void wrapupGeneration(CodeStream codeStream) { } @Override public void fullWrapupGeneration(CodeStream codeStream) { - this.primaryPattern.fullWrapupGeneration(codeStream); + this.primary().fullWrapupGeneration(codeStream); } @Override protected void generatePatternVariable(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel) { this.primary().generatePatternVariable(currentScope, codeStream, trueLabel, falseLabel); } + private class PatternVariableCounter extends ASTVisitor { + + public int numVars = 0; + + @Override + public boolean visit(TypePattern pattern, BlockScope scope) { + if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ + this.numVars++; + } + return true; + } + + @Override + public boolean visit(RecordPattern pattern, BlockScope scope) { + if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ + this.numVars++; + } + return true; + } + + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java index 9bbecb7a59f..4ac0c0a9598 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java @@ -139,8 +139,36 @@ public Pattern primary() { return this; } + public int countNamedVariables(BlockScope scope) { + var pvc = new PatternVariableCounter(); + this.traverse(pvc, scope); + return pvc.numVars; + } + @Override public StringBuilder print(int indent, StringBuilder output) { return this.printExpression(indent, output); } + + private class PatternVariableCounter extends ASTVisitor { + + public int numVars = 0; + + @Override + public boolean visit(TypePattern pattern, BlockScope scope) { + if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ + this.numVars++; + } + return true; + } + + @Override + public boolean visit(RecordPattern pattern, BlockScope scope) { + if (pattern.local != null && (pattern.local.name.length != 1 || pattern.local.name[0] != '_') && !"\\u005F".equals(pattern.local.name.toString())) { //$NON-NLS-1$ + this.numVars++; + } + return true; + } + + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java index c1eed49c471..013b1578d77 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java @@ -272,6 +272,12 @@ public boolean dominates(Pattern p) { return false; } } + } else if (p instanceof GuardedPattern gp) { + for (Pattern gpChild : gp.patterns) { + if (!this.dominates(gpChild)) { + return false; + } + } } return true; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index 9d999894426..0a3d1c5391a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -985,15 +985,13 @@ private void generateCodePatternCaseEpilogue(CodeStream codeStream, int caseInde if (this.switchPatternRestartTarget != null && caseStatement != null && caseStatement.patternIndex != -1 // for null ) { - Pattern pattern = (Pattern) caseStatement.constantExpressions[caseStatement.patternIndex]; - pattern.elseTarget.place(); + Pattern pattern = (Pattern) caseStatement.constantExpressions[caseStatement.constantExpressions.length - 1]; pattern.suspendVariables(codeStream, this.scope); + pattern.elseTarget.place(); caseIndex = this.nullProcessed ? caseIndex - 1 : caseIndex; - if (!pattern.isAlwaysTrue()) { - codeStream.loadInt(caseIndex); - codeStream.store(this.restartIndexLocal, false); - codeStream.goto_(this.switchPatternRestartTarget); - } + codeStream.loadInt(caseIndex); + codeStream.store(this.restartIndexLocal, false); + codeStream.goto_(this.switchPatternRestartTarget); pattern.thenTarget.place(); pattern.resumeVariables(codeStream, this.scope); } else if (this.containsNull && caseStatement != null) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 5d4674f74f3..0151524a21e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -2227,7 +2227,7 @@ public void testBug574563_004() { 3. ERROR in X.java (at line 4) case null, Integer i when i > 10 -> System.out.println(0); ^^^^^ - Syntax error on tokens, TypeElidedFormalParameter expected instead + Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead ---------- 4. ERROR in X.java (at line 4) case null, Integer i when i > 10 -> System.out.println(0); @@ -2270,7 +2270,7 @@ public void testBug574563_005() { 3. ERROR in X.java (at line 4) case Integer i when i > 10, null -> System.out.println(0); ^^^^^^^^ - Syntax error on tokens, TypeElidedFormalParameter expected instead + Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead ---------- 4. ERROR in X.java (at line 4) case Integer i when i > 10, null -> System.out.println(0); @@ -6666,7 +6666,7 @@ public void testIssue1351_7() { 3. ERROR in X.java (at line 5) case Byte p when p.equals(exp), (byte) 0 -> { ^^^^ - Syntax error on tokens, TypeElidedFormalParameter expected instead + Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead ---------- 4. ERROR in X.java (at line 8) } @@ -6807,7 +6807,7 @@ public void testIssue1351_10() { 3. ERROR in X.java (at line 5) case Byte p when p.equals(exp), null -> { ^^^^^^^ - Syntax error on tokens, TypeElidedFormalParameter expected instead + Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead ---------- 4. ERROR in X.java (at line 8) } @@ -6853,7 +6853,7 @@ public void testIssue1351_11() { 3. ERROR in X.java (at line 5) case Byte p when p.equals(exp), default -> { ^^^^^^^^^^^^^^^^^^^^^^^^^ - Syntax error on tokens, TypeElidedFormalParameter expected instead + Syntax error on tokens, TypeElidedUnnamedFormalParameter expected instead ---------- 4. ERROR in X.java (at line 8) } @@ -7186,6 +7186,7 @@ public static void main(String[] args) { }, "Default"); } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1856 // [switch][record patterns] NPE: Cannot invoke "org.eclipse.jdt.internal.compiler.lookup.MethodBinding.isStatic()" public void testGHI1856() { @@ -7275,4 +7276,5 @@ case WrapperRec(ExhaustiveSwitch.Data data) when data.name.isEmpty() -> { } + "Record component with type Data is not compatible with type ExhaustiveSwitch.Data\n" + "----------\n"); } + } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java new file mode 100644 index 00000000000..1c1c2ad8889 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java @@ -0,0 +1,301 @@ +package org.eclipse.jdt.core.tests.compiler.regression; + +import java.util.Map; + +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; + +import junit.framework.Test; + +public class SwitchPatternTest21 extends AbstractBatchCompilerTest { + + private static String[] JAVAC_OPTIONS = new String[] { "--enable-preview" }; + + public static Test suite() { + return buildMinimalComplianceTestSuite(SwitchPatternTest21.class, F_21); + } + + public SwitchPatternTest21(String name) { + super(name); + } + + @Override + protected Map getCompilerOptions() { + CompilerOptions compilerOptions = new CompilerOptions(super.getCompilerOptions()); + if (compilerOptions.sourceLevel == ClassFileConstants.JDK21) { + compilerOptions.enablePreviewFeatures = true; + } + return compilerOptions.getMap(); + } + + public void runConformTest(String[] files, String expectedOutput) { + super.runConformTest(files, expectedOutput, null, JAVAC_OPTIONS); + } + + public void testListOfPatterns_000() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Melon(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) : System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_001() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Chartreuse(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) : System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_002() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Licorice(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) : System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "failure"); + } + + public void testListOfPatterns_003() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Melon(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when 1 == 1 && 2 == 2: System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_004() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Chartreuse(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when 1 == 1 && 2 == 2 : System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_005() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Licorice(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when 1 == 1 && 2 == 2 : System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "failure"); + } + + // next three tests: beat the static analysis so that the `when` clause comparison isn't optimized away + + public void testListOfPatterns_006() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + String myString = "asdf"; + Object o = new Pantaloon(new Melon(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when myString.length() == 4: System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_007() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + String myString = "asdf"; + Object o = new Pantaloon(new Chartreuse(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when myString.length() == 4: System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success"); + } + + public void testListOfPatterns_008() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + String myString = "asdf"; + Object o = new Pantaloon(new Licorice(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int _), Pantaloon(Melon _, int _) when myString.length() == 4: System.out.println("success"); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "failure"); + } + + public void testTwoCasesWithRecordPatternsShouldNotDominateRegression() { + this.runConformTest( + new String[] { "X.java", + """ + public class X { + + public static void main(String... args) { + Object o = new Pantaloon(new Chartreuse(), 12); + switch (o) { + case Pantaloon(Chartreuse _, int i) when true: System.out.println("success " + i); break; + case Pantaloon(Melon _, int i) when true : System.out.println("success " + i); break; + default: System.out.println("failure"); break; + } + } + + static sealed abstract class Flavour permits Chartreuse, Orange, Licorice, Melon { } + static final class Chartreuse extends Flavour {} + static final class Orange extends Flavour {} + static final class Licorice extends Flavour {} + static final class Melon extends Flavour {} + static record Pantaloon(Flavour flavour, int i) {} + + } + """, }, + "success 12"); + } + +}