mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8366968: Exhaustive switch expression rejected by for not covering all possible values
Reviewed-by: abimpoudis
This commit is contained in:
parent
f3dfdfa3fd
commit
87a4772198
@ -210,7 +210,6 @@ public class ExhaustivenessComputer {
|
||||
if (clazz.isSealed() && clazz.isAbstract() &&
|
||||
//if a binding pattern for clazz already exists, no need to analyze it again:
|
||||
!existingBindings.contains(clazz)) {
|
||||
ListBuffer<PatternDescription> bindings = new ListBuffer<>();
|
||||
//do not reduce to types unrelated to the selector type:
|
||||
Type clazzErasure = types.erasure(clazz.type);
|
||||
if (components(selectorType).stream()
|
||||
@ -230,30 +229,36 @@ public class ExhaustivenessComputer {
|
||||
return instantiated != null && types.isCastable(selectorType, instantiated);
|
||||
});
|
||||
|
||||
//the set of pending permitted subtypes needed to cover clazz:
|
||||
Set<Symbol> pendingPermitted = new HashSet<>(permitted);
|
||||
|
||||
for (PatternDescription pdOther : patterns) {
|
||||
if (pdOther instanceof BindingPattern bpOther) {
|
||||
Set<Symbol> currentPermittedSubTypes =
|
||||
allPermittedSubTypes(bpOther.type.tsym, s -> true);
|
||||
//remove all types from pendingPermitted that we can
|
||||
//cover using bpOther:
|
||||
|
||||
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
|
||||
Symbol perm = it.next();
|
||||
//all types that are permitted subtypes of bpOther's type:
|
||||
pendingPermitted.removeIf(pending -> types.isSubtype(types.erasure(pending.type),
|
||||
types.erasure(bpOther.type)));
|
||||
|
||||
for (Symbol currentPermitted : currentPermittedSubTypes) {
|
||||
if (types.isSubtype(types.erasure(currentPermitted.type),
|
||||
types.erasure(perm.type))) {
|
||||
it.remove();
|
||||
continue PERMITTED;
|
||||
}
|
||||
}
|
||||
if (types.isSubtype(types.erasure(perm.type),
|
||||
types.erasure(bpOther.type))) {
|
||||
it.remove();
|
||||
}
|
||||
if (bpOther.type.tsym.isAbstract()) {
|
||||
//all types that are in a diamond hierarchy with bpOther's type
|
||||
//i.e. there's a common subtype of the given type and bpOther's type:
|
||||
Predicate<Symbol> check =
|
||||
pending -> permitted.stream()
|
||||
.filter(perm -> types.isSubtype(types.erasure(perm.type),
|
||||
types.erasure(bpOther.type)))
|
||||
.filter(perm -> types.isSubtype(types.erasure(perm.type),
|
||||
types.erasure(pending.type)))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
|
||||
pendingPermitted.removeIf(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (permitted.isEmpty()) {
|
||||
if (pendingPermitted.isEmpty()) {
|
||||
toAdd.add(new BindingPattern(clazz.type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368
|
||||
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968
|
||||
* @summary Check exhaustiveness of switches over sealed types.
|
||||
* @library /tools/lib
|
||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||
@ -2182,6 +2182,135 @@ public class Exhaustiveness extends TestRunner {
|
||||
""");
|
||||
}
|
||||
|
||||
@Test //JDK-8366968
|
||||
public void testNonSealedDiamond(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Demo {
|
||||
|
||||
sealed interface Base permits Special, Value {}
|
||||
|
||||
non-sealed interface Value extends Base {}
|
||||
|
||||
sealed interface Special extends Base permits SpecialValue {}
|
||||
|
||||
non-sealed interface SpecialValue extends Value, Special {}
|
||||
|
||||
static int demo(final Base base) {
|
||||
return switch (base) {
|
||||
case Value value -> 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Test //JDK-8366968
|
||||
public void testNonSealedDiamond2(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Demo {
|
||||
|
||||
sealed interface Base permits Special, Value {}
|
||||
|
||||
non-sealed interface Value extends Base {}
|
||||
|
||||
non-sealed interface Special extends Base {}
|
||||
|
||||
interface SpecialValue extends Value, Special {}
|
||||
|
||||
static int demo(final Base base) {
|
||||
return switch (base) {
|
||||
case Value value -> 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
""",
|
||||
"Demo.java:12:16: compiler.err.not.exhaustive",
|
||||
"1 error");
|
||||
}
|
||||
|
||||
@Test //JDK-8366968
|
||||
public void testNonAbstract(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Demo {
|
||||
sealed interface I permits Base, C3 { }
|
||||
sealed class Base implements I permits C1, C2 { }
|
||||
final class C1 extends Base { }
|
||||
final class C2 extends Base { }
|
||||
final class C3 implements I { }
|
||||
|
||||
void method1(I i) {
|
||||
switch (i) {
|
||||
case C1 _ -> {}
|
||||
case C2 _ -> {}
|
||||
case C3 _ -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
""",
|
||||
"Demo.java:9:9: compiler.err.not.exhaustive.statement",
|
||||
"1 error");
|
||||
}
|
||||
|
||||
@Test //JDK-8366968
|
||||
public void testNonSealedDiamondGeneric(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Demo {
|
||||
class SomeType {}
|
||||
sealed interface Base<T extends SomeType> permits Special, Value {}
|
||||
non-sealed interface Value<T extends SomeType> extends Base<T> {}
|
||||
sealed interface Special<T extends SomeType> extends Base<T> permits SpecialValue {}
|
||||
non-sealed interface SpecialValue<T extends SomeType> extends Value<T>, Special<T> {}
|
||||
|
||||
static <T extends SomeType> int demo(final Base<T> base) {
|
||||
return switch (base) {
|
||||
case Value<T> value -> 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Test //JDK-8366968
|
||||
public void testNonSealedDiamondMultiple(Path base) throws Exception {
|
||||
doTest(base,
|
||||
new String[0],
|
||||
"""
|
||||
class Demo {
|
||||
|
||||
sealed interface Base permits Special, Value {}
|
||||
|
||||
non-sealed interface Value extends Base {}
|
||||
|
||||
sealed interface Special extends Base permits SpecialValue, Special2 {}
|
||||
|
||||
non-sealed interface SpecialValue extends Value, Special {}
|
||||
non-sealed interface Special2 extends Special {}
|
||||
|
||||
static int demo(final Base base) {
|
||||
return switch (base) {
|
||||
case Value value -> 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
""",
|
||||
"Demo.java:13:16: compiler.err.not.exhaustive",
|
||||
"1 error");
|
||||
}
|
||||
|
||||
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
|
||||
doTest(base, libraryCode, testCode, false, expectedErrors);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user