[IR] Replaced compilation errors with just exceptions
This commit is contained in:
+21
-18
@@ -32,22 +32,25 @@ internal fun CommonBackendContext.reportCompilationWarning(message: String) {
|
||||
}
|
||||
|
||||
internal fun error(irFile: IrFile?, element: IrElement?, message: String): Nothing {
|
||||
error(buildString {
|
||||
append("Internal compiler error: $message\n")
|
||||
if (element == null) {
|
||||
append("(IR element is null)")
|
||||
} else {
|
||||
if (irFile != null) {
|
||||
val location = element.getCompilerMessageLocation(irFile)
|
||||
append("at $location\n")
|
||||
}
|
||||
|
||||
val renderedElement = try {
|
||||
element.render()
|
||||
} catch (e: Throwable) {
|
||||
"(unable to render IR element)"
|
||||
}
|
||||
append(renderedElement)
|
||||
}
|
||||
})
|
||||
error(renderCompilerError(irFile, element, message))
|
||||
}
|
||||
|
||||
internal fun renderCompilerError(irFile: IrFile?, element: IrElement?, message: String) =
|
||||
buildString {
|
||||
append("Internal compiler error: $message\n")
|
||||
if (element == null) {
|
||||
append("(IR element is null)")
|
||||
} else {
|
||||
if (irFile != null) {
|
||||
val location = element.getCompilerMessageLocation(irFile)
|
||||
append("at $location\n")
|
||||
}
|
||||
|
||||
val renderedElement = try {
|
||||
element.render()
|
||||
} catch (e: Throwable) {
|
||||
"(unable to render IR element)"
|
||||
}
|
||||
append(renderedElement)
|
||||
}
|
||||
}
|
||||
+66
-138
@@ -55,8 +55,8 @@ internal interface KotlinStubs {
|
||||
fun getUniqueCName(prefix: String): String
|
||||
fun getUniqueKotlinFunctionReferenceClassName(prefix: String): String
|
||||
|
||||
fun reportError(location: IrElement, message: String): Nothing
|
||||
fun throwCompilerError(element: IrElement?, message: String): Nothing
|
||||
fun renderCompilerError(element: IrElement?, message: String = "Failed requirement."): String
|
||||
}
|
||||
|
||||
private class KotlinToCCallBuilder(
|
||||
@@ -113,7 +113,7 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
|
||||
|
||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
||||
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
||||
require(expression.dispatchReceiver == null)
|
||||
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||
|
||||
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
||||
|
||||
@@ -141,7 +141,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
||||
)
|
||||
}
|
||||
} else {
|
||||
require(expression.extensionReceiver == null)
|
||||
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||
targetPtrParameter = null
|
||||
targetFunctionName = this.getUniqueCName("target")
|
||||
|
||||
@@ -153,9 +153,9 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
||||
|
||||
val returnValuePassing = if (isInvoke) {
|
||||
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
|
||||
mapReturnType(returnType, TypeLocation.FunctionCallResult(expression), signature = null)
|
||||
mapReturnType(returnType, expression, signature = null)
|
||||
} else {
|
||||
mapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression), signature = callee)
|
||||
mapReturnType(callee.returnType, expression, signature = callee)
|
||||
}
|
||||
|
||||
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
||||
@@ -178,7 +178,7 @@ private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, ca
|
||||
arguments.forEachIndexed { index, argument ->
|
||||
val parameter = callee.valueParameters[index]
|
||||
if (parameter.isVararg) {
|
||||
require(index == arguments.lastIndex)
|
||||
require(index == arguments.lastIndex) { stubs.renderCompilerError(argument) }
|
||||
addVariadicArguments(argument)
|
||||
cFunctionBuilder.variadic = true
|
||||
} else {
|
||||
@@ -220,14 +220,9 @@ private fun KotlinToCCallBuilder.unwrapVariadicArguments(
|
||||
is IrExpression -> listOf(it)
|
||||
is IrSpreadElement -> {
|
||||
val expression = it.expression
|
||||
if (expression is IrCall && expression.symbol == symbols.arrayOf) {
|
||||
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
|
||||
unwrapVariadicArguments(elements)
|
||||
}
|
||||
} else {
|
||||
stubs.reportError(it, "When calling variadic " +
|
||||
(if (isObjCMethod) "Objective-C methods " else "C functions ") +
|
||||
"spread operator is supported only for *arrayOf(...)")
|
||||
require(expression is IrCall && expression.symbol == symbols.arrayOf) { stubs.renderCompilerError(it) }
|
||||
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
|
||||
unwrapVariadicArguments(elements)
|
||||
}
|
||||
}
|
||||
else -> stubs.throwCompilerError(it, "unexpected IrVarargElement")
|
||||
@@ -260,15 +255,12 @@ private fun <R> KotlinToCCallBuilder.handleArgumentForVarargParameter(
|
||||
val variable = argument.symbol.owner
|
||||
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar) {
|
||||
val initializer = variable.initializer
|
||||
if (initializer is IrVararg) {
|
||||
block(variable, initializer.elements)
|
||||
} else {
|
||||
stubs.throwCompilerError(initializer, "unexpected initializer")
|
||||
}
|
||||
require(initializer is IrVararg) { stubs.renderCompilerError(initializer) }
|
||||
block(variable, initializer.elements)
|
||||
} else if (variable is IrValueParameter && FunctionReferenceLowering.isLoweredFunctionReference(variable)) {
|
||||
val location = variable.parent // Parameter itself has incorrect location.
|
||||
val kind = if (this.isObjCMethod) "Objective-C methods" else "C functions"
|
||||
stubs.reportError(location, "callable references to variadic $kind are not supported")
|
||||
stubs.throwCompilerError(location, "callable references to variadic $kind are not supported")
|
||||
} else {
|
||||
stubs.throwCompilerError(variable, "unexpected value declaration")
|
||||
}
|
||||
@@ -383,8 +375,7 @@ internal fun KotlinStubs.generateObjCCall(
|
||||
|
||||
callBuilder.addArguments(arguments, method)
|
||||
|
||||
val returnValuePassing =
|
||||
mapReturnType(method.returnType, TypeLocation.FunctionCallResult(call), signature = method)
|
||||
val returnValuePassing = mapReturnType(method.returnType, call, signature = method)
|
||||
|
||||
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
||||
|
||||
@@ -398,7 +389,7 @@ internal fun KotlinStubs.generateObjCCall(
|
||||
|
||||
internal fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
|
||||
val classDescriptor = symbol.descriptor
|
||||
assert(!classDescriptor.isObjCMetaClass())
|
||||
require(!classDescriptor.isObjCMetaClass())
|
||||
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(symbol.typeWithStarProjections))
|
||||
}
|
||||
|
||||
@@ -437,25 +428,14 @@ private fun CCallbackBuilder.passThroughBridge(
|
||||
}
|
||||
|
||||
private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParameter: IrValueParameter) {
|
||||
val typeLocation = if (isObjCMethod) {
|
||||
TypeLocation.ObjCMethodParameter(it.index, functionParameter)
|
||||
} else {
|
||||
TypeLocation.FunctionPointerParameter(cFunctionBuilder.numberOfParameters, location)
|
||||
}
|
||||
|
||||
if (functionParameter.isVararg) {
|
||||
stubs.reportError(typeLocation.element, if (isObjCMethod) {
|
||||
"overriding variadic Objective-C methods is not supported"
|
||||
} else {
|
||||
"variadic function pointers are not supported"
|
||||
})
|
||||
}
|
||||
val location = if (isObjCMethod) functionParameter else location
|
||||
require(!functionParameter.isVararg) { stubs.renderCompilerError(location) }
|
||||
|
||||
val valuePassing = stubs.mapFunctionParameterType(
|
||||
it.type,
|
||||
retained = it.isObjCConsumed(),
|
||||
variadic = false,
|
||||
location = typeLocation
|
||||
location = location
|
||||
)
|
||||
|
||||
val kotlinArgument = with(valuePassing) { receiveValue() }
|
||||
@@ -463,12 +443,11 @@ private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParamete
|
||||
}
|
||||
|
||||
private fun CCallbackBuilder.build(function: IrSimpleFunction, signature: IrSimpleFunction): String {
|
||||
val typeLocation = if (isObjCMethod) {
|
||||
TypeLocation.ObjCMethodReturnValue(function)
|
||||
} else {
|
||||
TypeLocation.FunctionPointerReturnValue(location)
|
||||
}
|
||||
val valueReturning = stubs.mapReturnType(signature.returnType, typeLocation, signature)
|
||||
val valueReturning = stubs.mapReturnType(
|
||||
signature.returnType,
|
||||
location = if (isObjCMethod) function else location,
|
||||
signature = signature
|
||||
)
|
||||
buildValueReturn(function, valueReturning)
|
||||
return buildCFunction()
|
||||
}
|
||||
@@ -512,7 +491,7 @@ private fun KotlinStubs.generateCFunction(
|
||||
|
||||
if (isObjCMethod) {
|
||||
val receiver = signature.dispatchReceiverParameter!!
|
||||
require(receiver.type.isObjCReferenceType(target, irBuiltIns))
|
||||
require(receiver.type.isObjCReferenceType(target, irBuiltIns)) { renderCompilerError(signature) }
|
||||
val valuePassing = ObjCReferenceValuePassing(symbols, receiver.type, retained = signature.objCConsumesReceiver())
|
||||
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
|
||||
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
|
||||
@@ -520,7 +499,7 @@ private fun KotlinStubs.generateCFunction(
|
||||
// Selector is ignored:
|
||||
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
|
||||
} else {
|
||||
require(signature.dispatchReceiverParameter == null)
|
||||
require(signature.dispatchReceiverParameter == null) { renderCompilerError(signature) }
|
||||
}
|
||||
|
||||
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it, function.extensionReceiverParameter!!) }
|
||||
@@ -633,11 +612,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
||||
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
|
||||
|
||||
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) -> {
|
||||
if (variadic && isObjCMethod) {
|
||||
stubs.reportError(argument, "Passing String as variadic Objective-C argument is ambiguous; " +
|
||||
"cast it to NSString or pass with '.cstr' as C string")
|
||||
// TODO: consider reporting a warning for C functions.
|
||||
}
|
||||
require(!variadic || !isObjCMethod) { stubs.renderCompilerError(argument) }
|
||||
CStringArgumentPassing()
|
||||
}
|
||||
|
||||
@@ -648,7 +623,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
||||
type,
|
||||
retained = parameter?.isObjCConsumed() ?: false,
|
||||
variadic = variadic,
|
||||
location = TypeLocation.FunctionArgument(argument)
|
||||
location = argument
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -657,29 +632,15 @@ private fun KotlinStubs.mapFunctionParameterType(
|
||||
type: IrType,
|
||||
retained: Boolean,
|
||||
variadic: Boolean,
|
||||
location: TypeLocation
|
||||
location: IrElement
|
||||
): ArgumentPassing = when {
|
||||
type.isUnit() && !variadic -> IgnoredUnitArgumentPassing
|
||||
else -> mapType(type, retained = retained, variadic = variadic, location = location)
|
||||
}
|
||||
|
||||
private sealed class TypeLocation(val element: IrElement) {
|
||||
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
|
||||
class FunctionCallResult(val call: IrFunctionAccessExpression) : TypeLocation(call)
|
||||
|
||||
class FunctionPointerParameter(val index: Int, element: IrElement) : TypeLocation(element)
|
||||
class FunctionPointerReturnValue(element: IrElement) : TypeLocation(element)
|
||||
|
||||
class ObjCMethodParameter(val index: Int, element: IrElement) : TypeLocation(element)
|
||||
class ObjCMethodReturnValue(element: IrElement) : TypeLocation(element)
|
||||
|
||||
class BlockParameter(val index: Int, val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
|
||||
class BlockReturnValue(val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
|
||||
}
|
||||
|
||||
private fun KotlinStubs.mapReturnType(
|
||||
type: IrType,
|
||||
location: TypeLocation,
|
||||
location: IrElement,
|
||||
signature: IrSimpleFunction?
|
||||
): ValueReturning = when {
|
||||
type.isUnit() -> VoidReturning
|
||||
@@ -689,39 +650,29 @@ private fun KotlinStubs.mapReturnType(
|
||||
private fun KotlinStubs.mapBlockType(
|
||||
type: IrType,
|
||||
retained: Boolean,
|
||||
location: TypeLocation
|
||||
location: IrElement
|
||||
): ObjCBlockPointerValuePassing {
|
||||
type as IrSimpleType
|
||||
require(type.classifier == symbols.functionN(type.arguments.size - 1))
|
||||
require(type is IrSimpleType) { renderCompilerError(location) }
|
||||
require(type.classifier == symbols.functionN(type.arguments.size - 1)) { renderCompilerError(location) }
|
||||
|
||||
val returnTypeArgument = type.arguments.last()
|
||||
val valueReturning = when (returnTypeArgument) {
|
||||
is IrTypeProjection -> if (returnTypeArgument.variance == Variance.INVARIANT) {
|
||||
mapReturnType(returnTypeArgument.type, TypeLocation.BlockReturnValue(location), null)
|
||||
} else {
|
||||
reportUnsupportedType("${returnTypeArgument.variance.label}-variance of return type", type, location)
|
||||
}
|
||||
is IrStarProjection -> reportUnsupportedType("* as return type", type, location)
|
||||
else -> error(returnTypeArgument)
|
||||
}
|
||||
val parameterValuePassings = type.arguments.dropLast(1).mapIndexed { index, argument ->
|
||||
when (argument) {
|
||||
is IrTypeProjection -> if (argument.variance == Variance.INVARIANT) {
|
||||
mapType(
|
||||
argument.type,
|
||||
retained = false,
|
||||
variadic = false,
|
||||
location = TypeLocation.BlockParameter(index, location)
|
||||
)
|
||||
} else {
|
||||
reportUnsupportedType("${argument.variance.label}-variance of ${index + 1} parameter type", type, location)
|
||||
}
|
||||
is IrStarProjection -> reportUnsupportedType("* as ${index + 1} parameter type", type, location)
|
||||
else -> error(argument)
|
||||
}
|
||||
require(returnTypeArgument is IrTypeProjection) { renderCompilerError(location) }
|
||||
require(returnTypeArgument.variance == Variance.INVARIANT) { renderCompilerError(location) }
|
||||
val valueReturning = mapReturnType(returnTypeArgument.type, location, null)
|
||||
|
||||
val parameterValuePassings = type.arguments.dropLast(1).map { argument ->
|
||||
require(argument is IrTypeProjection) { renderCompilerError(location) }
|
||||
require(argument.variance == Variance.INVARIANT) { renderCompilerError(location) }
|
||||
mapType(
|
||||
argument.type,
|
||||
retained = false,
|
||||
variadic = false,
|
||||
location = location
|
||||
)
|
||||
}
|
||||
return ObjCBlockPointerValuePassing(
|
||||
this,
|
||||
location.element,
|
||||
location,
|
||||
type,
|
||||
valueReturning,
|
||||
parameterValuePassings,
|
||||
@@ -729,20 +680,17 @@ private fun KotlinStubs.mapBlockType(
|
||||
)
|
||||
}
|
||||
|
||||
private fun KotlinStubs.mapType(type: IrType, retained: Boolean, variadic: Boolean, location: TypeLocation): ValuePassing =
|
||||
mapType(type, retained, variadic, location, { reportUnsupportedType(it, type, location) })
|
||||
|
||||
private fun KotlinStubs.mapType(
|
||||
type: IrType,
|
||||
retained: Boolean,
|
||||
variadic: Boolean,
|
||||
typeLocation: TypeLocation,
|
||||
reportUnsupportedType: (String) -> Nothing
|
||||
location: IrElement
|
||||
): ValuePassing = when {
|
||||
type.isBoolean() -> BooleanValuePassing(
|
||||
cBoolType(target) ?: reportUnsupportedType("unavailable on target platform"),
|
||||
irBuiltIns
|
||||
)
|
||||
type.isBoolean() -> {
|
||||
val cBoolType = cBoolType(target)
|
||||
require(cBoolType != null) { renderCompilerError(location) }
|
||||
BooleanValuePassing(cBoolType, irBuiltIns)
|
||||
}
|
||||
|
||||
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
|
||||
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
|
||||
@@ -750,7 +698,7 @@ private fun KotlinStubs.mapType(
|
||||
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
|
||||
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
|
||||
type.isDouble() -> TrivialValuePassing(irBuiltIns.doubleType, CTypes.double)
|
||||
type.classifierOrNull == symbols.interopCPointer -> TrivialValuePassing(type, CTypes.voidPtr)
|
||||
type.isCPointer(symbols) -> TrivialValuePassing(type, CTypes.voidPtr)
|
||||
type.isTypeOfNullLiteral() && variadic -> TrivialValuePassing(symbols.interopCPointer.typeWithStarProjections.makeNullable(), CTypes.voidPtr)
|
||||
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
|
||||
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
|
||||
@@ -768,33 +716,32 @@ private fun KotlinStubs.mapType(
|
||||
CEnumValuePassing(
|
||||
enumClass,
|
||||
value,
|
||||
mapType(value.getter!!.returnType, retained, variadic, typeLocation) as SimpleValuePassing
|
||||
mapType(value.getter!!.returnType, retained, variadic, location) as SimpleValuePassing
|
||||
)
|
||||
}
|
||||
|
||||
type.classifierOrNull == symbols.interopCValue -> if (type.isNullable()) {
|
||||
reportUnsupportedType("must not be nullable")
|
||||
} else {
|
||||
type.isCValue(symbols) -> {
|
||||
require(!type.isNullable()) { renderCompilerError(location) }
|
||||
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
|
||||
?: reportUnsupportedType("must be parameterized with concrete class")
|
||||
require(kotlinClass != null) { renderCompilerError(location) }
|
||||
val cStructType = getNamedCStructType(kotlinClass)
|
||||
require(cStructType != null) { renderCompilerError(location) }
|
||||
|
||||
StructValuePassing(kotlinClass, getNamedCStructType(kotlinClass)
|
||||
?: reportUnsupportedType("not a structure or too complex"))
|
||||
StructValuePassing(kotlinClass, cStructType)
|
||||
}
|
||||
|
||||
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true -> {
|
||||
TrivialValuePassing(type, CTypes.voidPtr)
|
||||
}
|
||||
|
||||
type.isFunction() -> if (variadic){
|
||||
reportUnsupportedType("not supported as variadic argument")
|
||||
} else {
|
||||
mapBlockType(type, retained = retained, location = typeLocation)
|
||||
type.isFunction() -> {
|
||||
require(!variadic) { renderCompilerError(location) }
|
||||
mapBlockType(type, retained = retained, location = location)
|
||||
}
|
||||
|
||||
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
||||
|
||||
else -> reportUnsupportedType("doesn't correspond to any C type")
|
||||
else -> throwCompilerError(location, "doesn't correspond to any C type")
|
||||
}
|
||||
|
||||
private class CExpression(val expression: String, val type: CType)
|
||||
@@ -1211,7 +1158,7 @@ private class ObjCBlockPointerValuePassing(
|
||||
}
|
||||
|
||||
val parameterCount = parameterValuePassings.size
|
||||
assert(functionType.arguments.size == parameterCount + 1)
|
||||
require(functionType.arguments.size == parameterCount + 1) { stubs.renderCompilerError(location) }
|
||||
|
||||
val overriddenInvokeMethod = (functionType.classifier.owner as IrClass).simpleFunctions()
|
||||
.single { it.name == OperatorNameConventions.INVOKE }
|
||||
@@ -1308,7 +1255,7 @@ private class ObjCBlockPointerValuePassing(
|
||||
}
|
||||
}
|
||||
|
||||
assert(functionType.isFunction())
|
||||
require(functionType.isFunction()) { stubs.renderCompilerError(location) }
|
||||
val invokeFunction = (functionType.classifier.owner as IrClass)
|
||||
.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }
|
||||
|
||||
@@ -1447,22 +1394,3 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing {
|
||||
}
|
||||
|
||||
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
|
||||
|
||||
private fun KotlinStubs.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
|
||||
// TODO: report errors in frontend instead.
|
||||
fun TypeLocation.render(): String = when (this) {
|
||||
is TypeLocation.FunctionArgument -> ""
|
||||
is TypeLocation.FunctionCallResult -> " of return value"
|
||||
is TypeLocation.FunctionPointerParameter -> " of callback parameter ${index + 1}"
|
||||
is TypeLocation.FunctionPointerReturnValue -> " of callback return value"
|
||||
is TypeLocation.ObjCMethodParameter -> " of overridden Objective-C method parameter"
|
||||
is TypeLocation.ObjCMethodReturnValue -> " of overridden Objective-C method return value"
|
||||
is TypeLocation.BlockParameter -> " of ${index + 1} parameter in Objective-C block type${blockLocation.render()}"
|
||||
is TypeLocation.BlockReturnValue -> " of return value of Objective-C block type${blockLocation.render()}"
|
||||
}
|
||||
|
||||
val typeLocation: String = location.render()
|
||||
|
||||
reportError(location.element, "type ${type.render()} $typeLocation is not supported here" +
|
||||
if (reason.isNotEmpty()) ": $reason" else "")
|
||||
}
|
||||
|
||||
+1
-1
@@ -355,7 +355,7 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
|
||||
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
|
||||
}
|
||||
}
|
||||
else -> error("Unexpected accessor function: ${accessor.name}")
|
||||
else -> failCompilation("Unexpected accessor function: ${accessor.name}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+84
-338
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
@@ -62,33 +61,6 @@ internal class InteropLowering(context: Context) : FileLoweringPass {
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrExpression.isNonCapturingFunction(): Boolean {
|
||||
if (!type.isFunctionTypeOrSubtype())
|
||||
return false
|
||||
|
||||
val fromContainerExpression = fun(expr: IrExpression): IrConstructorCall? {
|
||||
if (expr !is IrContainerExpression)
|
||||
return null
|
||||
if (expr.statements.size != 2)
|
||||
return null
|
||||
|
||||
val firstStatement = expr.statements[0]
|
||||
if (firstStatement !is IrContainerExpression || firstStatement.statements.size != 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
val secondStatement = expr.statements[1]
|
||||
|
||||
return secondStatement as? IrConstructorCall
|
||||
}
|
||||
|
||||
val constructorCall = this as? IrConstructorCall
|
||||
?: fromContainerExpression(this)
|
||||
?: return false
|
||||
|
||||
return constructorCall.valueArgumentsCount == 0
|
||||
}
|
||||
|
||||
private abstract class BaseInteropIrTransformer(private val context: Context) : IrBuildingTransformer(context) {
|
||||
|
||||
protected inline fun <T> generateWithStubs(element: IrElement? = null, block: KotlinStubs.() -> T): T =
|
||||
@@ -131,15 +103,18 @@ private abstract class BaseInteropIrTransformer(private val context: Context) :
|
||||
|
||||
override val target get() = context.config.target
|
||||
|
||||
override fun reportError(location: IrElement, message: String): Nothing =
|
||||
context.reportCompilationError(message, irFile, location)
|
||||
|
||||
override fun throwCompilerError(element: IrElement?, message: String): Nothing {
|
||||
error(irFile, element, message)
|
||||
}
|
||||
|
||||
override fun renderCompilerError(element: IrElement?, message: String) =
|
||||
renderCompilerError(irFile, element, message)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun renderCompilerError(element: IrElement?, message: String = "Failed requirement") =
|
||||
renderCompilerError(irFile, element, message)
|
||||
|
||||
protected abstract val irFile: IrFile
|
||||
protected abstract fun addTopLevel(declaration: IrDeclaration)
|
||||
}
|
||||
@@ -177,12 +152,6 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
putValueArgument(0, classPtr)
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.getObjCClass(classSymbol: IrClassSymbol): IrExpression {
|
||||
val classDescriptor = classSymbol.descriptor
|
||||
assert(!classDescriptor.isObjCMetaClass())
|
||||
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(classSymbol.typeWithStarProjections))
|
||||
}
|
||||
|
||||
private val outerClasses = mutableListOf<IrClass>()
|
||||
|
||||
override fun visitClass(declaration: IrClass): IrStatement {
|
||||
@@ -220,7 +189,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
|
||||
if (irClass.annotations.hasAnnotation(interop.exportObjCClass.fqNameSafe)) {
|
||||
val irBuilder = context.createIrBuilder(currentFile.symbol).at(irClass)
|
||||
topLevelInitializers.add(irBuilder.getObjCClass(irClass.symbol))
|
||||
topLevelInitializers.add(irBuilder.getObjCClass(symbols, irClass.symbol))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,38 +210,15 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
constructor.overridesConstructor(it)
|
||||
}.toList()
|
||||
|
||||
val superConstructor = superConstructors.singleOrNull() ?: run {
|
||||
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
||||
if (superConstructors.isEmpty()) {
|
||||
context.reportCompilationError(
|
||||
"""
|
||||
constructor with @$annotation doesn't override any super class constructor.
|
||||
It must completely match by parameter names and types.""".trimIndent(),
|
||||
currentFile,
|
||||
constructor
|
||||
)
|
||||
} else {
|
||||
context.reportCompilationError(
|
||||
"constructor with @$annotation matches more than one of super constructors",
|
||||
currentFile,
|
||||
constructor
|
||||
)
|
||||
}
|
||||
}
|
||||
val superConstructor = superConstructors.singleOrNull()
|
||||
require(superConstructor != null) { renderCompilerError(constructor) }
|
||||
|
||||
val initMethod = superConstructor.getObjCInitMethod()!!
|
||||
|
||||
// Remove fake overrides of this init method, also check for explicit overriding:
|
||||
irClass.declarations.removeAll {
|
||||
if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols) {
|
||||
if (it.isReal) {
|
||||
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
||||
context.reportCompilationError(
|
||||
"constructor with @$annotation overrides initializer that is already overridden explicitly",
|
||||
currentFile,
|
||||
constructor
|
||||
)
|
||||
}
|
||||
require(it.isFakeOverride) { renderCompilerError(constructor) }
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -319,7 +265,8 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
)
|
||||
}
|
||||
|
||||
assert(result.getObjCMethodInfo() != null) // Ensure it gets correctly recognized by the compiler.
|
||||
// Ensure it gets correctly recognized by the compiler.
|
||||
require(result.getObjCMethodInfo() != null) { renderCompilerError(constructor) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,55 +282,17 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
}
|
||||
|
||||
private fun generateActionImp(function: IrSimpleFunction): IrSimpleFunction {
|
||||
val action = "@${context.interopBuiltIns.objCAction.name}"
|
||||
|
||||
function.extensionReceiverParameter?.let {
|
||||
context.reportCompilationError("$action method must not have extension receiver",
|
||||
currentFile, it)
|
||||
}
|
||||
|
||||
function.valueParameters.forEach {
|
||||
val kotlinType = it.descriptor.type
|
||||
if (!kotlinType.isObjCObjectType()) {
|
||||
context.reportCompilationError("Unexpected $action method parameter type: $kotlinType\n" +
|
||||
"Only Objective-C object types are supported here",
|
||||
currentFile, it)
|
||||
}
|
||||
}
|
||||
|
||||
val returnType = function.returnType
|
||||
|
||||
if (!returnType.isUnit()) {
|
||||
context.reportCompilationError("Unexpected $action method return type: ${returnType.toKotlinType()}\n" +
|
||||
"Only 'Unit' is supported here",
|
||||
currentFile, function
|
||||
)
|
||||
}
|
||||
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
|
||||
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
|
||||
require(function.returnType.isUnit()) { renderCompilerError(function) }
|
||||
|
||||
return generateFunctionImp(inferObjCSelector(function.descriptor), function)
|
||||
}
|
||||
|
||||
private fun generateOutletSetterImp(property: IrProperty): IrSimpleFunction {
|
||||
val descriptor = property.descriptor
|
||||
|
||||
val outlet = "@${context.interopBuiltIns.objCOutlet.name}"
|
||||
|
||||
if (!descriptor.isVar) {
|
||||
context.reportCompilationError("$outlet property must be var",
|
||||
currentFile, property)
|
||||
}
|
||||
|
||||
property.getter?.extensionReceiverParameter?.let {
|
||||
context.reportCompilationError("$outlet must not have extension receiver",
|
||||
currentFile, it)
|
||||
}
|
||||
|
||||
val type = descriptor.type
|
||||
if (!type.isObjCObjectType()) {
|
||||
context.reportCompilationError("Unexpected $outlet type: $type\n" +
|
||||
"Only Objective-C object types are supported here",
|
||||
currentFile, property)
|
||||
}
|
||||
require(property.isVar) { renderCompilerError(property) }
|
||||
require(property.getter?.extensionReceiverParameter == null) { renderCompilerError(property) }
|
||||
require(property.descriptor.type.isObjCObjectType()) { renderCompilerError(property) }
|
||||
|
||||
val name = property.name.asString()
|
||||
val selector = "set${name.capitalize()}:"
|
||||
@@ -392,39 +301,29 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
}
|
||||
|
||||
private fun getMethodSignatureEncoding(function: IrFunction): String {
|
||||
assert(function.extensionReceiverParameter == null)
|
||||
assert(function.valueParameters.all { it.type.isObjCObjectType() })
|
||||
assert(function.returnType.isUnit())
|
||||
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
|
||||
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
|
||||
require(function.returnType.isUnit()) { renderCompilerError(function) }
|
||||
|
||||
// Note: these values are valid for x86_64 and arm64.
|
||||
return when (function.valueParameters.size) {
|
||||
0 -> "v16@0:8"
|
||||
1 -> "v24@0:8@16"
|
||||
2 -> "v32@0:8@16@24"
|
||||
else -> context.reportCompilationError("Only 0, 1 or 2 parameters are supported here",
|
||||
currentFile, function
|
||||
)
|
||||
else -> error(irFile, function, "Only 0, 1 or 2 parameters are supported here")
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateFunctionImp(selector: String, function: IrFunction): IrSimpleFunction {
|
||||
val signatureEncoding = getMethodSignatureEncoding(function)
|
||||
|
||||
val returnType = function.returnType
|
||||
assert(returnType.isUnit())
|
||||
|
||||
val nativePtrType = context.ir.symbols.nativePtrType
|
||||
|
||||
val parameterTypes = mutableListOf(nativePtrType) // id self
|
||||
|
||||
parameterTypes.add(nativePtrType) // SEL _cmd
|
||||
|
||||
function.valueParameters.mapTo(parameterTypes) {
|
||||
when {
|
||||
it.descriptor.type.isObjCObjectType() -> nativePtrType
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
function.valueParameters.mapTo(parameterTypes) { nativePtrType }
|
||||
|
||||
val newFunction = WrappedSimpleFunctionDescriptor().let {
|
||||
IrFunctionImpl(
|
||||
@@ -434,7 +333,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
("imp:$selector").synthesizedName,
|
||||
DescriptorVisibilities.PRIVATE,
|
||||
Modality.FINAL,
|
||||
returnType,
|
||||
function.returnType,
|
||||
isInline = false,
|
||||
isExternal = false,
|
||||
isTailrec = false,
|
||||
@@ -516,52 +415,23 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
}
|
||||
|
||||
private fun checkKotlinObjCClass(irClass: IrClass) {
|
||||
val kind = irClass.descriptor.kind
|
||||
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
|
||||
context.reportCompilationError(
|
||||
"Only classes are supported as subtypes of Objective-C types",
|
||||
currentFile, irClass
|
||||
)
|
||||
}
|
||||
|
||||
if (!irClass.descriptor.isFinalClass) {
|
||||
context.reportCompilationError(
|
||||
"Non-final Kotlin subclasses of Objective-C classes are not yet supported",
|
||||
currentFile, irClass
|
||||
)
|
||||
}
|
||||
|
||||
irClass.companionObject()?.let {
|
||||
if (it.hasFields() ||
|
||||
it.getSuperClassNotAny()?.hasFields() ?: false) {
|
||||
context.reportCompilationError(
|
||||
"Fields are not supported for Companion of subclass of ObjC type",
|
||||
currentFile, irClass
|
||||
)
|
||||
}
|
||||
}
|
||||
val kind = irClass.kind
|
||||
require(kind == ClassKind.CLASS || kind == ClassKind.OBJECT) { renderCompilerError(irClass) }
|
||||
require(irClass.isFinalClass) { renderCompilerError(irClass) }
|
||||
require(irClass.companionObject()?.hasFields() != true) { renderCompilerError(irClass) }
|
||||
require(irClass.companionObject()?.getSuperClassNotAny()?.hasFields() != true) { renderCompilerError(irClass) }
|
||||
|
||||
var hasObjCClassSupertype = false
|
||||
irClass.descriptor.defaultType.constructor.supertypes.forEach {
|
||||
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
|
||||
if (!descriptor.isObjCClass()) {
|
||||
context.reportCompilationError(
|
||||
"Mixing Kotlin and Objective-C supertypes is not supported",
|
||||
currentFile, irClass
|
||||
)
|
||||
}
|
||||
require(descriptor.isObjCClass()) { renderCompilerError(irClass) }
|
||||
|
||||
if (descriptor.kind == ClassKind.CLASS) {
|
||||
hasObjCClassSupertype = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasObjCClassSupertype) {
|
||||
context.reportCompilationError(
|
||||
"Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)",
|
||||
currentFile, irClass
|
||||
)
|
||||
}
|
||||
require(hasObjCClassSupertype) { renderCompilerError(irClass) }
|
||||
|
||||
val methodsOfAny =
|
||||
context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
|
||||
@@ -571,20 +441,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
it in methodsOfAny
|
||||
}
|
||||
|
||||
if (overriddenMethodOfAny != null) {
|
||||
val correspondingObjCMethod = when (method.name.asString()) {
|
||||
"toString" -> "'description'"
|
||||
"hashCode" -> "'hash'"
|
||||
"equals" -> "'isEqual:'"
|
||||
else -> "corresponding Objective-C method"
|
||||
}
|
||||
|
||||
context.report(
|
||||
method,
|
||||
"can't override '${method.name}', override $correspondingObjCMethod instead",
|
||||
isError = true
|
||||
)
|
||||
}
|
||||
require(overriddenMethodOfAny == null) { renderCompilerError(method) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,23 +473,15 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
|
||||
// Calling super constructor from Kotlin Objective-C class.
|
||||
|
||||
assert(constructedClass.getSuperClassNotAny() == delegatingCallConstructingClass)
|
||||
require(constructedClass.getSuperClassNotAny() == delegatingCallConstructingClass) { renderCompilerError(expression) }
|
||||
require(expression.symbol.owner.objCConstructorIsDesignated()) { renderCompilerError(expression) }
|
||||
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||
|
||||
val initMethod = expression.symbol.owner.getObjCInitMethod()!!
|
||||
|
||||
if (!expression.symbol.owner.objCConstructorIsDesignated()) {
|
||||
context.reportCompilationError(
|
||||
"Unable to call non-designated initializer as super constructor",
|
||||
currentFile,
|
||||
expression
|
||||
)
|
||||
}
|
||||
|
||||
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||
|
||||
assert(expression.dispatchReceiver == null)
|
||||
assert(expression.extensionReceiver == null)
|
||||
|
||||
val initCall = builder.genLoweredObjCMethodCall(
|
||||
initMethodInfo,
|
||||
superQualifier = delegatingCallConstructingClass.symbol,
|
||||
@@ -714,13 +563,13 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
val initMethod = callee.getObjCInitMethod()
|
||||
if (initMethod != null) {
|
||||
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||
assert(expression.extensionReceiver == null)
|
||||
assert(expression.dispatchReceiver == null)
|
||||
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||
|
||||
val constructedClass = callee.constructedClass
|
||||
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||
return builder.at(expression).run {
|
||||
val classPtr = getObjCClass(constructedClass.symbol)
|
||||
val classPtr = getObjCClass(symbols, constructedClass.symbol)
|
||||
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
|
||||
}
|
||||
}
|
||||
@@ -770,21 +619,9 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
|
||||
if (!useKotlinDispatch) {
|
||||
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||
assert(expression.dispatchReceiver == null || expression.extensionReceiver == null)
|
||||
|
||||
if (expression.superQualifierSymbol?.owner?.isObjCMetaClass() == true) {
|
||||
context.reportCompilationError(
|
||||
"Super calls to Objective-C meta classes are not supported yet",
|
||||
currentFile, expression
|
||||
)
|
||||
}
|
||||
|
||||
if (expression.superQualifierSymbol?.owner?.isInterface == true) {
|
||||
context.reportCompilationError(
|
||||
"Super calls to Objective-C protocols are not allowed",
|
||||
currentFile, expression
|
||||
)
|
||||
}
|
||||
require(expression.dispatchReceiver == null || expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||
require(expression.superQualifierSymbol?.owner?.isObjCMetaClass() != true) { renderCompilerError(expression) }
|
||||
require(expression.superQualifierSymbol?.owner?.isInterface != true) { renderCompilerError(expression) }
|
||||
|
||||
builder.at(expression)
|
||||
return builder.genLoweredObjCMethodCall(
|
||||
@@ -809,7 +646,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
val irClass = classSymbol.owner
|
||||
|
||||
val companionObject = irClass.companionObject() ?:
|
||||
error("native variable class ${irClass.descriptor} must have the companion object")
|
||||
error(irFile, expression, "native variable class ${irClass.descriptor} must have the companion object")
|
||||
|
||||
builder.at(expression).irGetObject(companionObject.symbol)
|
||||
}
|
||||
@@ -841,8 +678,8 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
||||
// Note: in interop stubs const val initializer is either `IrConst` or quite simple expression,
|
||||
// so it is ok to compute it every time.
|
||||
|
||||
assert(declaration.setter == null)
|
||||
assert(!declaration.isVar)
|
||||
require(declaration.setter == null) { renderCompilerError(declaration) }
|
||||
require(!declaration.isVar) { renderCompilerError(declaration) }
|
||||
|
||||
declaration.transformChildrenVoid()
|
||||
declaration
|
||||
@@ -941,16 +778,15 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
|
||||
val callee = expression.symbol.owner
|
||||
val inlinedClass = callee.returnType.getInlinedClassNative()
|
||||
if (inlinedClass?.descriptor == interop.cPointer || inlinedClass?.descriptor == interop.nativePointed) {
|
||||
context.reportCompilationError("Native interop types constructors must not be called directly",
|
||||
irFile, expression)
|
||||
}
|
||||
require(inlinedClass?.descriptor != interop.cPointer) { renderCompilerError(expression) }
|
||||
require(inlinedClass?.descriptor != interop.nativePointed) { renderCompilerError(expression) }
|
||||
|
||||
val constructedClass = callee.constructedClass
|
||||
if (!constructedClass.isObjCClass())
|
||||
return expression
|
||||
|
||||
assert(constructedClass.isKotlinObjCClass()) // Calls to other ObjC class constructors must be lowered.
|
||||
// Calls to other ObjC class constructors must be lowered.
|
||||
require(constructedClass.isKotlinObjCClass()) { renderCompilerError(expression) }
|
||||
return builder.at(expression).irBlock {
|
||||
val rawPtr = irTemporary(irCall(symbols.interopAllocObjCObject.owner).apply {
|
||||
putValueArgument(0, getObjCClass(symbols, constructedClass.symbol))
|
||||
@@ -987,14 +823,11 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
?.takeIf { it.isConst }
|
||||
?: return null
|
||||
|
||||
val irConstant = (constantProperty.backingField
|
||||
?.initializer
|
||||
?.expression
|
||||
?: error("Constant property ${constantProperty.name} has no initializer!"))
|
||||
as IrConst<*>
|
||||
val initializer = constantProperty.backingField?.initializer?.expression
|
||||
require(initializer is IrConst<*>) { renderCompilerError(expression) }
|
||||
|
||||
// Avoid node duplication
|
||||
return irConstant.copy()
|
||||
return initializer.copy()
|
||||
}
|
||||
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
@@ -1002,30 +835,23 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
|
||||
// Need to do this separately as otherwise [expression.transformChildrenVoid(this)] would be called
|
||||
// and the [IrConstructorCall] would be transformed which is not what we want.
|
||||
val intrinsic = interop.objCObjectInitBy.name
|
||||
|
||||
val argument = expression.getValueArgument(0)!!
|
||||
val constructorCall = argument as? IrConstructorCall
|
||||
?: context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
|
||||
irFile, argument)
|
||||
require(argument is IrConstructorCall) { renderCompilerError(argument) }
|
||||
|
||||
val constructedClass = constructorCall.symbol.owner.constructedClass
|
||||
val constructedClass = argument.symbol.owner.constructedClass
|
||||
|
||||
val extensionReceiver = expression.extensionReceiver!!
|
||||
if (extensionReceiver !is IrGetValue ||
|
||||
!extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) {
|
||||
require(extensionReceiver is IrGetValue &&
|
||||
extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) { renderCompilerError(extensionReceiver) }
|
||||
|
||||
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
|
||||
irFile, extensionReceiver)
|
||||
}
|
||||
|
||||
constructorCall.transformChildrenVoid(this)
|
||||
argument.transformChildrenVoid(this)
|
||||
|
||||
return builder.at(expression).irBlock {
|
||||
val instance = extensionReceiver.symbol.owner
|
||||
+irCall(symbols.initInstance).apply {
|
||||
putValueArgument(0, irGet(instance))
|
||||
putValueArgument(1, constructorCall)
|
||||
putValueArgument(1, argument)
|
||||
}
|
||||
+irGet(instance)
|
||||
}
|
||||
@@ -1052,7 +878,7 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
|
||||
}
|
||||
|
||||
val failCompilation = { msg: String -> context.reportCompilationError(msg, irFile, expression) }
|
||||
val failCompilation = { msg: String -> error(irFile, expression, msg) }
|
||||
tryGenerateInteropMemberAccess(expression, symbols, builder, failCompilation)?.let { return it }
|
||||
|
||||
tryGenerateInteropConstantRead(expression)?.let { return it }
|
||||
@@ -1080,14 +906,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
|
||||
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
|
||||
|
||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()
|
||||
|| irCallableReference.symbol !is IrSimpleFunctionSymbol) {
|
||||
context.reportCompilationError(
|
||||
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
|
||||
irFile, expression
|
||||
)
|
||||
// TODO: should probably be reported during analysis.
|
||||
}
|
||||
require(irCallableReference != null && irCallableReference.getArguments().isEmpty()
|
||||
&& irCallableReference.symbol is IrSimpleFunctionSymbol) { renderCompilerError(expression) }
|
||||
|
||||
val targetSymbol = irCallableReference.symbol
|
||||
val target = targetSymbol.owner
|
||||
@@ -1096,13 +916,9 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
function.typeParameters.indices.forEach { index ->
|
||||
val typeArgument = expression.getTypeArgument(index)!!.toKotlinType()
|
||||
val signatureType = signatureTypes[index].toKotlinType()
|
||||
if (typeArgument.constructor != signatureType.constructor ||
|
||||
typeArgument.isMarkedNullable != signatureType.isMarkedNullable) {
|
||||
context.reportCompilationError(
|
||||
"C function signature element mismatch: expected '$signatureType', got '$typeArgument'",
|
||||
irFile, expression
|
||||
)
|
||||
}
|
||||
|
||||
require(typeArgument.constructor == signatureType.constructor &&
|
||||
typeArgument.isMarkedNullable == signatureType.isMarkedNullable) { renderCompilerError(expression) }
|
||||
}
|
||||
|
||||
generateCFunctionPointer(target as IrSimpleFunction, expression)
|
||||
@@ -1118,36 +934,19 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
|
||||
val receiver = expression.extensionReceiver!!
|
||||
val typeOperand = expression.getSingleTypeArgument()
|
||||
val kotlinTypeOperand = typeOperand.toKotlinType()
|
||||
|
||||
val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) }
|
||||
val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) }
|
||||
|
||||
val receiverKotlinType = receiver.type.toKotlinType()
|
||||
|
||||
if (receiverTypeIndex == -1) {
|
||||
context.reportCompilationError("Receiver's type $receiverKotlinType is not an integer type",
|
||||
irFile, receiver)
|
||||
}
|
||||
|
||||
if (typeOperandIndex == -1) {
|
||||
context.reportCompilationError("Type argument $kotlinTypeOperand is not an integer type",
|
||||
irFile, expression)
|
||||
}
|
||||
require(receiverTypeIndex >= 0) { renderCompilerError(receiver) }
|
||||
require(typeOperandIndex >= 0) { renderCompilerError(expression) }
|
||||
|
||||
when (intrinsicType) {
|
||||
IntrinsicType.INTEROP_SIGN_EXTEND -> if (receiverTypeIndex > typeOperandIndex) {
|
||||
context.reportCompilationError("unable to sign extend $receiverKotlinType to $kotlinTypeOperand",
|
||||
irFile, expression)
|
||||
}
|
||||
|
||||
IntrinsicType.INTEROP_NARROW -> if (receiverTypeIndex < typeOperandIndex) {
|
||||
context.reportCompilationError("unable to narrow $receiverKotlinType to $kotlinTypeOperand",
|
||||
irFile, expression)
|
||||
}
|
||||
|
||||
else -> context.reportCompilationError("unexpected intrinsic $intrinsicType",
|
||||
irFile, expression)
|
||||
IntrinsicType.INTEROP_SIGN_EXTEND ->
|
||||
require(receiverTypeIndex <= typeOperandIndex) { renderCompilerError(expression) }
|
||||
IntrinsicType.INTEROP_NARROW ->
|
||||
require(receiverTypeIndex >= typeOperandIndex) { renderCompilerError(expression) }
|
||||
else -> error(intrinsicType)
|
||||
}
|
||||
|
||||
val receiverClass = symbols.integerClasses.single {
|
||||
@@ -1170,26 +969,20 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
val typeOperand = expression.getTypeArgument(0)!!
|
||||
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
|
||||
val source = receiverType.classifierOrFail as IrClassSymbol
|
||||
assert(source in integerClasses)
|
||||
require(source in integerClasses) { renderCompilerError(expression) }
|
||||
require(typeOperand is IrSimpleType && typeOperand.classifier in integerClasses
|
||||
&& !typeOperand.hasQuestionMark) { renderCompilerError(expression) }
|
||||
|
||||
if (typeOperand is IrSimpleType && typeOperand.classifier in integerClasses && !typeOperand.hasQuestionMark) {
|
||||
val target = typeOperand.classifier as IrClassSymbol
|
||||
val valueToConvert = expression.extensionReceiver!!
|
||||
val target = typeOperand.classifier as IrClassSymbol
|
||||
val valueToConvert = expression.extensionReceiver!!
|
||||
|
||||
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
|
||||
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
|
||||
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
|
||||
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
|
||||
builder.irConvertInteger(signedTarget, target, widened)
|
||||
} else {
|
||||
builder.irConvertInteger(source, target, valueToConvert)
|
||||
}
|
||||
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
|
||||
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
|
||||
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
|
||||
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
|
||||
builder.irConvertInteger(signedTarget, target, widened)
|
||||
} else {
|
||||
context.reportCompilationError(
|
||||
"unable to convert ${receiverType.toKotlinType()} to ${typeOperand.toKotlinType()}",
|
||||
irFile,
|
||||
expression
|
||||
)
|
||||
builder.irConvertInteger(source, target, valueToConvert)
|
||||
}
|
||||
}
|
||||
IntrinsicType.INTEROP_MEMORY_COPY -> {
|
||||
@@ -1198,12 +991,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
IntrinsicType.WORKER_EXECUTE -> {
|
||||
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(2)!!)
|
||||
|
||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
|
||||
context.reportCompilationError(
|
||||
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
|
||||
irFile, expression
|
||||
)
|
||||
}
|
||||
require(irCallableReference != null
|
||||
&& irCallableReference.getArguments().isEmpty()) { renderCompilerError(expression) }
|
||||
|
||||
val targetSymbol = irCallableReference.symbol
|
||||
val jobPointer = IrFunctionReferenceImpl(
|
||||
@@ -1230,17 +1019,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
builder.irCall(symbols.interopCPointerGetRawValue).apply {
|
||||
extensionReceiver = expression.dispatchReceiver
|
||||
}
|
||||
// TODO: Move this check out of InteropLowering.
|
||||
symbols.createCleaner.owner -> {
|
||||
val irCallableReference = expression.getValueArgument(1)
|
||||
if (irCallableReference == null || !irCallableReference.isNonCapturingFunction()) {
|
||||
context.reportCompilationError(
|
||||
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
|
||||
irFile, expression
|
||||
)
|
||||
}
|
||||
expression
|
||||
}
|
||||
else -> expression
|
||||
}
|
||||
}
|
||||
@@ -1260,38 +1038,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrType.ensureSupportedInCallbacks(isReturnType: Boolean, reportError: (String) -> Nothing) {
|
||||
this.checkCTypeNullability(reportError)
|
||||
|
||||
if (isReturnType && this.isUnit()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isPrimitiveType()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (UnsignedTypes.isUnsignedType(this.toKotlinType()) && !this.containsNull()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.getClass()?.descriptor == interop.cPointer) {
|
||||
return
|
||||
}
|
||||
|
||||
reportError("Type ${this.toKotlinType()} is not supported in callback signature")
|
||||
}
|
||||
|
||||
private fun IrType.checkCTypeNullability(reportError: (String) -> Nothing) {
|
||||
if (this.isNullablePrimitiveType() || UnsignedTypes.isUnsignedType(this.toKotlinType()) && this.containsNull()) {
|
||||
reportError("Type ${this.toKotlinType()} must not be nullable when used in C function signature")
|
||||
}
|
||||
|
||||
if (this.getClass() == interop.cPointer && !this.isSimpleTypeWithQuestionMark) {
|
||||
reportError("Type ${this.toKotlinType()} must be nullable when used in C function signature")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unwrapStaticFunctionArgument(argument: IrExpression): IrFunctionReference? {
|
||||
if (argument is IrFunctionReference) {
|
||||
return argument
|
||||
|
||||
+6
-15
@@ -10,7 +10,7 @@ import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.error
|
||||
import org.jetbrains.kotlin.backend.konan.reportCompilationError
|
||||
import org.jetbrains.kotlin.backend.konan.renderCompilerError
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
@@ -77,23 +77,14 @@ internal class PostInlineLowering(val context: Context) : FileLoweringPass {
|
||||
// Convert arguments of the binary blob to special IrConst<String> structure, so that
|
||||
// vararg lowering will not affect it.
|
||||
val args = expression.getValueArgument(0) as? IrVararg
|
||||
?: throw Error("varargs shall not be lowered yet")
|
||||
if (args.elements.any { it is IrSpreadElement }) {
|
||||
context.reportCompilationError("no spread elements allowed here", irFile, args)
|
||||
}
|
||||
?: error("varargs shall not be lowered yet")
|
||||
val builder = StringBuilder()
|
||||
args.elements.forEach {
|
||||
if (it !is IrConst<*>) {
|
||||
context.reportCompilationError(
|
||||
"all elements of binary blob must be constants", irFile, it)
|
||||
require(it is IrConst<*>) { renderCompilerError(irFile, it, "expected const") }
|
||||
val value = (it as? IrConst<*>)?.value
|
||||
require(value is Short && value >= 0 && value <= 0xff) {
|
||||
renderCompilerError(irFile, it, "incorrect value for binary data: $value")
|
||||
}
|
||||
val value = when (it.kind) {
|
||||
IrConstKind.Short -> (it.value as Short).toInt()
|
||||
else ->
|
||||
context.reportCompilationError("incorrect value for binary data: $it.value", irFile, it)
|
||||
}
|
||||
if (value < 0 || value > 0xff)
|
||||
context.reportCompilationError("incorrect value for binary data: $value", irFile, it)
|
||||
// Luckily, all values in range 0x00 .. 0xff represent valid UTF-16 symbols,
|
||||
// block 0 (Basic Latin) and block 1 (Latin-1 Supplement) in
|
||||
// Basic Multilingual Plane, so we could just append data "as is".
|
||||
|
||||
Reference in New Issue
Block a user