diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index b8b6261fad7..df45319f809 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -110,13 +110,6 @@ internal val lowerAfterInlinePhase = makeKonanModuleOpPhase( description = "Special operations processing after inlining" ) -internal val interopPart1Phase = makeKonanModuleLoweringPhase( - ::InteropLoweringPart1, - name = "InteropPart1", - description = "Interop lowering, part 1", - prerequisite = setOf(inlinePhase) -) - /* IrFile phases */ internal val lateinitPhase = makeKonanFileLoweringPhase( @@ -246,6 +239,13 @@ internal val callableReferencePhase = makeKonanFileLoweringPhase( prerequisite = setOf(delegationPhase) // TODO: make weak dependency on `testProcessorPhase` ) +internal val interopPart1Phase = makeKonanFileLoweringPhase( + ::InteropLoweringPart1, + name = "InteropPart1", + description = "Interop lowering, part 1", + prerequisite = setOf(inlinePhase) +) + internal val interopPart2Phase = makeKonanFileLoweringPhase( ::InteropLoweringPart2, name = "InteropPart2", diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 7393648f161..0fdac1d120c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -315,7 +315,6 @@ internal val allLoweringsPhase = namedIrModulePhase( inlinePhase then provisionalFunctionExpressionPhase then lowerAfterInlinePhase then - interopPart1Phase then performByIrFile( name = "IrLowerByFile", description = "IR Lowering by file", @@ -336,6 +335,7 @@ internal val allLoweringsPhase = namedIrModulePhase( enumClassPhase then delegationPhase then callableReferencePhase then + interopPart1Phase then interopPart2Phase then varargPhase then compileTimeEvaluatePhase then diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt index f25004c548f..3510780f9ab 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InteropLowering.kt @@ -649,10 +649,25 @@ internal class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfo val initMethodInfo = initMethod.getExternalObjCMethodInfo()!! return builder.at(expression).run { val classPtr = getObjCClass(constructedClass.symbol) - irForceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod)) + ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod)) } } + private fun IrBuilderWithScope.ensureObjCReferenceNotNull(expression: IrExpression): IrExpression = + if (!expression.type.containsNull()) { + expression + } else { + irBlock(resultType = expression.type) { + val temp = irTemporary(expression) + +irIfThen( + context.irBuiltIns.unitType, + irEqeqeq(irGet(temp), irNull()), + irCall(symbols.ThrowNullPointerException) + ) + +irGet(temp) + } + } + override fun visitCall(expression: IrCall): IrExpression { expression.transformChildrenVoid() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt index 84a58b2a0a4..4311474ac9e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/ir/util/IrUtils2.kt @@ -166,16 +166,6 @@ internal fun KonanBackendContext.report(declaration: IrDeclaration, message: Str if (isError) throw KonanCompilationException() } -fun IrBuilderWithScope.irForceNotNull(expression: IrExpression): IrExpression { - if (!expression.type.containsNull()) { - return expression - } - - return irCall(context.irBuiltIns.checkNotNullSymbol, expression.type.makeNotNull()).apply { - putValueArgument(0, expression) - } -} - fun IrFunctionAccessExpression.addArguments(args: Map) { val unhandledParameters = args.keys.toMutableSet() fun getArg(parameter: IrValueParameter) = args[parameter]?.also { unhandledParameters -= parameter } diff --git a/backend.native/tests/interop/objc/smoke.h b/backend.native/tests/interop/objc/smoke.h index cfbf7116050..0d96a914897 100644 --- a/backend.native/tests/interop/objc/smoke.h +++ b/backend.native/tests/interop/objc/smoke.h @@ -239,3 +239,13 @@ int getCustomStringValue(CustomString* str) { } extern BOOL customStringDeallocated; + +@interface TestConstructorReturnsNull : NSObject +- (instancetype)init; +@end; + +@interface TestCallableReferences : NSObject +@property int value; +- (int)instanceMethod; ++ (int)classMethod:(int)first :(int)second; +@end; \ No newline at end of file diff --git a/backend.native/tests/interop/objc/smoke.kt b/backend.native/tests/interop/objc/smoke.kt index e86fecd5e9b..01289d53541 100644 --- a/backend.native/tests/interop/objc/smoke.kt +++ b/backend.native/tests/interop/objc/smoke.kt @@ -31,6 +31,8 @@ fun run() { testExportObjCClass() testCustomString() testLocalizedStrings() + testConstructorReturnsNull() + testCallableReferences() assertEquals(2, ForwardDeclaredEnum.TWO.value) @@ -481,6 +483,37 @@ fun testLocalizedStrings() { assertEquals("Plural: 5 apples", string) } +fun testConstructorReturnsNull() { + assertFailsWith() { + TestConstructorReturnsNull() + } +} + +fun testCallableReferences() { + val createTestCallableReferences = ::TestCallableReferences + assertEquals("", createTestCallableReferences.name) + val testCallableReferences: Any = createTestCallableReferences() + assertTrue(testCallableReferences is TestCallableReferences) + + val valueRef: kotlin.reflect.KMutableProperty0 = testCallableReferences::value + assertEquals("value", valueRef.name) + assertEquals(0, valueRef()) + valueRef.set(42) + assertEquals(42, valueRef()) + + val classMethodRef = (TestCallableReferences)::classMethod + assertEquals("classMethod", classMethodRef.name) + assertEquals(3, classMethodRef(1, 2)) + + val instanceMethodRef = TestCallableReferences::instanceMethod + assertEquals("instanceMethod", instanceMethodRef.name) + assertEquals(42, instanceMethodRef(testCallableReferences)) + + val boundInstanceMethodRef = testCallableReferences::instanceMethod + assertEquals("instanceMethod", boundInstanceMethodRef.name) + assertEquals(42, boundInstanceMethodRef()) +} + private val Any.objCClassName: String get() = object_getClassName(this)!!.toKString() diff --git a/backend.native/tests/interop/objc/smoke.m b/backend.native/tests/interop/objc/smoke.m index 3b708c141f7..5197ac80827 100644 --- a/backend.native/tests/interop/objc/smoke.m +++ b/backend.native/tests/interop/objc/smoke.m @@ -320,3 +320,19 @@ BOOL customStringDeallocated = NO; customStringDeallocated = YES; } @end; + +@implementation TestConstructorReturnsNull +- (instancetype)init { + return nil; +} +@end; + +@implementation TestCallableReferences +- (int)instanceMethod { + return self.value; +} + ++ (int)classMethod:(int)first :(int)second { + return first + second; +} +@end; \ No newline at end of file