8327368: javac crash when computing exhaustiveness checks

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2024-06-05 05:45:59 +00:00
parent 7dbd03388e
commit 1f09467230
2 changed files with 98 additions and 7 deletions

View File

@ -937,7 +937,7 @@ public class Flow {
for (PatternDescription pdOther : patterns) { for (PatternDescription pdOther : patterns) {
if (pdOther instanceof BindingPattern bpOther) { if (pdOther instanceof BindingPattern bpOther) {
Set<Symbol> currentPermittedSubTypes = Set<Symbol> currentPermittedSubTypes =
allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true); allPermittedSubTypes(bpOther.type.tsym, s -> true);
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) { PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
Symbol perm = it.next(); Symbol perm = it.next();
@ -973,9 +973,9 @@ public class Flow {
return patterns; return patterns;
} }
private Set<Symbol> allPermittedSubTypes(ClassSymbol root, Predicate<ClassSymbol> accept) { private Set<Symbol> allPermittedSubTypes(TypeSymbol root, Predicate<ClassSymbol> accept) {
Set<Symbol> permitted = new HashSet<>(); Set<Symbol> permitted = new HashSet<>();
List<ClassSymbol> permittedSubtypesClosure = List.of(root); List<ClassSymbol> permittedSubtypesClosure = baseClasses(root);
while (permittedSubtypesClosure.nonEmpty()) { while (permittedSubtypesClosure.nonEmpty()) {
ClassSymbol current = permittedSubtypesClosure.head; ClassSymbol current = permittedSubtypesClosure.head;
@ -999,6 +999,20 @@ public class Flow {
return permitted; return permitted;
} }
private List<ClassSymbol> baseClasses(TypeSymbol root) {
if (root instanceof ClassSymbol clazz) {
return List.of(clazz);
} else if (root instanceof TypeVariableSymbol tvar) {
ListBuffer<ClassSymbol> result = new ListBuffer<>();
for (Type bound : tvar.getBounds()) {
result.appendList(baseClasses(bound.tsym));
}
return result.toList();
} else {
return List.nil();
}
}
/* Among the set of patterns, find sub-set of patterns such: /* Among the set of patterns, find sub-set of patterns such:
* $record($prefix$, $nested, $suffix$) * $record($prefix$, $nested, $suffix$)
* Where $record, $prefix$ and $suffix$ is the same for each pattern * Where $record, $prefix$ and $suffix$ is the same for each pattern
@ -1144,7 +1158,7 @@ public class Flow {
reducedNestedPatterns[i] = newNested; reducedNestedPatterns[i] = newNested;
} }
covered &= isBpCovered(componentType[i], newNested); covered &= checkCovered(componentType[i], List.of(newNested));
} }
if (covered) { if (covered) {
return new BindingPattern(rpOne.recordType); return new BindingPattern(rpOne.recordType);
@ -1175,7 +1189,7 @@ public class Flow {
} }
} }
} else if (pd instanceof BindingPattern bp) { } else if (pd instanceof BindingPattern bp) {
Set<Symbol> permittedSymbols = allPermittedSubTypes((ClassSymbol) bp.type.tsym, cs -> true); Set<Symbol> permittedSymbols = allPermittedSubTypes(bp.type.tsym, cs -> true);
if (!permittedSymbols.isEmpty()) { if (!permittedSymbols.isEmpty()) {
for (Symbol permitted : permittedSymbols) { for (Symbol permitted : permittedSymbols) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,7 +23,7 @@
/** /**
* @test * @test
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368
* @summary Check exhaustiveness of switches over sealed types. * @summary Check exhaustiveness of switches over sealed types.
* @library /tools/lib * @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api * @modules jdk.compiler/com.sun.tools.javac.api
@ -2105,6 +2105,83 @@ public class Exhaustiveness extends TestRunner {
}"""); }""");
} }
@Test
public void testNestedIntersectionType(Path base) throws Exception {
doTest(base,
new String[0],
"""
public class Test {
static abstract class Abs {}
sealed interface B permits V {}
static final class V extends Abs implements B {}
final static record R<T extends B>(T b) {}
static <T extends Abs & B> int r(R<T> r) {
return switch (r) {
case R(B b) -> 3;
};
}
}
""");
}
@Test //JDK-8327368
public void testExpandForTypeVariables(Path base) throws Exception {
doTest(base,
new String[0],
"""
public class Test {
sealed interface A permits T, U {}
sealed interface B permits V, W {}
static final class T implements A { public T() {} }
static final class U implements A { public U() {} }
static final class V implements B { public V() {} }
static final class W implements B { public W() {} }
final static record R<T1 extends A, T2 extends B>(T1 a, T2 b) { }
static <T1 extends A, T2 extends B> int r(R<T1, T2> r) {
return switch (r) {
case R(A a, V b) -> 1; // Any A with specific B
case R(T a, var b) -> 2; // Specific A with any B
case R(U a, W b) -> 3; // Specific A with specific B
};
}
}
""");
doTest(base,
new String[0],
"""
public class Test {
sealed interface A permits T, U {}
sealed interface B permits V, W {}
static final class T implements A { public T() {} }
static final class U implements A { public U() {} }
static final class V extends Abs implements B { public V() {} }
static final class W extends Abs implements B { public W() {} }
static abstract class Abs {}
final static record R<T1 extends A, T2 extends B>(T1 a, T2 b) { }
static <T1 extends A, T2 extends Abs & B> int r(R<T1, T2> r) {
return switch (r) {
case R(A a, V b) -> 1; // Any A with specific B
case R(T a, var b) -> 2; // Specific A with any B
case R(U a, W b) -> 3; // Specific A with specific B
};
}
}
""");
}
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException { private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
doTest(base, libraryCode, testCode, false, expectedErrors); doTest(base, libraryCode, testCode, false, expectedErrors);
} }