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.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 {
|
||||
|
||||
+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.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 : CallableMemberDescriptor> T.allOverriddenDescriptors: List<T>
|
||||
get() {
|
||||
val result = mutableListOf<T>()
|
||||
|
||||
+2
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -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?
|
||||
|
||||
+9
-1
@@ -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)!!
|
||||
|
||||
+17
-13
@@ -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.
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user