Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.jupiter.api.Test;

/**
* The sealed counterpart of {@link NonExtendableCastOperandPrimitiveTest}. The cast's operand has
* type {@code SealedBase}, which permits only {@code Known} (JLS 8.1.1.2), so the synthetic {@code
* Baz} cannot be made a subtype of it and the return type falls back instead. Without that, the
* output declares {@code Baz extends SealedBase}, which does not compile.
*/
public class NonExtendableCastOperandSealedTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"nonextendablecastoperandsealed",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#target(Foo)"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.jupiter.api.Test;

/**
* A sealed class as an assignment target. Only the classes named in {@code SealedBase}'s {@code
* permits} clause may extend it (JLS 8.1.1.2), and a placeholder Specimin invents is never one of
* them, so the return type of {@code get} cannot be made to fit this assignment and falls back to
* an unconstrained type variable. Without that, the output declares {@code GetReturnType extends
* SealedBase}, which does not compile because {@code GetReturnType} is not permitted.
*
* <p>The cast on the second line is what makes the sealed classification the deciding factor rather
* than incidental. It keeps the placeholder return type alive until the assignment is examined; if
* the assignment instead saw a return type that already existed, the repair for a conflict that no
* supertype can fix would handle this program without consulting sealedness at all.
*/
public class NonExtendableTargetSealedTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"nonextendabletargetsealed",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#target(Foo)"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.checkerframework.specimin;

import java.io.IOException;
import org.junit.jupiter.api.Test;

/**
* The one case in which a sealed class does take a synthetic subtype: its own {@code permits}
* clause names a type Specimin has to generate. {@code Baz} must then be declared {@code extends
* SealedBase}, because a permitted class is required to be a direct subclass, and it must be
* declared {@code non-sealed} so that it can be extended in turn.
*
* <p>Treating sealed types as unextendable therefore must not be read as "no synthetic type may
* name this as a supertype". It is the {@code permits} clause that establishes this relationship,
* where it is processed; {@link NonExtendableCastOperandSealedTest} is what asks for it from the
* outside, and is refused. This fixture guards the boundary between those two.
*
* <p>The cast that appears here is precisely such an outside request, and its being refused is
* visible in the output: {@code get}'s return type falls back rather than {@code Baz} acquiring
* {@code SealedBase} a second time. When the cast did impose it, the two routes disagreed about
* what kind of supertype it was and the output said {@code implements} for a class.
*/
public class SealedPermitsGeneratedSubtypeTest {
@Test
public void runTest() throws IOException {
SpeciminTestExecutor.runTestWithoutJarPaths(
"sealedpermitsgeneratedsubtype",
new String[] {"com/example/Simple.java"},
new String[] {"com.example.Simple#target(Foo)"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public final class Known extends SealedBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Known {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example;

import org.example.Baz;
import org.example.Foo;

public class Simple {

public void target(Foo f) {
Baz b = (Baz) f.get();
SealedBase s = f.get();
System.out.println(b + "" + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example;
public class Baz {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example;
public class Foo {

public <T> T get() {
throw new java.lang.Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public final class Known extends SealedBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Known {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example;

import org.example.Baz;
import org.example.Foo;

public class Simple {
public void target(Foo f) {
Baz b = (Baz) f.get();
SealedBase s = f.get();
System.out.println(b + "" + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public final class Known extends SealedBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Known {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example;

import org.example.Baz;
import org.example.Foo;

public class Simple {

public void target(Foo f) {
SealedBase s = f.get();
Baz b = (Baz) f.get();
System.out.println(b + "" + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example;
public class Baz extends org.example.GetReturnType {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example;
public class Foo {

public <T> T get() {
throw new java.lang.Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example;
public class GetReturnType {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public final class Known extends SealedBase {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Known {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example;

import org.example.Baz;
import org.example.Foo;

public class Simple {
public void target(Foo f) {
SealedBase s = f.get();
Baz b = (Baz) f.get();
System.out.println(b + "" + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;
public non-sealed class Baz extends com.example.SealedBase {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Baz {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.example;

import org.example.Foo;

public class Simple {

public void target(Foo f) {
Baz b = (Baz) f.get();
SealedBase s = f.get();
System.out.println(b + "" + s);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example;
public class Foo {

public <T> T get() {
throw new java.lang.Error();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.example;

public sealed class SealedBase permits Baz {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example;

import org.example.Foo;

public class Simple {
public void target(Foo f) {
Baz b = (Baz) f.get();
SealedBase s = f.get();
System.out.println(b + "" + s);
}
}
Loading