Skip to content
Permalink
Browse files
8273328: Compiler implementation for Pattern Matching for switch (Sec…
…ond Preview)

Reviewed-by: vromero, mcimadamore
  • Loading branch information
Jan Lahoda committed Nov 24, 2021
1 parent 6d73460 commit d085c2b8a790a5ddfbb01a0ea4edd4051cfb704b
Showing 9 changed files with 207 additions and 49 deletions.
@@ -1680,7 +1680,8 @@ private void handleSwitch(JCTree switchTree,
// Attribute all cases and
// check that there are no duplicate case labels or default clauses.
Set<Object> labels = new HashSet<>(); // The set of case labels.
List<Type> coveredTypes = List.nil();
List<Type> coveredTypesForPatterns = List.nil();
List<Type> coveredTypesForConstants = List.nil();
boolean hasDefault = false; // Is there a default label?
boolean hasTotalPattern = false; // Is there a total pattern?
boolean hasNullPattern = false; // Is there a null pattern?
@@ -1718,7 +1719,7 @@ private void handleSwitch(JCTree switchTree,
} else if (!labels.add(sym)) {
log.error(pat.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(pat.pos(), coveredTypes, sym.type);
checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, sym.type);
}
} else if (errorEnumSwitch) {
//error recovery: the selector is erroneous, and all the case labels
@@ -1751,7 +1752,7 @@ private void handleSwitch(JCTree switchTree,
} else if (!labels.add(pattype.constValue())) {
log.error(c.pos(), Errors.DuplicateCaseLabel);
} else {
checkCaseLabelDominated(pat.pos(), coveredTypes, types.boxedTypeOrType(pattype));
checkCaseLabelDominated(pat.pos(), coveredTypesForConstants, types.boxedTypeOrType(pattype));
}
}
}
@@ -1784,9 +1785,12 @@ private void handleSwitch(JCTree switchTree,
}
hasTotalPattern = true;
}
checkCaseLabelDominated(pat.pos(), coveredTypes, patternType);
if (primary.unconditional() && !patternType.isErroneous()) {
coveredTypes = coveredTypes.prepend(patternType);
checkCaseLabelDominated(pat.pos(), coveredTypesForPatterns, patternType);
if (!patternType.isErroneous()) {
coveredTypesForConstants = coveredTypesForConstants.prepend(patternType);
if (primary.unconditional()) {
coveredTypesForPatterns = coveredTypesForPatterns.prepend(patternType);
}
}
}
currentBindings = matchBindingsComputer.switchCase(pat, currentBindings, matchBindings);
@@ -751,7 +751,7 @@ private void handleConstantCaseLabel(Set<Symbol> constants, JCCaseLabel pat) {
}
}

