Skip to content

Commit d085c2b

Browse files
committed
8273328: Compiler implementation for Pattern Matching for switch (Second Preview)
Reviewed-by: vromero, mcimadamore
1 parent 6d73460 commit d085c2b

File tree

9 files changed

+207
-49
lines changed

9 files changed

+207
-49
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,7 +1680,8 @@ private void handleSwitch(JCTree switchTree,
16801680
// Attribute all cases and
16811681
// check that there are no duplicate case labels or default clauses.
16821682
Set<Object> labels = new HashSet<>(); // The set of case labels.
1683-
List<Type> coveredTypes = List.nil();
1683+
List<Type> coveredTypesForPatterns = List.nil();
1684+
List<Type> coveredTypesForConstants = List.nil();
16841685
boolean hasDefault = false; // Is there a default label?
16851686
boolean hasTotalPattern = false; // Is there a total pattern?
16861687
boolean hasNullPattern = false; // Is there a null pattern?
@@ -1718,7 +1719,7 @@ private void handleSwitch(JCTree switchTree,
17181719
} else if (!labels.add(sym)) {
17191720
log.error(pat.pos(), Errors.DuplicateCaseLabel);
17201721
} else {
1721-
checkCaseLabelDominated(pat.pos(), coveredTypes, sym.type);
1722+
checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, sym.type);
17221723
}
17231724
} else if (errorEnumSwitch) {
17241725
//error recovery: the selector is erroneous, and all the case labels
@@ -1751,7 +1752,7 @@ private void handleSwitch(JCTree switchTree,
17511752
} else if (!labels.add(pattype.constValue())) {
17521753
log.error(c.pos(), Errors.DuplicateCaseLabel);
17531754
} else {
1754-
checkCaseLabelDominated(pat.pos(), coveredTypes, types.boxedTypeOrType(pattype));
1755+
checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype));
17551756
}
17561757
}
17571758
}
@@ -1784,9 +1785,12 @@ private void handleSwitch(JCTree switchTree,
17841785
}
17851786
hasTotalPattern = true;
17861787
}
1787-
checkCaseLabelDominated(pat.pos(), coveredTypes, patternType);
1788-
if (primary.unconditional() && !patternType.isErroneous()) {
1789-
coveredTypes = coveredTypes.prepend(patternType);
1788+
checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType);
1789+
if (!patternType.isErroneous()) {
1790+
coveredTypesForConstants = coveredTypesForConstants.prepend(patternType);
1791+
if (primary.unconditional()) {
1792+
coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType);
1793+
}
17901794
}
17911795
}
17921796
currentBindings = matchBindingsComputer.switchCase(pat, currentBindings, matchBindings);

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -751,7 +751,7 @@ private void handleConstantCaseLabel(Set<Symbol> constants, JCCaseLabel pat) {
751751
}
752752
}
753753

