From d7412c449ea6ddf004d6140e339917aa20a60efd Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Tue, 16 Aug 2016 18:18:26 +0300 Subject: [PATCH] IR: augmented assignment for array access expressions --- .../org/jetbrains/kotlin/psi2ir/IrUtils.kt | 2 +- .../psi2ir/generators/IrCallGenerator.kt | 29 ++-- .../IrOperatorExpressionGenerator.kt | 33 +++-- .../psi2ir/generators/IrStatementGenerator.kt | 7 +- .../kotlin/psi2ir/generators/IrValue.kt | 124 ++++++++++++++++-- .../kotlin/ir/AbstractIrGeneratorTestCase.kt | 4 - .../src/org/jetbrains/kotlin/ir/IrElement.kt | 3 +- .../kotlin/ir/expressions/IrCallExpression.kt | 4 +- .../kotlin/ir/util/RenderIrElement.kt | 2 + .../kotlin/ir/util/ValidateIrTree.kt | 40 ++++++ .../testData/ir/irText/arrayAssignment.kt | 5 + .../testData/ir/irText/arrayAssignment.txt | 15 +++ .../ir/irText/augmentedAssignment1.kt | 21 ++- .../ir/irText/augmentedAssignment1.txt | 50 ++++++- .../ir/irText/augmentedAssignment2.kt | 24 +++- .../ir/irText/augmentedAssignment2.txt | 66 ++++++++-- .../ir/irText/augmentedAssignmentArray.kt | 9 ++ .../ir/irText/augmentedAssignmentArray.txt | 36 +++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 12 ++ 19 files changed, 425 insertions(+), 61 deletions(-) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ValidateIrTree.kt create mode 100644 compiler/testData/ir/irText/arrayAssignment.kt create mode 100644 compiler/testData/ir/irText/arrayAssignment.txt create mode 100644 compiler/testData/ir/irText/augmentedAssignmentArray.kt create mode 100644 compiler/testData/ir/irText/augmentedAssignmentArray.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt index e7c718af98f..b13da7557f8 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrUtils.kt @@ -38,5 +38,5 @@ fun IrExpression.toExpectedType(expectedType: KotlinType?): IrExpression { ) } -fun IrVariable.implicitGetExpression(): IrExpression = +fun IrVariable.createDefaultGetExpression(): IrExpression = IrGetVariableExpressionImpl(startOffset, endOffset, descriptor) \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt index 205030083e3..e89a81f8ec7 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -35,7 +36,13 @@ class IrCallGenerator( private val expressionValues = HashMap() private val receiverValues = HashMap() - private val valueArgumentValues = HashMap() + private val valueArgumentValues = HashMap() + + fun createTemporary(ktExpression: KtExpression, irExpression: IrExpression): IrVariable { + val irTmpVar = irStatementGenerator.declarationFactory.createTemporaryVariable(irExpression) + putValue(ktExpression, IrTemporaryVariableValue(irTmpVar)) + return irTmpVar + } fun putValue(ktExpression: KtExpression, irValue: IrValue) { expressionValues[ktExpression] = irValue @@ -45,7 +52,7 @@ class IrCallGenerator( receiverValues[receiver] = irValue } - fun putValue(valueArgument: ResolvedValueArgument, irValue: IrValue) { + fun putValue(valueArgument: ValueParameterDescriptor, irValue: IrValue) { valueArgumentValues[valueArgument] = irValue } @@ -134,9 +141,15 @@ class IrCallGenerator( hasResult = isUsedAsExpression(ktExpression), isDesugared = true) + val valueArgumentsToValueParameters = HashMap() + for ((index, valueArgument) in resolvedCall.valueArgumentsByIndex!!.withIndex()) { + val valueParameter = resolvedCall.resultingDescriptor.valueParameters[index] + valueArgumentsToValueParameters[valueArgument] = valueParameter + } + val temporariesForValueArguments = HashMap>() for (valueArgument in valueArgumentsInEvaluationOrder) { - val irArgument = generateValueArgument(valueArgument) ?: continue + val irArgument = generateValueArgument(valueArgument, valueArgumentsToValueParameters[valueArgument]!!) ?: continue val irTemporary = irStatementGenerator.declarationFactory.createTemporaryVariable(irArgument) temporariesForValueArguments[valueArgument] = Pair(irTemporary.descriptor, irArgument) irBlock.addStatement(irTemporary) @@ -187,17 +200,17 @@ class IrCallGenerator( fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? = if (valueParameterDescriptor.varargElementType != null) { - generateValueArgument(valueArgument) + doGenerateValueArgument(valueArgument, valueParameterDescriptor) } else { - generateValueArgument(valueArgument)?.toExpectedType(valueParameterDescriptor.type) + doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(valueParameterDescriptor.type) } - fun generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = + private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? = if (valueArgument is DefaultValueArgument) null else - valueArgumentValues[valueArgument]?.load() ?: doGenerateValueArgument(valueArgument) + valueArgumentValues[valueParameterDescriptor]?.load() ?: doGenerateValueArgument(valueArgument) private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = @@ -205,7 +218,7 @@ class IrCallGenerator( is ExpressionValueArgument -> generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!) is VarargValueArgument -> - TODO("vararg") + createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg") else -> TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt index e492523f21b..b2986b08598 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt @@ -22,12 +22,11 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.types.KotlinType val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf( KtTokens.PLUSEQ to IrOperator.PLUSEQ, @@ -54,18 +53,22 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat val ktLeft = expression.left!! val irOperator = getIrOperator(ktOperator) - val lhs = generateReferenceForAssignmentLhs(ktLeft, irOperator) + val irLhs = generateLValue(ktLeft, irOperator) val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false val operatorCall = getResolvedCall(expression)!! - val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktLeft, lhs) } + if (isSimpleAssignment && irLhs is IrLValueWithAugmentedStore) { + return irLhs.augmentedStore(operatorCall, irStatementGenerator.generateExpression(expression.right!!)) + } + + val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktLeft, irLhs) } val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) return if (isSimpleAssignment) { // Set( Op( Get(), RHS ) ) - lhs.store(irOpCall) + irLhs.store(irOpCall) } else { // Op( Get(), RHS ) @@ -80,11 +83,22 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat private fun generateAssignment(expression: KtBinaryExpression): IrExpression { val ktLeft = expression.left!! val ktRight = expression.right!! - val lhsReference = generateReferenceForAssignmentLhs(ktLeft, IrOperator.EQ) + val lhsReference = generateLValue(ktLeft, IrOperator.EQ) return lhsReference.store(irStatementGenerator.generateExpression(ktRight)) } - private fun generateReferenceForAssignmentLhs(ktLeft: KtExpression, irOperator: IrOperator?): IrReference { + private fun generateLValue(ktLeft: KtExpression, irOperator: IrOperator?): IrLValue { + if (ktLeft is KtArrayAccessExpression) { + val irArrayValue = IrGenerateExpressionValue(irStatementGenerator, ktLeft.arrayExpression!!) + val indexExpressions = ktLeft.indexExpressions.map { + it to IrGenerateExpressionValue(irStatementGenerator, it) + } + val indexedGetCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft) + val indexedSetCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft) + return IrIndexedLValue(irStatementGenerator, ktLeft, irOperator, + irArrayValue, indexExpressions, indexedGetCall, indexedSetCall) + } + val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") val descriptor = resolvedCall.candidateDescriptor @@ -93,10 +107,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat if (descriptor.isDelegated) TODO("Delegated local variable") else - IrVariableReferenceValue(ktLeft, irOperator, descriptor) + IrVariableLValueValue(ktLeft, irOperator, descriptor) is PropertyDescriptor -> IrCallGenerator(irStatementGenerator).run { - IrPropertyReferenceValue( + IrPropertyLValueValue( ktLeft, irOperator, descriptor, generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter), generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter), @@ -107,5 +121,4 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat TODO("Other cases of LHS") } } - } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt index 3582ce663f2..62c990b21e6 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.deparenthesize -import org.jetbrains.kotlin.psi2ir.implicitGetExpression import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContextUtils @@ -73,11 +72,11 @@ class IrStatementGenerator( val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, null, hasResult = false, isDesugared = true) val ktInitializer = multiDeclaration.initializer!! - val irInitializerVal = declarationFactory.createTemporaryVariable(ktInitializer.genExpr()) - irBlock.addStatement(irInitializerVal) + val irTmpInitializer = declarationFactory.createTemporaryVariable(ktInitializer.genExpr()) + irBlock.addStatement(irTmpInitializer) val irCallGenerator = IrCallGenerator(this) - irCallGenerator.putValue(ktInitializer, justExpressionValue { irInitializerVal.implicitGetExpression() }) + irCallGenerator.putValue(ktInitializer, IrTemporaryVariableValue(irTmpInitializer)) for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrValue.kt index add58644e30..df7566c8d21 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrValue.kt @@ -18,55 +18,74 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.psi2ir.createDefaultGetExpression import org.jetbrains.kotlin.psi2ir.toExpectedType +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall interface IrValue { fun load(): IrExpression } -inline fun justExpressionValue(crossinline makeExpression: () -> IrExpression) = +inline fun onDemandExpressionValue(crossinline makeExpression: () -> IrExpression) = object : IrValue { override fun load() = makeExpression() } -interface IrReference : IrValue { - fun store(value: IrExpression): IrExpression +class IrTemporaryVariableValue(val irVariable: IrVariable) : IrValue { + override fun load(): IrExpression = + irVariable.createDefaultGetExpression() } -class IrVariableReferenceValue( +class IrGenerateExpressionValue(val irStatementGenerator: IrStatementGenerator, val ktExpression: KtExpression) : IrValue { + override fun load(): IrExpression = + irStatementGenerator.generateExpression(ktExpression) +} + +interface IrLValue : IrValue { + fun store(irExpression: IrExpression): IrExpression +} + +interface IrLValueWithAugmentedStore : IrLValue { + fun augmentedStore(operatorCall: ResolvedCall<*>, irRhs: IrExpression): IrExpression +} + +class IrVariableLValueValue( val ktElement: KtElement, val irOperator: IrOperator?, val descriptor: VariableDescriptor -) : IrReference { +) : IrLValue { override fun load(): IrExpression = IrGetVariableExpressionImpl( ktElement.startOffset, ktElement.endOffset, descriptor, irOperator ) - override fun store(value: IrExpression): IrExpression = + override fun store(irExpression: IrExpression): IrExpression = IrSetVariableExpressionImpl( ktElement.startOffset, ktElement.endOffset, - descriptor, value.toExpectedType(descriptor.type), irOperator + descriptor, irExpression.toExpectedType(descriptor.type), irOperator ) } -class IrPropertyReferenceValue( +class IrPropertyLValueValue( val ktElement: KtElement, val irOperator: IrOperator?, val descriptor: PropertyDescriptor, val dispatchReceiver: IrExpression?, val extensionReceiver: IrExpression?, val isSafe: Boolean -) : IrReference { +) : IrLValue { private fun IrPropertyAccessExpression.setReceivers() = apply { - dispatchReceiver = this@IrPropertyReferenceValue.dispatchReceiver - extensionReceiver = this@IrPropertyReferenceValue.extensionReceiver + dispatchReceiver = this@IrPropertyLValueValue.dispatchReceiver + extensionReceiver = this@IrPropertyLValueValue.extensionReceiver } override fun load(): IrExpression = @@ -75,9 +94,88 @@ class IrPropertyReferenceValue( descriptor.type, isSafe, descriptor, irOperator ).setReceivers() - override fun store(value: IrExpression): IrExpression = + override fun store(irExpression: IrExpression): IrExpression = IrSetPropertyExpressionImpl( ktElement.startOffset, ktElement.endOffset, - isSafe, descriptor, value.toExpectedType(descriptor.type), irOperator + isSafe, descriptor, irExpression.toExpectedType(descriptor.type), irOperator ).setReceivers() +} + +class IrIndexedLValue( + var irStatementGenerator: IrStatementGenerator, + val ktArrayAccessExpression: KtArrayAccessExpression, + val irOperator: IrOperator?, + val arrayValue: IrValue, + val indexValues: List>, + val indexedGetCall: ResolvedCall<*>?, + val indexedSetCall: ResolvedCall<*>? +) : IrLValueWithAugmentedStore { + override fun load(): IrExpression { + if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") + + val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset, + indexedGetCall.resultingDescriptor.returnType, + hasResult = true, isDesugared = true) + + val callGenerator = IrCallGenerator(irStatementGenerator) + + defineContextVariables(irBlock, callGenerator) + + irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator)) + + return irBlock + } + + override fun store(irExpression: IrExpression): IrExpression { + if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") + + val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset, + indexedSetCall.resultingDescriptor.returnType, + hasResult = true, isDesugared = true) + + val callGenerator = IrCallGenerator(irStatementGenerator) + + defineContextVariables(irBlock, callGenerator) + + callGenerator.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), + onDemandExpressionValue { irExpression }) + + irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)) + + return irBlock + } + + override fun augmentedStore(operatorCall: ResolvedCall<*>, irRhs: IrExpression): IrExpression { + if (indexedGetCall == null) throw AssertionError("Indexed LValue has no 'get' call: ${ktArrayAccessExpression.text}") + if (indexedSetCall == null) throw AssertionError("Indexed LValue has no 'set' call: ${ktArrayAccessExpression.text}") + + val irBlock = IrBlockExpressionImpl(ktArrayAccessExpression.startOffset, ktArrayAccessExpression.endOffset, + indexedSetCall.resultingDescriptor.returnType, + hasResult = true, isDesugared = true) + + val callGenerator = IrCallGenerator(irStatementGenerator) + + defineContextVariables(irBlock, callGenerator) + + callGenerator.putValue(operatorCall.resultingDescriptor.valueParameters[0], onDemandExpressionValue { irRhs }) + + val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver + callGenerator.putValue(operatorCallReceiver!!, + onDemandExpressionValue { callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator) }) + + callGenerator.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), + onDemandExpressionValue { callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator) }) + + irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)) + + return irBlock + } + + private fun defineContextVariables(irBlock: IrBlockExpression, callGenerator: IrCallGenerator) { + irBlock.addStatement(callGenerator.createTemporary(ktArrayAccessExpression.arrayExpression!!, arrayValue.load())) + + for ((ktIndexExpression, irIndexValue) in indexValues) { + irBlock.addStatement(callGenerator.createTemporary(ktIndexExpression, irIndexValue.load())) + } + } } \ No newline at end of file diff --git a/compiler/ir/ir.tests/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt b/compiler/ir/ir.tests/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt index 184ea41917e..f49ebc3e1b6 100644 --- a/compiler/ir/ir.tests/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt +++ b/compiler/ir/ir.tests/tests/org/jetbrains/kotlin/ir/AbstractIrGeneratorTestCase.kt @@ -18,11 +18,7 @@ package org.jetbrains.kotlin.ir import org.jetbrains.kotlin.codegen.CodegenTestCase import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrModuleImpl import org.jetbrains.kotlin.psi2ir.Psi2IrTranslator -import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext -import org.jetbrains.kotlin.psi2ir.generators.IrModuleGenerator -import org.jetbrains.kotlin.psi2ir.transformations.collapseDesugaredBlocks import org.jetbrains.kotlin.resolve.AnalyzingUtils import org.jetbrains.kotlin.resolve.lazy.JvmResolveUtil import org.jetbrains.kotlin.test.ConfigurationKind diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt index 5a1d9248740..8c5f2794f8a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrElement.kt @@ -50,7 +50,8 @@ fun T.detach(): T { } fun IrElement.replaceWith(otherElement: IrElement) { - parent?.run { replaceChild(slot, otherElement.detach()) } ?: throw AssertionError("Can't replace a non-root element $this") + val parent = this.parent ?: throw AssertionError("Can't replace a non-root element $this") + parent.replaceChild(slot, otherElement.detach()) } fun IrElement.assertChild(child: IrElement) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCallExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCallExpression.kt index 38f524e74a8..8682d35b958 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCallExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrCallExpression.kt @@ -48,7 +48,9 @@ class IrCallExpressionImpl( argumentsByParameterIndex[index] override fun putArgument(index: Int, valueArgument: IrExpression?) { - if (index >= argumentsByParameterIndex.size) throw AssertionError("$this: No such argument slot: $index") + if (index >= argumentsByParameterIndex.size) { + throw AssertionError("$this: No such argument slot: $index") + } valueArgument?.assertDetached() argumentsByParameterIndex[index]?.detach() argumentsByParameterIndex[index] = valueArgument diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt index a386bb84c53..3602914db86 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt @@ -27,6 +27,8 @@ import org.jetbrains.kotlin.renderer.DescriptorRendererModifier import org.jetbrains.kotlin.renderer.OverrideRenderingPolicy import org.jetbrains.kotlin.types.KotlinType +fun IrElement.render() = accept(RenderIrElementVisitor(), null) + class RenderIrElementVisitor : IrElementVisitor { override fun visitElement(element: IrElement, data: Nothing?): String = "? ${element.javaClass.simpleName}" diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ValidateIrTree.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ValidateIrTree.kt new file mode 100644 index 00000000000..dc767c141a5 --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/ValidateIrTree.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.ir.util + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor + +inline fun IrElement.validateTree(crossinline onFailBody: (IrElement, IrElement) -> Unit) { + accept(object : IrTreeValidationVisitor() { + override fun onFail(irElement: IrElement, expectedParent: IrElement) { + onFailBody(irElement, expectedParent) + } + }, null) +} + +abstract class IrTreeValidationVisitor : IrElementVisitor { + override fun visitElement(element: IrElement, data: IrElement?) { + if (data != null && element.parent != data) { + onFail(element, data) + } + element.acceptChildren(this, element) + } + + protected abstract fun onFail(irElement: IrElement, expectedParent: IrElement) +} + diff --git a/compiler/testData/ir/irText/arrayAssignment.kt b/compiler/testData/ir/irText/arrayAssignment.kt new file mode 100644 index 00000000000..b454e892f2c --- /dev/null +++ b/compiler/testData/ir/irText/arrayAssignment.kt @@ -0,0 +1,5 @@ +// <<< arrayAssignment.txt +fun test() { + val x = intArrayOf(1, 2, 3) + x[1] = 0 +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/arrayAssignment.txt b/compiler/testData/ir/irText/arrayAssignment.txt new file mode 100644 index 00000000000..512be742c0c --- /dev/null +++ b/compiler/testData/ir/irText/arrayAssignment.txt @@ -0,0 +1,15 @@ +IrFile /arrayAssignment.kt + IrFunction public fun test(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + VAR val x: kotlin.IntArray + CALL .intArrayOf type=kotlin.IntArray operator= + elements: DUMMY vararg type=kotlin.Int + VAR val tmp0: kotlin.IntArray + GET_VAR x type=kotlin.IntArray + VAR val tmp1: kotlin.Int + LITERAL Int type=kotlin.Int value='1' + CALL .set type=kotlin.Unit operator=EQ + $this: GET_VAR tmp0 type=kotlin.IntArray + index: GET_VAR tmp1 type=kotlin.Int + value: LITERAL Int type=kotlin.Int value='0' diff --git a/compiler/testData/ir/irText/augmentedAssignment1.kt b/compiler/testData/ir/irText/augmentedAssignment1.kt index 27015b59f50..808546a6ad3 100644 --- a/compiler/testData/ir/irText/augmentedAssignment1.kt +++ b/compiler/testData/ir/irText/augmentedAssignment1.kt @@ -1,6 +1,19 @@ // <<< augmentedAssignment1.txt -fun test(): Int { +var p = 0 + +fun testVariable() { var x = 0 - x += 10 - return x -} \ No newline at end of file + x += 1 + x -= 2 + x *= 3 + x /= 4 + x %= 5 +} + +fun testProperty() { + p += 1 + p -= 2 + p *= 3 + p /= 4 + p %= 5 +} diff --git a/compiler/testData/ir/irText/augmentedAssignment1.txt b/compiler/testData/ir/irText/augmentedAssignment1.txt index 66990e40178..000cd9e3506 100644 --- a/compiler/testData/ir/irText/augmentedAssignment1.txt +++ b/compiler/testData/ir/irText/augmentedAssignment1.txt @@ -1,12 +1,52 @@ IrFile /augmentedAssignment1.kt - IrFunction public fun test(): kotlin.Int + IrProperty public var p: kotlin.Int getter=null setter=null IrExpressionBody - BLOCK type= hasResult=false isDesugared=false + LITERAL Int type=kotlin.Int value='0' + IrFunction public fun testVariable(): kotlin.Unit + IrExpressionBody + BLOCK type=kotlin.Int hasResult=true isDesugared=false VAR var x: kotlin.Int LITERAL Int type=kotlin.Int value='0' SET_VAR x type= CALL .plus type=kotlin.Int operator=PLUSEQ $this: GET_VAR x type=kotlin.Int - other: LITERAL Int type=kotlin.Int value='10' - RETURN type= - GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='1' + SET_VAR x type= + CALL .minus type=kotlin.Int operator=MINUSEQ + $this: GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='2' + SET_VAR x type= + CALL .times type=kotlin.Int operator=MULTEQ + $this: GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='3' + SET_VAR x type= + CALL .div type=kotlin.Int operator=DIVEQ + $this: GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='4' + SET_VAR x type= + CALL .mod type=kotlin.Int operator=PERCEQ + $this: GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='5' + IrFunction public fun testProperty(): kotlin.Unit + IrExpressionBody + BLOCK type=kotlin.Int hasResult=true isDesugared=false + SET_PROPERTY .ptype= + $value: CALL .plus type=kotlin.Int operator=PLUSEQ + $this: GET_PROPERTY .p type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='1' + SET_PROPERTY .ptype= + $value: CALL .minus type=kotlin.Int operator=MINUSEQ + $this: GET_PROPERTY .p type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='2' + SET_PROPERTY .ptype= + $value: CALL .times type=kotlin.Int operator=MULTEQ + $this: GET_PROPERTY .p type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='3' + SET_PROPERTY .ptype= + $value: CALL .div type=kotlin.Int operator=DIVEQ + $this: GET_PROPERTY .p type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='4' + SET_PROPERTY .ptype= + $value: CALL .mod type=kotlin.Int operator=PERCEQ + $this: GET_PROPERTY .p type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='5' diff --git a/compiler/testData/ir/irText/augmentedAssignment2.kt b/compiler/testData/ir/irText/augmentedAssignment2.kt index f2d9c377f3d..b9c20b8ed57 100644 --- a/compiler/testData/ir/irText/augmentedAssignment2.kt +++ b/compiler/testData/ir/irText/augmentedAssignment2.kt @@ -1,8 +1,28 @@ +// <<< augmentedAssignment2.txt + class A operator fun A.plusAssign(s: String) {} +operator fun A.minusAssign(s: String) {} +operator fun A.timesAssign(s: String) {} +operator fun A.divAssign(s: String) {} +operator fun A.modAssign(s: String) {} -fun test() { // <<< augmentedAssignment2.txt +val p = A() + +fun testVariable() { val a = A() - a += "" + a += "+=" + a -= "-=" + a *= "*=" + a /= "/=" + a %= "*=" +} + +fun testProperty() { + p += "+=" + p -= "-=" + p *= "*=" + p /= "/=" + p %= "*=" } diff --git a/compiler/testData/ir/irText/augmentedAssignment2.txt b/compiler/testData/ir/irText/augmentedAssignment2.txt index 422340b5fef..7768c051aae 100644 --- a/compiler/testData/ir/irText/augmentedAssignment2.txt +++ b/compiler/testData/ir/irText/augmentedAssignment2.txt @@ -1,8 +1,58 @@ -IrFunction public fun test(): kotlin.Unit - IrExpressionBody - BLOCK type= hasResult=false isDesugared=false - VAR val a: A - CALL . type=A operator= - CALL .plusAssign type=kotlin.Unit operator=PLUSEQ - $receiver: GET_VAR a type=A - s: ? IrStringConcatenationExpressionImpl type=kotlin.String +IrFile /augmentedAssignment2.kt + DUMMY A + IrFunction public operator fun A.plusAssign(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public operator fun A.minusAssign(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public operator fun A.timesAssign(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public operator fun A.divAssign(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrFunction public operator fun A.modAssign(/*0*/ s: kotlin.String): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + IrProperty public val p: A getter=null setter=null + IrExpressionBody + CALL . type=A operator= + IrFunction public fun testVariable(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + VAR val a: A + CALL . type=A operator= + CALL .plusAssign type=kotlin.Unit operator=PLUSEQ + $receiver: GET_VAR a type=A + s: LITERAL String type=kotlin.String value='+=' + CALL .minusAssign type=kotlin.Unit operator=MINUSEQ + $receiver: GET_VAR a type=A + s: LITERAL String type=kotlin.String value='-=' + CALL .timesAssign type=kotlin.Unit operator=MULTEQ + $receiver: GET_VAR a type=A + s: LITERAL String type=kotlin.String value='*=' + CALL .divAssign type=kotlin.Unit operator=DIVEQ + $receiver: GET_VAR a type=A + s: LITERAL String type=kotlin.String value='/=' + CALL .modAssign type=kotlin.Unit operator=PERCEQ + $receiver: GET_VAR a type=A + s: LITERAL String type=kotlin.String value='*=' + IrFunction public fun testProperty(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + CALL .plusAssign type=kotlin.Unit operator=PLUSEQ + $receiver: GET_PROPERTY .p type=A + s: LITERAL String type=kotlin.String value='+=' + CALL .minusAssign type=kotlin.Unit operator=MINUSEQ + $receiver: GET_PROPERTY .p type=A + s: LITERAL String type=kotlin.String value='-=' + CALL .timesAssign type=kotlin.Unit operator=MULTEQ + $receiver: GET_PROPERTY .p type=A + s: LITERAL String type=kotlin.String value='*=' + CALL .divAssign type=kotlin.Unit operator=DIVEQ + $receiver: GET_PROPERTY .p type=A + s: LITERAL String type=kotlin.String value='/=' + CALL .modAssign type=kotlin.Unit operator=PERCEQ + $receiver: GET_PROPERTY .p type=A + s: LITERAL String type=kotlin.String value='*=' diff --git a/compiler/testData/ir/irText/augmentedAssignmentArray.kt b/compiler/testData/ir/irText/augmentedAssignmentArray.kt new file mode 100644 index 00000000000..a779f4ee26c --- /dev/null +++ b/compiler/testData/ir/irText/augmentedAssignmentArray.kt @@ -0,0 +1,9 @@ +// <<< augmentedAssignmentArray.txt +fun foo(): IntArray = intArrayOf(1, 2, 3) + +fun testVariable() { + var x = foo() + x[0] += 1 + foo()[0] *= 2 +} + diff --git a/compiler/testData/ir/irText/augmentedAssignmentArray.txt b/compiler/testData/ir/irText/augmentedAssignmentArray.txt new file mode 100644 index 00000000000..2b16255befa --- /dev/null +++ b/compiler/testData/ir/irText/augmentedAssignmentArray.txt @@ -0,0 +1,36 @@ +IrFile /augmentedAssignmentArray.kt + IrFunction public fun foo(): kotlin.IntArray + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .intArrayOf type=kotlin.IntArray operator= + elements: DUMMY vararg type=kotlin.Int + IrFunction public fun testVariable(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false isDesugared=false + VAR var x: kotlin.IntArray + CALL .foo type=kotlin.IntArray operator= + VAR val tmp0: kotlin.IntArray + GET_VAR x type=kotlin.IntArray + VAR val tmp1: kotlin.Int + LITERAL Int type=kotlin.Int value='0' + CALL .set type=kotlin.Unit operator=PLUSEQ + $this: GET_VAR tmp0 type=kotlin.IntArray + index: GET_VAR tmp1 type=kotlin.Int + value: CALL .plus type=kotlin.Int operator=PLUSEQ + $this: CALL .get type=kotlin.Int operator=PLUSEQ + $this: GET_VAR tmp0 type=kotlin.IntArray + index: GET_VAR tmp1 type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='1' + VAR val tmp2: kotlin.IntArray + CALL .foo type=kotlin.IntArray operator= + VAR val tmp3: kotlin.Int + LITERAL Int type=kotlin.Int value='0' + CALL .set type=kotlin.Unit operator=MULTEQ + $this: GET_VAR tmp2 type=kotlin.IntArray + index: GET_VAR tmp3 type=kotlin.Int + value: CALL .times type=kotlin.Int operator=MULTEQ + $this: CALL .get type=kotlin.Int operator=MULTEQ + $this: GET_VAR tmp2 type=kotlin.IntArray + index: GET_VAR tmp3 type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='2' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index c63935daaef..5ec52aded88 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -35,6 +35,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("arrayAssignment.kt") + public void testArrayAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/arrayAssignment.kt"); + doTest(fileName); + } + @TestMetadata("assignments.kt") public void testAssignments() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/assignments.kt"); @@ -53,6 +59,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("augmentedAssignmentArray.kt") + public void testAugmentedAssignmentArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/augmentedAssignmentArray.kt"); + doTest(fileName); + } + @TestMetadata("boxOk.kt") public void testBoxOk() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/boxOk.kt");