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.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 {
@@ -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>()
@@ -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)
}
}
@@ -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?
@@ -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)!!
@@ -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.
@@ -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)),