8273328: Compiler implementation for Pattern Matching for switch (Second Preview)
Reviewed-by: vromero, mcimadamore
This commit is contained in:
parent
6d734604a3
commit
d085c2b8a7
src/jdk.compiler/share/classes/com/sun/tools/javac/comp
test/langtools/tools/javac/patterns
@ -1680,7 +1680,8 @@ public class Attr extends JCTree.Visitor {
|
||||
// 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 @@ public class Attr extends JCTree.Visitor {
|
||||
} 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 @@ public class Attr extends JCTree.Visitor {
|
||||
} 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 @@ public class Attr extends JCTree.Visitor {
|
||||
}
|
||||
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 @@ public class Flow {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class Flow {
|
||||
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 @@ public class Flow {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class Flow {
|
||||
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 @@ public class Flow {
|
||||
}
|
||||
|
||||
private boolean isExhaustive(Type seltype, Set<Symbol> covered) {
|
||||
transitiveCovers(covered);
|
||||
transitiveCovers(seltype, covered);
|
||||
return switch (seltype.getTag()) {
|
||||
case CLASS -> {
|
||||
if (seltype.isCompound()) {
|
||||
|
@ -68,6 +68,20 @@ public class Domination {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -75,6 +89,20 @@ public class Domination {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class Domination {
|
||||
}
|
||||
}
|
||||
|
||||
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,8 +52,8 @@ public class EnumTypeChanges {
|
||||
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"; }
|
||||
}
|
||||
}
|
||||
@ -61,8 +61,8 @@ public class EnumTypeChanges {
|
||||
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 @@ public class Exhaustiveness extends TestRunner {
|
||||
""");
|
||||
}
|
||||
|
||||
@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 @@ public class SwitchErrors {
|
||||
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 @@ public class SwitchErrors {
|
||||
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
|
||||
|
@ -265,8 +265,8 @@ public class Switches {
|
||||
switch (e) {
|
||||
case A: return "a";
|
||||
case B: return "b";
|
||||
case E x && "A".equals(x.name()): return "broken";
|
||||
case C: return String.valueOf(e);
|
||||
case E x && "A".equals(x.name()): return "broken";
|
||||
case null, E x: return String.valueOf(x);
|
||||
}
|
||||
}
|
||||
@ -275,8 +275,8 @@ public class Switches {
|
||||
return switch (e) {
|
||||
case A -> "a";
|
||||
case B -> "b";
|
||||
case E x && "A".equals(x.name()) -> "broken";
|
||||
case C -> String.valueOf(e);
|
||||
case E x && "A".equals(x.name()) -> "broken";
|
||||
case null, E x -> String.valueOf(x);
|
||||
};
|
||||
}
|
||||
@ -286,8 +286,7 @@ public class Switches {
|
||||
case A: return "a";
|
||||
case B: return "b";
|
||||
case E x && "C".equals(x.name()): return "C";
|
||||
case C: return "broken";
|
||||
case null, E x: return String.valueOf(x);
|
||||
case null, E x: return e == E.C ? "broken" : String.valueOf(x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,8 +295,7 @@ public class Switches {
|
||||
case A -> "a";
|
||||
case B -> "b";
|
||||
case E x && "C".equals(x.name()) -> "C";
|
||||
case C -> "broken";
|
||||
case null, E x -> String.valueOf(x);
|
||||
case null, E x -> e == E.C ? "broken" : String.valueOf(x);
|
||||
};
|
||||
}
|
||||
|
||||
@ -306,8 +304,7 @@ public class Switches {
|
||||
case A: return "a";
|
||||
case B: return "b";
|
||||
case Object x && "C".equals(x.toString()): return "C";
|
||||
case C: return "broken";
|
||||
case null, E x: return String.valueOf(x);
|
||||
case null, E x: return e == E.C ? "broken" : String.valueOf(x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,8 +313,7 @@ public class Switches {
|
||||
case A -> "a";
|
||||
case B -> "b";
|
||||
case Object x && "C".equals(x.toString()) -> "C";
|
||||
case C -> "broken";
|
||||
case null, E x -> String.valueOf(x);
|
||||
case null, E x -> e == E.C ? "broken" : String.valueOf(x);
|
||||
};
|
||||
}
|
||||
|
||||
@ -326,8 +322,7 @@ public class Switches {
|
||||
case A: return "a";
|
||||
case B: return "b";
|
||||
case Runnable x && "C".equals(x.toString()): return "C";
|
||||
case C: return "broken";
|
||||
case null, E x: return String.valueOf(x);
|
||||
case null, E x: return e == E.C ? "broken" : String.valueOf(x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,8 +331,7 @@ public class Switches {
|
||||
case A -> "a";
|
||||
case B -> "b";
|
||||
case Runnable x && "C".equals(x.toString()) -> "C";
|
||||
case C -> "broken";
|
||||
case null, E x -> String.valueOf(x);
|
||||
case null, E x -> e == E.C ? "broken" : String.valueOf(x);
|
||||
};
|
||||
}
|
||||
|
||||
@ -346,8 +340,7 @@ public class Switches {
|
||||
case "A": return "a";
|
||||
case Switches.ConstantClassClash: return "b";
|
||||
case String x && "C".equals(x): return "C";
|
||||
case "C": return "broken";
|
||||
case null, String x: return String.valueOf(x);
|
||||
case null, String x: return "C".equals(x) ? "broken" : String.valueOf(x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,8 +349,7 @@ public class Switches {
|
||||
case "A" -> "a";
|
||||
case ConstantClassClash -> "b";
|
||||
case String x && "C".equals(x) -> "C";
|
||||
case "C" -> "broken";
|
||||
case null, String x -> String.valueOf(x);
|
||||
case null, String x -> e == E.C ? "broken" : String.valueOf(x);
|
||||
};
|
||||
}
|
||||
|
||||
@ -366,8 +358,7 @@ public class Switches {
|
||||
case 0: return "a";
|
||||
case 1: return "b";
|
||||
case Integer x && x.equals(2): return "C";
|
||||
case 2: return "broken";
|
||||
case null, Integer x: return String.valueOf(x);
|
||||
case null, Integer x: return Objects.equals(x, 2) ? "broken" : String.valueOf(x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,8 +367,7 @@ public class Switches {
|
||||
case 0 -> "a";
|
||||
case 1 -> "b";
|
||||
case Integer x && x.equals(2) -> "C";
|
||||
case 2 -> "broken";
|
||||
case null, Integer x -> String.valueOf(x);
|
||||
case null, Integer x -> Objects.equals(x, 2) ? "broken" : String.valueOf(x);
|
||||
};
|
||||
}
|
||||
|
||||
@ -412,7 +402,6 @@ public class Switches {
|
||||
switch (i) {
|
||||
case Integer o && o != null:
|
||||
r = 1;
|
||||
case -1: r = 1;
|
||||
case null, default:
|
||||
r = 2;
|
||||
}
|
||||
@ -424,7 +413,6 @@ public class Switches {
|
||||
int r = switch (i) {
|
||||
case Integer o && o != null:
|
||||
r = 1;
|
||||
case -1: r = 1;
|
||||
case null, default:
|
||||
r = 2;
|
||||
yield r;
|
||||
|
Loading…
x
Reference in New Issue
Block a user