754-
private void transitiveCovers(Set<Symbol> covered) {
754+
private void transitiveCovers(Type seltype, Set<Symbol> covered) {
755755
List<Symbol> todo = List.from(covered);
756756
while (todo.nonEmpty()) {
757757
Symbol sym = todo.head;
@@ -773,7 +773,7 @@ private void transitiveCovers(Set<Symbol> covered) {
773773
case TYP -> {
774774
for (Type sup : types.directSupertypes(sym.type)) {
775775
if (sup.tsym.kind == TYP) {
776-
if (isTransitivelyCovered(sup.tsym, covered) &&
776+
if (isTransitivelyCovered(seltype, sup.tsym, covered) &&
777777
covered.add(sup.tsym)) {
778778
todo = todo.prepend(sup.tsym);
779779
}
@@ -784,7 +784,7 @@ private void transitiveCovers(Set<Symbol> covered) {
784784
}
785785
}
786786

787-
private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
787+
private boolean isTransitivelyCovered(Type seltype, Symbol sealed, Set<Symbol> covered) {
788788
DeferredCompletionFailureHandler.Handler prevHandler =
789789
dcfh.setHandler(dcfh.speculativeCodeHandler);
790790
try {
@@ -793,7 +793,10 @@ private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
793793
if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) {
794794
return ((ClassSymbol) sealed).permitted
795795
.stream()
796-
.allMatch(s -> isTransitivelyCovered(s, covered));
796+
.filter(s -> {
797+
return types.isCastable(seltype, s.type/*, types.noWarnings*/);
798+
})
799+
.allMatch(s -> isTransitivelyCovered(seltype, s, covered));
797800
}
798801
return false;
799802
} catch (CompletionFailure cf) {
@@ -805,7 +808,7 @@ private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
805808
}
806809

807810
private boolean isExhaustive(Type seltype, Set<Symbol> covered) {
808-
transitiveCovers(covered);
811+
transitiveCovers(seltype, covered);
809812
return switch (seltype.getTag()) {
810813
case CLASS -> {
811814
if (seltype.isCompound()) {

test/langtools/tools/javac/patterns/Domination.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,41 @@ int testDominatesStringConstant(String str) {
6868
}
6969
}
7070

71+
int testDominatesStringConstant2(String str) {
72+
switch (str) {
73+
case (String s && s.isEmpty()): return 1;
74+
case "": return -1;
75+
}
76+
}
77+
78+
int testDominatesStringConstant3(String str) {
79+
switch (str) {
80+
case (String s && !s.isEmpty()): return 1;
81+
case "": return -1;
82+
}
83+
}
84+
7185
int testDominatesIntegerConstant(Integer i) {
7286
switch (i) {
7387
case Integer j: return 1;
7488
case 0: return -1;
7589
}
7690
}
7791

92+
int testDominatesIntegerConstant2(Integer i) {
93+
switch (i) {
94+
case (Integer j && j == 0): return 1;
95+
case 0: return -1;
96+
}
97+
}
98+
99+
int testDominatesIntegerConstant3(Integer i) {
100+
switch (i) {
101+
case (Integer j && j == 1): return 1;
102+
case 0: return -1;
103+
}
104+
}
105+
78106
int testDominatesEnumConstant() {
79107
enum E {
80108
A, B;
@@ -86,4 +114,26 @@ enum E {
86114
}
87115
}
88116

117+
int testDominatesEnumConstant2() {
118+
enum E {
119+
A, B;
120+
}
121+
E e = E.A;
122+
switch (e) {
123+
case (E d && d == E.A): return 1;
124+
case A: return -1;
125+
}
126+
}
127+
128+
int testDominatesEnumConstant3() {
129+
enum E {
130+
A, B;
131+
}
132+
E e = E.A;
133+
switch (e) {
134+
case (E d && d == E.B): return 1;
135+
case A: return -1;
136+
}
137+
}
138+
89139
}

test/langtools/tools/javac/patterns/Domination.out

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ Domination.java:43:18: compiler.err.pattern.dominated
33
Domination.java:51:18: compiler.err.pattern.dominated
44
Domination.java:67:18: compiler.err.pattern.dominated
55
Domination.java:74:18: compiler.err.pattern.dominated
6-
Domination.java:85:18: compiler.err.pattern.dominated
6+
Domination.java:81:18: compiler.err.pattern.dominated
7+
Domination.java:88:18: compiler.err.pattern.dominated
8+
Domination.java:95:18: compiler.err.pattern.dominated
9+
Domination.java:102:18: compiler.err.pattern.dominated
10+
Domination.java:113:18: compiler.err.pattern.dominated
11+
Domination.java:124:18: compiler.err.pattern.dominated
12+
Domination.java:135:18: compiler.err.pattern.dominated
713
- compiler.note.preview.filename: Domination.java, DEFAULT
814
- compiler.note.preview.recompile
9-
6 errors
15+
12 errors

test/langtools/tools/javac/patterns/EnumTypeChanges.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ void doRun(Function<EnumTypeChangesEnum, String> c) throws Exception {
5252
String statementEnum(EnumTypeChangesEnum e) {
5353
switch (e) {
5454
case A -> { return "A"; }
55-
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
5655
case B -> { return "B"; }
56+
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
5757
default -> { return "D"; }
5858
}
5959
}
6060

6161
String expressionEnum(EnumTypeChangesEnum e) {
6262
return switch (e) {
6363
case A -> "A";
64-
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
6564
case B -> "B";
65+
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
6666
default -> "D";
6767
};
6868
}

test/langtools/tools/javac/patterns/Exhaustiveness.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,94 @@ void test(A arg) {
799799
""");
800800
}
801801

802+
@Test
803+
public void testOnlyApplicable(Path base) throws Exception {
804+
record TestCase(String cases, String... errors) {}
805+
TestCase[] subCases = new TestCase[] {
806+
new TestCase("""
807+
case C3<Integer> c -> {}
808+
case C5<Integer, ?> c -> {}
809+
case C6<?, Integer> c -> {}
810+
"""), //OK
811+
new TestCase("""
812+
case C5<Integer, ?> c -> {}
813+
case C6<?, Integer> c -> {}
814+
""",
815+
"Test.java:11:9: compiler.err.not.exhaustive.statement",
816+
"- compiler.note.preview.filename: Test.java, DEFAULT",
817+
"- compiler.note.preview.recompile",
818+
"1 error"),
819+
new TestCase("""
820+
case C3<Integer> c -> {}
821+
case C6<?, Integer> c -> {}
822+
""",
823+
"Test.java:11:9: compiler.err.not.exhaustive.statement",
824+
"- compiler.note.preview.filename: Test.java, DEFAULT",
825+
"- compiler.note.preview.recompile",
826+
"1 error"),
827+
new TestCase("""
828+
case C3<Integer> c -> {}
829+
case C5<Integer, ?> c -> {}
830+
""",
831+
"Test.java:11:9: compiler.err.not.exhaustive.statement",
832+
"- compiler.note.preview.filename: Test.java, DEFAULT",
833+
"- compiler.note.preview.recompile",
834+
"1 error"),
835+
new TestCase("""
836+
case C1 c -> {}
837+
case C3<Integer> c -> {}
838+
case C5<Integer, ?> c -> {}
839+
case C6<?, Integer> c -> {}
840+
""",
841+
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C1)",
842+
"- compiler.note.preview.filename: Test.java, DEFAULT",
843+
"- compiler.note.preview.recompile",
844+
"1 error"),
845+
new TestCase("""
846+
case C2<?> c -> {}
847+
case C3<Integer> c -> {}
848+
case C5<Integer, ?> c -> {}
849+
case C6<?, Integer> c -> {}
850+
""",
851+
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C2<?>)",
852+
"- compiler.note.preview.filename: Test.java, DEFAULT",
853+
"- compiler.note.preview.recompile",
854+
"1 error"),
855+
new TestCase("""
856+
case C4<?, ?> c -> {}
857+
case C3<Integer> c -> {}
858+
case C5<Integer, ?> c -> {}
859+
case C6<?, Integer> c -> {}
860+
""",
861+
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C4<?,?>)",
862+
"- compiler.note.preview.filename: Test.java, DEFAULT",
863+
"- compiler.note.preview.recompile",
864+
"1 error"),
865+
};
866+
for (TestCase tc : subCases) {
867+
doTest(base,
868+
new String[0],
869+
"""
870+
package test;
871+
public class Test {
872+
sealed interface I<T> {}
873+
final class C1 implements I<String> {}
874+
final class C2<T> implements I<String> {}
875+
final class C3<T> implements I<T> {}
876+
final class C4<T, E> implements I<String> {}
877+
final class C5<T, E> implements I<T> {}
878+
final class C6<T, E> implements I<E> {}
879+
void t(I<Integer> i) {
880+
switch (i) {
881+
${cases}
882+
}
883+
}
884+
}
885+
""".replace("${cases}", tc.cases),
886+
tc.errors);
887+
}
888+
}
889+
802890
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
803891
Path current = base.resolve(".");
804892
Path libClasses = current.resolve("libClasses");

test/langtools/tools/javac/patterns/SwitchErrors.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,16 @@ Object guardWithMatchingExpression(Object o1, Object o2) {
185185
default -> null;
186186
};
187187
}
188-
void test8269146a(Integer i) {
188+
void test8269146a1(Integer i) {
189+
switch (i) {
190+
//error - illegal combination of pattern and constant:
191+
case 1, Integer o && o != null:
192+
break;
193+
default:
194+
break;
195+
}
196+
}
197+
void test8269146a2(Integer i) {
189198
switch (i) {
190199
//error - illegal combination of pattern and constant:
191200
case Integer o && o != null, 1:
@@ -210,7 +219,14 @@ void test8269146c(Integer i) {
210219
break;
211220
}
212221
}
213-
void test8269301(Integer i) {
222+
void test8269301a(Integer i) {
223+
switch (i) {
224+
//error - illegal combination of pattern, constant and default
225+
case 1, Integer o && o != null, default:
226+
break;
227+
}
228+
}
229+
void test8269301b(Integer i) {
214230
switch (i) {
215231
//error - illegal combination of pattern, constant and default
216232
case Integer o && o != null, 1, default:

test/langtools/tools/javac/patterns/SwitchErrors.out

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ SwitchErrors.java:160:18: compiler.err.pattern.dominated
3131
SwitchErrors.java:172:18: compiler.err.pattern.expected
3232
SwitchErrors.java:178:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
3333
SwitchErrors.java:184:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
34-
SwitchErrors.java:191:42: compiler.err.flows.through.from.pattern
35-
SwitchErrors.java:200:24: compiler.err.flows.through.to.pattern
36-
SwitchErrors.java:209:29: compiler.err.total.pattern.and.default
37-
SwitchErrors.java:216:42: compiler.err.flows.through.from.pattern
38-
SwitchErrors.java:216:45: compiler.err.flows.through.from.pattern
39-
SwitchErrors.java:228:18: compiler.err.duplicate.total.pattern
34+
SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern
35+
SwitchErrors.java:200:42: compiler.err.pattern.dominated
36+
SwitchErrors.java:209:24: compiler.err.flows.through.to.pattern
37+
SwitchErrors.java:218:29: compiler.err.total.pattern.and.default
38+
SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern
39+
SwitchErrors.java:225:45: compiler.err.flows.through.from.pattern
40+
SwitchErrors.java:232:42: compiler.err.pattern.dominated
41+
SwitchErrors.java:232:45: compiler.err.flows.through.from.pattern
42+
SwitchErrors.java:244:18: compiler.err.duplicate.total.pattern
4043
SwitchErrors.java:9:9: compiler.err.not.exhaustive.statement
4144
SwitchErrors.java:15:9: compiler.err.not.exhaustive.statement
4245
SwitchErrors.java:21:9: compiler.err.not.exhaustive.statement
@@ -48,7 +51,7 @@ SwitchErrors.java:91:9: compiler.err.not.exhaustive.statement
4851
SwitchErrors.java:97:9: compiler.err.not.exhaustive.statement
4952
SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement
5053
SwitchErrors.java:164:9: compiler.err.not.exhaustive.statement
51-
SwitchErrors.java:221:9: compiler.err.not.exhaustive.statement
54+
SwitchErrors.java:237:9: compiler.err.not.exhaustive.statement
5255
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT
5356
- compiler.note.preview.recompile
54-
51 errors
57+
54 errors

0 commit comments

Comments
 (0)