KT-28235: Make sure array access produces unique elements

This requires generating calls on-demand in
LValueWithGetterAndSetterCalls.
This commit is contained in:
Dmitry Petrov
2018-11-15 18:06:21 +03:00
parent 080c177ed2
commit a0d74be7cf
4 changed files with 51 additions and 35 deletions
@@ -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
)
}
@@ -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()
@@ -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)
}