Avoid using Nothing in codegen as if it was an object. (#2458)
This commit is contained in:
+2
-1
@@ -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.descriptors.explicitParameters
|
||||||
import org.jetbrains.kotlin.backend.common.pop
|
import org.jetbrains.kotlin.backend.common.pop
|
||||||
import org.jetbrains.kotlin.backend.common.push
|
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.descriptors.isUnit
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||||
@@ -934,7 +935,7 @@ internal class CAdapterGenerator(
|
|||||||
descriptor.defaultType.binaryTypeIsReference()
|
descriptor.defaultType.binaryTypeIsReference()
|
||||||
|
|
||||||
internal fun isMappedToVoid(descriptor: ClassDescriptor): Boolean {
|
internal fun isMappedToVoid(descriptor: ClassDescriptor): Boolean {
|
||||||
return descriptor.isUnit()
|
return descriptor.isUnit() || descriptor.isNothing()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateName(name: String): String {
|
fun translateName(name: String): String {
|
||||||
|
|||||||
+4
@@ -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.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.serialization.konan.KonanPackageFragment
|
import org.jetbrains.kotlin.serialization.konan.KonanPackageFragment
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty
|
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.isUnit() = this.defaultType.isUnit()
|
||||||
|
|
||||||
|
internal fun ClassDescriptor.isNothing() = this.defaultType.isNothing()
|
||||||
|
|
||||||
|
|
||||||
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
|
internal val <T : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
|
||||||
get() {
|
get() {
|
||||||
val result = mutableListOf<T>()
|
val result = mutableListOf<T>()
|
||||||
|
|||||||
+2
-3
@@ -9,6 +9,7 @@ import llvm.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
|
import org.jetbrains.kotlin.backend.konan.optimizations.DataFlowIR
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.isNothing
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isUnit
|
||||||
|
|
||||||
private val primitiveToLlvm = PrimitiveBinaryType.values().associate {
|
private val primitiveToLlvm = PrimitiveBinaryType.values().associate {
|
||||||
@@ -36,9 +37,7 @@ internal fun RuntimeAware.getLLVMType(type: DataFlowIR.Type) =
|
|||||||
|
|
||||||
internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef {
|
internal fun RuntimeAware.getLLVMReturnType(type: IrType): LLVMTypeRef {
|
||||||
return when {
|
return when {
|
||||||
type.isUnit() -> LLVMVoidType()!!
|
type.isUnit() || type.isNothing() -> LLVMVoidType()!!
|
||||||
// TODO: stdlib have methods taking Nothing, such as kotlin.collections.EmptySet.contains().
|
|
||||||
// KotlinBuiltIns.isNothing(type) -> LLVMVoidType()
|
|
||||||
else -> getLLVMType(type)
|
else -> getLLVMType(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-1
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.*
|
|||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.util.defaultType
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
@@ -418,7 +419,10 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
|
|||||||
} else {
|
} else {
|
||||||
"kfun:" + qualifyInternalName(descriptor)
|
"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?
|
// TODO: do we still need it?
|
||||||
|
|||||||
+9
-1
@@ -271,13 +271,16 @@ private val nounwindAttrKindId by lazy {
|
|||||||
getAttributeKindId("nounwind")
|
getAttributeKindId("nounwind")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val noreturnAttrKindId by lazy {
|
||||||
|
getAttributeKindId("noreturn")
|
||||||
|
}
|
||||||
|
|
||||||
private val signextAttrKindId by lazy {
|
private val signextAttrKindId by lazy {
|
||||||
getAttributeKindId("signext")
|
getAttributeKindId("signext")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun isFunctionNoUnwind(function: LLVMValueRef): Boolean {
|
fun isFunctionNoUnwind(function: LLVMValueRef): Boolean {
|
||||||
|
|
||||||
val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, nounwindAttrKindId)
|
val attribute = LLVMGetEnumAttributeAtIndex(function, LLVMAttributeFunctionIndex, nounwindAttrKindId)
|
||||||
return attribute != null
|
return attribute != null
|
||||||
}
|
}
|
||||||
@@ -295,6 +298,11 @@ fun setFunctionNoUnwind(function: LLVMValueRef) {
|
|||||||
LLVMAddAttributeAtIndex(function, LLVMAttributeFunctionIndex, attribute)
|
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?) {
|
fun addFunctionSignext(function: LLVMValueRef, index: Int, type: LLVMTypeRef?) {
|
||||||
if (type == int1Type || type == int8Type || type == int16Type) {
|
if (type == int1Type || type == int8Type || type == int16Type) {
|
||||||
val attribute = LLVMCreateEnumAttribute(LLVMGetTypeContext(function.type), signextAttrKindId, 0)!!
|
val attribute = LLVMCreateEnumAttribute(LLVMGetTypeContext(function.type), signextAttrKindId, 0)!!
|
||||||
|
|||||||
+17
-13
@@ -22,9 +22,15 @@ import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
|
internal fun TypeBridge.makeNothing() = when (this) {
|
||||||
|
is ReferenceBridge -> kNullInt8Ptr
|
||||||
|
is ValueTypeBridge -> LLVMConstNull(this.objCValueType.llvmType)!!
|
||||||
|
}
|
||||||
|
|
||||||
internal class ObjCExportCodeGenerator(
|
internal class ObjCExportCodeGenerator(
|
||||||
codegen: CodeGenerator,
|
codegen: CodeGenerator,
|
||||||
val namer: ObjCExportNamerImpl,
|
val namer: ObjCExportNamerImpl,
|
||||||
@@ -113,16 +119,19 @@ internal class ObjCExportCodeGenerator(
|
|||||||
fun FunctionGenerationContext.kotlinToObjC(
|
fun FunctionGenerationContext.kotlinToObjC(
|
||||||
value: LLVMValueRef,
|
value: LLVMValueRef,
|
||||||
typeBridge: TypeBridge
|
typeBridge: TypeBridge
|
||||||
): LLVMValueRef = when (typeBridge) {
|
): LLVMValueRef = when {
|
||||||
is ReferenceBridge -> kotlinReferenceToObjC(value)
|
LLVMTypeOf(value) == voidType -> typeBridge.makeNothing()
|
||||||
is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
|
typeBridge is ReferenceBridge -> kotlinReferenceToObjC(value)
|
||||||
}
|
typeBridge is ValueTypeBridge -> kotlinToObjC(value, typeBridge.objCValueType)
|
||||||
|
else -> TODO()
|
||||||
|
}
|
||||||
|
|
||||||
fun FunctionGenerationContext.objCToKotlin(
|
fun FunctionGenerationContext.objCToKotlin(
|
||||||
value: LLVMValueRef,
|
value: LLVMValueRef,
|
||||||
typeBridge: TypeBridge,
|
typeBridge: TypeBridge,
|
||||||
resultLifetime: Lifetime
|
resultLifetime: Lifetime
|
||||||
): LLVMValueRef = when (typeBridge) {
|
): LLVMValueRef = when (typeBridge) {
|
||||||
|
// TODO: if we add value type check here, we could bridge on Unit better.
|
||||||
is ReferenceBridge -> objCReferenceToKotlin(value, resultLifetime)
|
is ReferenceBridge -> objCReferenceToKotlin(value, resultLifetime)
|
||||||
is ValueTypeBridge -> objCToKotlin(value, typeBridge.objCValueType)
|
is ValueTypeBridge -> objCToKotlin(value, typeBridge.objCValueType)
|
||||||
}
|
}
|
||||||
@@ -608,7 +617,6 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret(genReturnValueOnSuccess(returnType))
|
ret(genReturnValueOnSuccess(returnType))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
|
private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
|
||||||
@@ -737,11 +745,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
|
|||||||
val actualReturnType = descriptor.returnType!!
|
val actualReturnType = descriptor.returnType!!
|
||||||
|
|
||||||
val retVal = when {
|
val retVal = when {
|
||||||
actualReturnType.isUnit() -> {
|
actualReturnType.isUnit() || actualReturnType.isNothing() -> {
|
||||||
genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge)
|
genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge)
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
baseReturnType.isUnit() -> {
|
baseReturnType.isUnit() || baseReturnType.isNothing() -> {
|
||||||
genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge)
|
genKotlinBaseMethodResult(Lifetime.ARGUMENT, methodBridge.returnBridge)
|
||||||
codegen.theUnitInstanceRef.llvm
|
codegen.theUnitInstanceRef.llvm
|
||||||
}
|
}
|
||||||
@@ -919,13 +927,9 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Mark it as non-overridable:
|
// Mark it as non-overridable:
|
||||||
baseMethods.distinctBy { namer.getSelector(it) }.forEach { base ->
|
baseMethods.distinctBy { namer.getSelector(it) }.forEach { baseMethod ->
|
||||||
reverseAdapters += KotlinToObjCMethodAdapter(
|
reverseAdapters += KotlinToObjCMethodAdapter(
|
||||||
namer.getSelector(base),
|
namer.getSelector(baseMethod), -1, -1, NullPointer(int8Type))
|
||||||
-1,
|
|
||||||
-1,
|
|
||||||
NullPointer(int8Type)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: some fake-overrides can be skipped.
|
// TODO: some fake-overrides can be skipped.
|
||||||
|
|||||||
+2
-1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.name.ClassId
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||||
|
|
||||||
internal abstract class ObjCExportMapper {
|
internal abstract class ObjCExportMapper {
|
||||||
@@ -159,7 +160,7 @@ private fun ObjCExportMapper.bridgeReturnType(
|
|||||||
MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
|
MethodBridge.ReturnValue.Mapped(bridgePropertyType(descriptor.correspondingProperty))
|
||||||
}
|
}
|
||||||
|
|
||||||
returnType.isUnit() -> if (convertExceptionsToErrors) {
|
returnType.isUnit() || returnType.isNothing() -> if (convertExceptionsToErrors) {
|
||||||
MethodBridge.ReturnValue.WithError.Success
|
MethodBridge.ReturnValue.WithError.Success
|
||||||
} else {
|
} else {
|
||||||
MethodBridge.ReturnValue.Void
|
MethodBridge.ReturnValue.Void
|
||||||
|
|||||||
@@ -242,3 +242,23 @@ fun isFrozen(obj: Any): Boolean = obj.isFrozen
|
|||||||
fun kotlinLambda(block: (Any) -> Any): Any = block
|
fun kotlinLambda(block: (Any) -> Any): Any = block
|
||||||
|
|
||||||
fun multiply(int: Int, long: Long) = int * long
|
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()
|
||||||
|
}
|
||||||
@@ -275,6 +275,33 @@ func testFunctions() throws {
|
|||||||
try assertEquals(actual: ValuesKt.multiply(int: 3, long: 2), expected: 6)
|
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 {
|
func testFuncType() throws {
|
||||||
let s = "str"
|
let s = "str"
|
||||||
let fFunc: () -> String = { return s }
|
let fFunc: () -> String = { return s }
|
||||||
@@ -462,6 +489,7 @@ class ValuesTests : TestProvider {
|
|||||||
TestCase(name: "TestNulls", method: withAutorelease(testNulls)),
|
TestCase(name: "TestNulls", method: withAutorelease(testNulls)),
|
||||||
TestCase(name: "TestAnyVar", method: withAutorelease(testAnyVar)),
|
TestCase(name: "TestAnyVar", method: withAutorelease(testAnyVar)),
|
||||||
TestCase(name: "TestFunctions", method: withAutorelease(testFunctions)),
|
TestCase(name: "TestFunctions", method: withAutorelease(testFunctions)),
|
||||||
|
TestCase(name: "TestExceptions", method: withAutorelease(testExceptions)),
|
||||||
TestCase(name: "TestFuncType", method: withAutorelease(testFuncType)),
|
TestCase(name: "TestFuncType", method: withAutorelease(testFuncType)),
|
||||||
TestCase(name: "TestGenericsFoo", method: withAutorelease(testGenericsFoo)),
|
TestCase(name: "TestGenericsFoo", method: withAutorelease(testGenericsFoo)),
|
||||||
TestCase(name: "TestVararg", method: withAutorelease(testVararg)),
|
TestCase(name: "TestVararg", method: withAutorelease(testVararg)),
|
||||||
|
|||||||
Reference in New Issue
Block a user