8281100: Spurious "variable might not have been initialized" with sealed class switch

Reviewed-by: vromero
This commit is contained in:
Jan Lahoda 2022-02-11 12:11:29 +00:00
parent d254cf28c5
commit 4ff5824f5b
4 changed files with 104 additions and 17 deletions
src/jdk.compiler/share/classes/com/sun/tools/javac
test/langtools/tools/javac/patterns

@ -664,10 +664,7 @@ public class Flow {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
scan(tree.selector);
boolean exhaustiveSwitch = tree.patternSwitch ||
tree.cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(l -> TreeInfo.isNull(l));
boolean exhaustiveSwitch = TreeInfo.expectedExhaustive(tree);
Set<Symbol> constants = exhaustiveSwitch ? new HashSet<>() : null;
for (List<JCCase> l = tree.cases; l.nonEmpty(); l = l.tail) {
alive = Liveness.ALIVE;
@ -689,10 +686,13 @@ public class Flow {
l.tail.head.pos(),
Warnings.PossibleFallThroughIntoCase);
}
if (!tree.hasTotalPattern && exhaustiveSwitch &&
!TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) &&
(constants == null || !isExhaustive(tree.selector.pos(), tree.selector.type, constants))) {
log.error(tree, Errors.NotExhaustiveStatement);
tree.isExhaustive = tree.hasTotalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases);
if (exhaustiveSwitch) {
tree.isExhaustive |= isExhaustive(tree.selector.pos(), tree.selector.type, constants);
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustiveStatement);
}
}
if (!tree.hasTotalPattern) {
alive = Liveness.ALIVE;
@ -725,8 +725,10 @@ public class Flow {
}
}
}
if (!tree.hasTotalPattern && !TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) &&
!isExhaustive(tree.selector.pos(), tree.selector.type, constants)) {
tree.isExhaustive = tree.hasTotalPattern ||
TreeInfo.isErrorEnumSwitch(tree.selector, tree.cases) ||
isExhaustive(tree.selector.pos(), tree.selector.type, constants);
if (!tree.isExhaustive) {
log.error(tree, Errors.NotExhaustive);
}
alive = prevAlive;
@ -2432,15 +2434,15 @@ public class Flow {
}
public void visitSwitch(JCSwitch tree) {
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive);
}
public void visitSwitchExpression(JCSwitchExpression tree) {
handleSwitch(tree, tree.selector, tree.cases, tree.hasTotalPattern);
handleSwitch(tree, tree.selector, tree.cases, tree.isExhaustive);
}
private void handleSwitch(JCTree tree, JCExpression selector,
List<JCCase> cases, boolean hasTotalPattern) {
List<JCCase> cases, boolean isExhaustive) {
ListBuffer<PendingExit> prevPendingExits = pendingExits;
pendingExits = new ListBuffer<>();
int nextadrPrev = nextadr;
@ -2478,10 +2480,10 @@ public class Flow {
addVars(c.stats, initsSwitch, uninitsSwitch);
// Warn about fall-through if lint switch fallthrough enabled.
}
if (!hasTotalPattern) {
if (!isExhaustive) {
if (tree.hasTag(SWITCH_EXPRESSION)) {
markDead();
} else {
} else if (tree.hasTag(SWITCH) && !TreeInfo.expectedExhaustive((JCSwitch) tree)) {
inits.assign(initsSwitch);
uninits.assign(uninits.andSet(uninitsSwitch));
}

@ -1285,6 +1285,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** Position of closing brace, optional. */
public int endpos = Position.NOPOS;
public boolean hasTotalPattern;
public boolean isExhaustive;
public boolean patternSwitch;
protected JCSwitch(JCExpression selector, List<JCCase> cases) {
this.selector = selector;
@ -1371,6 +1372,7 @@ public abstract class JCTree implements Tree, Cloneable, DiagnosticPosition {
/** Position of closing brace, optional. */
public int endpos = Position.NOPOS;
public boolean hasTotalPattern;
public boolean isExhaustive;
public boolean patternSwitch;
protected JCSwitchExpression(JCExpression selector, List<JCCase> cases) {
this.selector = selector;

@ -1384,4 +1384,10 @@ public class TreeInfo {
public record PatternPrimaryType(Type type, boolean unconditional) {}
public static boolean expectedExhaustive(JCSwitch tree) {
return tree.patternSwitch ||
tree.cases.stream()
.flatMap(c -> c.labels.stream())
.anyMatch(l -> TreeInfo.isNull(l));
}
}

@ -23,7 +23,7 @@
/**
* @test
* @bug 8262891 8268871 8274363
* @bug 8262891 8268871 8274363 8281100
* @summary Check exhaustiveness of switches over sealed types.
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -791,6 +791,82 @@ public class Exhaustiveness extends TestRunner {
}
}
@Test
public void testDefiniteAssignment(Path base) throws Exception {
doTest(base,
new String[]{"""
package lib;
public sealed interface S permits A, B {}
""",
"""
package lib;
public final class A implements S {}
""",
"""
package lib;
public final class B implements S {}
"""},
"""
package test;
import lib.*;
public class Test {
private void testStatement(S obj) {
int data;
switch (obj) {
case A a -> data = 0;
case B b -> data = 0;
};
System.err.println(data);
}
private void testExpression(S obj) {
int data;
int v = switch (obj) {
case A a -> data = 0;
case B b -> data = 0;
};
System.err.println(data);
}
private void testStatementNotExhaustive(S obj) {
int data;
switch (obj) {
case A a -> data = 0;
};
System.err.println(data);
}
private void testExpressionNotExhaustive(S obj) {
int data;
int v = switch (obj) {
case A a -> data = 0;
};
System.err.println(data);
}
private void testStatementErrorEnum(E e) { //"E" is intentionally unresolvable
int data;
switch (e) {
case A -> data = 0;
case B -> data = 0;
};
System.err.println(data);
}
private void testExpressionErrorEnum(E e) { //"E" is intentionally unresolvable
int data;
int v = switch (e) {
case A -> data = 0;
case B -> data = 0;
};
System.err.println(data);
}
}
""",
"Test.java:34:41: compiler.err.cant.resolve.location: kindname.class, E, , , (compiler.misc.location: kindname.class, test.Test, null)",
"Test.java:42:42: compiler.err.cant.resolve.location: kindname.class, E, , , (compiler.misc.location: kindname.class, test.Test, null)",
"Test.java:22:9: compiler.err.not.exhaustive.statement",
"Test.java:29:17: compiler.err.not.exhaustive",
"- compiler.note.preview.filename: Test.java, DEFAULT",
"- compiler.note.preview.recompile",
"4 errors");
}
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
Path current = base.resolve(".");
Path libClasses = current.resolve("libClasses");
@ -825,7 +901,8 @@ public class Exhaustiveness extends TestRunner {
"-source", JAVA_VERSION,
"-XDrawDiagnostics",
"-Xlint:-preview",
"--class-path", libClasses.toString())
"--class-path", libClasses.toString(),
"-XDshould-stop.at=FLOW")
.outdir(classes)
.files(tb.findJavaFiles(src))
.run(expectedErrors.length > 0 ? Task.Expect.FAIL : Task.Expect.SUCCESS)