/* * Copyright (c) 2019, 2020, 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 * @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 * @compile --enable-preview -source ${jdk.version} SealedCompilationTests.java * @run testng/othervm -DuseAP=false --enable-preview SealedCompilationTests * @run testng/othervm -DuseAP=true --enable-preview SealedCompilationTests */ import java.lang.constant.ClassDesc; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; 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 static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import org.testng.annotations.Test; import tools.javac.combo.CompilationTestCase; import toolbox.ToolBox; import toolbox.JavacTask; import toolbox.Task; import toolbox.Task.OutputKind; @Test public class SealedCompilationTests extends CompilationTestCase { ToolBox tb = new ToolBox(); // When sealed classes become a permanent feature, we don't need these any more private static String[] PREVIEW_OPTIONS = { "--enable-preview", "-source", Integer.toString(Runtime.version().feature()) }; private static String[] PREVIEW_OPTIONS_WITH_AP = { "--enable-preview", "-source", Integer.toString(Runtime.version().feature()), "-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 annotations, RoundEnvironment roundEnv) { return true; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } } public SealedCompilationTests() { boolean useAP = System.getProperty("useAP") == null ? false : System.getProperty("useAP").equals("true"); setDefaultFilename("SealedTest.java"); setCompileOptions(useAP ? PREVIEW_OPTIONS_WITH_AP : PREVIEW_OPTIONS); 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 SHELLS = List.of(NO_SHELL, NEST_SHELL, AUX_SHELL); public void testSimpleExtension() { String CC1 = """ sealed class Sup # { } # class Sub extends Sup { } """; String CC2 = """ sealed class Sup # { } # class Sub extends Sup { } """; String CC3 = """ sealed class Sup # { } # class Sub extends Sup { } """; String AC1 = """ sealed abstract class Sup # { } # class Sub extends Sup { } """; String AC2 = """ sealed abstract class Sup # { } # class Sub extends Sup { } """; String AC3 = """ sealed abstract class Sup # { } # class Sub extends Sup { } """; String I1 = """ sealed interface Sup # { } # class Sub implements Sup { } """; String I11 = """ sealed interface Sup # { } # class Sub implements Sup { } """; String I12 = """ sealed interface Sup # { } # class Sub implements Sup { } """; 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)); } public 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 public 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); } public 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; } }" )) { assertOK(s); } for (String s : List.of( "class sealed {}", "enum sealed {}", "record sealed() {}", "interface sealed {}", "@interface sealed {}" )) { 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) {} }" )) { assertFail("compiler.err.restricted.type.not.allowed.here", s); } for (String s : List.of( "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 permits {}", "enum permits {}", "record permits() {}", "interface permits {}", "@interface permits {}" )) { assertFail("compiler.err.restricted.type.not.allowed", s); } for (String s : List.of( "class Foo { permits m() {} }", "class Foo { permits i; }", "class Foo { void m(permits i) {} }" )) { assertFail("compiler.err.restricted.type.not.allowed.here", s); } String[] testOptions = {/* no options */}; String[] previousCompOptions = getCompileOptions(); setCompileOptions(testOptions); // now testing with preview disabled for (String s : List.of( "sealed class S {}", "class Outer { sealed class S {} }", "class Outer { void m() { sealed class S {} } }", "non-sealed class S {}", "class Outer { non-sealed class S {} }", "class Outer { void m() { non-sealed class S {} } }" )) { assertFail("compiler.err.preview.feature.disabled.plural", s); } setCompileOptions(previousCompOptions); } public 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" + "}"); } public 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 */ public 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" + "}")) assertFail("compiler.err.illegal.combination.of.modifiers", s); } public 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); } public 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); } public 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); } public 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); } public 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); } public void testEnumsCanImplementSealedInterfaces() { for (String s : List.of( """ sealed interface I {} enum E implements I {E1} """)) assertOK(s); } public void testClassesCanExtendNonSealed() { for (String s : List.of( """ sealed class C {} non-sealed class Sub extends C {} class Sub2 extends Sub {} """)) { assertOK(s); } } public void testEmptyPermits() { for (String s : List.of( """ sealed class C permits {} non-sealed class Sub extends C {} """)) { assertFail("compiler.err.expected", s); } } public void testTypeVarInPermits() { for (String s : List.of( """ class Outer { sealed class C permits T {} } """)) { assertFail("compiler.err.invalid.permits.clause", s); } } public 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); } } public 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); } } public 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); } } public 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 output = new JavacTask(tb) .outdir(out) .options("--enable-preview", "-source", Integer.toString(Runtime.version().feature()), "-Xprint") .files(findJavaFiles(test)) .run() .writeAll() .getOutputLines(OutputKind.STDOUT); List 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); } } public void testNonSealedErroneousSuper() { assertFail("compiler.err.cant.resolve", d -> { if (diags.keys().size() != 1) { fail("Unexpected errors: " + diags.toString()); } }, """ non-sealed class C extends Undefined {} """); } public 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[]" }; public void testPermitsClause() { for (String s : List.of( // can't include a parameterized type """ sealed class C permits Sub {} final class Sub extends C {} """, """ sealed class C permits Sub {} final class Sub extends C {} """, """ sealed class C permits Sub {} non-sealed class Sub extends C {} """, """ sealed interface IC permits ID {} non-sealed interface ID extends IC {} """, // can't include an array type """ sealed class C permits Sub[] {} final class Sub extends C {} """, """ sealed class C permits Sub[] {} non-sealed class Sub extends C {} """, """ sealed interface IC permits ID[] {} non-sealed interface ID extends IC {} """ )) { assertFail("compiler.err.expected", s); } for (String s : List.of( // can't include primitives, void or primitive arrays """ sealed class C permits # {} final class Sub extends C {} """, """ sealed class C permits # {} non-sealed class Sub extends C {} """, """ sealed interface IC permits # {} non-sealed interface ID 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); } public 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 templateList = List.of(template1, template2); List 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); } } } } } public 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); } } public void testDoNotAllowSealedAnnotation() { for (String s : List.of( """ sealed @interface A {} non-sealed interface I extends A {} """ )) { assertFail("compiler.err.expected4", s); } } public 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; } } """ )) { 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; } } """ )) { assertOK(s); } } }