[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
This commit is contained in:
Pavel Kunyavskiy
2023-06-29 17:55:01 +02:00
committed by Space Team
parent 1fdda8962d
commit 396cfb3956
9 changed files with 128 additions and 6 deletions
@@ -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
}
@@ -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 ->
@@ -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<KonanInteropTest> configureClosure) {
Task interopTestBase(String name, boolean multiFile, boolean interop2ForMainOnly, Closure<KonanInteropTest> 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<KonanInteropTest> 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<KonanInteropTest> c
}
Task interopTest(String name, Closure<KonanInteropTest> configureClosure) {
return interopTestBase(name, false, configureClosure)
return interopTestBase(name, false, false, configureClosure)
}
Task interopTestMultifile(String name, Closure<KonanInteropTest> 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"
@@ -0,0 +1,18 @@
language = Objective-C
---
#import <Foundation/Foundation.h>
#include <stdio.h>
struct ForwardDeclaredStruct;
@class ForwardDeclaredClass;
@protocol ForwardDeclaredProtocol;
NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
return [NSString stringWithUTF8String:"Protocol"];
}
NSString* consumeClass(ForwardDeclaredClass* s) {
return [NSString stringWithUTF8String:"Class"];
}
NSString* consumeStruct(struct ForwardDeclaredStruct* s) {
return [NSString stringWithUTF8String:"Struct"];
}
@@ -0,0 +1,2 @@
language = Objective-C
---
@@ -0,0 +1,18 @@
#define NS_FORMAT_ARGUMENT(X)
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
@interface ForwardDeclaredProtocolImpl : NSObject<ForwardDeclaredProtocol>
@end;
@interface ForwardDeclaredClass : NSObject
@end;
struct ForwardDeclaredStruct {};
id<ForwardDeclaredProtocol> produceProtocol();
ForwardDeclaredClass* produceClass();
struct ForwardDeclaredStruct* produceStruct();
@@ -0,0 +1,21 @@
#import "b.h"
@implementation ForwardDeclaredProtocolImpl : NSObject
@end;
@implementation ForwardDeclaredClass : NSObject
@end;
id<ForwardDeclaredProtocol> produceProtocol() {
return [ForwardDeclaredProtocolImpl new];
}
ForwardDeclaredClass* produceClass() {
return [ForwardDeclaredClass new];
}
struct ForwardDeclaredStruct S;
struct ForwardDeclaredStruct* produceStruct() {
return &S;
}
@@ -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<ForwardDeclaredStruct>)
fun testClass(s: Any?) = consumeClass(s as ForwardDeclaredClass)
fun testProtocol(s: Any?) = consumeProtocol(s as ForwardDeclaredProtocolProtocol)
fun <T : ForwardDeclaredClass> testClass2Impl(s: Any?) = consumeClass(s as T)
fun <T : ForwardDeclaredProtocolProtocol> testProtocol2Impl(s: Any?) = consumeProtocol(s as T)
fun testClass2(s: Any?) = testClass2Impl<ForwardDeclaredClass>(s)
fun testProtocol2(s: Any?) = testProtocol2Impl<ForwardDeclaredProtocolProtocol>(s)
@@ -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()
}