Skip to content

Commit

Permalink
[Sealed types] Regression in instanceof check for sealed generic clas…
Browse files Browse the repository at this point in the history
…ses (#3149)

* Fixes #3121

#3038
  • Loading branch information
srikanth-sankaran authored Oct 24, 2024
1 parent 6e5d315 commit 983369c
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1110,33 +1110,18 @@ public boolean isRawSubstitution() {
@Override
public ReferenceBinding[] permittedTypes() {
List<ReferenceBinding> permittedTypes = new ArrayList<>();
NextPermittedType:
for (ReferenceBinding pt : this.type.permittedTypes()) {
// Step 1: Gather all type variables that would need to be solved.
Map<TypeVariableBinding, TypeBinding> map = new HashMap<>();
TypeBinding current = pt;
do {
if (current.kind() == Binding.GENERIC_TYPE) {
for (TypeVariableBinding tvb : current.typeVariables()) {
map.put(tvb, null);
}
}
current = current.enclosingType();
} while (current != null);

// Step 2: Collect substitutes
current = this;
TypeBinding sooper = pt.findSuperTypeOriginatingFrom(this);
if (sooper == null || !sooper.isValidBinding() || sooper.isProvablyDistinct(this))
continue;
TypeBinding current = this;
Map<TypeVariableBinding, TypeBinding> map = new HashMap<>();
do {
if (sooper.isParameterizedType()) {
if (current.isParameterizedType()) {
for (int i = 0, length = sooper.typeArguments().length; i < length; i++) {
TypeBinding t = sooper.typeArguments()[i];
if (t instanceof TypeVariableBinding tvb) {
map.put(tvb, current.typeArguments()[i]);
} else if (TypeBinding.notEquals(t, this.typeArguments()[i])) {
continue NextPermittedType;
}
if (sooper.isParameterizedType() && current.isParameterizedType()) {
for (int i = 0, length = sooper.typeArguments().length; i < length; i++) {
TypeBinding t = sooper.typeArguments()[i];
if (t instanceof TypeVariableBinding tvb) {
map.put(tvb, current.typeArguments()[i]);
}
}
}
Expand All @@ -1163,12 +1148,7 @@ public TypeBinding substitute(TypeVariableBinding typeVariable) {
return retVal;
}
};

// Step 3: compute subtype with parameterizations if any.
pt = (ReferenceBinding) Scope.substitute(substitution, pt);

if (pt.isCompatibleWith(this))
permittedTypes.add(pt);
permittedTypes.add((ReferenceBinding) Scope.substitute(substitution, pt));
}

return permittedTypes.toArray(new ReferenceBinding[0]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6517,4 +6517,221 @@ final class AB implements I2 {}
"Syntax error on token \"I2\", permits expected after this token\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
public void testIssue3121() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Maybe<Long> {}
final class Maybe2 extends Maybe<Long> implements SuperInt {}
}
abstract sealed class SurelyNot<N extends Number> {
final class SurelyNot1 extends SurelyNot<Long> {}
final class SurelyNot2 extends SurelyNot<Long> {}
}
abstract sealed class SurelyYes<N extends Number> {
final class SurelyYes1 extends SurelyYes<Long> implements SuperInt {}
final class SurelyYes2 extends SurelyYes<Long> implements SuperInt {}
}
class Test {
void testMaybe(Maybe<?> maybe, SurelyNot<?> surelyNot, SurelyYes<?> surelyYes) {
if (maybe == null || surelyNot == null || surelyYes == null) return;
if (maybe instanceof SuperInt sup) {}
if (surelyNot instanceof SuperInt sup) {}
if (surelyYes instanceof SuperInt sup) {}
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 24)\n" +
" if (surelyNot instanceof SuperInt sup) {}\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Incompatible conditional operand types SurelyNot<capture#5-of ?> and SuperInt\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
public void testIssue3121_2() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
class Outer<T> {
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Maybe<Long> {}
}
}
class Test {
void testMaybe(Outer<String>.Maybe<?> maybe) {
if (maybe instanceof SuperInt sup) {}
return null;
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 12)\n" +
" if (maybe instanceof SuperInt sup) {}\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Incompatible conditional operand types Outer<String>.Maybe<capture#1-of ?> and SuperInt\n" +
"----------\n" +
"2. ERROR in X.java (at line 13)\n" +
" return null;\n" +
" ^^^^^^^^^^^^\n" +
"Void methods cannot return a value\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
public void testIssue3121_2_1() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
class Outer<T> {
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Maybe<Long> implements SuperInt {}
}
}
class Test {
void testMaybe(Outer<String>.Maybe<?> maybe) {
if (maybe instanceof SuperInt sup) {}
return null;
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 13)\n" +
" return null;\n" +
" ^^^^^^^^^^^^\n" +
"Void methods cannot return a value\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
public void testIssue3121_3() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
class Outer<T> {
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Outer<Test>.Maybe<Long> {}
}
}
class Test {
void testMaybe(Outer<Test>.Maybe<?> maybe) {
if (maybe == null) return;
if (maybe instanceof SuperInt sup) {}
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 13)\n" +
" if (maybe instanceof SuperInt sup) {}\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Incompatible conditional operand types Outer<Test>.Maybe<capture#2-of ?> and SuperInt\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
public void testIssue3121_4() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
class Outer<T> {
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Outer<Test>.Maybe<Long> implements SuperInt {}
}
}
class Test {
void testMaybe(Outer<Test>.Maybe<?> maybe) {
if (maybe == null) return;
if (maybe instanceof SuperInt sup) {}
return null;
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 14)\n" +
" return null;\n" +
" ^^^^^^^^^^^^\n" +
"Void methods cannot return a value\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121
// [Sealed types] Regression in instanceof check for sealed generic classes
// NOTE: javac does not report error#1 but that looks like a defect
public void testIssue3121_5() {
runNegativeTest(
new String[] {
"X.java",
"""
interface SuperInt {}
class Outer<T> {
abstract sealed class Maybe<N extends Number> {
final class Maybe1 extends Outer<Test>.Maybe<Long> implements SuperInt {}
}
}
class Test {
void testMaybe(Outer<String>.Maybe<?> maybe) {
if (maybe == null) return;
if (maybe instanceof SuperInt sup) {}
return null;
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 13)\n" +
" if (maybe instanceof SuperInt sup) {}\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Incompatible conditional operand types Outer<String>.Maybe<capture#2-of ?> and SuperInt\n" +
"----------\n" +
"2. ERROR in X.java (at line 14)\n" +
" return null;\n" +
" ^^^^^^^^^^^^\n" +
"Void methods cannot return a value\n" +
"----------\n");
}
}

0 comments on commit 983369c

Please sign in to comment.