Avoid using Nothing in codegen as if it was an object. (#2458)

This commit is contained in:
Nikolay Igotti
2018-12-12 11:33:02 +03:00
committed by GitHub
parent fe2b680be8
commit 2d9332bf5a
9 changed files with 89 additions and 20 deletions
@@ -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 {
@@ -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>()
@@ -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)
} }
} }
@@ -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?
@@ -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)!!
@@ -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.
@@ -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)),