From 1f09467230971d6458918ec19ffeb259b8c028a3 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 5 Jun 2024 05:45:59 +0000 Subject: [PATCH] 8327368: javac crash when computing exhaustiveness checks Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Flow.java | 24 ++++-- .../tools/javac/patterns/Exhaustiveness.java | 81 ++++++++++++++++++- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java index da008bc4b53..89709bd3ec7 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Flow.java @@ -937,7 +937,7 @@ public class Flow { for (PatternDescription pdOther : patterns) { if (pdOther instanceof BindingPattern bpOther) { Set currentPermittedSubTypes = - allPermittedSubTypes((ClassSymbol) bpOther.type.tsym, s -> true); + allPermittedSubTypes(bpOther.type.tsym, s -> true); PERMITTED: for (Iterator it = permitted.iterator(); it.hasNext();) { Symbol perm = it.next(); @@ -973,9 +973,9 @@ public class Flow { return patterns; } - private Set allPermittedSubTypes(ClassSymbol root, Predicate accept) { + private Set allPermittedSubTypes(TypeSymbol root, Predicate accept) { Set permitted = new HashSet<>(); - List permittedSubtypesClosure = List.of(root); + List permittedSubtypesClosure = baseClasses(root); while (permittedSubtypesClosure.nonEmpty()) { ClassSymbol current = permittedSubtypesClosure.head; @@ -999,6 +999,20 @@ public class Flow { return permitted; } + private List baseClasses(TypeSymbol root) { + if (root instanceof ClassSymbol clazz) { + return List.of(clazz); + } else if (root instanceof TypeVariableSymbol tvar) { + ListBuffer 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: * $record($prefix$, $nested, $suffix$) * Where $record, $prefix$ and $suffix$ is the same for each pattern @@ -1144,7 +1158,7 @@ public class Flow { reducedNestedPatterns[i] = newNested; } - covered &= isBpCovered(componentType[i], newNested); + covered &= checkCovered(componentType[i], List.of(newNested)); } if (covered) { return new BindingPattern(rpOne.recordType); @@ -1175,7 +1189,7 @@ public class Flow { } } } else if (pd instanceof BindingPattern bp) { - Set permittedSymbols = allPermittedSubTypes((ClassSymbol) bp.type.tsym, cs -> true); + Set permittedSymbols = allPermittedSubTypes(bp.type.tsym, cs -> true); if (!permittedSymbols.isEmpty()) { for (Symbol permitted : permittedSymbols) { diff --git a/test/langtools/tools/javac/patterns/Exhaustiveness.java b/test/langtools/tools/javac/patterns/Exhaustiveness.java index f8ee25f0d32..3f86ada6e7e 100644 --- a/test/langtools/tools/javac/patterns/Exhaustiveness.java +++ b/test/langtools/tools/javac/patterns/Exhaustiveness.java @@ -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. * * 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 + * @bug 8262891 8268871 8274363 8281100 8294670 8311038 8311815 8325215 8333169 8327368 * @summary Check exhaustiveness of switches over sealed types. * @library /tools/lib * @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 b) {} + + static int r(R 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 a, T2 b) { } + + static int r(R 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 a, T2 b) { } + + static int r(R 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 { doTest(base, libraryCode, testCode, false, expectedErrors); }