From 983369c4b02463627745dfc36b9fd8c1cec8ac86 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:00:38 +0530 Subject: [PATCH] [Sealed types] Regression in instanceof check for sealed generic classes (#3149) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3038 --- .../lookup/ParameterizedTypeBinding.java | 40 +--- .../compiler/regression/SealedTypesTests.java | 217 ++++++++++++++++++ 2 files changed, 227 insertions(+), 30 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java index 35d2a9fb398..a3e54cd2ebf 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java @@ -1110,33 +1110,18 @@ public boolean isRawSubstitution() { @Override public ReferenceBinding[] permittedTypes() { List permittedTypes = new ArrayList<>(); -NextPermittedType: for (ReferenceBinding pt : this.type.permittedTypes()) { - // Step 1: Gather all type variables that would need to be solved. - Map 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 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]); } } } @@ -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]); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index e7cfeb89efb..b8e84a44987 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -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 { + final class Maybe1 extends Maybe {} + final class Maybe2 extends Maybe implements SuperInt {} + } + + + abstract sealed class SurelyNot { + final class SurelyNot1 extends SurelyNot {} + final class SurelyNot2 extends SurelyNot {} + } + + abstract sealed class SurelyYes { + final class SurelyYes1 extends SurelyYes implements SuperInt {} + final class SurelyYes2 extends SurelyYes 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 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 { + abstract sealed class Maybe { + final class Maybe1 extends Maybe {} + } + } + + class Test { + + void testMaybe(Outer.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.Maybe 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 { + abstract sealed class Maybe { + final class Maybe1 extends Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.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 { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe {} + } + } + + class Test { + + void testMaybe(Outer.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.Maybe 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 { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.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 { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.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.Maybe 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"); + } }