From d6fcde7316944e69c83efb9282beee511ac56f54 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Fri, 13 Dec 2019 18:29:22 +0300 Subject: [PATCH] JS_IR: fix typecheck corner case Consider `fun foo(a: Any?) = a as? E`, where I is an interface. This check used to fail, because the `a == null` was missing, and the `isInterface` stdlib method crashes if the first argument is null. This change adds the null check. Also this change prettifies the instance check in case of type parameter left operand. --- .../src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt | 2 +- .../ir/backend/js/lower/TypeOperatorLowering.kt | 13 ++++++++++--- ...afeCastToTypeParameterWithInterfaceUpperBound.kt | 5 +++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++++ .../codegen/LightAnalysisModeTestGenerated.java | 5 +++++ .../codegen/ir/FirBlackBoxCodegenTestGenerated.java | 5 +++++ .../codegen/ir/IrBlackBoxCodegenTestGenerated.java | 5 +++++ .../ir/semantics/IrJsCodegenBoxTestGenerated.java | 5 +++++ .../test/semantics/JsCodegenBoxTestGenerated.java | 5 +++++ .../kotlin/kotlinp/test/KotlinpTestGenerated.java | 2 +- 10 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index 4d343e6b36e..029c1da6062 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -41,7 +41,7 @@ private fun IrType.isClassWithNamePrefix(prefix: String, packageFqName: FqName): return parent.fqName == packageFqName } -private fun IrType.superTypes(): List = classifierOrNull?.superTypes() ?: emptyList() +fun IrType.superTypes(): List = classifierOrNull?.superTypes() ?: emptyList() fun IrType.isFunctionTypeOrSubtype(): Boolean = DFS.ifAny(listOf(this), IrType::superTypes, IrType::isFunction) fun IrType.isSuspendFunctionTypeOrSubtype(): Boolean = DFS.ifAny(listOf(this), IrType::superTypes, IrType::isSuspendFunction) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index bda30b419e0..c5ae8884950 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -151,6 +151,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { // Note: native `instanceOf` is not used which is important because of null-behaviour private fun advancedCheckRequired(type: IrType) = type.isInterface() || + type.isTypeParameter() && type.superTypes().any { it.isInterface() } || type.isArray() || type.isPrimitiveArray() || isTypeOfCheckingType(type) @@ -261,10 +262,16 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { // TODO either remove functions with reified type parameters or support this case // assert(!typeParameter.isReified) { "reified parameters have to be lowered before" } - return typeParameter.superTypes.fold(litTrue) { r, t -> + + return typeParameter.superTypes.fold(null) { r, t -> val check = generateTypeCheckNonNull(argument.copy(), t.makeNotNull()) - calculator.and(r, check) - } + + if (r == null) { + check + } else { + calculator.andand(r, check) + } + } ?: litTrue } // private fun generateCheckForChar(argument: IrExpression) = diff --git a/compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt b/compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt new file mode 100644 index 00000000000..0b0c484db3a --- /dev/null +++ b/compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt @@ -0,0 +1,5 @@ +interface I + +fun foo(a: Any?): E? = a as? E + +fun box() = foo(null) ?: "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 282df342804..5ef1a9ca1c5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2821,6 +2821,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 8713d9e5612..c9c3aa3a088 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -2821,6 +2821,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 3f837c69af7..f4cdd6dc47a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -2801,6 +2801,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ec6d3b09a67..689877ffa43 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -2801,6 +2801,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); 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 f32d23bd186..ffb5b980992 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 @@ -2246,6 +2246,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); 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 d3448ed6ebb..1b619afbd01 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 @@ -2246,6 +2246,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/casts/notIs.kt"); } + @TestMetadata("nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt") + public void testNullableSafeCastToTypeParameterWithInterfaceUpperBound() throws Exception { + runTest("compiler/testData/codegen/box/casts/nullableSafeCastToTypeParameterWithInterfaceUpperBound.kt"); + } + @TestMetadata("unitAsAny.kt") public void testUnitAsAny() throws Exception { runTest("compiler/testData/codegen/box/casts/unitAsAny.kt"); diff --git a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java index c55eac605e6..91bb4a908a4 100644 --- a/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java +++ b/libraries/tools/kotlinp/test/org/jetbrains/kotlin/kotlinp/test/KotlinpTestGenerated.java @@ -25,7 +25,7 @@ public class KotlinpTestGenerated extends AbstractKotlinpTest { } public void testAllFilesPresentInTestData() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("libraries/tools/kotlinp/testData"), Pattern.compile("^(.+)\\.kt$"), true); + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("libraries/tools/kotlinp/testData"), Pattern.compile("^(.+)\\.kt$"), null, true); } @TestMetadata("Annotations.kt")