JVM_IR: generate flags for adapted function references

This commit is contained in:
Dmitry Petrov
2020-04-28 20:55:55 +03:00
parent 94bc8eaf83
commit f9bb07e128
4 changed files with 64 additions and 16 deletions
@@ -124,24 +124,33 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
private val useOptimizedSuperClass =
context.state.generateOptimizedCallableReferenceSuperClasses
private val adaptedReferenceOriginalTarget = if (callee.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
// The body of a callable reference adapter contains either only a call, or an IMPLICIT_COERCION_TO_UNIT type operator
// applied to a call. That call's target is the original function which we need to get owner/name/signature.
val call = when (val statement = callee.body!!.statements.single()) {
is IrTypeOperatorCall -> {
assert(statement.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) {
"Unexpected type operator in ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}"
private val adaptedReferenceOriginalTarget: IrFunction?
private val adapteeCall: IrFunctionAccessExpression?
init {
if (callee.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) {
// The body of a callable reference adapter contains either only a call, or an IMPLICIT_COERCION_TO_UNIT type operator
// applied to a call. That call's target is the original function which we need to get owner/name/signature.
val call = when (val statement = callee.body!!.statements.single()) {
is IrTypeOperatorCall -> {
assert(statement.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) {
"Unexpected type operator in ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}"
}
statement.argument
}
statement.argument
is IrReturn -> statement.value
else -> statement
}
is IrReturn -> statement.value
else -> statement
if (call !is IrFunctionAccessExpression) {
throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}")
}
adapteeCall = call
adaptedReferenceOriginalTarget = call.symbol.owner
} else {
adapteeCall = null
adaptedReferenceOriginalTarget = null
}
if (call !is IrFunctionAccessExpression) {
throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${callee.render()}")
}
call.symbol.owner
} else null
}
private val superType =
samSuperType ?: when {
@@ -269,7 +278,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
putValueArgument(index++, generateSignature(callableReferenceTarget.symbol))
putValueArgument(
index,
irInt(if (callableReferenceTarget.parent.let { it is IrClass && it.isFileClass }) 1 else 0)
irInt(getFunctionReferenceFlags(callableReferenceTarget))
)
}
}
@@ -279,6 +288,36 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext)
}
}
private fun getFunctionReferenceFlags(callableReferenceTarget: IrFunction): Int {
val isTopLevelBit = if (callableReferenceTarget.parent.let { it is IrClass && it.isFileClass }) 1 else 0
val adaptedCallableReferenceFlags = getAdaptedCallableReferenceFlags()
return isTopLevelBit + (adaptedCallableReferenceFlags shl 1)
}
private fun getAdaptedCallableReferenceFlags(): Int {
if (adaptedReferenceOriginalTarget == null) return 0
val isVarargMappedToElementBit = if (hasVarargMappedToElement()) 1 else 0
val isSuspendConvertedBit = if (!adaptedReferenceOriginalTarget.isSuspend && callee.isSuspend) 1 else 0
val isCoercedToUnitBit = if (!adaptedReferenceOriginalTarget.returnType.isUnit() && callee.returnType.isUnit()) 1 else 0
return isVarargMappedToElementBit +
(isSuspendConvertedBit shl 1) +
(isCoercedToUnitBit shl 2)
}
private fun hasVarargMappedToElement(): Boolean {
if (adapteeCall == null) return false
for (i in 0 until adapteeCall.valueArgumentsCount) {
val arg = adapteeCall.getValueArgument(i) ?: continue
if (arg !is IrVararg) continue
for (varargElement in arg.elements) {
if (varargElement is IrGetValue) return true
}
}
return false
}
private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction =
functionReferenceClass.addFunction {
setSourceRange(if (isLambda) callee else irFunctionReference)
@@ -16,6 +16,7 @@ fun target(x: Int, y: String = "", z: String = ""): Int = x
fun captureNoDefaults(fn: (Int, String, String) -> Unit): Any = fn
fun captureOneDefault(fn: (Int, String) -> Unit): Any = fn
fun captureAllDefaults(fn: (Int) -> Unit): Any = fn
fun captureOneDefaultWithoutCoercionToUnit(fn: (Int, String) -> Int): Any = fn
fun box(): String {
checkEqual(captureNoDefaults(::target), captureNoDefaults(::target))
@@ -26,6 +27,8 @@ fun box(): String {
checkNotEqual(captureNoDefaults(::target), captureOneDefault(::target))
checkNotEqual(captureNoDefaults(::target), captureAllDefaults(::target))
checkNotEqual(captureOneDefault(::target), captureOneDefaultWithoutCoercionToUnit(::target))
return "OK"
}
@@ -16,6 +16,9 @@ fun target(x: Int, vararg ys: String): Int = x + ys.size
fun captureVararg1(fn: (Int, String) -> Unit): Any = fn
fun captureVararg0(fn: (Int) -> Unit): Any = fn
fun captureNoVararg(fn: (Int, Array<String>) -> Int): Any = fn
fun captureNoVarargCoerced(fn: (Int, Array<String>) -> Unit): Any = fn
fun box(): String {
checkEqual(captureVararg1(::target), captureVararg1(::target))
checkEqual(captureVararg0(::target), captureVararg0(::target))
@@ -23,6 +26,8 @@ fun box(): String {
checkNotEqual(captureVararg1(::target), captureVararg0(::target))
checkNotEqual(captureNoVararg(::target), captureNoVarargCoerced(::target))
return "OK"
}
@@ -1,6 +1,7 @@
// !LANGUAGE: +SuspendConversion
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JVM_IR, JS_IR
// ^ TODO fix suspend coercion for bound function references in JVM_IR
// FILE: suspendCovnersion.kt
fun checkNotEqual(x: Any, y: Any) {