Skip to content

Commit 87a4772

Browse files
committed
8366968: Exhaustive switch expression rejected by for not covering all possible values
Reviewed-by: abimpoudis
1 parent f3dfdfa commit 87a4772

File tree

2 files changed

+155
-21
lines changed

2 files changed

+155
-21
lines changed

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ExhaustivenessComputer.java

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,6 @@ private Set<PatternDescription> reduceBindingPatterns(Type selectorType, Set<Pat
210210
if (clazz.isSealed() && clazz.isAbstract() &&
211211
//if a binding pattern for clazz already exists, no need to analyze it again:
212212
!existingBindings.contains(clazz)) {
213-
ListBuffer<PatternDescription> bindings = new ListBuffer<>();
214213
//do not reduce to types unrelated to the selector type:
215214
Type clazzErasure = types.erasure(clazz.type);
216215
if (components(selectorType).stream()
@@ -230,30 +229,36 @@ private Set<PatternDescription> reduceBindingPatterns(Type selectorType, Set<Pat
230229
return instantiated != null && types.isCastable(selectorType, instantiated);
231230
});
232231

232+
//the set of pending permitted subtypes needed to cover clazz:
233+
Set<Symbol> pendingPermitted = new HashSet<>(permitted);
234+
233235
for (PatternDescription pdOther : patterns) {
234236
if (pdOther instanceof BindingPattern bpOther) {
235-
Set<Symbol> currentPermittedSubTypes =
236-
allPermittedSubTypes(bpOther.type.tsym, s -> true);
237-
238-
PERMITTED: for (Iterator<Symbol> it = permitted.iterator(); it.hasNext();) {
239-
Symbol perm = it.next();
240-
241-
for (Symbol currentPermitted : currentPermittedSubTypes) {
242-
if (types.isSubtype(types.erasure(currentPermitted.type),
243-
types.erasure(perm.type))) {
244-
it.remove();
245-
continue PERMITTED;
246-
}
247-
}
248-
if (types.isSubtype(types.erasure(perm.type),
249-
types.erasure(bpOther.type))) {
250-
it.remove();
251-
}
237+
//remove all types from pendingPermitted that we can
238+
//cover using bpOther:
239+
240+
//all types that are permitted subtypes of bpOther's type:
241+
pendingPermitted.removeIf(pending -> types.isSubtype(types.erasure(pending.type),
242+
types.erasure(bpOther.type)));
243+
244+
if (bpOther.type.tsym.isAbstract()) {
245+
//all types that are in a diamond hierarchy with bpOther's type
246+
//i.e. there's a common subtype of the given type and bpOther's type:
247+
Predicate<Symbol> check =
248+
pending -> permitted.stream()
249+
.filter(perm -> types.isSubtype(types.erasure(perm.type),
250+
types.erasure(bpOther.type)))
251+
.filter(perm -> types.isSubtype(types.erasure(perm.type),
252+
types.erasure(pending.type)))
253+
.findAny()
254+
.isPresent();
255+
256+
pendingPermitted.removeIf(check);
252257
}
253258
}
254259
}
255260

256-
if (permitted.isEmpty()) {
261+
if (pendingPermitted.isEmpty()) {
257262
toAdd.add(new BindingPattern(clazz.type));
258263
}
259264
}

test/langtools/tools/javac/patterns/Exhaustiveness.java

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,7 @@
2323

2424
/**
2525
* @test
26-
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368
26+
* @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 8366968
2727
* @summary Check exhaustiveness of switches over sealed types.
2828
* @library /tools/lib
2929
* @modules jdk.compiler/com.sun.tools.javac.api
@@ -2182,6 +2182,135 @@ static <T1 extends A, T2 extends Abs & B> int r(R<T1, T2> r) {
21822182
""");
21832183
}
21842184

2185+
@Test //JDK-8366968
2186+
public void testNonSealedDiamond(Path base) throws Exception {
2187+
doTest(base,
2188+
new String[0],
2189+
"""
2190+
class Demo {
2191+
2192+
sealed interface Base permits Special, Value {}
2193+
2194+
non-sealed interface Value extends Base {}
2195+
2196+
sealed interface Special extends Base permits SpecialValue {}
2197+
2198+
non-sealed interface SpecialValue extends Value, Special {}
2199+
2200+
static int demo(final Base base) {
2201+
return switch (base) {
2202+
case Value value -> 0;
2203+
};
2204+
2205+
}
2206+
2207+
}
2208+
""");
2209+
}
2210+
2211+
@Test //JDK-8366968
2212+
public void testNonSealedDiamond2(Path base) throws Exception {
2213+
doTest(base,
2214+
new String[0],
2215+
"""
2216+
class Demo {
2217+
2218+
sealed interface Base permits Special, Value {}
2219+
2220+
non-sealed interface Value extends Base {}
2221+
2222+
non-sealed interface Special extends Base {}
2223+
2224+
interface SpecialValue extends Value, Special {}
2225+
2226+
static int demo(final Base base) {
2227+
return switch (base) {
2228+
case Value value -> 0;
2229+
};
2230+
2231+
}
2232+
2233+
}
2234+
""",
2235+
"Demo.java:12:16: compiler.err.not.exhaustive",
2236+
"1 error");
2237+
}
2238+
2239+
@Test //JDK-8366968
2240+
public void testNonAbstract(Path base) throws Exception {
2241+
doTest(base,
2242+
new String[0],
2243+
"""
2244+
class Demo {
2245+
sealed interface I permits Base, C3 { }
2246+
sealed class Base implements I permits C1, C2 { }
2247+
final class C1 extends Base { }
2248+
final class C2 extends Base { }
2249+
final class C3 implements I { }
2250+
2251+
void method1(I i) {
2252+
switch (i) {
2253+
case C1 _ -> {}
2254+
case C2 _ -> {}
2255+
case C3 _ -> {}
2256+
}
2257+
}
2258+
}
2259+
""",
2260+
"Demo.java:9:9: compiler.err.not.exhaustive.statement",
2261+
"1 error");
2262+
}
2263+
2264+
@Test //JDK-8366968
2265+
public void testNonSealedDiamondGeneric(Path base) throws Exception {
2266+
doTest(base,
2267+
new String[0],
2268+
"""
2269+
class Demo {
2270+
class SomeType {}
2271+
sealed interface Base<T extends SomeType> permits Special, Value {}
2272+
non-sealed interface Value<T extends SomeType> extends Base<T> {}
2273+
sealed interface Special<T extends SomeType> extends Base<T> permits SpecialValue {}
2274+
non-sealed interface SpecialValue<T extends SomeType> extends Value<T>, Special<T> {}
2275+
2276+
static <T extends SomeType> int demo(final Base<T> base) {
2277+
return switch (base) {
2278+
case Value<T> value -> 0;
2279+
};
2280+
}
2281+
}
2282+
""");
2283+
}
2284+
2285+
@Test //JDK-8366968
2286+
public void testNonSealedDiamondMultiple(Path base) throws Exception {
2287+
doTest(base,
2288+
new String[0],
2289+
"""
2290+
class Demo {
2291+
2292+
sealed interface Base permits Special, Value {}
2293+
2294+
non-sealed interface Value extends Base {}
2295+
2296+
sealed interface Special extends Base permits SpecialValue, Special2 {}
2297+
2298+
non-sealed interface SpecialValue extends Value, Special {}
2299+
non-sealed interface Special2 extends Special {}
2300+
2301+
static int demo(final Base base) {
2302+
return switch (base) {
2303+
case Value value -> 0;
2304+
};
2305+
2306+
}
2307+
2308+
}
2309+
""",
2310+
"Demo.java:13:16: compiler.err.not.exhaustive",
2311+
"1 error");
2312+
}
2313+
21852314
private void doTest(Path base, String[] libraryCode, String testCode, String... expectedErrors) throws IOException {
21862315
doTest(base, libraryCode, testCode, false, expectedErrors);
21872316
}

0 commit comments

Comments
 (0)