private void transitiveCovers(Set<Symbol> covered) {
private void transitiveCovers(Type seltype, Set<Symbol> covered) {
List<Symbol> todo = List.from(covered);
while (todo.nonEmpty()) {
Symbol sym = todo.head;
@@ -773,7 +773,7 @@ private void transitiveCovers(Set<Symbol> covered) {
case TYP -> {
for (Type sup : types.directSupertypes(sym.type)) {
if (sup.tsym.kind == TYP) {
if (isTransitivelyCovered(sup.tsym, covered) &&
if (isTransitivelyCovered(seltype, sup.tsym, covered) &&
covered.add(sup.tsym)) {
todo = todo.prepend(sup.tsym);
}
@@ -784,7 +784,7 @@ private void transitiveCovers(Set<Symbol> covered) {
}
}

private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
private boolean isTransitivelyCovered(Type seltype, Symbol sealed, Set<Symbol> covered) {
DeferredCompletionFailureHandler.Handler prevHandler =
dcfh.setHandler(dcfh.speculativeCodeHandler);
try {
@@ -793,7 +793,10 @@ private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
if (sealed.kind == TYP && sealed.isAbstract() && sealed.isSealed()) {
return ((ClassSymbol) sealed).permitted
.stream()
.allMatch(s -> isTransitivelyCovered(s, covered));
.filter(s -> {
return types.isCastable(seltype, s.type/*, types.noWarnings*/);
})
.allMatch(s -> isTransitivelyCovered(seltype, s, covered));
}
return false;
} catch (CompletionFailure cf) {
@@ -805,7 +808,7 @@ private boolean isTransitivelyCovered(Symbol sealed, Set<Symbol> covered) {
}

private boolean isExhaustive(Type seltype, Set<Symbol> covered) {
transitiveCovers(covered);
transitiveCovers(seltype, covered);
return switch (seltype.getTag()) {
case CLASS -> {
if (seltype.isCompound()) {
@@ -68,13 +68,41 @@ int testDominatesStringConstant(String str) {
}
}

int testDominatesStringConstant2(String str) {
switch (str) {
case (String s && s.isEmpty()): return 1;
case "": return -1;
}
}

int testDominatesStringConstant3(String str) {
switch (str) {
case (String s && !s.isEmpty()): return 1;
case "": return -1;
}
}

int testDominatesIntegerConstant(Integer i) {
switch (i) {
case Integer j: return 1;
case 0: return -1;
}
}

int testDominatesIntegerConstant2(Integer i) {
switch (i) {
case (Integer j && j == 0): return 1;
case 0: return -1;
}
}

int testDominatesIntegerConstant3(Integer i) {
switch (i) {
case (Integer j && j == 1): return 1;
case 0: return -1;
}
}

int testDominatesEnumConstant() {
enum E {
A, B;
@@ -86,4 +114,26 @@ enum E {
}
}

int testDominatesEnumConstant2() {
enum E {
A, B;
}
E e = E.A;
switch (e) {
case (E d && d == E.A): return 1;
case A: return -1;
}
}

int testDominatesEnumConstant3() {
enum E {
A, B;
}
E e = E.A;
switch (e) {
case (E d && d == E.B): return 1;
case A: return -1;
}
}

}
@@ -3,7 +3,13 @@ Domination.java:43:18: compiler.err.pattern.dominated
Domination.java:51:18: compiler.err.pattern.dominated
Domination.java:67:18: compiler.err.pattern.dominated
Domination.java:74:18: compiler.err.pattern.dominated
Domination.java:85:18: compiler.err.pattern.dominated
Domination.java:81:18: compiler.err.pattern.dominated
Domination.java:88:18: compiler.err.pattern.dominated
Domination.java:95:18: compiler.err.pattern.dominated
Domination.java:102:18: compiler.err.pattern.dominated
Domination.java:113:18: compiler.err.pattern.dominated
Domination.java:124:18: compiler.err.pattern.dominated
Domination.java:135:18: compiler.err.pattern.dominated
- compiler.note.preview.filename: Domination.java, DEFAULT
- compiler.note.preview.recompile
6 errors
12 errors
@@ -52,17 +52,17 @@ void doRun(Function<EnumTypeChangesEnum, String> c) throws Exception {
String statementEnum(EnumTypeChangesEnum e) {
switch (e) {
case A -> { return "A"; }
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
case B -> { return "B"; }
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
default -> { return "D"; }
}
}

String expressionEnum(EnumTypeChangesEnum e) {
return switch (e) {
case A -> "A";
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
case B -> "B";
case EnumTypeChangesEnum e1 && false -> throw new AssertionError();
default -> "D";
};
}
@@ -799,6 +799,94 @@ void test(A arg) {
""");
}

@Test
public void testOnlyApplicable(Path base) throws Exception {
record TestCase(String cases, String... errors) {}
TestCase[] subCases = new TestCase[] {
new TestCase("""
case C3<Integer> c -> {}
case C5<Integer, ?> c -> {}
case C6<?, Integer> c -> {}
"""), //OK
new TestCase("""
case C5<Integer, ?> c -> {}
case C6<?, Integer> c -> {}
""",
"Test.java:11:9: compiler.err.not.exhaustive.statement",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
new TestCase("""
case C3<Integer> c -> {}
case C6<?, Integer> c -> {}
""",
"Test.java:11:9: compiler.err.not.exhaustive.statement",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
new TestCase("""
case C3<Integer> c -> {}
case C5<Integer, ?> c -> {}
""",
"Test.java:11:9: compiler.err.not.exhaustive.statement",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
new TestCase("""
case C1 c -> {}
case C3<Integer> c -> {}
case C5<Integer, ?> c -> {}
case C6<?, Integer> c -> {}
""",
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C1)",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
new TestCase("""
case C2<?> c -> {}
case C3<Integer> c -> {}
case C5<Integer, ?> c -> {}
case C6<?, Integer> c -> {}
""",
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C2<?>)",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
new TestCase("""
case C4<?, ?> c -> {}
case C3<Integer> c -> {}
case C5<Integer, ?> c -> {}
case C6<?, Integer> c -> {}
""",
"Test.java:12:18: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: test.Test.I<java.lang.Integer>, test.Test.C4<?,?>)",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"1 error"),
};
for (TestCase tc : subCases) {
doTest(base,
new String[0],
"""
package test;
public class Test {
sealed interface I<T> {}
final class C1 implements I<String> {}
final class C2<T> implements I<String> {}
final class C3<T> implements I<T> {}
final class C4<T, E> implements I<String> {}
final class C5<T, E> implements I<T> {}
final class C6<T, E> implements I<E> {}
void t(I<Integer> i) {
switch (i) {
${cases}
}
}
}
""".replace("${cases}", tc.cases),
tc.errors);
}
}

private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path libClasses = current.resolve("libClasses");
@@ -185,7 +185,16 @@ Object guardWithMatchingExpression(Object o1, Object o2) {
default -> null;
};
}
void test8269146a(Integer i) {
void test8269146a1(Integer i) {
switch (i) {
//error - illegal combination of pattern and constant:
case 1, Integer o && o != null:
break;
default:
break;
}
}
void test8269146a2(Integer i) {
switch (i) {
//error - illegal combination of pattern and constant:
case Integer o && o != null, 1:
@@ -210,7 +219,14 @@ void test8269146c(Integer i) {
break;
}
}
void test8269301(Integer i) {
void test8269301a(Integer i) {
switch (i) {
//error - illegal combination of pattern, constant and default
case 1, Integer o && o != null, default:
break;
}
}
void test8269301b(Integer i) {
switch (i) {
//error - illegal combination of pattern, constant and default
case Integer o && o != null, 1, default:
@@ -31,12 +31,15 @@ SwitchErrors.java:160:18: compiler.err.pattern.dominated
SwitchErrors.java:172:18: compiler.err.pattern.expected
SwitchErrors.java:178:76: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:184:71: compiler.err.cant.resolve.location: kindname.variable, n, , , (compiler.misc.location: kindname.class, SwitchErrors, null)
SwitchErrors.java:191:42: compiler.err.flows.through.from.pattern
SwitchErrors.java:200:24: compiler.err.flows.through.to.pattern
SwitchErrors.java:209:29: compiler.err.total.pattern.and.default
SwitchErrors.java:216:42: compiler.err.flows.through.from.pattern
SwitchErrors.java:216:45: compiler.err.flows.through.from.pattern
SwitchErrors.java:228:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:191:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:200:42: compiler.err.pattern.dominated
SwitchErrors.java:209:24: compiler.err.flows.through.to.pattern
SwitchErrors.java:218:29: compiler.err.total.pattern.and.default
SwitchErrors.java:225:21: compiler.err.flows.through.to.pattern
SwitchErrors.java:225:45: compiler.err.flows.through.from.pattern
SwitchErrors.java:232:42: compiler.err.pattern.dominated
SwitchErrors.java:232:45: compiler.err.flows.through.from.pattern
SwitchErrors.java:244:18: compiler.err.duplicate.total.pattern
SwitchErrors.java:9:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:15:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:21:9: compiler.err.not.exhaustive.statement
@@ -48,7 +51,7 @@ SwitchErrors.java:91:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:97:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:104:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:164:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:221:9: compiler.err.not.exhaustive.statement
SwitchErrors.java:237:9: compiler.err.not.exhaustive.statement
- compiler.note.preview.filename: SwitchErrors.java, DEFAULT
- compiler.note.preview.recompile
51 errors
54 errors

1 comment on commit d085c2b

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on d085c2b Nov 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.