[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 {
|
internal fun error(irFile: IrFile?, element: IrElement?, message: String): Nothing {
|
||||||
error(buildString {
|
error(renderCompilerError(irFile, element, message))
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 getUniqueCName(prefix: String): String
|
||||||
fun getUniqueKotlinFunctionReferenceClassName(prefix: String): String
|
fun getUniqueKotlinFunctionReferenceClassName(prefix: String): String
|
||||||
|
|
||||||
fun reportError(location: IrElement, message: String): Nothing
|
|
||||||
fun throwCompilerError(element: IrElement?, message: String): Nothing
|
fun throwCompilerError(element: IrElement?, message: String): Nothing
|
||||||
|
fun renderCompilerError(element: IrElement?, message: String = "Failed requirement."): String
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KotlinToCCallBuilder(
|
private class KotlinToCCallBuilder(
|
||||||
@@ -113,7 +113,7 @@ private fun KotlinToCCallBuilder.buildKotlinBridgeCall(transformCall: (IrMemberA
|
|||||||
|
|
||||||
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWithScope, isInvoke: Boolean,
|
||||||
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default): IrExpression {
|
||||||
require(expression.dispatchReceiver == null)
|
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||||
|
|
||||||
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
val callBuilder = KotlinToCCallBuilder(builder, this, isObjCMethod = false, foreignExceptionMode)
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
require(expression.extensionReceiver == null)
|
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||||
targetPtrParameter = null
|
targetPtrParameter = null
|
||||||
targetFunctionName = this.getUniqueCName("target")
|
targetFunctionName = this.getUniqueCName("target")
|
||||||
|
|
||||||
@@ -153,9 +153,9 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
|
|
||||||
val returnValuePassing = if (isInvoke) {
|
val returnValuePassing = if (isInvoke) {
|
||||||
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
|
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
|
||||||
mapReturnType(returnType, TypeLocation.FunctionCallResult(expression), signature = null)
|
mapReturnType(returnType, expression, signature = null)
|
||||||
} else {
|
} else {
|
||||||
mapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression), signature = callee)
|
mapReturnType(callee.returnType, expression, signature = callee)
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
||||||
@@ -178,7 +178,7 @@ private fun KotlinToCCallBuilder.addArguments(arguments: List<IrExpression?>, ca
|
|||||||
arguments.forEachIndexed { index, argument ->
|
arguments.forEachIndexed { index, argument ->
|
||||||
val parameter = callee.valueParameters[index]
|
val parameter = callee.valueParameters[index]
|
||||||
if (parameter.isVararg) {
|
if (parameter.isVararg) {
|
||||||
require(index == arguments.lastIndex)
|
require(index == arguments.lastIndex) { stubs.renderCompilerError(argument) }
|
||||||
addVariadicArguments(argument)
|
addVariadicArguments(argument)
|
||||||
cFunctionBuilder.variadic = true
|
cFunctionBuilder.variadic = true
|
||||||
} else {
|
} else {
|
||||||
@@ -220,14 +220,9 @@ private fun KotlinToCCallBuilder.unwrapVariadicArguments(
|
|||||||
is IrExpression -> listOf(it)
|
is IrExpression -> listOf(it)
|
||||||
is IrSpreadElement -> {
|
is IrSpreadElement -> {
|
||||||
val expression = it.expression
|
val expression = it.expression
|
||||||
if (expression is IrCall && expression.symbol == symbols.arrayOf) {
|
require(expression is IrCall && expression.symbol == symbols.arrayOf) { stubs.renderCompilerError(it) }
|
||||||
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
|
handleArgumentForVarargParameter(expression.getValueArgument(0)) { _, elements ->
|
||||||
unwrapVariadicArguments(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(...)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> stubs.throwCompilerError(it, "unexpected IrVarargElement")
|
else -> stubs.throwCompilerError(it, "unexpected IrVarargElement")
|
||||||
@@ -260,15 +255,12 @@ private fun <R> KotlinToCCallBuilder.handleArgumentForVarargParameter(
|
|||||||
val variable = argument.symbol.owner
|
val variable = argument.symbol.owner
|
||||||
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar) {
|
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar) {
|
||||||
val initializer = variable.initializer
|
val initializer = variable.initializer
|
||||||
if (initializer is IrVararg) {
|
require(initializer is IrVararg) { stubs.renderCompilerError(initializer) }
|
||||||
block(variable, initializer.elements)
|
block(variable, initializer.elements)
|
||||||
} else {
|
|
||||||
stubs.throwCompilerError(initializer, "unexpected initializer")
|
|
||||||
}
|
|
||||||
} else if (variable is IrValueParameter && FunctionReferenceLowering.isLoweredFunctionReference(variable)) {
|
} else if (variable is IrValueParameter && FunctionReferenceLowering.isLoweredFunctionReference(variable)) {
|
||||||
val location = variable.parent // Parameter itself has incorrect location.
|
val location = variable.parent // Parameter itself has incorrect location.
|
||||||
val kind = if (this.isObjCMethod) "Objective-C methods" else "C functions"
|
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 {
|
} else {
|
||||||
stubs.throwCompilerError(variable, "unexpected value declaration")
|
stubs.throwCompilerError(variable, "unexpected value declaration")
|
||||||
}
|
}
|
||||||
@@ -383,8 +375,7 @@ internal fun KotlinStubs.generateObjCCall(
|
|||||||
|
|
||||||
callBuilder.addArguments(arguments, method)
|
callBuilder.addArguments(arguments, method)
|
||||||
|
|
||||||
val returnValuePassing =
|
val returnValuePassing = mapReturnType(method.returnType, call, signature = method)
|
||||||
mapReturnType(method.returnType, TypeLocation.FunctionCallResult(call), signature = method)
|
|
||||||
|
|
||||||
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
val result = callBuilder.buildCall(targetFunctionName, returnValuePassing)
|
||||||
|
|
||||||
@@ -398,7 +389,7 @@ internal fun KotlinStubs.generateObjCCall(
|
|||||||
|
|
||||||
internal fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
|
internal fun IrBuilderWithScope.getObjCClass(symbols: KonanSymbols, symbol: IrClassSymbol): IrExpression {
|
||||||
val classDescriptor = symbol.descriptor
|
val classDescriptor = symbol.descriptor
|
||||||
assert(!classDescriptor.isObjCMetaClass())
|
require(!classDescriptor.isObjCMetaClass())
|
||||||
return irCall(symbols.interopGetObjCClass, symbols.nativePtrType, listOf(symbol.typeWithStarProjections))
|
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) {
|
private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParameter: IrValueParameter) {
|
||||||
val typeLocation = if (isObjCMethod) {
|
val location = if (isObjCMethod) functionParameter else location
|
||||||
TypeLocation.ObjCMethodParameter(it.index, functionParameter)
|
require(!functionParameter.isVararg) { stubs.renderCompilerError(location) }
|
||||||
} 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 valuePassing = stubs.mapFunctionParameterType(
|
val valuePassing = stubs.mapFunctionParameterType(
|
||||||
it.type,
|
it.type,
|
||||||
retained = it.isObjCConsumed(),
|
retained = it.isObjCConsumed(),
|
||||||
variadic = false,
|
variadic = false,
|
||||||
location = typeLocation
|
location = location
|
||||||
)
|
)
|
||||||
|
|
||||||
val kotlinArgument = with(valuePassing) { receiveValue() }
|
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 {
|
private fun CCallbackBuilder.build(function: IrSimpleFunction, signature: IrSimpleFunction): String {
|
||||||
val typeLocation = if (isObjCMethod) {
|
val valueReturning = stubs.mapReturnType(
|
||||||
TypeLocation.ObjCMethodReturnValue(function)
|
signature.returnType,
|
||||||
} else {
|
location = if (isObjCMethod) function else location,
|
||||||
TypeLocation.FunctionPointerReturnValue(location)
|
signature = signature
|
||||||
}
|
)
|
||||||
val valueReturning = stubs.mapReturnType(signature.returnType, typeLocation, signature)
|
|
||||||
buildValueReturn(function, valueReturning)
|
buildValueReturn(function, valueReturning)
|
||||||
return buildCFunction()
|
return buildCFunction()
|
||||||
}
|
}
|
||||||
@@ -512,7 +491,7 @@ private fun KotlinStubs.generateCFunction(
|
|||||||
|
|
||||||
if (isObjCMethod) {
|
if (isObjCMethod) {
|
||||||
val receiver = signature.dispatchReceiverParameter!!
|
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 valuePassing = ObjCReferenceValuePassing(symbols, receiver.type, retained = signature.objCConsumesReceiver())
|
||||||
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
|
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
|
||||||
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
|
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
|
||||||
@@ -520,7 +499,7 @@ private fun KotlinStubs.generateCFunction(
|
|||||||
// Selector is ignored:
|
// Selector is ignored:
|
||||||
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
|
with(TrivialValuePassing(symbols.nativePtrType, CTypes.voidPtr)) { callbackBuilder.receiveValue() }
|
||||||
} else {
|
} else {
|
||||||
require(signature.dispatchReceiverParameter == null)
|
require(signature.dispatchReceiverParameter == null) { renderCompilerError(signature) }
|
||||||
}
|
}
|
||||||
|
|
||||||
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it, function.extensionReceiverParameter!!) }
|
signature.extensionReceiverParameter?.let { callbackBuilder.addParameter(it, function.extensionReceiverParameter!!) }
|
||||||
@@ -633,11 +612,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
|||||||
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
|
classifier == symbols.interopCValuesRef -> CValuesRefArgumentPassing
|
||||||
|
|
||||||
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) -> {
|
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) -> {
|
||||||
if (variadic && isObjCMethod) {
|
require(!variadic || !isObjCMethod) { stubs.renderCompilerError(argument) }
|
||||||
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.
|
|
||||||
}
|
|
||||||
CStringArgumentPassing()
|
CStringArgumentPassing()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,7 +623,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
|||||||
type,
|
type,
|
||||||
retained = parameter?.isObjCConsumed() ?: false,
|
retained = parameter?.isObjCConsumed() ?: false,
|
||||||
variadic = variadic,
|
variadic = variadic,
|
||||||
location = TypeLocation.FunctionArgument(argument)
|
location = argument
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -657,29 +632,15 @@ private fun KotlinStubs.mapFunctionParameterType(
|
|||||||
type: IrType,
|
type: IrType,
|
||||||
retained: Boolean,
|
retained: Boolean,
|
||||||
variadic: Boolean,
|
variadic: Boolean,
|
||||||
location: TypeLocation
|
location: IrElement
|
||||||
): ArgumentPassing = when {
|
): ArgumentPassing = when {
|
||||||
type.isUnit() && !variadic -> IgnoredUnitArgumentPassing
|
type.isUnit() && !variadic -> IgnoredUnitArgumentPassing
|
||||||
else -> mapType(type, retained = retained, variadic = variadic, location = location)
|
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(
|
private fun KotlinStubs.mapReturnType(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
location: TypeLocation,
|
location: IrElement,
|
||||||
signature: IrSimpleFunction?
|
signature: IrSimpleFunction?
|
||||||
): ValueReturning = when {
|
): ValueReturning = when {
|
||||||
type.isUnit() -> VoidReturning
|
type.isUnit() -> VoidReturning
|
||||||
@@ -689,39 +650,29 @@ private fun KotlinStubs.mapReturnType(
|
|||||||
private fun KotlinStubs.mapBlockType(
|
private fun KotlinStubs.mapBlockType(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
retained: Boolean,
|
retained: Boolean,
|
||||||
location: TypeLocation
|
location: IrElement
|
||||||
): ObjCBlockPointerValuePassing {
|
): ObjCBlockPointerValuePassing {
|
||||||
type as IrSimpleType
|
require(type is IrSimpleType) { renderCompilerError(location) }
|
||||||
require(type.classifier == symbols.functionN(type.arguments.size - 1))
|
require(type.classifier == symbols.functionN(type.arguments.size - 1)) { renderCompilerError(location) }
|
||||||
|
|
||||||
val returnTypeArgument = type.arguments.last()
|
val returnTypeArgument = type.arguments.last()
|
||||||
val valueReturning = when (returnTypeArgument) {
|
require(returnTypeArgument is IrTypeProjection) { renderCompilerError(location) }
|
||||||
is IrTypeProjection -> if (returnTypeArgument.variance == Variance.INVARIANT) {
|
require(returnTypeArgument.variance == Variance.INVARIANT) { renderCompilerError(location) }
|
||||||
mapReturnType(returnTypeArgument.type, TypeLocation.BlockReturnValue(location), null)
|
val valueReturning = mapReturnType(returnTypeArgument.type, location, null)
|
||||||
} else {
|
|
||||||
reportUnsupportedType("${returnTypeArgument.variance.label}-variance of return type", type, location)
|
val parameterValuePassings = type.arguments.dropLast(1).map { argument ->
|
||||||
}
|
require(argument is IrTypeProjection) { renderCompilerError(location) }
|
||||||
is IrStarProjection -> reportUnsupportedType("* as return type", type, location)
|
require(argument.variance == Variance.INVARIANT) { renderCompilerError(location) }
|
||||||
else -> error(returnTypeArgument)
|
mapType(
|
||||||
}
|
argument.type,
|
||||||
val parameterValuePassings = type.arguments.dropLast(1).mapIndexed { index, argument ->
|
retained = false,
|
||||||
when (argument) {
|
variadic = false,
|
||||||
is IrTypeProjection -> if (argument.variance == Variance.INVARIANT) {
|
location = location
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ObjCBlockPointerValuePassing(
|
return ObjCBlockPointerValuePassing(
|
||||||
this,
|
this,
|
||||||
location.element,
|
location,
|
||||||
type,
|
type,
|
||||||
valueReturning,
|
valueReturning,
|
||||||
parameterValuePassings,
|
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(
|
private fun KotlinStubs.mapType(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
retained: Boolean,
|
retained: Boolean,
|
||||||
variadic: Boolean,
|
variadic: Boolean,
|
||||||
typeLocation: TypeLocation,
|
location: IrElement
|
||||||
reportUnsupportedType: (String) -> Nothing
|
|
||||||
): ValuePassing = when {
|
): ValuePassing = when {
|
||||||
type.isBoolean() -> BooleanValuePassing(
|
type.isBoolean() -> {
|
||||||
cBoolType(target) ?: reportUnsupportedType("unavailable on target platform"),
|
val cBoolType = cBoolType(target)
|
||||||
irBuiltIns
|
require(cBoolType != null) { renderCompilerError(location) }
|
||||||
)
|
BooleanValuePassing(cBoolType, irBuiltIns)
|
||||||
|
}
|
||||||
|
|
||||||
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
|
type.isByte() -> TrivialValuePassing(irBuiltIns.byteType, CTypes.signedChar)
|
||||||
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
|
type.isShort() -> TrivialValuePassing(irBuiltIns.shortType, CTypes.short)
|
||||||
@@ -750,7 +698,7 @@ private fun KotlinStubs.mapType(
|
|||||||
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
|
type.isLong() -> TrivialValuePassing(irBuiltIns.longType, CTypes.longLong)
|
||||||
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
|
type.isFloat() -> TrivialValuePassing(irBuiltIns.floatType, CTypes.float)
|
||||||
type.isDouble() -> TrivialValuePassing(irBuiltIns.doubleType, CTypes.double)
|
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.isTypeOfNullLiteral() && variadic -> TrivialValuePassing(symbols.interopCPointer.typeWithStarProjections.makeNullable(), CTypes.voidPtr)
|
||||||
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
|
type.isUByte() -> UnsignedValuePassing(type, CTypes.signedChar, CTypes.unsignedChar)
|
||||||
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
|
type.isUShort() -> UnsignedValuePassing(type, CTypes.short, CTypes.unsignedShort)
|
||||||
@@ -768,33 +716,32 @@ private fun KotlinStubs.mapType(
|
|||||||
CEnumValuePassing(
|
CEnumValuePassing(
|
||||||
enumClass,
|
enumClass,
|
||||||
value,
|
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()) {
|
type.isCValue(symbols) -> {
|
||||||
reportUnsupportedType("must not be nullable")
|
require(!type.isNullable()) { renderCompilerError(location) }
|
||||||
} else {
|
|
||||||
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
|
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)
|
StructValuePassing(kotlinClass, cStructType)
|
||||||
?: reportUnsupportedType("not a structure or too complex"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true -> {
|
type.classOrNull?.isSubtypeOfClass(symbols.nativePointed) == true -> {
|
||||||
TrivialValuePassing(type, CTypes.voidPtr)
|
TrivialValuePassing(type, CTypes.voidPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type.isFunction() -> if (variadic){
|
type.isFunction() -> {
|
||||||
reportUnsupportedType("not supported as variadic argument")
|
require(!variadic) { renderCompilerError(location) }
|
||||||
} else {
|
mapBlockType(type, retained = retained, location = location)
|
||||||
mapBlockType(type, retained = retained, location = typeLocation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
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)
|
private class CExpression(val expression: String, val type: CType)
|
||||||
@@ -1211,7 +1158,7 @@ private class ObjCBlockPointerValuePassing(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val parameterCount = parameterValuePassings.size
|
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()
|
val overriddenInvokeMethod = (functionType.classifier.owner as IrClass).simpleFunctions()
|
||||||
.single { it.name == OperatorNameConventions.INVOKE }
|
.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)
|
val invokeFunction = (functionType.classifier.owner as IrClass)
|
||||||
.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }
|
.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }
|
||||||
|
|
||||||
@@ -1447,22 +1394,3 @@ private object IgnoredUnitArgumentPassing : ArgumentPassing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun CType.cast(expression: String): String = "((${this.render("")})$expression)"
|
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 -> 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.IntrinsicType
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||||
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
|
||||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
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) {
|
private abstract class BaseInteropIrTransformer(private val context: Context) : IrBuildingTransformer(context) {
|
||||||
|
|
||||||
protected inline fun <T> generateWithStubs(element: IrElement? = null, block: KotlinStubs.() -> T): T =
|
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 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 {
|
override fun throwCompilerError(element: IrElement?, message: String): Nothing {
|
||||||
error(irFile, element, message)
|
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 val irFile: IrFile
|
||||||
protected abstract fun addTopLevel(declaration: IrDeclaration)
|
protected abstract fun addTopLevel(declaration: IrDeclaration)
|
||||||
}
|
}
|
||||||
@@ -177,12 +152,6 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
putValueArgument(0, classPtr)
|
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>()
|
private val outerClasses = mutableListOf<IrClass>()
|
||||||
|
|
||||||
override fun visitClass(declaration: IrClass): IrStatement {
|
override fun visitClass(declaration: IrClass): IrStatement {
|
||||||
@@ -220,7 +189,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
|
|
||||||
if (irClass.annotations.hasAnnotation(interop.exportObjCClass.fqNameSafe)) {
|
if (irClass.annotations.hasAnnotation(interop.exportObjCClass.fqNameSafe)) {
|
||||||
val irBuilder = context.createIrBuilder(currentFile.symbol).at(irClass)
|
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)
|
constructor.overridesConstructor(it)
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
val superConstructor = superConstructors.singleOrNull() ?: run {
|
val superConstructor = superConstructors.singleOrNull()
|
||||||
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
require(superConstructor != null) { renderCompilerError(constructor) }
|
||||||
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 initMethod = superConstructor.getObjCInitMethod()!!
|
val initMethod = superConstructor.getObjCInitMethod()!!
|
||||||
|
|
||||||
// Remove fake overrides of this init method, also check for explicit overriding:
|
// Remove fake overrides of this init method, also check for explicit overriding:
|
||||||
irClass.declarations.removeAll {
|
irClass.declarations.removeAll {
|
||||||
if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols) {
|
if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols) {
|
||||||
if (it.isReal) {
|
require(it.isFakeOverride) { renderCompilerError(constructor) }
|
||||||
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
|
||||||
context.reportCompilationError(
|
|
||||||
"constructor with @$annotation overrides initializer that is already overridden explicitly",
|
|
||||||
currentFile,
|
|
||||||
constructor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
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 {
|
private fun generateActionImp(function: IrSimpleFunction): IrSimpleFunction {
|
||||||
val action = "@${context.interopBuiltIns.objCAction.name}"
|
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
|
||||||
|
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
|
||||||
function.extensionReceiverParameter?.let {
|
require(function.returnType.isUnit()) { renderCompilerError(function) }
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return generateFunctionImp(inferObjCSelector(function.descriptor), function)
|
return generateFunctionImp(inferObjCSelector(function.descriptor), function)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateOutletSetterImp(property: IrProperty): IrSimpleFunction {
|
private fun generateOutletSetterImp(property: IrProperty): IrSimpleFunction {
|
||||||
val descriptor = property.descriptor
|
require(property.isVar) { renderCompilerError(property) }
|
||||||
|
require(property.getter?.extensionReceiverParameter == null) { renderCompilerError(property) }
|
||||||
val outlet = "@${context.interopBuiltIns.objCOutlet.name}"
|
require(property.descriptor.type.isObjCObjectType()) { renderCompilerError(property) }
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
val name = property.name.asString()
|
val name = property.name.asString()
|
||||||
val selector = "set${name.capitalize()}:"
|
val selector = "set${name.capitalize()}:"
|
||||||
@@ -392,39 +301,29 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getMethodSignatureEncoding(function: IrFunction): String {
|
private fun getMethodSignatureEncoding(function: IrFunction): String {
|
||||||
assert(function.extensionReceiverParameter == null)
|
require(function.extensionReceiverParameter == null) { renderCompilerError(function) }
|
||||||
assert(function.valueParameters.all { it.type.isObjCObjectType() })
|
require(function.valueParameters.all { it.type.isObjCObjectType() }) { renderCompilerError(function) }
|
||||||
assert(function.returnType.isUnit())
|
require(function.returnType.isUnit()) { renderCompilerError(function) }
|
||||||
|
|
||||||
// Note: these values are valid for x86_64 and arm64.
|
// Note: these values are valid for x86_64 and arm64.
|
||||||
return when (function.valueParameters.size) {
|
return when (function.valueParameters.size) {
|
||||||
0 -> "v16@0:8"
|
0 -> "v16@0:8"
|
||||||
1 -> "v24@0:8@16"
|
1 -> "v24@0:8@16"
|
||||||
2 -> "v32@0:8@16@24"
|
2 -> "v32@0:8@16@24"
|
||||||
else -> context.reportCompilationError("Only 0, 1 or 2 parameters are supported here",
|
else -> error(irFile, function, "Only 0, 1 or 2 parameters are supported here")
|
||||||
currentFile, function
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateFunctionImp(selector: String, function: IrFunction): IrSimpleFunction {
|
private fun generateFunctionImp(selector: String, function: IrFunction): IrSimpleFunction {
|
||||||
val signatureEncoding = getMethodSignatureEncoding(function)
|
val signatureEncoding = getMethodSignatureEncoding(function)
|
||||||
|
|
||||||
val returnType = function.returnType
|
|
||||||
assert(returnType.isUnit())
|
|
||||||
|
|
||||||
val nativePtrType = context.ir.symbols.nativePtrType
|
val nativePtrType = context.ir.symbols.nativePtrType
|
||||||
|
|
||||||
val parameterTypes = mutableListOf(nativePtrType) // id self
|
val parameterTypes = mutableListOf(nativePtrType) // id self
|
||||||
|
|
||||||
parameterTypes.add(nativePtrType) // SEL _cmd
|
parameterTypes.add(nativePtrType) // SEL _cmd
|
||||||
|
|
||||||
function.valueParameters.mapTo(parameterTypes) {
|
function.valueParameters.mapTo(parameterTypes) { nativePtrType }
|
||||||
when {
|
|
||||||
it.descriptor.type.isObjCObjectType() -> nativePtrType
|
|
||||||
else -> TODO()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val newFunction = WrappedSimpleFunctionDescriptor().let {
|
val newFunction = WrappedSimpleFunctionDescriptor().let {
|
||||||
IrFunctionImpl(
|
IrFunctionImpl(
|
||||||
@@ -434,7 +333,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
("imp:$selector").synthesizedName,
|
("imp:$selector").synthesizedName,
|
||||||
DescriptorVisibilities.PRIVATE,
|
DescriptorVisibilities.PRIVATE,
|
||||||
Modality.FINAL,
|
Modality.FINAL,
|
||||||
returnType,
|
function.returnType,
|
||||||
isInline = false,
|
isInline = false,
|
||||||
isExternal = false,
|
isExternal = false,
|
||||||
isTailrec = false,
|
isTailrec = false,
|
||||||
@@ -516,52 +415,23 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkKotlinObjCClass(irClass: IrClass) {
|
private fun checkKotlinObjCClass(irClass: IrClass) {
|
||||||
val kind = irClass.descriptor.kind
|
val kind = irClass.kind
|
||||||
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
|
require(kind == ClassKind.CLASS || kind == ClassKind.OBJECT) { renderCompilerError(irClass) }
|
||||||
context.reportCompilationError(
|
require(irClass.isFinalClass) { renderCompilerError(irClass) }
|
||||||
"Only classes are supported as subtypes of Objective-C types",
|
require(irClass.companionObject()?.hasFields() != true) { renderCompilerError(irClass) }
|
||||||
currentFile, irClass
|
require(irClass.companionObject()?.getSuperClassNotAny()?.hasFields() != true) { renderCompilerError(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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasObjCClassSupertype = false
|
var hasObjCClassSupertype = false
|
||||||
irClass.descriptor.defaultType.constructor.supertypes.forEach {
|
irClass.descriptor.defaultType.constructor.supertypes.forEach {
|
||||||
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
|
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
|
||||||
if (!descriptor.isObjCClass()) {
|
require(descriptor.isObjCClass()) { renderCompilerError(irClass) }
|
||||||
context.reportCompilationError(
|
|
||||||
"Mixing Kotlin and Objective-C supertypes is not supported",
|
|
||||||
currentFile, irClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.kind == ClassKind.CLASS) {
|
if (descriptor.kind == ClassKind.CLASS) {
|
||||||
hasObjCClassSupertype = true
|
hasObjCClassSupertype = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasObjCClassSupertype) {
|
require(hasObjCClassSupertype) { renderCompilerError(irClass) }
|
||||||
context.reportCompilationError(
|
|
||||||
"Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)",
|
|
||||||
currentFile, irClass
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val methodsOfAny =
|
val methodsOfAny =
|
||||||
context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
|
context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
|
||||||
@@ -571,20 +441,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
it in methodsOfAny
|
it in methodsOfAny
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overriddenMethodOfAny != null) {
|
require(overriddenMethodOfAny == null) { renderCompilerError(method) }
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,23 +473,15 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
|
|
||||||
// Calling super constructor from Kotlin Objective-C class.
|
// 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()!!
|
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()!!
|
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||||
|
|
||||||
assert(expression.dispatchReceiver == null)
|
|
||||||
assert(expression.extensionReceiver == null)
|
|
||||||
|
|
||||||
val initCall = builder.genLoweredObjCMethodCall(
|
val initCall = builder.genLoweredObjCMethodCall(
|
||||||
initMethodInfo,
|
initMethodInfo,
|
||||||
superQualifier = delegatingCallConstructingClass.symbol,
|
superQualifier = delegatingCallConstructingClass.symbol,
|
||||||
@@ -714,13 +563,13 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
val initMethod = callee.getObjCInitMethod()
|
val initMethod = callee.getObjCInitMethod()
|
||||||
if (initMethod != null) {
|
if (initMethod != null) {
|
||||||
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||||
assert(expression.extensionReceiver == null)
|
require(expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||||
assert(expression.dispatchReceiver == null)
|
require(expression.dispatchReceiver == null) { renderCompilerError(expression) }
|
||||||
|
|
||||||
val constructedClass = callee.constructedClass
|
val constructedClass = callee.constructedClass
|
||||||
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
val initMethodInfo = initMethod.getExternalObjCMethodInfo()!!
|
||||||
return builder.at(expression).run {
|
return builder.at(expression).run {
|
||||||
val classPtr = getObjCClass(constructedClass.symbol)
|
val classPtr = getObjCClass(symbols, constructedClass.symbol)
|
||||||
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
|
ensureObjCReferenceNotNull(callAllocAndInit(classPtr, initMethodInfo, arguments, expression, initMethod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -770,21 +619,9 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
|
|
||||||
if (!useKotlinDispatch) {
|
if (!useKotlinDispatch) {
|
||||||
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||||
assert(expression.dispatchReceiver == null || expression.extensionReceiver == null)
|
require(expression.dispatchReceiver == null || expression.extensionReceiver == null) { renderCompilerError(expression) }
|
||||||
|
require(expression.superQualifierSymbol?.owner?.isObjCMetaClass() != true) { renderCompilerError(expression) }
|
||||||
if (expression.superQualifierSymbol?.owner?.isObjCMetaClass() == true) {
|
require(expression.superQualifierSymbol?.owner?.isInterface != true) { renderCompilerError(expression) }
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.at(expression)
|
builder.at(expression)
|
||||||
return builder.genLoweredObjCMethodCall(
|
return builder.genLoweredObjCMethodCall(
|
||||||
@@ -809,7 +646,7 @@ private class InteropLoweringPart1(val context: Context) : BaseInteropIrTransfor
|
|||||||
val irClass = classSymbol.owner
|
val irClass = classSymbol.owner
|
||||||
|
|
||||||
val companionObject = irClass.companionObject() ?:
|
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)
|
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,
|
// Note: in interop stubs const val initializer is either `IrConst` or quite simple expression,
|
||||||
// so it is ok to compute it every time.
|
// so it is ok to compute it every time.
|
||||||
|
|
||||||
assert(declaration.setter == null)
|
require(declaration.setter == null) { renderCompilerError(declaration) }
|
||||||
assert(!declaration.isVar)
|
require(!declaration.isVar) { renderCompilerError(declaration) }
|
||||||
|
|
||||||
declaration.transformChildrenVoid()
|
declaration.transformChildrenVoid()
|
||||||
declaration
|
declaration
|
||||||
@@ -941,16 +778,15 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
|
|
||||||
val callee = expression.symbol.owner
|
val callee = expression.symbol.owner
|
||||||
val inlinedClass = callee.returnType.getInlinedClassNative()
|
val inlinedClass = callee.returnType.getInlinedClassNative()
|
||||||
if (inlinedClass?.descriptor == interop.cPointer || inlinedClass?.descriptor == interop.nativePointed) {
|
require(inlinedClass?.descriptor != interop.cPointer) { renderCompilerError(expression) }
|
||||||
context.reportCompilationError("Native interop types constructors must not be called directly",
|
require(inlinedClass?.descriptor != interop.nativePointed) { renderCompilerError(expression) }
|
||||||
irFile, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
val constructedClass = callee.constructedClass
|
val constructedClass = callee.constructedClass
|
||||||
if (!constructedClass.isObjCClass())
|
if (!constructedClass.isObjCClass())
|
||||||
return expression
|
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 {
|
return builder.at(expression).irBlock {
|
||||||
val rawPtr = irTemporary(irCall(symbols.interopAllocObjCObject.owner).apply {
|
val rawPtr = irTemporary(irCall(symbols.interopAllocObjCObject.owner).apply {
|
||||||
putValueArgument(0, getObjCClass(symbols, constructedClass.symbol))
|
putValueArgument(0, getObjCClass(symbols, constructedClass.symbol))
|
||||||
@@ -987,14 +823,11 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
?.takeIf { it.isConst }
|
?.takeIf { it.isConst }
|
||||||
?: return null
|
?: return null
|
||||||
|
|
||||||
val irConstant = (constantProperty.backingField
|
val initializer = constantProperty.backingField?.initializer?.expression
|
||||||
?.initializer
|
require(initializer is IrConst<*>) { renderCompilerError(expression) }
|
||||||
?.expression
|
|
||||||
?: error("Constant property ${constantProperty.name} has no initializer!"))
|
|
||||||
as IrConst<*>
|
|
||||||
|
|
||||||
// Avoid node duplication
|
// Avoid node duplication
|
||||||
return irConstant.copy()
|
return initializer.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
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) {
|
if (intrinsicType == IntrinsicType.OBJC_INIT_BY) {
|
||||||
// Need to do this separately as otherwise [expression.transformChildrenVoid(this)] would be called
|
// 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.
|
// and the [IrConstructorCall] would be transformed which is not what we want.
|
||||||
val intrinsic = interop.objCObjectInitBy.name
|
|
||||||
|
|
||||||
val argument = expression.getValueArgument(0)!!
|
val argument = expression.getValueArgument(0)!!
|
||||||
val constructorCall = argument as? IrConstructorCall
|
require(argument is IrConstructorCall) { renderCompilerError(argument) }
|
||||||
?: context.reportCompilationError("Argument of '$intrinsic' must be a constructor call",
|
|
||||||
irFile, argument)
|
|
||||||
|
|
||||||
val constructedClass = constructorCall.symbol.owner.constructedClass
|
val constructedClass = argument.symbol.owner.constructedClass
|
||||||
|
|
||||||
val extensionReceiver = expression.extensionReceiver!!
|
val extensionReceiver = expression.extensionReceiver!!
|
||||||
if (extensionReceiver !is IrGetValue ||
|
require(extensionReceiver is IrGetValue &&
|
||||||
!extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) {
|
extensionReceiver.symbol.owner.isDispatchReceiverFor(constructedClass)) { renderCompilerError(extensionReceiver) }
|
||||||
|
|
||||||
context.reportCompilationError("Receiver of '$intrinsic' must be a 'this' of the constructed class",
|
argument.transformChildrenVoid(this)
|
||||||
irFile, extensionReceiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
constructorCall.transformChildrenVoid(this)
|
|
||||||
|
|
||||||
return builder.at(expression).irBlock {
|
return builder.at(expression).irBlock {
|
||||||
val instance = extensionReceiver.symbol.owner
|
val instance = extensionReceiver.symbol.owner
|
||||||
+irCall(symbols.initInstance).apply {
|
+irCall(symbols.initInstance).apply {
|
||||||
putValueArgument(0, irGet(instance))
|
putValueArgument(0, irGet(instance))
|
||||||
putValueArgument(1, constructorCall)
|
putValueArgument(1, argument)
|
||||||
}
|
}
|
||||||
+irGet(instance)
|
+irGet(instance)
|
||||||
}
|
}
|
||||||
@@ -1052,7 +878,7 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
return generateWithStubs { generateCCall(expression, builder, isInvoke = false, exceptionMode) }
|
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 }
|
tryGenerateInteropMemberAccess(expression, symbols, builder, failCompilation)?.let { return it }
|
||||||
|
|
||||||
tryGenerateInteropConstantRead(expression)?.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 -> {
|
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
|
||||||
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
|
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(0)!!)
|
||||||
|
|
||||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()
|
require(irCallableReference != null && irCallableReference.getArguments().isEmpty()
|
||||||
|| irCallableReference.symbol !is IrSimpleFunctionSymbol) {
|
&& irCallableReference.symbol is IrSimpleFunctionSymbol) { renderCompilerError(expression) }
|
||||||
context.reportCompilationError(
|
|
||||||
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
|
|
||||||
irFile, expression
|
|
||||||
)
|
|
||||||
// TODO: should probably be reported during analysis.
|
|
||||||
}
|
|
||||||
|
|
||||||
val targetSymbol = irCallableReference.symbol
|
val targetSymbol = irCallableReference.symbol
|
||||||
val target = targetSymbol.owner
|
val target = targetSymbol.owner
|
||||||
@@ -1096,13 +916,9 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
function.typeParameters.indices.forEach { index ->
|
function.typeParameters.indices.forEach { index ->
|
||||||
val typeArgument = expression.getTypeArgument(index)!!.toKotlinType()
|
val typeArgument = expression.getTypeArgument(index)!!.toKotlinType()
|
||||||
val signatureType = signatureTypes[index].toKotlinType()
|
val signatureType = signatureTypes[index].toKotlinType()
|
||||||
if (typeArgument.constructor != signatureType.constructor ||
|
|
||||||
typeArgument.isMarkedNullable != signatureType.isMarkedNullable) {
|
require(typeArgument.constructor == signatureType.constructor &&
|
||||||
context.reportCompilationError(
|
typeArgument.isMarkedNullable == signatureType.isMarkedNullable) { renderCompilerError(expression) }
|
||||||
"C function signature element mismatch: expected '$signatureType', got '$typeArgument'",
|
|
||||||
irFile, expression
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generateCFunctionPointer(target as IrSimpleFunction, 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 receiver = expression.extensionReceiver!!
|
||||||
val typeOperand = expression.getSingleTypeArgument()
|
val typeOperand = expression.getSingleTypeArgument()
|
||||||
val kotlinTypeOperand = typeOperand.toKotlinType()
|
|
||||||
|
|
||||||
val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) }
|
val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) }
|
||||||
val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) }
|
val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) }
|
||||||
|
|
||||||
val receiverKotlinType = receiver.type.toKotlinType()
|
require(receiverTypeIndex >= 0) { renderCompilerError(receiver) }
|
||||||
|
require(typeOperandIndex >= 0) { renderCompilerError(expression) }
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (intrinsicType) {
|
when (intrinsicType) {
|
||||||
IntrinsicType.INTEROP_SIGN_EXTEND -> if (receiverTypeIndex > typeOperandIndex) {
|
IntrinsicType.INTEROP_SIGN_EXTEND ->
|
||||||
context.reportCompilationError("unable to sign extend $receiverKotlinType to $kotlinTypeOperand",
|
require(receiverTypeIndex <= typeOperandIndex) { renderCompilerError(expression) }
|
||||||
irFile, expression)
|
IntrinsicType.INTEROP_NARROW ->
|
||||||
}
|
require(receiverTypeIndex >= typeOperandIndex) { renderCompilerError(expression) }
|
||||||
|
else -> error(intrinsicType)
|
||||||
IntrinsicType.INTEROP_NARROW -> if (receiverTypeIndex < typeOperandIndex) {
|
|
||||||
context.reportCompilationError("unable to narrow $receiverKotlinType to $kotlinTypeOperand",
|
|
||||||
irFile, expression)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> context.reportCompilationError("unexpected intrinsic $intrinsicType",
|
|
||||||
irFile, expression)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val receiverClass = symbols.integerClasses.single {
|
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 typeOperand = expression.getTypeArgument(0)!!
|
||||||
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
|
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
|
||||||
val source = receiverType.classifierOrFail as IrClassSymbol
|
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 target = typeOperand.classifier as IrClassSymbol
|
val valueToConvert = expression.extensionReceiver!!
|
||||||
val valueToConvert = expression.extensionReceiver!!
|
|
||||||
|
|
||||||
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
|
if (source in symbols.signedIntegerClasses && target in symbols.unsignedIntegerClasses) {
|
||||||
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
|
// Default Kotlin signed-to-unsigned widening integer conversions don't follow C rules.
|
||||||
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
|
val signedTarget = symbols.unsignedToSignedOfSameBitWidth[target]!!
|
||||||
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
|
val widened = builder.irConvertInteger(source, signedTarget, valueToConvert)
|
||||||
builder.irConvertInteger(signedTarget, target, widened)
|
builder.irConvertInteger(signedTarget, target, widened)
|
||||||
} else {
|
|
||||||
builder.irConvertInteger(source, target, valueToConvert)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
context.reportCompilationError(
|
builder.irConvertInteger(source, target, valueToConvert)
|
||||||
"unable to convert ${receiverType.toKotlinType()} to ${typeOperand.toKotlinType()}",
|
|
||||||
irFile,
|
|
||||||
expression
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IntrinsicType.INTEROP_MEMORY_COPY -> {
|
IntrinsicType.INTEROP_MEMORY_COPY -> {
|
||||||
@@ -1198,12 +991,8 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
IntrinsicType.WORKER_EXECUTE -> {
|
IntrinsicType.WORKER_EXECUTE -> {
|
||||||
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(2)!!)
|
val irCallableReference = unwrapStaticFunctionArgument(expression.getValueArgument(2)!!)
|
||||||
|
|
||||||
if (irCallableReference == null || irCallableReference.getArguments().isNotEmpty()) {
|
require(irCallableReference != null
|
||||||
context.reportCompilationError(
|
&& irCallableReference.getArguments().isEmpty()) { renderCompilerError(expression) }
|
||||||
"${function.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda",
|
|
||||||
irFile, expression
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val targetSymbol = irCallableReference.symbol
|
val targetSymbol = irCallableReference.symbol
|
||||||
val jobPointer = IrFunctionReferenceImpl(
|
val jobPointer = IrFunctionReferenceImpl(
|
||||||
@@ -1230,17 +1019,6 @@ private class InteropTransformer(val context: Context, override val irFile: IrFi
|
|||||||
builder.irCall(symbols.interopCPointerGetRawValue).apply {
|
builder.irCall(symbols.interopCPointerGetRawValue).apply {
|
||||||
extensionReceiver = expression.dispatchReceiver
|
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
|
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? {
|
private fun unwrapStaticFunctionArgument(argument: IrExpression): IrFunctionReference? {
|
||||||
if (argument is IrFunctionReference) {
|
if (argument is IrFunctionReference) {
|
||||||
return argument
|
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.common.lower.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.error
|
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.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
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
|
// Convert arguments of the binary blob to special IrConst<String> structure, so that
|
||||||
// vararg lowering will not affect it.
|
// vararg lowering will not affect it.
|
||||||
val args = expression.getValueArgument(0) as? IrVararg
|
val args = expression.getValueArgument(0) as? IrVararg
|
||||||
?: throw Error("varargs shall not be lowered yet")
|
?: error("varargs shall not be lowered yet")
|
||||||
if (args.elements.any { it is IrSpreadElement }) {
|
|
||||||
context.reportCompilationError("no spread elements allowed here", irFile, args)
|
|
||||||
}
|
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
args.elements.forEach {
|
args.elements.forEach {
|
||||||
if (it !is IrConst<*>) {
|
require(it is IrConst<*>) { renderCompilerError(irFile, it, "expected const") }
|
||||||
context.reportCompilationError(
|
val value = (it as? IrConst<*>)?.value
|
||||||
"all elements of binary blob must be constants", irFile, it)
|
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,
|
// Luckily, all values in range 0x00 .. 0xff represent valid UTF-16 symbols,
|
||||||
// block 0 (Basic Latin) and block 1 (Latin-1 Supplement) in
|
// block 0 (Basic Latin) and block 1 (Latin-1 Supplement) in
|
||||||
// Basic Multilingual Plane, so we could just append data "as is".
|
// Basic Multilingual Plane, so we could just append data "as is".
|
||||||
|
|||||||
Reference in New Issue
Block a user