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,
|
ktLeft: KtArrayAccessExpression,
|
||||||
origin: IrStatementOrigin
|
origin: IrStatementOrigin
|
||||||
): ArrayAccessAssignmentReceiver {
|
): ArrayAccessAssignmentReceiver {
|
||||||
val irArray = ktLeft.arrayExpression!!.genExpr()
|
|
||||||
val irIndexExpressions = ktLeft.indexExpressions.map { it.genExpr() }
|
|
||||||
|
|
||||||
val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
|
val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
|
||||||
val indexedGetCall = indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
|
|
||||||
|
|
||||||
val indexedSetResolvedCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
|
val indexedSetResolvedCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft)
|
||||||
val indexedSetCall = indexedSetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
|
|
||||||
|
|
||||||
return ArrayAccessAssignmentReceiver(
|
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),
|
CallGenerator(statementGenerator),
|
||||||
ktLeft.startOffsetSkippingComments, ktLeft.endOffset, origin
|
ktLeft.startOffsetSkippingComments,
|
||||||
|
ktLeft.endOffset,
|
||||||
|
origin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-15
@@ -16,31 +16,40 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.psi2ir.intermediate
|
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.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.inlineStatement
|
import org.jetbrains.kotlin.ir.expressions.impl.inlineStatement
|
||||||
import org.jetbrains.kotlin.ir.expressions.isAssignmentOperatorWithResult
|
import org.jetbrains.kotlin.ir.expressions.isAssignmentOperatorWithResult
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import java.lang.AssertionError
|
|
||||||
|
|
||||||
class ArrayAccessAssignmentReceiver(
|
class ArrayAccessAssignmentReceiver(
|
||||||
private val irArray: IrExpression,
|
private val irArray: IrExpression,
|
||||||
private val irIndices: List<IrExpression>,
|
private val irIndices: List<IrExpression>,
|
||||||
private val indexedGetCall: CallBuilder?,
|
private val indexedGetResolvedCall: ResolvedCall<FunctionDescriptor>?,
|
||||||
private val indexedSetCall: CallBuilder?,
|
private val indexedSetResolvedCall: ResolvedCall<FunctionDescriptor>?,
|
||||||
|
private val indexedGetCall: () -> CallBuilder?,
|
||||||
|
private val indexedSetCall: () -> CallBuilder?,
|
||||||
private val callGenerator: CallGenerator,
|
private val callGenerator: CallGenerator,
|
||||||
private val startOffset: Int,
|
private val startOffset: Int,
|
||||||
private val endOffset: Int,
|
private val endOffset: Int,
|
||||||
private val origin: IrStatementOrigin
|
private val origin: IrStatementOrigin
|
||||||
) : AssignmentReceiver {
|
) : AssignmentReceiver {
|
||||||
|
|
||||||
private val kotlinType: KotlinType =
|
private val descriptor =
|
||||||
indexedGetCall?.run { descriptor.returnType!! } ?: indexedSetCall?.run { descriptor.valueParameters.last().type }
|
indexedGetResolvedCall?.resultingDescriptor
|
||||||
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
|
?: indexedSetResolvedCall?.resultingDescriptor
|
||||||
|
?: throw AssertionError("Array access should have either indexed-get call or indexed-set call")
|
||||||
|
|
||||||
override fun assign(withLValue: (LValue) -> IrExpression): IrExpression {
|
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 hasResult = origin.isAssignmentOperatorWithResult()
|
||||||
val resultType = if (hasResult) kotlinType else callGenerator.context.builtIns.unitType
|
val resultType = if (hasResult) kotlinType else callGenerator.context.builtIns.unitType
|
||||||
val irResultType = callGenerator.translateType(resultType)
|
val irResultType = callGenerator.translateType(resultType)
|
||||||
@@ -52,11 +61,11 @@ class ArrayAccessAssignmentReceiver(
|
|||||||
callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irIndex, irBlock, "index$i")
|
callGenerator.scope.createTemporaryVariableInBlock(callGenerator.context, irIndex, irBlock, "index$i")
|
||||||
}
|
}
|
||||||
|
|
||||||
indexedGetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
|
|
||||||
indexedSetCall?.fillArrayAndIndexArguments(irArrayValue, irIndexValues)
|
|
||||||
val irLValue = LValueWithGetterAndSetterCalls(
|
val irLValue = LValueWithGetterAndSetterCalls(
|
||||||
callGenerator,
|
callGenerator,
|
||||||
indexedGetCall, indexedSetCall,
|
descriptor,
|
||||||
|
{ indexedGetCall()?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) },
|
||||||
|
{ indexedSetCall()?.fillArrayAndIndexArguments(irArrayValue, irIndexValues) },
|
||||||
callGenerator.translateType(kotlinType),
|
callGenerator.translateType(kotlinType),
|
||||||
startOffset, endOffset, origin
|
startOffset, endOffset, origin
|
||||||
)
|
)
|
||||||
@@ -66,16 +75,16 @@ class ArrayAccessAssignmentReceiver(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun assign(value: IrExpression): IrExpression {
|
override fun assign(value: IrExpression): IrExpression {
|
||||||
if (indexedSetCall == null) throw AssertionError("Array access without indexed-get call")
|
val call = indexedSetCall() ?: throw AssertionError("Array access without indexed-get call")
|
||||||
indexedSetCall.setExplicitReceiverValue(OnceExpressionValue(irArray))
|
call.setExplicitReceiverValue(OnceExpressionValue(irArray))
|
||||||
irIndices.forEachIndexed { i, irIndex ->
|
irIndices.forEachIndexed { i, irIndex ->
|
||||||
indexedSetCall.irValueArgumentsByIndex[i] = irIndex
|
call.irValueArgumentsByIndex[i] = irIndex
|
||||||
}
|
}
|
||||||
indexedSetCall.lastArgument = value
|
call.lastArgument = value
|
||||||
return callGenerator.generateCall(startOffset, endOffset, indexedSetCall, IrStatementOrigin.EQ)
|
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)
|
setExplicitReceiverValue(arrayValue)
|
||||||
indexValues.forEachIndexed { i, irIndexValue ->
|
indexValues.forEachIndexed { i, irIndexValue ->
|
||||||
irValueArgumentsByIndex[i] = irIndexValue.load()
|
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.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
import org.jetbrains.kotlin.psi2ir.generators.CallGenerator
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
|
||||||
|
|
||||||
class LValueWithGetterAndSetterCalls(
|
class LValueWithGetterAndSetterCalls(
|
||||||
val callGenerator: CallGenerator,
|
val callGenerator: CallGenerator,
|
||||||
val getterCall: CallBuilder?,
|
val descriptor: CallableDescriptor,
|
||||||
val setterCall: CallBuilder?,
|
val getterCall: () -> CallBuilder?,
|
||||||
|
val setterCall: () -> CallBuilder?,
|
||||||
override val type: IrType,
|
override val type: IrType,
|
||||||
val startOffset: Int,
|
val startOffset: Int,
|
||||||
val endOffset: Int,
|
val endOffset: Int,
|
||||||
val origin: IrStatementOrigin? = null
|
val origin: IrStatementOrigin? = null
|
||||||
) : LValue {
|
) : 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 {
|
override fun load(): IrExpression {
|
||||||
if (getterCall == null) throw AssertionError("No getter call for $descriptor")
|
val call = getterCall() ?: throw AssertionError("No getter call for $descriptor")
|
||||||
return callGenerator.generateCall(startOffset, endOffset, getterCall, origin)
|
return callGenerator.generateCall(startOffset, endOffset, call, origin)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun store(irExpression: IrExpression): IrExpression {
|
override fun store(irExpression: IrExpression): IrExpression {
|
||||||
if (setterCall == null) throw AssertionError("No setter call for $descriptor")
|
val call = setterCall() ?: throw AssertionError("No setter call for $descriptor")
|
||||||
setterCall.irValueArgumentsByIndex[setterCall.argumentsCount - 1] = irExpression
|
call.irValueArgumentsByIndex[call.argumentsCount - 1] = irExpression
|
||||||
return callGenerator.generateCall(startOffset, endOffset, setterCall, origin)
|
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) {
|
fun verifyWithAssert(irFile: IrFile) {
|
||||||
irFile.acceptChildrenVoid(this)
|
irFile.acceptChildrenVoid(this)
|
||||||
|
irFile.acceptChildrenVoid(elementsAreUniqueChecker)
|
||||||
TestCase.assertFalse(errorsAsMessage + "\n\n\n" + irFile.dump(), hasErrors)
|
TestCase.assertFalse(errorsAsMessage + "\n\n\n" + irFile.dump(), hasErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user