KT-28235: Make sure array access produces unique elements
This requires generating calls on-demand in LValueWithGetterAndSetterCalls.
This commit is contained in:
+9
-8
@@ -263,19 +263,20 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
|
||||
ktLeft: KtArrayAccessExpression,
|
||||
origin: IrStatementOrigin
|
||||
): ArrayAccessAssignmentReceiver {
|
||||
val irArray = ktLeft.arrayExpression!!.genExpr()
|
||||
val irIndexExpressions = ktLeft.indexExpressions.map { it.genExpr() }
|
||||
|
||||
val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
|
||||
val indexedGetCall = indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
|
||||
|
||||
val indexedSetResolvedCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
|
||||
val indexedSetCall = indexedSetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
|
||||
|
||||
return ArrayAccessAssignmentReceiver(
|
||||
irArray, irIndexExpressions, indexedGetCall, indexedSetCall,
|
||||
ktLeft.arrayExpression!!.genExpr(),
|
||||
ktLeft.indexExpressions.map { it.genExpr() },
|
||||
indexedGetResolvedCall,
|
||||
indexedSetResolvedCall,
|
||||
{ indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) } },
|
||||
{ indexedSetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) } },
|
||||
CallGenerator(statementGenerator),
|
||||
ktLeft.startOffsetSkippingComments, ktLeft.endOffset, origin
|
||||
ktLeft.startOffsetSkippingComments,
|
||||
ktLeft.endOffset,
|
||||
origin
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+24
-15
@@ -16,31 +16,40 @@
|
||||
|
||||
package org.jetbrains.kotlin.psi2ir.intermediate
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.inlineStatement
|
||||
import org.jetbrains.kotlin.ir.expressions.isAssignmentOperatorWithResult
|
||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import java.lang.AssertionError
|
||||
|
||||
class ArrayAccessAssignmentReceiver(
|
||||
private val irArray: IrExpression,
|
||||
private val irIndices: List<IrExpression>,
|
||||
private val indexedGetCall: CallBuilder?,
|
||||
private val indexedSetCall: CallBuilder?,
|
||||
private val indexedGetResolvedCall: ResolvedCall<FunctionDescriptor>?,
|
||||
private val indexedSetResolvedCall: ResolvedCall<FunctionDescriptor>?,
|
||||
private val indexedGetCall: () -> CallBuilder?,
|
||||
private val indexedSetCall: () -> CallBuilder?,
|
||||
private val callGenerator: CallGenerator,
|
||||
private val startOffset: Int,
|
||||
private val endOffset: Int,
|
||||
private val origin: IrStatementOrigin
|
||||
) : AssignmentReceiver {
|
||||
|
||||
private val kotlinType: KotlinType =
|
||||
indexedGetCall?.run { descriptor.returnType!! } ?: indexedSetCall?.run { descriptor.valueParameters.last().type }
|
||||
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
|
||||
private val descriptor =
|
||||
indexedGetResolvedCall?.resultingDescriptor
|
||||
?: indexedSetResolvedCall?.resultingDescriptor
|
||||
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
|
||||
|
||||
override fun assign(withLValue: (LValue) -> IrExpression): IrExpression {
|
||||
val kotlinType: KotlinType =
|
||||
indexedGetResolvedCall?.run { resultingDescriptor.returnType!! }
|
||||
?: indexedSetResolvedCall?.run { resultingDescriptor.valueParameters.last().type }
|
||||
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
|
||||
|
||||
val hasResult = origin.isAssignmentOperatorWithResult()
|
||||
val resultType = if (hasResult) kotlinType else callGenerator.context.builtIns.unitType
|
||||
val irResultType = callGenerator.translateType(resultType)
|
||||
@@ -52,11 +61,11 @@ class ArrayAccessAssignmentReceiver(
|
||||
callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irIndex, irBlock, "index$i")
|
||||
}
|
||||
|
||||
indexedGetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
|
||||
indexedSetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
|
||||
val irLValue = LValueWithGetterAndSetterCalls(
|
||||
callGenerator,
|
||||
indexedGetCall, indexedSetCall,
|
||||
descriptor,
|
||||
{ indexedGetCall()?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) },
|
||||
{ indexedSetCall()?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) },
|
||||
callGenerator.translateType(kotlinType),
|
||||
startOffset, endOffset, origin
|
||||
)
|
||||
@@ -66,16 +75,16 @@ class ArrayAccessAssignmentReceiver(
|
||||
}
|
||||
|
||||
override fun assign(value: IrExpression): IrExpression {
|
||||
if (indexedSetCall == null) throw AssertionError("Array access without indexed-get call")
|
||||
indexedSetCall.setExplicitReceiverValue(OnceExpressionValue(irArray))
|
||||
val call = indexedSetCall() ?: throw AssertionError("Array access without indexed-get call")
|
||||
call.setExplicitReceiverValue(OnceExpressionValue(irArray))
|
||||
irIndices.forEachIndexed { i, irIndex ->
|
||||
indexedSetCall.irValueArgumentsByIndex[i] = irIndex
|
||||
call.irValueArgumentsByIndex[i] = irIndex
|
||||
}
|
||||
indexedSetCall.lastArgument = value
|
||||
return callGenerator.generateCall(startOffset, endOffset, indexedSetCall, IrStatementOrigin.EQ)
|
||||
call.lastArgument = value
|
||||
return callGenerator.generateCall(startOffset, endOffset, call, IrStatementOrigin.EQ)
|
||||
}
|
||||
|
||||
private fun CallBuilder.fillArrayAndIndexArguments(arrayValue: IntermediateValue, indexValues: List<IntermediateValue>) {
|
||||
private fun CallBuilder.fillArrayAndIndexArguments(arrayValue: IntermediateValue, indexValues: List<IntermediateValue>) = apply {
|
||||
setExplicitReceiverValue(arrayValue)
|
||||
indexValues.forEachIndexed { i, irIndexValue ->
|
||||
irValueArgumentsByIndex[i] = irIndexValue.load()
|
||||
|
||||
+8
-12
@@ -21,31 +21,27 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
class LValueWithGetterAndSetterCalls(
|
||||
val callGenerator: CallGenerator,
|
||||
val getterCall: CallBuilder?,
|
||||
val setterCall: CallBuilder?,
|
||||
val descriptor: CallableDescriptor,
|
||||
val getterCall: () -> CallBuilder?,
|
||||
val setterCall: () -> CallBuilder?,
|
||||
override val type: IrType,
|
||||
val startOffset: Int,
|
||||
val endOffset: Int,
|
||||
val origin: IrStatementOrigin? = null
|
||||
) : LValue {
|
||||
|
||||
private val descriptor: CallableDescriptor =
|
||||
getterCall?.descriptor ?: setterCall?.descriptor
|
||||
?: throw AssertionError("Call-based LValue should have either a getter or a setter call")
|
||||
|
||||
override fun load(): IrExpression {
|
||||
if (getterCall == null) throw AssertionError("No getter call for $descriptor")
|
||||
return callGenerator.generateCall(startOffset, endOffset, getterCall, origin)
|
||||
val call = getterCall() ?: throw AssertionError("No getter call for $descriptor")
|
||||
return callGenerator.generateCall(startOffset, endOffset, call, origin)
|
||||
}
|
||||
|
||||
override fun store(irExpression: IrExpression): IrExpression {
|
||||
if (setterCall == null) throw AssertionError("No setter call for $descriptor")
|
||||
setterCall.irValueArgumentsByIndex[setterCall.argumentsCount - 1] = irExpression
|
||||
return callGenerator.generateCall(startOffset, endOffset, setterCall, origin)
|
||||
val call = setterCall() ?: throw AssertionError("No setter call for $descriptor")
|
||||
call.irValueArgumentsByIndex[call.argumentsCount - 1] = irExpression
|
||||
return callGenerator.generateCall(startOffset, endOffset, call, origin)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -136,8 +136,18 @@ abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private val elementsAreUniqueChecker = object : IrElementVisitorVoid {
|
||||
private val elements = HashSet<IrElement>()
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
require(elements.add(element)) { "Non-unique element: ${element.render()}" }
|
||||
element.acceptChildrenVoid(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun verifyWithAssert(irFile: IrFile) {
|
||||
irFile.acceptChildrenVoid(this)
|
||||
irFile.acceptChildrenVoid(elementsAreUniqueChecker)
|
||||
TestCase.assertFalse(errorsAsMessage + "\n\n\n" + irFile.dump(), hasErrors)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user