From f903fb8ad355139793efb882730826649ad9e95e Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 15 Aug 2016 13:03:35 +0300 Subject: [PATCH] IR: destructuring declarations in regular statements --- .../kotlin/psi2ir/IrCallGenerator.kt | 45 ++++++--- .../kotlin/psi2ir/IrDeclarationFactory.kt | 21 +++-- .../kotlin/psi2ir/IrStatementGenerator.kt | 37 ++++++-- .../kotlin/ir/declarations/IrVariable.kt | 10 ++ .../kotlin/ir/expressions/IrOperator.kt | 92 +++++++++---------- .../testData/ir/irText/destructuring1.txt | 12 ++- 6 files changed, 142 insertions(+), 75 deletions(-) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrCallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrCallGenerator.kt index fbb0c3ccf71..f89fc54546a 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrCallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrCallGenerator.kt @@ -17,7 +17,6 @@ package org.jetbrains.kotlin.psi2ir import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.assertCast import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -26,30 +25,52 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.scopes.receivers.* import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.newHashMapWithExpectedSize import java.util.* -class IrCallGenerator( - override val context: IrGeneratorContext, - val irStatementGenerator: IrStatementGenerator -) : IrGenerator { +class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenerator { + override val context: IrGeneratorContext get() = irStatementGenerator.context + + private val temporaries = newHashMapWithExpectedSize(3) + + fun putTemporary(ktExpression: KtExpression, temporaryVariableDescriptor: VariableDescriptor) { + temporaries[ktExpression] = temporaryVariableDescriptor + } + + private fun generateExpressionOrTemporary(ktExpression: KtExpression): IrExpression { + val temporary = temporaries[ktExpression] + return if (temporary != null) + IrGetVariableExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, getType(ktExpression), temporary) + else + irStatementGenerator.generateExpression(ktExpression) + } + fun generateCall( ktExpression: KtExpression, resolvedCall: ResolvedCall, operator: IrOperator? = null, superQualifier: ClassDescriptor? = null + ) = generateCall(ktExpression, getTypeOrFail(ktExpression), resolvedCall, operator, superQualifier) + + fun generateCall( + ktExpression: KtExpression, + resultType: KotlinType?, + resolvedCall: ResolvedCall, + operator: IrOperator? = null, + superQualifier: ClassDescriptor? = null ): IrExpression { val descriptor = resolvedCall.resultingDescriptor return when (descriptor) { is PropertyDescriptor -> IrGetPropertyExpressionImpl( - ktExpression.startOffset, ktExpression.endOffset, getTypeOrFail(ktExpression), + ktExpression.startOffset, ktExpression.endOffset, resultType, resolvedCall.call.isSafeCall(), descriptor ).apply { dispatchReceiver = generateReceiver(ktExpression, resolvedCall.dispatchReceiver) extensionReceiver = generateReceiver(ktExpression, resolvedCall.extensionReceiver) } is FunctionDescriptor -> - generateFunctionCall(descriptor, ktExpression, operator, resolvedCall, superQualifier) + generateFunctionCall(descriptor, ktExpression, resultType, operator, resolvedCall, superQualifier) else -> TODO("Unexpected callable descriptor: $descriptor ${descriptor.javaClass.simpleName}") } @@ -72,11 +93,11 @@ class IrCallGenerator( private fun generateFunctionCall( descriptor: FunctionDescriptor, ktExpression: KtExpression, + resultType: KotlinType?, operator: IrOperator?, resolvedCall: ResolvedCall, superQualifier: ClassDescriptor? ): IrExpression { - val resultType = getTypeOrFail(ktExpression) val irCall = IrCallExpressionImpl( ktExpression.startOffset, ktExpression.endOffset, resultType, descriptor, resolvedCall.call.isSafeCall(), operator, superQualifier @@ -103,8 +124,10 @@ class IrCallGenerator( irCall: IrCallExpression, ktExpression: KtExpression, resolvedCall: ResolvedCall, - resultType: KotlinType + resultType: KotlinType? ): IrExpression { + // TODO use IrLetExpression? + val valueArgumentsInEvaluationOrder = resolvedCall.valueArguments.values val irBlock = IrBlockExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, resultType, @@ -142,7 +165,7 @@ class IrCallGenerator( IrThisExpressionImpl(receiverExpression.startOffset, receiverExpression.endOffset, receiver.type, receiver.classDescriptor) } ?: TODO("Non-implicit ThisClassReceiver should be an expression receiver") is ExpressionReceiver -> - irStatementGenerator.generateStatement(receiver.expression).assertCast() + generateExpressionOrTemporary(receiver.expression) is ClassValueReceiver -> IrGetObjectValueExpressionImpl(receiver.expression.startOffset, receiver.expression.endOffset, receiver.type, receiver.classQualifier.descriptor) @@ -160,7 +183,7 @@ class IrCallGenerator( is DefaultValueArgument -> return null is ExpressionValueArgument -> - return irStatementGenerator.generateStatement(valueArgument.valueArgument!!.getArgumentExpression()!!).assertCast() + return generateExpressionOrTemporary(valueArgument.valueArgument!!.getArgumentExpression()!!) is VarargValueArgument -> TODO("vararg") else -> diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrDeclarationFactory.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrDeclarationFactory.kt index 06e21e51895..d8dde51b1f3 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrDeclarationFactory.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrDeclarationFactory.kt @@ -71,21 +71,22 @@ class IrLocalDeclarationsFactory(val scopeOwner: DeclarationDescriptor) : IrDecl IrTemporaryVariableDescriptorImpl(scopeOwner, Name.identifier("tmp${nextTemporaryIndex()}"), type) fun createTemporaryVariable(ktElement: KtElement, type: KotlinType): IrVariable = - IrVariableImpl(ktElement.startOffset, ktElement.endOffset, - IrDeclarationOriginKind.IR_TEMPORARY_VARIABLE, + IrVariableImpl(ktElement.startOffset, ktElement.endOffset, IrDeclarationOriginKind.IR_TEMPORARY_VARIABLE, createDescriptorForTemporaryVariable(type)) fun createTemporaryVariable(irExpression: IrExpression): IrVariable = - IrVariableImpl(irExpression.startOffset, irExpression.endOffset, - IrDeclarationOriginKind.IR_TEMPORARY_VARIABLE, + IrVariableImpl(irExpression.startOffset, irExpression.endOffset, IrDeclarationOriginKind.IR_TEMPORARY_VARIABLE, createDescriptorForTemporaryVariable( - irExpression.type - ?: throw AssertionError("No type for $irExpression") - ) - ).apply { initializer = irExpression } + irExpression.type + ?: throw AssertionError("No type for $irExpression") + ), + irExpression) fun createLocalVariable(ktElement: KtElement, descriptor: VariableDescriptor): IrVariable = - IrVariableImpl(ktElement.startOffset, ktElement.endOffset, - IrDeclarationOriginKind.DEFINED, + IrVariableImpl(ktElement.startOffset, ktElement.endOffset, IrDeclarationOriginKind.DEFINED, descriptor) + + fun createLocalVariable(ktElement: KtElement, descriptor: VariableDescriptor, initializer: IrExpression): IrVariable = + IrVariableImpl(ktElement.startOffset, ktElement.endOffset, IrDeclarationOriginKind.DEFINED, + descriptor, initializer) } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrStatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrStatementGenerator.kt index 6191f8b81b8..94e15bf4390 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrStatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/IrStatementGenerator.kt @@ -37,14 +37,14 @@ class IrStatementGenerator( override val context: IrGeneratorContext, val declarationFactory: IrLocalDeclarationsFactory ) : KtVisitor(), IrGenerator { - private val irCallGenerator = IrCallGenerator(context, this) fun generateStatement(ktExpression: KtExpression) = ktExpression.generate() + fun generateExpression(ktExpression: KtExpression) = ktExpression.generateExpression() private fun KtElement.generate(): IrStatement = deparenthesize() - .accept(this@IrStatementGenerator, null) - .applySmartCastIfNeeded(this) + .accept(this@IrStatementGenerator, null) + .applySmartCastIfNeeded(this) private fun KtElement.generateExpression(): IrExpression = generate().assertCast() @@ -56,7 +56,7 @@ class IrStatementGenerator( return IrTypeOperatorExpressionImpl( ktElement.startOffset, ktElement.endOffset, smartCastType, IrTypeOperator.SMART_AS, this@applySmartCastIfNeeded, smartCastType - ) + ) } } return this @@ -76,6 +76,29 @@ class IrStatementGenerator( return irLocalVariable } + override fun visitDestructuringDeclaration(multiDeclaration: KtDestructuringDeclaration, data: Nothing?): IrStatement { + // TODO use some special form that introduces multiple declarations into surrounding scope? + + val irBlock = IrBlockExpressionImpl(multiDeclaration.startOffset, multiDeclaration.endOffset, getType(multiDeclaration), + hasResult = false, isDesugared = true) + val ktInitializer = multiDeclaration.initializer!! + val irInitializer = declarationFactory.createTemporaryVariable(ktInitializer.generateExpression()) + irBlock.addStatement(irInitializer) + + val irCallGenerator = IrCallGenerator(this).apply { putTemporary(ktInitializer, irInitializer.descriptor) } + + for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { + val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) + val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry) + val irComponentCall = irCallGenerator.generateCall(ktEntry, componentVariable.type, componentResolvedCall, + IrOperator.COMPONENT_N.withIndex(index + 1)) + val irComponentVar = declarationFactory.createLocalVariable(ktEntry, componentVariable, irComponentCall) + irBlock.addStatement(irComponentVar) + } + + return irBlock + } + override fun visitBlockExpression(expression: KtBlockExpression, data: Nothing?): IrStatement { val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), hasResult = isUsedAsExpression(expression), isDesugared = false) @@ -133,9 +156,9 @@ class IrStatementGenerator( IrGetEnumValueExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), descriptor) else IrGetObjectValueExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), - descriptor.companionObjectDescriptor ?: error("Class value without companion object: $descriptor")) + descriptor.companionObjectDescriptor ?: error("Class value without companion object: $descriptor")) is PropertyDescriptor -> { - irCallGenerator.generateCall(expression, resolvedCall) + IrCallGenerator(this).generateCall(expression, resolvedCall) } is VariableDescriptor -> IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, getType(expression), descriptor) @@ -153,7 +176,7 @@ class IrStatementGenerator( TODO("VariableAsFunctionResolvedCall = variable call + invoke call") } - return irCallGenerator.generateCall(expression, resolvedCall) + return IrCallGenerator(this).generateCall(expression, resolvedCall) } override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression, data: Nothing?): IrStatement = diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt index 51e71e931d1..3c584517d1d 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrVariable.kt @@ -36,6 +36,16 @@ class IrVariableImpl( originKind: IrDeclarationOriginKind, override val descriptor: VariableDescriptor ) : IrDeclarationBase(startOffset, endOffset, originKind), IrVariable { + constructor( + startOffset: Int, + endOffset: Int, + originKind: IrDeclarationOriginKind, + descriptor: VariableDescriptor, + initializer: IrExpression + ) : this(startOffset, endOffset, originKind, descriptor) { + this.initializer = initializer + } + override var initializer: IrExpression? = null set(value) { value?.assertDetached() diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt index 6eba894c757..24a7e8d9767 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt @@ -16,54 +16,54 @@ package org.jetbrains.kotlin.ir.expressions -enum class IrOperator { - INVOKE, - PREFIX_INCR, PREFIX_DECR, POSTFIX_INCR, POSTFIX_DECR, - UMINUS, - EXCL, - EXCLEXCL, - ELVIS, - LT, GT, LTEQ, GTEQ, - EQEQ, EQEQEQ, EXCLEQ, EXCLEQEQ, - IN, NOT_IN, - ANDAND, OROR, - RANGE, - PLUS, MINUS, MUL, DIV, MOD, - EQ, - PLUSEQ, MINUSEQ, MULEQ, DIVEQ, MODEQ, - DESTRUCTURING; -} +sealed class IrOperator { + object INVOKE : IrOperator() + object PREFIX_INCR : IrOperator() + object PREFIX_DECR : IrOperator() + object POSTFIX_INCR : IrOperator() + object POSTFIX_DECR : IrOperator() + object UMINUS : IrOperator() + object EXCL : IrOperator() + object EXCLEXCL : IrOperator() + object ELVIS : IrOperator() + + object LT : IrOperator() + object GT : IrOperator() + object LTEQ : IrOperator() + object GTEQ : IrOperator() + + object EQEQ : IrOperator() + object EQEQEQ : IrOperator() + object EXCLEQ : IrOperator() + object EXCLEQEQ : IrOperator() + object IN : IrOperator() + object NOT_IN : IrOperator() + object ANDAND : IrOperator() + object OROR : IrOperator() + object RANGE : IrOperator() -private val CAO_START = IrOperator.PLUSEQ.ordinal -private val CAO_END = IrOperator.MODEQ.ordinal -private val DUAL_START = IrOperator.PLUS.ordinal -private val DUAL_END = IrOperator.MOD.ordinal -private val INCR_DECR_START = IrOperator.PREFIX_INCR.ordinal -private val INCR_DECR_END = IrOperator.POSTFIX_DECR.ordinal -private val RELATIONAL_START = IrOperator.LT.ordinal -private val RELATIONAL_END = IrOperator.GTEQ.ordinal + object PLUS : IrOperator() + object MINUS : IrOperator() + object MUL : IrOperator() + object DIV : IrOperator() + object MOD : IrOperator() -fun IrOperator.isIncrementOrDecrement(): Boolean = - this.ordinal in INCR_DECR_START..INCR_DECR_END + object EQ : IrOperator() + object PLUSEQ : IrOperator() + object MINUSEQ : IrOperator() + object MULEQ : IrOperator() + object DIVEQ : IrOperator() + object MODEQ : IrOperator() -fun IrOperator.isCompoundAssignment(): Boolean = - this.ordinal in CAO_START .. CAO_END + data class COMPONENT_N private constructor(val index: Int) : IrOperator() { + companion object { + private val precreatedComponents = Array(32, ::COMPONENT_N) -fun IrOperator.isAssignmentOrCompoundAssignment(): Boolean = - this == IrOperator.EQ || isCompoundAssignment() - -fun IrOperator.hasCompoundAssignmentDual(): Boolean = - this.ordinal in DUAL_START .. DUAL_END - -fun IrOperator.toDualOperator(): IrOperator = - when { - isCompoundAssignment() -> - IrOperator.values()[this.ordinal - CAO_START + DUAL_START] - hasCompoundAssignmentDual() -> - IrOperator.values()[this.ordinal - DUAL_START + CAO_START] - else -> - throw UnsupportedOperationException("Operator $this is not a compound assignment") + fun withIndex(index: Int) = + if (index < precreatedComponents.size) + precreatedComponents[index] + else + COMPONENT_N(index) } - -fun IrOperator.isRelational(): Boolean = - this.ordinal in RELATIONAL_START .. RELATIONAL_END \ No newline at end of file + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/destructuring1.txt b/compiler/testData/ir/irText/destructuring1.txt index 55fdf970106..a490a107101 100644 --- a/compiler/testData/ir/irText/destructuring1.txt +++ b/compiler/testData/ir/irText/destructuring1.txt @@ -1,4 +1,14 @@ IrFunction public fun B.test(): kotlin.Unit IrExpressionBody BLOCK type=kotlin.Unit hasResult=false isDesugared=false - DUMMY KtDestructuringDeclaration type=kotlin.Unit + BLOCK type=kotlin.Unit hasResult=false isDesugared=true + VAR val tmp0: A + GET_VAR A type=A + VAR val x: kotlin.Int + CALL .component1 type=kotlin.Int operator=COMPONENT_N(index=1) + $this: $RECEIVER of: test type=B + $receiver: GET_VAR tmp0 type=A + VAR val y: kotlin.Int + CALL .component2 type=kotlin.Int operator=COMPONENT_N(index=2) + $this: $RECEIVER of: test type=B + $receiver: GET_VAR tmp0 type=A