diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index a8f4bdefafe..f9a2d47982d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.descriptors.allParameters import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters import org.jetbrains.kotlin.backend.common.pop import org.jetbrains.kotlin.backend.common.push +import org.jetbrains.kotlin.backend.konan.descriptors.isNothing import org.jetbrains.kotlin.backend.konan.descriptors.isUnit import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.builtins.UnsignedType @@ -934,7 +935,7 @@ internal class CAdapterGenerator( descriptor.defaultType.binaryTypeIsReference() internal fun isMappedToVoid(descriptor: ClassDescriptor): Boolean { - return descriptor.isUnit() + return descriptor.isUnit() || descriptor.isNothing() } fun translateName(name: String): String { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt index cf9c0778d89..43053516217 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor import org.jetbrains.kotlin.serialization.konan.KonanPackageFragment import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty @@ -83,6 +84,9 @@ internal val FunctionDescriptor.isFunctionInvoke: Boolean internal fun ClassDescriptor.isUnit() = this.defaultType.isUnit() +internal fun ClassDescriptor.isNothing() = this.defaultType.isNothing() + + internal val T.allOverriddenDescriptors: List get() { val result = mutableListOf() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt index 20b954470b0..61ee36e41e6 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt @@ -9,6 +9,7 @@ import llvm.* import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isUnit private val primitiveToLlvm = PrimitiveBinaryType.values().associate { @@ -36,9 +37,7 @@ internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) = internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef { return when { - type.isUnit() -> LLVMVoidType()!! - // TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains(). - // KotlinBuiltIns.isNothing(type) -> LLVMVoidType() + type.isUnit() || type.isNothing() -> LLVMVoidType()!! else -> getLLVMType(type) } } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 415e4b1a1d7..4eeac819b83 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.irasdescriptors.* import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -418,7 +419,10 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : } else { "kfun:" + qualifyInternalName(descriptor) } - LLVMAddFunction(context.llvmModule, symbolName, llvmFunctionType)!! + val function = LLVMAddFunction(context.llvmModule, symbolName, llvmFunctionType)!! + if (descriptor.returnType.isNothing()) + setFunctionNoReturn(function) + function } // TODO: do we still need it? diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index a6cd9a9aabe..1c0ad17e1d4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -271,13 +271,16 @@ private val nounwindAttrKindId by lazy { getAttributeKindId("nounwind") } +private val noreturnAttrKindId by lazy { + getAttributeKindId("noreturn") +} + private val signextAttrKindId by lazy { getAttributeKindId("signext") } fun isFunctionNoUnwind(function: LLVMValueRef): Boolean { - val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, nounwindAttrKindId) return attribute != null } @@ -295,6 +298,11 @@ fun setFunctionNoUnwind(function: LLVMValueRef) { LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex, attribute) } +fun setFunctionNoReturn(function: LLVMValueRef) { + val attribute = LLVMCreateEnumAttribute(LLVMGetTypeContext(function.type), noreturnAttrKindId, 0)!! + LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex, attribute) +} + fun addFunctionSignext(function: LLVMValueRef, index: Int, type: LLVMTypeRef?) { if (type == int1Type || type == int8Type || type == int16Type) { val attribute = LLVMCreateEnumAttribute(LLVMGetTypeContext(function.type), signextAttrKindId, 0)!! diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 4c949feb776..7f92e6a3822 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -22,9 +22,15 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit import org.jetbrains.kotlin.util.OperatorNameConventions +internal fun TypeBridge.makeNothing() = when (this) { + is ReferenceBridge -> kNullInt8Ptr + is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!! +} + internal class ObjCExportCodeGenerator( codegen: CodeGenerator, val namer: ObjCExportNamerImpl, @@ -113,16 +119,19 @@ internal class ObjCExportCodeGenerator( fun FunctionGenerationContext.kotlinToObjC( value: LLVMValueRef, typeBridge: TypeBridge - ): LLVMValueRef = when (typeBridge) { - is ReferenceBridge -> kotlinReferenceToObjC(value) - is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType) - } + ): LLVMValueRef = when { + LLVMTypeOf(value) == voidType -> typeBridge.makeNothing() + typeBridge is ReferenceBridge -> kotlinReferenceToObjC(value) + typeBridge is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType) + else -> TODO() + } fun FunctionGenerationContext.objCToKotlin( value: LLVMValueRef, typeBridge: TypeBridge, resultLifetime: Lifetime ): LLVMValueRef = when (typeBridge) { + // TODO: if we add value type check here, we could bridge on Unit better. is ReferenceBridge -> objCReferenceToKotlin(value, resultLifetime) is ValueTypeBridge -> objCToKotlin(value, typeBridge.objCValueType) } @@ -608,7 +617,6 @@ private fun ObjCExportCodeGenerator.generateObjCImp( } ret(genReturnValueOnSuccess(returnType)) - } private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor( @@ -737,11 +745,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge( val actualReturnType = descriptor.returnType!! val retVal = when { - actualReturnType.isUnit() -> { + actualReturnType.isUnit() || actualReturnType.isNothing() -> { genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge) null } - baseReturnType.isUnit() -> { + baseReturnType.isUnit() || baseReturnType.isNothing() -> { genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge) codegen.theUnitInstanceRef.llvm } @@ -919,13 +927,9 @@ private fun ObjCExportCodeGenerator.createTypeAdapter( } else { // Mark it as non-overridable: - baseMethods.distinctBy { namer.getSelector(it) }.forEach { base -> + baseMethods.distinctBy { namer.getSelector(it) }.forEach { baseMethod -> reverseAdapters += KotlinToObjCMethodAdapter( - namer.getSelector(base), - -1, - -1, - NullPointer(int8Type) - ) + namer.getSelector(baseMethod), -1, -1, NullPointer(int8Type)) } // TODO: some fake-overrides can be skipped. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt index 66bfecd368c..77bb821f620 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExportMapper.kt @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isUnit internal abstract class ObjCExportMapper { @@ -159,7 +160,7 @@ private fun ObjCExportMapper.bridgeReturnType( MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty)) } - returnType.isUnit() -> if (convertExceptionsToErrors) { + returnType.isUnit() || returnType.isNothing() -> if (convertExceptionsToErrors) { MethodBridge.ReturnValue.WithError.Success } else { MethodBridge.ReturnValue.Void diff --git a/backend.native/tests/framework/values/values.kt b/backend.native/tests/framework/values/values.kt index 6204954edba..466a5c0a1bc 100644 --- a/backend.native/tests/framework/values/values.kt +++ b/backend.native/tests/framework/values/values.kt @@ -242,3 +242,23 @@ fun isFrozen(obj: Any): Boolean = obj.isFrozen fun kotlinLambda(block: (Any) -> Any): Any = block fun multiply(int: Int, long: Long) = int * long + +class MyException : Exception() + +open class BridgeBase { + @Throws + open fun foo1(): Any = Any() + @Throws + open fun foo2(): Int = 42 + @Throws + open fun foo3(): Unit = Unit + @Throws + open fun foo4(): Nothing? = throw IllegalStateException() +} + +class Bridge : BridgeBase() { + override fun foo1() = throw MyException() + override fun foo2() = throw MyException() + override fun foo3() = throw MyException() + override fun foo4() = throw MyException() +} \ No newline at end of file diff --git a/backend.native/tests/framework/values/values.swift b/backend.native/tests/framework/values/values.swift index 279c55f6996..a98f8a2b213 100644 --- a/backend.native/tests/framework/values/values.swift +++ b/backend.native/tests/framework/values/values.swift @@ -275,6 +275,33 @@ func testFunctions() throws { try assertEquals(actual: ValuesKt.multiply(int: 3, long: 2), expected: 6) } + +func testExceptions() throws { + let bridge = Bridge() + do { + try bridge.foo1() + } catch let error as NSError { + try assertTrue(error.kotlinException is MyException) + } + do { + var result: Int32 = 0 + try bridge.foo2(result: &result) + } catch let error as NSError { + try assertTrue(error.kotlinException is MyException) + } + do { + try bridge.foo3() + } catch let error as NSError { + try assertTrue(error.kotlinException is MyException) + } + do { + var result: KotlinNothing? = nil + try bridge.foo4(result: &result) + } catch let error as NSError { + try assertTrue(error.kotlinException is MyException) + } +} + func testFuncType() throws { let s = "str" let fFunc: () -> String = { return s } @@ -462,6 +489,7 @@ class ValuesTests : TestProvider { TestCase(name: "TestNulls", method: withAutorelease(testNulls)), TestCase(name: "TestAnyVar", method: withAutorelease(testAnyVar)), TestCase(name: "TestFunctions", method: withAutorelease(testFunctions)), + TestCase(name: "TestExceptions", method: withAutorelease(testExceptions)), TestCase(name: "TestFuncType", method: withAutorelease(testFuncType)), TestCase(name: "TestGenericsFoo", method: withAutorelease(testGenericsFoo)), TestCase(name: "TestVararg", method: withAutorelease(testVararg)),