IR: preserve argument evaluation order more carefully

1. receivers should be evaluated before named arguments;
 2. just because an argument has no side effects doesn't mean it is not
    affected by the other arguments' side effects - in that case it
    should still be evaluated in source order.

 #KT-47660 Fixed
This commit is contained in:
pyos
2021-07-12 15:30:55 +02:00
committed by TeamCityServer
parent b5942204dc
commit 3dc7b6c3ee
14 changed files with 123 additions and 298 deletions
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.types.IrDynamicType
@@ -383,48 +384,41 @@ class CallGenerator(statementGenerator: StatementGenerator) : StatementGenerator
call: CallBuilder,
irResultType: IrType
): IrExpression {
val resolvedCall = call.original
val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val irBlock = IrBlockImpl(startOffset, endOffset, irResultType, IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL)
val valueArgumentsToValueParameters = HashMap<ResolvedValueArgument, ValueParameterDescriptor>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
val valueParameter = valueParameters[index]
valueArgumentsToValueParameters[valueArgument] = valueParameter
}
val irArgumentValues = HashMap<ValueParameterDescriptor, IntermediateValue>()
for (valueArgument in valueArgumentsInEvaluationOrder) {
val valueParameter = valueArgumentsToValueParameters[valueArgument]!!
val irArgument = call.getValueArgument(valueParameter) ?: continue
val irArgumentValue = if (irArgument.hasNoSideEffects())
generateExpressionValue(irArgument.type) { irArgument } // Computing a lambda has no side effects, can generate in place
fun IrExpression.freeze(nameHint: String) =
if (isUnchanging())
this
else
scope.createTemporaryVariableInBlock(context, irArgument, irBlock, valueParameter.name.asString())
irArgumentValues[valueParameter] = irArgumentValue
}
scope.createTemporaryVariableInBlock(context, this, irBlock, nameHint).load()
resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, _ ->
val valueParameter = valueParameters[index]
irCall.putValueArgument(index, irArgumentValues[valueParameter]?.load())
}
irCall.dispatchReceiver = irCall.dispatchReceiver?.freeze("\$this")
irCall.extensionReceiver = irCall.extensionReceiver?.freeze("\$receiver")
val resolvedCall = call.original
val valueParameters = resolvedCall.resultingDescriptor.valueParameters
val valueArgumentsToIndex = HashMap<ResolvedValueArgument, Int>()
for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) {
valueArgumentsToIndex[valueArgument] = index
}
for (valueArgument in resolvedCall.valueArguments.values) {
val index = valueArgumentsToIndex[valueArgument]!!
val irArgument = call.getValueArgument(valueParameters[index]) ?: continue
irCall.putValueArgument(index, irArgument.freeze(valueParameters[index].name.asString()))
}
irBlock.statements.add(irCall)
return irBlock
}
}
fun IrExpression.hasNoSideEffects() =
fun IrExpression.isUnchanging() =
this is IrFunctionExpression ||
(this is IrCallableReference<*> && dispatchReceiver == null && extensionReceiver == null) ||
this is IrClassReference ||
this is IrConst<*> ||
this is IrGetValue
(this is IrGetValue && !symbol.owner.let { it is IrVariable && it.isVar })
fun IrExpression.hasNoSideEffects() = isUnchanging() || this is IrGetValue
fun CallGenerator.generateCall(ktElement: KtElement, call: CallBuilder, origin: IrStatementOrigin? = null) =
generateCall(ktElement.startOffsetSkippingComments, ktElement.endOffset, call, origin)
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
import org.jetbrains.kotlin.psi2ir.isValueArgumentReorderingRequired
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
@@ -54,7 +55,7 @@ fun CallBuilder.getValueArgumentsInParameterOrder(): List<IrExpression?> =
descriptor.valueParameters.map { irValueArgumentsByIndex[it.index] }
fun CallBuilder.isValueArgumentReorderingRequired() =
original.isValueArgumentReorderingRequired()
original.isValueArgumentReorderingRequired() && irValueArgumentsByIndex.any { it != null && !it.hasNoSideEffects() }
val CallBuilder.hasExtensionReceiver: Boolean
get() =