Native: support Objective-C instancetype in more cases in cinterop

Objective-C has a special type, "instancetype". Generally, it is a type
that matches the method receiver type. So, if `Foo.foo` method returns
`instancetype`, then `Foo.foo()` would be of type `Foo`, while
`Bar.foo()` would be of type `Bar` (where `Bar` is a subclass of `Foo`).

Surprisingly, `instancetype` can be used not only as a return type, but
also somewhere inside a return type. cinterop wasn't ready for this.

This commit expands implementation of `instancetype` in cinterop to
cover more cases.

^KT-59597 Fixed
This commit is contained in:
Svyatoslav Scherbina
2023-12-14 08:57:29 +00:00
committed by Space Team
parent 149e1e02f6
commit 2cdf8cd7b1
8 changed files with 181 additions and 7 deletions
@@ -0,0 +1,2 @@
language = Objective-C
headers = objcInstancetype.h
@@ -0,0 +1,30 @@
#import "Foundation/NSString.h"
#import "Foundation/NSObject.h"
@interface Foo : NSObject
- (instancetype _Nonnull)atReturnType;
// https://youtrack.jetbrains.com/issue/KT-59597/KN-Usage-of-instancetype-in-block-return-type-crashes
+ (instancetype (^ _Nonnull)(void))atBlockReturnType;
- (instancetype (*)(void))atFunctionReturnType;
- (__strong instancetype*)atPointerType;
- (__strong instancetype** (^(^ _Nonnull)(void))(void))atComplexType;
//All these declarations are prohibited by Clang:
//@property (class) instancetype classProperty;
//@property instancetype instanceProperty;
//- (void (^)(instancetype))atBlockParameterType;
//- (void (*)(instancetype))atFunctionParameterType;
//- (struct { instancetype f; })atStructField;
//- (instancetype[])atArrayElementType;
//- (instancetype[42])atConstArrayElementType;
@end
@protocol Bar
- (instancetype) protocolMethod;
+ (instancetype _Nonnull) protocolClassMethod;
@end
@interface Baz : Foo <Bar>
@end
@@ -0,0 +1,68 @@
@file:OptIn(kotlinx.cinterop.ExperimentalForeignApi::class, kotlin.experimental.ExperimentalObjCName::class)
import objcInstancetype.*
import kotlin.test.*
import kotlinx.cinterop.*
fun testFoo() {
val foo = Foo()
val atReturnType: Foo = foo.atReturnType()
assertEquals(foo, atReturnType)
val atBlockReturnType: () -> Foo? = Foo.atBlockReturnType()
assertNotNull(atBlockReturnType())
val atFunctionReturnType: CPointer<CFunction<() -> Foo?>>? = foo.atFunctionReturnType()
assertNull(atFunctionReturnType!!())
val atPointerType: CPointer<ObjCObjectVar<Foo?>>? = foo.atPointerType()
assertNotNull(atPointerType)
assertNull(atPointerType.pointed.value)
val atComplexType: () -> (() -> CPointer<CPointerVar<ObjCObjectVar<Foo?>>>?)? = foo.atComplexType()
assertNull(atComplexType!!())
}
fun testBar() {
val bar: BarProtocol = Baz()
val barMeta: BarProtocolMeta = Baz
val protocolMethod: BarProtocol? = bar.protocolMethod()
assertEquals(bar, protocolMethod)
val protocolClassMethod: BarProtocol = barMeta.protocolClassMethod()
assertNotNull(protocolClassMethod)
}
fun testBaz() {
val baz = Baz()
val atReturnType: Baz = baz.atReturnType()
assertEquals(baz, atReturnType)
val atBlockReturnType: () -> Baz? = Baz.atBlockReturnType()
assertNotNull(atBlockReturnType())
val atFunctionReturnType: CPointer<CFunction<() -> Baz?>>? = baz.atFunctionReturnType()
assertNull(atFunctionReturnType!!())
val atPointerType: CPointer<ObjCObjectVar<Baz?>>? = baz.atPointerType()
assertNotNull(atPointerType)
assertNull(atPointerType.pointed.value)
val atComplexType: () -> (() -> CPointer<CPointerVar<ObjCObjectVar<Baz?>>>?)? = baz.atComplexType()
assertNull(atComplexType!!())
val protocolMethod: Baz? = baz.protocolMethod()
assertEquals(baz, protocolMethod)
val protocolClassMethod: Baz = Baz.protocolClassMethod()
assertNotNull(protocolClassMethod)
}
fun main() {
testFoo()
testBar()
testBaz()
}
@@ -0,0 +1,39 @@
#import "objcInstancetype.h"
static id returnNil() {
return nil;
}
static Foo* global = nil;
@implementation Foo
- (instancetype _Nonnull)atReturnType {
return self;
}
+ (instancetype (^ _Nonnull)(void))atBlockReturnType {
return ^(){ return [self new]; };
}
- (instancetype (*)(void))atFunctionReturnType {
return &returnNil;
}
- (__strong instancetype*)atPointerType {
return &global;
}
- (__strong Foo** (^(^ _Nonnull)(void))(void))atComplexType {
return (__strong Foo** (^(^ _Nonnull)(void))(void))^(){ return nil; };
}
@end
@implementation Baz
- (instancetype) protocolMethod {
return self;
}
+ (instancetype _Nonnull) protocolClassMethod {
return [Baz new];
}
@end