jdk-24/test/langtools/tools/javac/sealed/SealedCompilationTests.java
2024-11-08 12:27:31 +00:00

1345 lines
42 KiB
Java

/*
* Copyright (c) 2019, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* SealedCompilationTests
*
* @test
* @bug 8246353 8273257 8294550
* @summary Negative compilation tests, and positive compilation (smoke) tests for sealed classes
* @library /lib/combo /tools/lib
* @modules
* jdk.compiler/com.sun.tools.javac.util
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* @build toolbox.ToolBox toolbox.JavacTask
* @run junit/othervm -DuseAP=false SealedCompilationTests
* @run junit/othervm -DuseAP=true SealedCompilationTests
*/
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.lang.model.SourceVersion;
import com.sun.tools.javac.util.Assert;
import org.junit.jupiter.api.Test;
import tools.javac.combo.CompilationTestCase;
import toolbox.ToolBox;
import toolbox.JavacTask;
import toolbox.Task;
import toolbox.Task.OutputKind;
import static org.junit.jupiter.api.Assertions.fail;
class SealedCompilationTests extends CompilationTestCase {
ToolBox tb = new ToolBox();
private static String[] OPTIONS_WITH_AP = { "-processor", SimplestAP.class.getName() };
/* simplest annotation processor just to force a round of annotation processing for all tests
*/
@SupportedAnnotationTypes("*")
public static class SimplestAP extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return true;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
}
SealedCompilationTests() {
boolean useAP = System.getProperty("useAP") == null ? false : System.getProperty("useAP").equals("true");
setDefaultFilename("SealedTest.java");
setCompileOptions(useAP ? OPTIONS_WITH_AP : new String[]{});
System.out.println(useAP ? "running all tests using an annotation processor" : "running all tests without annotation processor");
}
private static final String NO_SHELL = """
#
""";
private static final String NEST_SHELL = """
class SealedTest {
#
}
""";
private static final String AUX_SHELL = """
class SealedTest {
}
#
""";
private static final List<String> SHELLS = List.of(NO_SHELL, NEST_SHELL, AUX_SHELL);
@Test
void testSimpleExtension() {
String CC1 =
"""
sealed class Sup # { }
# class Sub extends Sup { }
""";
String CC2 =
"""
sealed class Sup<T> # { }
# class Sub<T> extends Sup<T> { }
""";
String CC3 =
"""
sealed class Sup<T> # { }
# class Sub extends Sup<String> { }
""";
String AC1 =
"""
sealed abstract class Sup # { }
# class Sub extends Sup { }
""";
String AC2 =
"""
sealed abstract class Sup<T> # { }
# class Sub<T> extends Sup<T> { }
""";
String AC3 =
"""
sealed abstract class Sup<T> # { }
# class Sub extends Sup<String> { }
""";
String I1 =
"""
sealed interface Sup # { }
# class Sub implements Sup { }
""";
String I11 =
"""
sealed interface Sup<T> # { }
# class Sub<T> implements Sup<T> { }
""";
String I12 =
"""
sealed interface Sup<T> # { }
# class Sub<T> implements Sup<String> { }
""";
String I2 =
"""
sealed interface Sup # { }
# class Sub1 implements Sup { }
# class Sub2 implements Sup { }
""";
// Assert that all combinations work:
// { class, abs class, interface } x { implicit permits, explicit permits }
// x { final, non-sealed subtype }
for (String shell : SHELLS)
for (String b : List.of(CC1, CC2, CC3, AC1, AC2, AC3, I1, I11, I12))
for (String p : List.of("", "permits Sub"))
for (String m : List.of("final", "non-sealed", "non\u002Dsealed"))
assertOK(shell, b, p, m);
// Same for type with two subtypes
for (String shell : SHELLS)
for (String p : List.of("", "permits Sub1, Sub2"))
for (String m : List.of("final", "non-sealed", "non\u002Dsealed"))
assertOK(shell, expandMarkers(I2, p, m, m));
// Expect failure if there is no explicit final / sealed / non-sealed
for (String shell : SHELLS)
for (String b : List.of(CC1, CC2, CC3, AC1, AC2, AC3, I1, I11, I12))
for (String p : List.of("", "permits Sub"))
for (String m : List.of(""))
assertFail("compiler.err.non.sealed.sealed.or.final.expected", shell, expandMarkers(b, p, m));
}
@Test
void testSealedAndRecords() {
String P =
"""
sealed interface Sup # { }
record A(int a) implements Sup { }
record B(int b) implements Sup { }
record C(int c) implements Sup { }
""";
for (String shell : SHELLS)
for (String b : List.of(P))
for (String p : List.of("", "permits A, B, C"))
assertOK(shell, b, p);
}
// Test that a type that explicitly permits one type, can't be extended by another
@Test
void testBadExtension() {
String CC2 =
"""
sealed class Sup permits Sub1 { }
final class Sub1 extends Sup { }
final class Sub2 extends Sup { }
""";
String AC2 =
"""
sealed abstract class Sup permits Sub1 { }
final class Sub1 extends Sup { }
final class Sub2 extends Sup { }
""";
String I2c =
"""
sealed interface Sup permits Sub1 { }
final class Sub1 implements Sup { }
final class Sub2 implements Sup { }
""";
String I2i =
"""
sealed interface Sup permits Sub1 { }
non-sealed interface Sub1 extends Sup { }
non-sealed interface Sub2 extends Sup { }
""";
for (String shell : SHELLS)
for (String b : List.of(CC2, AC2, I2c, I2i))
assertFail("compiler.err.cant.inherit.from.sealed", shell, b);
}
@Test
void testRestrictedKeyword() {
for (String s : List.of(
"class SealedTest { String sealed; }",
"class SealedTest { int sealed = 0; int non = 0; int ns = non-sealed; }",
"class SealedTest { void test(String sealed) { } }",
"class SealedTest { void sealed(String sealed) { } }",
"class SealedTest { void test() { String sealed = null; } }",
"class SealedTest { String permits; }",
"class SealedTest { int permits = 0; }",
"class SealedTest { void test(String permits) { } }",
"class SealedTest { void permits(String permits) { } }",
"class SealedTest { void test() { String permits = null; } }"
)) {
assertOK(s);
}
for (String s : List.of(
"class sealed {}",
"enum sealed {}",
"record sealed() {}",
"interface sealed {}",
"@interface sealed {}",
"class permits {}",
"enum permits {}",
"record permits() {}",
"interface permits {}",
"@interface permits {}"
)) {
assertFail("compiler.err.restricted.type.not.allowed", s);
}
for (String s : List.of(
"class Foo { sealed m() {} }",
"class Foo { sealed i; }",
"class Foo { void m() { sealed i; } }",
"class Foo { void m(sealed i) {} }",
"class Foo { permits m() {} }",
"class Foo { permits i; }",
"class Foo { void m() { permits i; } }",
"class Foo { void m(permits i) {} }"
)) {
assertFail("compiler.err.restricted.type.not.allowed.here", s);
}
}
@Test
void testRejectPermitsInNonSealedClass() {
assertFail("compiler.err.invalid.permits.clause",
"class SealedTest {\n" +
" class NotSealed permits Sub {}\n" +
" class Sub extends NotSealed {}\n" +
"}");
assertFail("compiler.err.invalid.permits.clause",
"class SealedTest {\n" +
" interface NotSealed permits Sub {}\n" +
" class Sub implements NotSealed {}\n" +
"}");
}
@Test
void testTypeInPermitsIsSameClassOrSuper() {
assertFail("compiler.err.invalid.permits.clause",
"""
sealed class Sealed permits Sealed {}
"""
);
assertFail("compiler.err.invalid.permits.clause",
"""
interface I {}
sealed class Sealed implements I permits I {}
"""
);
assertFail("compiler.err.invalid.permits.clause",
"""
interface I {}
interface I2 extends I {}
sealed class Sealed implements I2 permits I {}
"""
);
}
/* It is a compile-time error if a class declaration has more than one of the class modifiers
* sealed, non-sealed and final
*/
@Test
void testBadModifiers() {
assertFail("compiler.err.non.sealed.with.no.sealed.supertype",
"class SealedTest { non-sealed class NoSealedSuper {} }");
assertFail("compiler.err.mod.not.allowed.here",
"class SealedTest { sealed public void m() {} }");
for (String s : List.of(
"class SealedTest { sealed non-sealed class Super {} }",
"class SealedTest { final non-sealed class Super {} }",
"class SealedTest { final sealed class Super {} }",
"class SealedTest { final sealed non-sealed class Super {} }",
"class SealedTest {\n" +
" sealed class Super {}\n" +
" sealed non-sealed class Sub extends Super {}\n" +
"}",
"sealed public @interface SealedTest { }"))
assertFail("compiler.err.illegal.combination.of.modifiers", s);
}
@Test
void testAnonymous_FunctionalExpr_and_Sealed() {
for (String s : List.of(
"""
sealed interface I extends Runnable {
public static I i = () -> {};
}
final class Sub implements I {}
""",
"""
sealed interface I extends Runnable {}
final class Sub implements I {
I a = Sub::action;
static void action() {}
}
"""
))
assertFail("compiler.err.prob.found.req", s);
for (String s : List.of(
"""
@FunctionalInterface
sealed interface Action {
void doAction();
}
final class C implements Action {
public void doAction() {}
}
"""
))
assertFail("compiler.err.bad.functional.intf.anno.1", s);
for (String s : List.of(
"""
sealed interface I extends Runnable {
public static I i = new I() { public void run() { } };
}
final class C implements I {
@Override public void run() {}
}
"""
))
assertFail("compiler.err.local.classes.cant.extend.sealed", s);
for (String s : List.of(
"""
sealed interface I extends Runnable {
public static void foo() { new I() { public void run() { } }; }
}
final class C implements I {
@Override public void run() {}
}
"""
))
assertFail("compiler.err.local.classes.cant.extend.sealed", s);
}
@Test
void testNoLocalSealedClasses() {
for (String s : List.of(
"""
sealed class C {
void m() {
sealed class D { }
}
}
""",
"""
sealed class C {
void m() {
non-sealed class D { }
}
}
"""))
assertFail("compiler.err.sealed.or.non.sealed.local.classes.not.allowed", s);
}
@Test
void testLocalCantExtendSealed() {
for (String s : List.of(
"""
sealed class C {
void m() {
final class D extends C { }
}
}
""",
"""
sealed class C {
void m() {
class L {
final class D extends C { }
}
}
}
""",
"""
sealed class C {
void m() {
class L {
void foo() {
final class D extends C { }
}
}
}
}
"""))
assertFail("compiler.err.local.classes.cant.extend.sealed", s);
}
@Test
void testSealedInterfaceAndAbstracClasses() {
for (String s : List.of(
"""
sealed interface I {}
""",
"""
sealed abstract class AC {}
""",
"""
sealed class C {}
"""))
assertFail("compiler.err.sealed.class.must.have.subclasses", s);
for (String s : List.of(
"""
sealed interface I {}
non-sealed interface I2 extends I {}
""",
"""
sealed interface I {}
sealed interface I2 extends I {}
non-sealed interface I3 extends I2 {}
""",
"""
sealed interface I permits I2 {}
non-sealed interface I2 extends I {}
""",
"""
sealed interface I permits I2 {}
sealed interface I2 extends I permits I3 {}
non-sealed interface I3 extends I2 {}
"""
))
assertOK(s);
}
@Test
void testEnumsCantBeSealedOrNonSealed() {
for (String s : List.of(
"""
sealed interface I {}
sealed enum E implements I {E1}
""",
"""
sealed interface I {}
non-sealed enum E implements I {E1}
"""))
assertFail("compiler.err.mod.not.allowed.here", s);
}
@Test
void testEnumsCanImplementSealedInterfaces() {
for (String s : List.of(
"""
sealed interface I {}
enum E implements I {E1}
"""))
assertOK(s);
}
@Test
void testClassesCanExtendNonSealed() {
for (String s : List.of(
"""
sealed class C {}
non-sealed class Sub extends C {}
class Sub2 extends Sub {}
""")) {
assertOK(s);
}
}
@Test
void testEmptyPermits() {
for (String s : List.of(
"""
sealed class C permits {}
non-sealed class Sub extends C {}
""")) {
assertFail("compiler.err.expected", s);
}
}
@Test
void testTypeVarInPermits() {
for (String s : List.of(
"""
class Outer<T> {
sealed class C permits T {}
}
""")) {
assertFail("compiler.err.invalid.permits.clause", s);
}
}
@Test
void testRepeatedTypeInPermits() {
for (String s : List.of(
"""
sealed class C permits Sub, Sub {}
final class Sub extends C {}
""")) {
assertFail("compiler.err.invalid.permits.clause", s);
}
}
@Test
void testSubtypeDoesntExtendSealed() {
for (String s : List.of(
"""
sealed class C permits Sub {}
final class Sub {}
""",
"""
sealed interface I permits Sub {}
final class Sub {}
""",
"""
sealed class C permits Sub1, Sub2 {}
sealed class Sub1 extends C permits Sub2 {}
final class Sub2 extends Sub1 {}
"""
)) {
assertFail("compiler.err.invalid.permits.clause", s);
}
}
@Test
void testAPIForPrimitiveAndArrayClasses() {
for (Class<?> c : new Class[]{byte.class, byte[].class, short.class, short[].class, int.class, int[].class, long.class, long[].class,
float.class, float[].class, double.class, double[].class, char.class, char[].class, boolean.class, boolean[].class, void.class,
String[].class}) {
Assert.check(!c.isSealed());
Assert.check(c.getPermittedSubclasses() == null);
}
}
@Test
void testPrinting() throws Exception {
Path base = Paths.get("testPrinting");
Path src = base.resolve("src");
Path test = src.resolve("Test");
tb.writeJavaFiles(test,
"""
sealed class SealedClassNoPermits {}
final class FinalSubClass extends SealedClassNoPermits {}
non-sealed class NonSealedSubClass extends SealedClassNoPermits {}
sealed interface SealedInterfaceNoPermits {}
non-sealed interface NonSealedInterface extends SealedInterfaceNoPermits {}
final class FinalSubClass2 implements SealedInterfaceNoPermits {}
sealed class SealedClassWithPermits permits SealedClassWithPermits, NonSealedSubClass2 {}
final class FinalSubClass3 extends SealedClassWithPermits {}
non-sealed class NonSealedSubClass2 extends SealedClassWithPermits {}
sealed interface SealedInterfaceWithPermits permits NonSealedInterface2, FinalSubClass4 {}
non-sealed interface NonSealedInterface2 extends SealedInterfaceWithPermits {}
final class FinalSubClass4 implements SealedInterfaceWithPermits {}
enum SealedEnum {
E {}
}
enum Enum {
E
}
"""
);
Path out = base.resolve("out");
Files.createDirectories(out);
List<String> output = new JavacTask(tb)
.outdir(out)
.options("-Xprint")
.files(findJavaFiles(test))
.run()
.writeAll()
.getOutputLines(OutputKind.STDOUT);
List<String> expected = List.of(
"sealed class SealedClassNoPermits permits FinalSubClass, NonSealedSubClass {",
" SealedClassNoPermits();",
"}",
"final class FinalSubClass extends SealedClassNoPermits {",
" FinalSubClass();",
"}",
"non-sealed class NonSealedSubClass extends SealedClassNoPermits {",
" NonSealedSubClass();",
"}",
"sealed interface SealedInterfaceNoPermits permits NonSealedInterface, FinalSubClass2 {",
"}",
"non-sealed interface NonSealedInterface extends SealedInterfaceNoPermits {",
"}",
"final class FinalSubClass2 implements SealedInterfaceNoPermits {",
" FinalSubClass2();",
"}",
"sealed class SealedClassWithPermits permits SealedClassWithPermits, NonSealedSubClass2 {",
" SealedClassWithPermits();",
"}",
"final class FinalSubClass3 extends SealedClassWithPermits {",
" FinalSubClass3();",
"}",
"non-sealed class NonSealedSubClass2 extends SealedClassWithPermits {",
" NonSealedSubClass2();",
"}",
"sealed interface SealedInterfaceWithPermits permits NonSealedInterface2, FinalSubClass4 {",
"}",
"non-sealed interface NonSealedInterface2 extends SealedInterfaceWithPermits {",
"}",
"final class FinalSubClass4 implements SealedInterfaceWithPermits {",
" FinalSubClass4();",
"}",
"enum SealedEnum {",
" E;",
" public static SealedEnum[] values();",
" public static SealedEnum valueOf(java.lang.String name);",
" private SealedEnum();",
"}",
"enum Enum {",
" E;",
" public static Enum[] values();",
" public static Enum valueOf(java.lang.String name);",
" private Enum();",
"}"
);
// remove empty strings
String newLine = System.getProperty("line.separator");
output = output.stream().filter(s -> !s.isEmpty()).map(s -> s.replaceAll(newLine, "\n").replaceAll("\n", "")).collect(Collectors.toList());
if (!output.containsAll(expected)) {
for (int i = 0; i < output.size(); i++) {
if (!output.get(i).equals(expected.get(i))) {
System.out.println("failing at index " + i);
System.out.println("expected:" + expected.get(i));
System.out.println("found:" + output.get(i));
}
}
throw new AssertionError("Expected output not found. Expected: " + expected);
}
}
@Test
void testNonSealedErroneousSuper() {
assertFail("compiler.err.cant.resolve",
d -> {
if (diags.keys().size() != 1) {
fail("Unexpected errors: " + diags.toString());
}
},
"""
non-sealed class C extends Undefined {}
""");
}
@Test
void testNonSealedErroneousSuperInterface() {
assertFail("compiler.err.cant.resolve",
d -> {
if (diags.keys().size() != 1) {
fail("Unexpected errors: " + diags.toString());
}
},
"""
non-sealed class C implements Undefined {}
""");
}
@Test
void testIllFormedNonSealed() {
for (String s : List.of(
"""
sealed class C permits Sub {}
non -sealed class Sub extends C {}
""",
"""
sealed class C permits Sub {}
non sealed class Sub extends C {}
""",
"""
sealed class C permits Sub {}
non - sealed class Sub extends C {}
""",
"""
sealed class C permits Sub {}
non/**/sealed class Sub extends C {}
"""
)) {
assertFail("compiler.err.expected4", s);
}
}
private static String[] PRIMITIVES_VOID_AND_PRIMITIVE_ARRAYS = new String[] {
"byte", "short", "int", "long", "float", "double", "char", "boolean", "void",
"byte[]", "short[]", "int[]", "long[]", "float[]", "double[]", "char[]", "boolean[]"
};
@Test
void testPermitsClause() {
for (String s : List.of(
// can't include a parameterized type
"""
sealed class C<T> permits Sub<T> {}
final class Sub<T> extends C<T> {}
""",
"""
sealed class C permits Sub<String> {}
final class Sub<T> extends C {}
""",
"""
sealed class C permits Sub<String> {}
non-sealed class Sub<T> extends C {}
""",
"""
sealed interface IC permits ID<String> {}
non-sealed interface ID<T> extends IC {}
""",
// can't include an array type
"""
sealed class C<T> permits Sub[] {}
final class Sub<T> extends C<T> {}
""",
"""
sealed class C permits Sub[] {}
non-sealed class Sub<T> extends C {}
""",
"""
sealed interface IC permits ID[] {}
non-sealed interface ID<T> extends IC {}
"""
)) {
assertFail("compiler.err.expected", s);
}
for (String s : List.of(
// can't include primitives, void or primitive arrays
"""
sealed class C<T> permits # {}
final class Sub<T> extends C<T> {}
""",
"""
sealed class C permits # {}
non-sealed class Sub<T> extends C {}
""",
"""
sealed interface IC permits # {}
non-sealed interface ID<T> extends IC {}
"""
)) {
for (String t: PRIMITIVES_VOID_AND_PRIMITIVE_ARRAYS){
assertFail("compiler.err.expected", s, t);
}
}
}
private Path[] findJavaFiles(Path... paths) throws IOException {
return tb.findJavaFiles(paths);
}
@Test
void testSealedNonSealedWithOtherModifiers() {
String template1 =
"""
@interface A {}
class Outer {
sealed class Sup { }
# # class Sub extends Sup {}
final class Sub2 extends Sub {}
}
""";
String template2 =
"""
@interface A {}
class Outer {
sealed interface Sup { }
# # interface Sub extends Sup {}
final class Sub2 implements Sub {}
}
""";
List<String> templateList = List.of(template1, template2);
List<String> otherModifiers = List.of(
"@A", "public", "protected", "private", "abstract", "static", "strictfp", "final", "sealed", "non-sealed"
);
for (String template : templateList) {
for (String sealed_non_sealed : List.of("sealed", "non-sealed")) {
for (String modifier : otherModifiers) {
if (sealed_non_sealed.equals(modifier)) {
assertFail("compiler.err.repeated.modifier", template, sealed_non_sealed, modifier);
} else if (modifier.equals("final") || modifier.equals("sealed") || modifier.equals("non-sealed")) {
assertFail("compiler.err.illegal.combination.of.modifiers", template, sealed_non_sealed, modifier);
} else {
assertOK(template, sealed_non_sealed, modifier);
}
}
}
}
}
@Test
void testSubClassBeforeSealedClassInSameCU() {
for (String s : List.of(
"""
final class Sub extends Sealed {}
sealed class Sealed {}
""",
"""
final class Sub extends Sealed {}
sealed class Sealed permits Sub {}
""",
"""
final class Sub extends Outer.Super {}
class Outer {
sealed static class Super {}
}
""",
"""
final class Sub extends Outer.Super {}
class Outer {
sealed static class Super permits Sub {}
}
""",
"""
class Outer {
final class Sub extends Super {}
}
sealed class Super {}
""",
"""
class Outer {
final class Sub extends Super {}
}
sealed class Super permits Outer.Sub{}
""",
"""
class Outer1 {
final class Sub extends Outer2.Super {}
}
class Outer2 {
sealed static class Super {}
}
""",
"""
class Outer1 {
final class Sub extends Outer2.Super {}
}
class Outer2 {
sealed static class Super permits Outer1.Sub {}
}
""",
"""
class Outer {
final class Sub extends Outer.Inner.Super {}
static class Inner {
sealed static class Super {}
}
}
""",
"""
class Outer {
final class Sub extends Outer.Inner.Super {}
static class Inner {
sealed static class Super permits Outer.Sub {}
}
}
"""
)) {
assertOK(s);
}
}
@Test
void testDoNotAllowSealedAnnotation() {
for (String s : List.of(
"""
sealed @interface A {}
non-sealed interface I extends A {}
"""
)) {
assertFail("compiler.err.expected4", s);
}
}
@Test
void testNarrowConversion() {
for (String s : List.of(
"""
interface I {}
sealed class C permits D {}
final class D extends C {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
sealed interface I permits C {}
final class C implements I {}
interface J {}
class Test {
void test () {
I i = null;
J j = (J) i;
}
}
""",
"""
interface I {}
sealed interface J permits C {}
final class C implements J {}
class Test {
void test () {
I i = null;
J j = (J) i;
}
}
""",
"""
sealed interface I permits A {}
sealed interface J permits B {}
final class A implements I {}
final class B implements J {}
class Test {
void test () {
I i = null;
J j = (J) i;
}
}
""",
"""
class C {}
sealed interface I permits A {}
final class A implements I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
final class C {}
interface I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
final class C {}
sealed interface I permits D {}
final class D implements I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
sealed class C permits D {}
final class D extends C {}
non-sealed interface I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
sealed class C permits D {}
final class D {}
sealed interface I permits E {}
final class E {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
sealed class C permits D {}
sealed class D permits NS {}
non-sealed class NS extends D {}
sealed interface I permits E {}
final class E {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
interface I {}
final class C {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
"""
interface I {}
sealed class C permits D {}
final class D {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
"""
sealed interface I permits D {}
final class D {}
class C {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
"""
sealed interface I permits D {}
final class D implements I {}
final class C {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
"""
sealed interface I permits D {}
final class D implements I {}
sealed class C permits E {}
final class E extends C {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
//JDK-8294550:
"""
interface I {}
sealed class C permits D {}
final class D extends C {}
class Test {
void test () {
C[] c = null;
I[] i = (I[]) c;
}
}
""",
"""
class Test {
sealed interface I permits C1 {}
non-sealed class C1 implements I {}
class C2 extends C1 {}
class C3 {}
I m(int s, C3 c3) {
I i = (I)c3;
}
}
""",
"""
class Test {
sealed interface I permits C1 {}
non-sealed class C1 implements I {}
class C2 extends C1 {}
class C3 {}
I m(int s, C3 c3) {
I i = (C1)c3;
}
}
""",
"""
class Test {
sealed interface I permits C1 {}
non-sealed class C1 implements I {}
class C2 extends C1 {}
class C3 {}
I m(int s, C3 c3) {
I i = (C2)c3;
}
}
"""
)) {
assertFail("compiler.err.prob.found.req", s);
}
for (String s : List.of(
"""
interface I {}
sealed class C permits D, E {}
non-sealed class D extends C {}
final class E extends C {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
interface I {}
interface J {}
class Test {
void test () {
I i = null;
J j = (J) i;
}
}
""",
"""
class C {}
interface I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
interface I {}
class C {}
class Test {
void test () {
I i = null;
C c = (C) i;
}
}
""",
"""
sealed class C permits D {}
sealed class D extends C permits NS {}
non-sealed class NS extends D {}
interface I {}
class Test {
void test () {
C c = null;
I i = (I) c;
}
}
""",
"""
sealed interface A permits B { }
non-sealed interface B extends A { }
interface C { }
class D implements C, B { }
class Test {
void m(A a, C c) {
a = (A)c;
}
}
""",
"""
sealed interface A<T> {
final class B implements A<Object> { }
}
class Test {
void f(A.B a, A<Object> b) {
a = (A.B)b;
}
}
""",
"""
sealed class C permits D {}
final class D extends C {}
class Test {
void test () {
C[] c = null;
D[] d = (D[]) c;
}
}
"""
)) {
assertOK(s);
}
}
@Test
void testIntersectionWithSealedClasses() {
assertOK(
"""
class A { }
sealed interface I permits B { }
final class B extends A implements I { }
class Foo<X extends A & I> {}
"""
);
assertOK(
"""
class Outer {
abstract class Base {}
interface Marker {}
sealed class B extends Base {}
final class C extends B implements Marker {}
private <T extends Base & Marker> void test(T obj) {
B b = (B) obj;
}
}
"""
);
}
}