8327368: javac crash when computing exhaustiveness checks
Reviewed-by: vromero
This commit is contained in:
parent
7dbd03388e
commit
1f09467230
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user