From 396cfb39560869b369be8f30ea8cfbd1d29f3322 Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Thu, 29 Jun 2023 17:55:01 +0200 Subject: [PATCH] [K/N] Make cast to objc forward declaration unchecked Before this commit it caused compiler crash. Unchecked cast warning to be done later. ^KT-59645 --- .../kotlin/backend/konan/ObjCInterop.kt | 4 +++ .../konan/lower/TypeOperatorLowering.kt | 14 ++++++++-- .../backend.native/tests/build.gradle | 26 ++++++++++++++++--- .../interop/forwardDeclarationsCast/a.def | 18 +++++++++++++ .../interop/forwardDeclarationsCast/b.def | 2 ++ .../tests/interop/forwardDeclarationsCast/b.h | 18 +++++++++++++ .../tests/interop/forwardDeclarationsCast/b.m | 21 +++++++++++++++ .../interop/forwardDeclarationsCast/lib.kt | 18 +++++++++++++ .../interop/forwardDeclarationsCast/main.kt | 13 ++++++++++ 9 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/a.def create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.def create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.h create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.m create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/lib.kt create mode 100644 kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/main.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index 25265e0c655..fd84a7ddc21 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -67,6 +67,10 @@ fun IrClass.isExternalObjCClass(): Boolean = this.isObjCClass() && fun ClassDescriptor.isObjCForwardDeclaration(): Boolean = this.findPackage().fqName.startsWith(objcnamesForwardDeclarationsPackageName) +fun IrClass.isObjCForwardDeclaration(): Boolean = + getPackageFragment().packageFqName.startsWith(objcnamesForwardDeclarationsPackageName) + + fun ClassDescriptor.isObjCMetaClass(): Boolean = this.getAllSuperClassifiers().any { it.fqNameSafe == objCClassFqName } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt index 856ffd68c52..1252d038794 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TypeOperatorLowering.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.konan.lower import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.* +import org.jetbrains.kotlin.backend.konan.isObjCForwardDeclaration import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -52,9 +53,18 @@ internal class TypeOperatorLowering(val context: CommonBackendContext) : FileLow irFile.transformChildren(this, null) } + private fun effectiveCheckType(type: IrType) : IrType { + val erasedType = type.erasure() + return if (erasedType.classOrNull?.owner?.isObjCForwardDeclaration() == true) { + context.irBuiltIns.anyType.mergeNullability(erasedType) + } else { + erasedType + } + } + private fun lowerCast(expression: IrTypeOperatorCall): IrExpression { builder.at(expression) - val typeOperand = expression.typeOperand.erasure() + val typeOperand = effectiveCheckType(expression.typeOperand) return if (typeOperand == expression.typeOperand) { expression } else { @@ -63,7 +73,7 @@ internal class TypeOperatorLowering(val context: CommonBackendContext) : FileLow } private fun lowerSafeCast(expression: IrTypeOperatorCall): IrExpression { - val typeOperand = expression.typeOperand.erasure() + val typeOperand = effectiveCheckType(expression.typeOperand) return builder.irBlock(expression) { +irLetS(expression.argument) { variable -> diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 570c8981d68..25876b90315 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -4023,6 +4023,16 @@ createInterop("cForwardDeclarationsTwoLibs2") { } if (PlatformInfo.isAppleTarget(project)) { + createInterop("forwardDeclarationsCastA") { + it.defFile "interop/forwardDeclarationsCast/a.def" + } + + createInterop("forwardDeclarationsCastB") { + it.defFile "interop/forwardDeclarationsCast/b.def" + it.headers "$projectDir/interop/forwardDeclarationsCast/b.h" + it.extraOpts "-Xcompile-source", "$projectDir/interop/forwardDeclarationsCast/b.m" + } + createInterop("objcSmoke") { it.defFile 'interop/objc/objcSmoke.def' it.headers "$projectDir/interop/objc/smoke.h" @@ -4155,7 +4165,7 @@ createInterop("exceptions_cCallback") { /** * Creates a task for the interop test. Configures runner and adds library and test build tasks. */ -Task interopTestBase(String name, boolean multiFile, Closure configureClosure) { +Task interopTestBase(String name, boolean multiFile, boolean interop2ForMainOnly, Closure configureClosure) { return KotlinNativeTestKt.createTest(project, name, KonanInteropTest) { task -> task.configure(configureClosure) if (task.enabled) { @@ -4173,7 +4183,7 @@ Task interopTestBase(String name, boolean multiFile, Closure c library(lib, targets: [targetName]) { libraries { artifact interopLib - if (interopLib2 != null) artifact interopLib2 + if (interopLib2 != null && !interop2ForMainOnly) artifact interopLib2 } srcFiles task.lib baseDir "$testOutputLocal/$name" @@ -4202,11 +4212,11 @@ Task interopTestBase(String name, boolean multiFile, Closure c } Task interopTest(String name, Closure configureClosure) { - return interopTestBase(name, false, configureClosure) + return interopTestBase(name, false, false, configureClosure) } Task interopTestMultifile(String name, Closure configureClosure) { - return interopTestBase(name, true, configureClosure) + return interopTestBase(name, true, false, configureClosure) } standaloneTest("interop_objc_allocException") { @@ -4447,6 +4457,14 @@ interopTest("interop_kt51925") { useGoldenData = true } +interopTestBase("interop_forwardDeclarationsCast", false, true) { + enabled = isAppleTarget(project) + interop = 'forwardDeclarationsCastA' + interop2 = 'forwardDeclarationsCastB' + lib = 'interop/forwardDeclarationsCast/lib.kt' + source = 'interop/forwardDeclarationsCast/main.kt' +} + dynamicTest("interop_kt43502") { interop = "kt43502" source = "interop/kt43502/main.kt" diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/a.def b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/a.def new file mode 100644 index 00000000000..7ede1f093d0 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/a.def @@ -0,0 +1,18 @@ +language = Objective-C +--- +#import +#include + +struct ForwardDeclaredStruct; +@class ForwardDeclaredClass; +@protocol ForwardDeclaredProtocol; + +NSString* consumeProtocol(id s) { + return [NSString stringWithUTF8String:"Protocol"]; +} +NSString* consumeClass(ForwardDeclaredClass* s) { + return [NSString stringWithUTF8String:"Class"]; +} +NSString* consumeStruct(struct ForwardDeclaredStruct* s) { + return [NSString stringWithUTF8String:"Struct"]; +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.def b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.def new file mode 100644 index 00000000000..c47329cf3c9 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.def @@ -0,0 +1,2 @@ +language = Objective-C +--- \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.h b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.h new file mode 100644 index 00000000000..14829714d74 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.h @@ -0,0 +1,18 @@ +#define NS_FORMAT_ARGUMENT(X) +#import + +@protocol ForwardDeclaredProtocol +@end + +@interface ForwardDeclaredProtocolImpl : NSObject +@end; + + +@interface ForwardDeclaredClass : NSObject +@end; + +struct ForwardDeclaredStruct {}; + +id produceProtocol(); +ForwardDeclaredClass* produceClass(); +struct ForwardDeclaredStruct* produceStruct(); \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.m b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.m new file mode 100644 index 00000000000..537d56aab0a --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/b.m @@ -0,0 +1,21 @@ +#import "b.h" + +@implementation ForwardDeclaredProtocolImpl : NSObject +@end; + +@implementation ForwardDeclaredClass : NSObject +@end; + +id produceProtocol() { + return [ForwardDeclaredProtocolImpl new]; +} + +ForwardDeclaredClass* produceClass() { + return [ForwardDeclaredClass new]; +} + +struct ForwardDeclaredStruct S; + +struct ForwardDeclaredStruct* produceStruct() { + return &S; +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/lib.kt b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/lib.kt new file mode 100644 index 00000000000..29947bb30c6 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/lib.kt @@ -0,0 +1,18 @@ +@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) + +import cnames.structs.ForwardDeclaredStruct +import objcnames.classes.ForwardDeclaredClass +import objcnames.protocols.ForwardDeclaredProtocolProtocol +import a.* +import kotlinx.cinterop.* + +fun testStruct(s: Any?) = consumeStruct(s as CPointer) +fun testClass(s: Any?) = consumeClass(s as ForwardDeclaredClass) +fun testProtocol(s: Any?) = consumeProtocol(s as ForwardDeclaredProtocolProtocol) + + +fun testClass2Impl(s: Any?) = consumeClass(s as T) +fun testProtocol2Impl(s: Any?) = consumeProtocol(s as T) + +fun testClass2(s: Any?) = testClass2Impl(s) +fun testProtocol2(s: Any?) = testProtocol2Impl(s) \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/main.kt b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/main.kt new file mode 100644 index 00000000000..d1467f7bfd0 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/forwardDeclarationsCast/main.kt @@ -0,0 +1,13 @@ +@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class) + +import a.* +import b.* + + +fun main() { + if (testStruct(produceStruct()) != "Struct") throw IllegalStateException() + if (testClass(produceClass()) != "Class") throw IllegalStateException() + if (testProtocol(produceProtocol()) != "Protocol") throw IllegalStateException() + if (testClass2(produceClass()) != "Class") throw IllegalStateException() + if (testProtocol2(produceProtocol()) != "Protocol") throw IllegalStateException() +} \ No newline at end of file