From eb804709da4f50cf613487eb38db453f5e3123c0 Mon Sep 17 00:00:00 2001 From: Juan Chen Date: Mon, 26 Oct 2020 18:06:33 +0000 Subject: [PATCH] [FIR] fix subtyping for nullable captured types. The issue is the type checker doesn't consider P? a subtype of CapturedType?, whereas P a subtype of CapturedType?. In AbstractTypeCheckerContext::checkSubtypeForSpecialCases, it checks if P? is a subtype of the lower type of the captured type, which is P, and returns false. This fix uses nullable version of the lower type when the captured type is marked nullable. To check if P? is a subtype of Captured?, we check the LHS, P?, against the nullable lower type of RHS, P?. ^KT-42825 Fixed --- .../ir/FirBlackBoxCodegenTestGenerated.java | 5 ++++ .../kotlin/ir/types/IrTypeCheckerContext.kt | 7 ++++++ .../box/javaInterop/generics/kt42825.kt | 23 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++++ .../LightAnalysisModeTestGenerated.java | 5 ++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++++ .../kotlin/types/AbstractTypeChecker.kt | 10 ++++++-- .../kotlin/types/model/TypeSystemContext.kt | 5 +--- .../IrJsCodegenBoxES6TestGenerated.java | 5 ++++ .../IrJsCodegenBoxTestGenerated.java | 5 ++++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++++ 11 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 compiler/testData/codegen/box/javaInterop/generics/kt42825.kt diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index edc9c50b4ea..5271fce5e28 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -16041,6 +16041,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt"); } + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } + @TestMetadata("propertyVarianceConflict.kt") public void testPropertyVarianceConflict() throws Exception { runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt"); diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt index f18f9e93ecf..e7dfd0f5878 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/IrTypeCheckerContext.kt @@ -39,6 +39,13 @@ open class IrTypeCheckerContext(override val irBuiltIns: IrBuiltIns) : IrTypeSys ): AbstractTypeCheckerContext = IrTypeCheckerContext(irBuiltIns) override fun KotlinTypeMarker.isUninferredParameter(): Boolean = false + override fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker { + if (this.isSimpleType()) { + return this.asSimpleType()!!.withNullability(nullable) + } else { + error("withNullability for non-simple types is not supported in IR") + } + } override fun captureFromExpression(type: KotlinTypeMarker): KotlinTypeMarker? = error("Captured type is unsupported in IR") diff --git a/compiler/testData/codegen/box/javaInterop/generics/kt42825.kt b/compiler/testData/codegen/box/javaInterop/generics/kt42825.kt new file mode 100644 index 00000000000..d3119ba3161 --- /dev/null +++ b/compiler/testData/codegen/box/javaInterop/generics/kt42825.kt @@ -0,0 +1,23 @@ +// FILE: Processor.java + +public interface Processor { + boolean process(T t); +} + +// FILE: test.kt + +interface PsiModifierListOwner +interface KtClassOrObject { + fun toLightClass(): PsiModifierListOwner? +} + +fun execute(declaration: Any, consumer: Processor) { + when (declaration) { + is KtClassOrObject -> { + val lightClass = declaration.toLightClass() + consumer.process(lightClass) + } + } +} + +fun box(): String = "OK" \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 84627960359..982fbd94a90 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -17441,6 +17441,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt"); } + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } + @TestMetadata("propertyVarianceConflict.kt") public void testPropertyVarianceConflict() throws Exception { runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 19c32945df6..7aa64fe4dd0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17441,6 +17441,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt"); } + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } + @TestMetadata("propertyVarianceConflict.kt") public void testPropertyVarianceConflict() throws Exception { runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b7261d7507b..7fdd83dafa7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -16041,6 +16041,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt"); } + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } + @TestMetadata("propertyVarianceConflict.kt") public void testPropertyVarianceConflict() throws Exception { runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt"); diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt index 992c4c0a8a5..74ce295bb8a 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/AbstractTypeChecker.kt @@ -407,9 +407,15 @@ object AbstractTypeChecker { val superTypeCaptured = superType.asCapturedType() val lowerType = superTypeCaptured?.lowerType() if (superTypeCaptured != null && lowerType != null) { + // If superType is nullable, e.g., to check if Foo? a subtype of Captured?, we check the LHS, Foo?, + // against the nullable version of the lower type of RHS. See KT-42825 + val nullableLowerType = + if (superType.isMarkedNullable()) + lowerType.withNullability(true) + else lowerType when (getLowerCapturedTypePolicy(subType, superTypeCaptured)) { - CHECK_ONLY_LOWER -> return isSubtypeOf(this, subType, lowerType) - CHECK_SUBTYPE_AND_LOWER -> if (isSubtypeOf(this, subType, lowerType)) return true + CHECK_ONLY_LOWER -> return isSubtypeOf(this, subType, nullableLowerType) + CHECK_SUBTYPE_AND_LOWER -> if (isSubtypeOf(this, subType, nullableLowerType)) return true SKIP_LOWER -> Unit } } diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt index d6bd8ab2765..29b34eb5741 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/TypeSystemContext.kt @@ -144,9 +144,6 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean - fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker - - fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker fun SimpleTypeMarker.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleTypeMarker @@ -241,7 +238,6 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun KotlinTypeMarker.isError(): Boolean fun TypeConstructorMarker.isError(): Boolean fun KotlinTypeMarker.isUninferredParameter(): Boolean - fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker? fun FlexibleTypeMarker.asRawType(): RawTypeMarker? @@ -259,6 +255,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext { fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker + fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker fun CapturedTypeMarker.typeConstructor(): CapturedTypeConstructorMarker fun CapturedTypeMarker.captureStatus(): CaptureStatus diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index 9e7dde16038..f98e7183e9e 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -13895,6 +13895,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes public void testAllFilesPresentInGenerics() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); } + + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } } @TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 1cc3f93e591..a6a2a19a7e0 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -13895,6 +13895,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { public void testAllFilesPresentInGenerics() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } + + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } } @TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions") diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 177ca42bf1b..ef4b905d1fc 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -13960,6 +13960,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testAllFilesPresentInGenerics() throws Exception { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); } + + @TestMetadata("kt42825.kt") + public void testKt42825() throws Exception { + runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt"); + } } @TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions")