From 570982609658b91c85e6467df5451cce807eccaa Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 19 Aug 2016 10:55:14 +0300 Subject: [PATCH] Refactoring. 'if' expressions (represented as 'when' without subject). --- .../org/jetbrains/kotlin/psi2ir/IrUtils.kt | 3 + .../kotlin/psi2ir/Psi2IrTranslator.kt | 8 +- .../{IrCallGenerator.kt => CallGenerator.kt} | 18 +- ...onGenerator.kt => DeclarationGenerator.kt} | 4 +- .../{IrGenerator.kt => Generator.kt} | 2 +- ...eneratorContext.kt => GeneratorContext.kt} | 2 +- ...rModuleGenerator.kt => ModuleGenerator.kt} | 4 +- ...rConventions.kt => OperatorConventions.kt} | 0 ...ator.kt => OperatorExpressionGenerator.kt} | 58 +++--- ...mentGenerator.kt => StatementGenerator.kt} | 52 ++++-- ...Factory.kt => TemporaryVariableFactory.kt} | 2 +- .../{IrIndexedLValue.kt => IndexedLValue.kt} | 24 +-- ...{IrPropertyLValue.kt => PropertyLValue.kt} | 6 +- ...zableValue.kt => RematerializableValue.kt} | 0 .../values/{IrValue.kt => Value.kt} | 0 ...{IrVariableLValue.kt => VariableLValue.kt} | 2 +- .../src/org/jetbrains/kotlin/ir/IrSlots.kt | 7 +- .../ir/expressions/IrBlockExpression.kt | 8 +- .../kotlin/ir/expressions/IrWhenExpression.kt | 171 ++++++++++++++++++ .../jetbrains/kotlin/ir/util/DumpIrTree.kt | 14 ++ .../kotlin/ir/util/RenderIrElement.kt | 11 +- .../kotlin/ir/visitors/IrElementVisitor.kt | 4 +- compiler/testData/ir/irText/elvis.txt | 10 +- compiler/testData/ir/irText/ifElseIf.kt | 4 + compiler/testData/ir/irText/ifElseIf.txt | 15 ++ compiler/testData/ir/irText/smartCasts.txt | 14 +- .../ir/irText/smartCastsWithDestructuring.txt | 4 +- .../kotlin/ir/IrTextTestCaseGenerated.java | 6 + 28 files changed, 362 insertions(+), 91 deletions(-) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrCallGenerator.kt => CallGenerator.kt} (95%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrDeclarationGenerator.kt => DeclarationGenerator.kt} (97%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrGenerator.kt => Generator.kt} (99%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrGeneratorContext.kt => GeneratorContext.kt} (97%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrModuleGenerator.kt => ModuleGenerator.kt} (92%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrOperatorConventions.kt => OperatorConventions.kt} (100%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrOperatorExpressionGenerator.kt => OperatorExpressionGenerator.kt} (82%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrStatementGenerator.kt => StatementGenerator.kt} (85%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/{IrTemporaryVariableFactory.kt => TemporaryVariableFactory.kt} (96%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/{IrIndexedLValue.kt => IndexedLValue.kt} (89%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/{IrPropertyLValue.kt => PropertyLValue.kt} (91%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/{IrRematerializableValue.kt => RematerializableValue.kt} (100%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/{IrValue.kt => Value.kt} (100%) rename compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/{IrVariableLValue.kt => VariableLValue.kt} (98%) create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhenExpression.kt create mode 100644 compiler/testData/ir/irText/ifElseIf.kt create mode 100644 compiler/testData/ir/irText/ifElseIf.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 ca48a990359..f28e4383e2a 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi2ir +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.types.KotlinType @@ -24,6 +25,8 @@ import org.jetbrains.kotlin.types.isNullabilityFlexible fun IrExpression.toExpectedType(expectedType: KotlinType?): IrExpression { if (expectedType == null) return this + if (KotlinBuiltIns.isUnit(expectedType)) return this // TODO expose coercion to Unit in IR? + val valueType = type ?: throw AssertionError("expectedType != null, valueType == null: $this") if (valueType.isNullabilityFlexible() && !expectedType.isMarkedNullable) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt index a99f948c832..06e311ba0e0 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/Psi2IrTranslator.kt @@ -20,8 +20,8 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrModule import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.generators.IrGeneratorContext -import org.jetbrains.kotlin.psi2ir.generators.IrModuleGenerator +import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext +import org.jetbrains.kotlin.psi2ir.generators.ModuleGenerator import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks import org.jetbrains.kotlin.resolve.BindingContext @@ -33,8 +33,8 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) { ) fun generateModule(moduleDescriptor: ModuleDescriptor, ktFiles: List, bindingContext: BindingContext): IrModule { - val irGeneratorContext = IrGeneratorContext(moduleDescriptor, bindingContext) - val irModule = IrModuleGenerator(irGeneratorContext).generateModule(ktFiles) + val irGeneratorContext = GeneratorContext(moduleDescriptor, bindingContext) + val irModule = ModuleGenerator(irGeneratorContext).generateModule(ktFiles) postprocess(irModule) return irModule } 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/CallGenerator.kt similarity index 95% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 20be721d5fa..942b8ed7574 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/CallGenerator.kt @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.ir.expressions.* 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.generators.values.IrVariableLValue +import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue import org.jetbrains.kotlin.psi2ir.generators.values.IrValue import org.jetbrains.kotlin.psi2ir.generators.values.createRematerializableValue import org.jetbrains.kotlin.psi2ir.toExpectedType @@ -32,12 +32,12 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.* import org.jetbrains.kotlin.types.KotlinType import java.util.* -class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenerator { - override val context: IrGeneratorContext get() = - irStatementGenerator.context +class CallGenerator(val statementGenerator: StatementGenerator) : IrGenerator { + override val context: GeneratorContext get() = + statementGenerator.context - private val temporaryVariableFactory: IrTemporaryVariableFactory get() = - irStatementGenerator.temporaryVariableFactory + private val temporaryVariableFactory: TemporaryVariableFactory get() = + statementGenerator.temporaryVariableFactory private val expressionValues = HashMap() private val receiverValues = HashMap() @@ -51,7 +51,7 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera } val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, nameHint) - putValue(ktExpression, IrVariableLValue(irTmpVar)) + putValue(ktExpression, VariableLValue(irTmpVar)) return irTmpVar } @@ -63,7 +63,7 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera } val irTmpVar = temporaryVariableFactory.createTemporaryVariable(irExpression, valueParameterDescriptor.name.asString()) - putValue(valueParameterDescriptor, IrVariableLValue(irTmpVar)) + putValue(valueParameterDescriptor, VariableLValue(irTmpVar)) return irTmpVar } @@ -249,6 +249,6 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera } private fun generateExpression(ktExpression: KtExpression): IrExpression = - expressionValues[ktExpression]?.load() ?: irStatementGenerator.generateExpression(ktExpression) + expressionValues[ktExpression]?.load() ?: statementGenerator.generateExpression(ktExpression) } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt similarity index 97% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt index ec70cfafcac..a4b312d4f5c 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrDeclarationGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/DeclarationGenerator.kt @@ -28,7 +28,7 @@ import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.singletonList -class IrDeclarationGenerator(override val context: IrGeneratorContext) : IrGenerator { +class DeclarationGenerator(override val context: GeneratorContext) : IrGenerator { fun generateAnnotationEntries(annotationEntries: List) { // TODO create IrAnnotation's for each KtAnnotationEntry } @@ -140,7 +140,7 @@ class IrDeclarationGenerator(override val context: IrGeneratorContext) : IrGener } private fun generateExpressionWithinContext(ktExpression: KtExpression, scopeOwner: DeclarationDescriptor): IrExpression = - IrStatementGenerator(context, scopeOwner, IrTemporaryVariableFactory(scopeOwner)) + StatementGenerator(context, scopeOwner, TemporaryVariableFactory(scopeOwner)) .generateExpression(ktExpression) .toExpectedType(getExpectedTypeForLastInferredCall(ktExpression)) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt similarity index 99% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt index 45a8b5b62fa..3377428f442 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt @@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice interface IrGenerator { - val context: IrGeneratorContext + val context: GeneratorContext } fun IrGenerator.get(slice: ReadOnlySlice, key: K): V? = diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGeneratorContext.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt similarity index 97% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGeneratorContext.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt index 0410e1647e9..ad1a12417d7 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrGeneratorContext.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/GeneratorContext.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi2ir.PsiSourceManager import org.jetbrains.kotlin.resolve.BindingContext -class IrGeneratorContext( +class GeneratorContext( val moduleDescriptor: ModuleDescriptor, val bindingContext: BindingContext ) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrModuleGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt similarity index 92% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrModuleGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt index 9b5bed71396..88189311927 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrModuleGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ModuleGenerator.kt @@ -20,9 +20,9 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.resolve.BindingContext -class IrModuleGenerator(override val context: IrGeneratorContext) : IrGenerator { +class ModuleGenerator(override val context: GeneratorContext) : IrGenerator { fun generateModule(ktFiles: List): IrModule { - val irDeclarationGenerator = IrDeclarationGenerator(context) + val irDeclarationGenerator = DeclarationGenerator(context) val irModule = IrModuleImpl(context.moduleDescriptor) for (ktFile in ktFiles) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorConventions.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt similarity index 100% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorConventions.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorConventions.kt 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/OperatorExpressionGenerator.kt similarity index 82% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/OperatorExpressionGenerator.kt index ec2d11a09bf..0e64d5fb0c5 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/OperatorExpressionGenerator.kt @@ -20,12 +20,10 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.expressions.* 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.psi.KtPrefixExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.generators.values.* @@ -36,8 +34,8 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.types.typeUtil.makeNullable -class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator { - override val context: IrGeneratorContext get() = irStatementGenerator.context +class OperatorExpressionGenerator(val statementGenerator: StatementGenerator): IrGenerator { + override val context: GeneratorContext get() = statementGenerator.context fun generatePrefixExpression(expression: KtPrefixExpression): IrExpression { val ktOperator = expression.operationReference.getReferencedNameElementType() @@ -50,6 +48,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat } } + fun generatePostfixExpression(expression: KtPostfixExpression): IrExpression { + TODO("not implemented") + } + fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression { val ktOperator = expression.operationReference.getReferencedNameElementType() if (ktOperator == KtTokens.IDENTIFIER) { @@ -77,8 +79,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat // TODO desugar '?:' to 'if'? val specialCallForElvis = getResolvedCall(expression)!! val returnType = specialCallForElvis.resultingDescriptor.returnType!! - val irArgument0 = irStatementGenerator.generateExpression(expression.left!!).toExpectedType(returnType.makeNullable()) - val irArgument1 = irStatementGenerator.generateExpression(expression.right!!).toExpectedType(returnType) + val irArgument0 = statementGenerator.generateExpression(expression.left!!).toExpectedType(returnType.makeNullable()) + val irArgument1 = statementGenerator.generateExpression(expression.right!!).toExpectedType(returnType) return IrBinaryOperatorExpressionImpl( expression.startOffset, expression.endOffset, returnType, IrOperator.ELVIS, null, irArgument0, irArgument1 @@ -86,8 +88,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat } private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrBinaryOperator): IrExpression { - val irArgument0 = irStatementGenerator.generateExpression(expression.left!!).toExpectedType(context.builtIns.booleanType) - val irArgument1 = irStatementGenerator.generateExpression(expression.right!!).toExpectedType(context.builtIns.booleanType) + val irArgument0 = statementGenerator.generateExpression(expression.left!!).toExpectedType(context.builtIns.booleanType) + val irArgument1 = statementGenerator.generateExpression(expression.right!!).toExpectedType(context.builtIns.booleanType) return IrBinaryOperatorExpressionImpl( expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator, getResolvedCall(expression)?.resultingDescriptor, irArgument0, irArgument1 @@ -97,7 +99,7 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat private fun generateInOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression { val operatorCall = getResolvedCall(expression)!! - val irOperatorCall = IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator) + val irOperatorCall = CallGenerator(statementGenerator).generateCall(expression, operatorCall, irOperator) return if (irOperator == IrOperator.IN) irOperatorCall @@ -107,8 +109,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat } private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrBinaryOperator): IrExpression { - val irArgument0 = irStatementGenerator.generateExpression(expression.left!!) - val irArgument1 = irStatementGenerator.generateExpression(expression.right!!) + val irArgument0 = statementGenerator.generateExpression(expression.left!!) + val irArgument1 = statementGenerator.generateExpression(expression.right!!) return IrBinaryOperatorExpressionImpl( expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator, null, irArgument0, irArgument1 @@ -119,7 +121,7 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat val relatedCall = getResolvedCall(expression)!! val relatedDescriptor = relatedCall.resultingDescriptor - val irCallGenerator = IrCallGenerator(irStatementGenerator) + val irCallGenerator = CallGenerator(statementGenerator) // NB special typing rules for equality operators: both arguments are nullable @@ -144,7 +146,7 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat val compareToCall = getResolvedCall(expression)!! val compareToDescriptor = compareToCall.resultingDescriptor - val irCallGenerator = IrCallGenerator(irStatementGenerator) + val irCallGenerator = CallGenerator(statementGenerator) return if (shouldKeepComparisonAsBinaryOperation(compareToDescriptor)) { val irArgument0 = irCallGenerator.generateReceiver(expression.left!!, compareToCall.dispatchReceiver, compareToDescriptor.dispatchReceiverParameter!!)!! @@ -172,7 +174,7 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, irOperator: IrOperator?): IrExpression { val operatorCall = getResolvedCall(expression)!! - return IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator) + return CallGenerator(statementGenerator).generateCall(expression, operatorCall, irOperator) } private fun generatePrefixIncrementDecrementOperator(expression: KtPrefixExpression, irOperator: IrOperator): IrExpression { @@ -184,10 +186,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat return irLValue.prefixAugmentedStore(operatorCall, irOperator) } - val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktBaseExpression, irLValue) } + val opCallGenerator = CallGenerator(statementGenerator).apply { putValue(ktBaseExpression, irLValue) } val irBlock = IrBlockExpressionImpl(expression.startOffset, expression.endOffset, irLValue.type, true, irOperator) val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) - val irTmp = irStatementGenerator.temporaryVariableFactory.createTemporaryVariable(irOpCall) + val irTmp = statementGenerator.temporaryVariableFactory.createTemporaryVariable(irOpCall) irBlock.addStatement(irTmp) irBlock.addStatement(irLValue.store(irTmp.load())) irBlock.addStatement(irTmp.load()) @@ -202,10 +204,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false if (isSimpleAssignment && irLValue is IrLValueWithAugmentedStore) { - return irLValue.augmentedStore(operatorCall, irOperator, irStatementGenerator.generateExpression(expression.right!!)) + return irLValue.augmentedStore(operatorCall, irOperator, statementGenerator.generateExpression(expression.right!!)) } - val opCallGenerator = IrCallGenerator(irStatementGenerator).apply { putValue(ktLeft, irLValue) } + val opCallGenerator = CallGenerator(statementGenerator).apply { putValue(ktLeft, irLValue) } val irOpCall = opCallGenerator.generateCall(expression, operatorCall, irOperator) return if (isSimpleAssignment) { @@ -222,20 +224,20 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat val ktLeft = expression.left!! val ktRight = expression.right!! val irLValue = generateLValue(ktLeft, IrOperator.EQ) - return irLValue.store(irStatementGenerator.generateExpression(ktRight)) + return irLValue.store(statementGenerator.generateExpression(ktRight)) } private fun generateLValue(ktLeft: KtExpression, irOperator: IrOperator?): IrLValue { if (ktLeft is KtArrayAccessExpression) { - val irArrayValue = irStatementGenerator.generateExpression(ktLeft.arrayExpression!!) - val indexExpressions = ktLeft.indexExpressions.map { it to irStatementGenerator.generateExpression(it) } + val irArrayValue = statementGenerator.generateExpression(ktLeft.arrayExpression!!) + val indexExpressions = ktLeft.indexExpressions.map { it to statementGenerator.generateExpression(it) } val indexedGetCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft) val indexedSetCall = get(BindingContext.INDEXED_LVALUE_SET, ktLeft) val type = indexedGetCall?.run { resultingDescriptor.returnType } ?: indexedSetCall?.run { resultingDescriptor.valueParameters.last().type } ?: throw AssertionError("Either 'get' or 'set' call should be present for an indexed LValue: ${ktLeft.text}") - return IrIndexedLValue(irStatementGenerator, ktLeft, irOperator, - irArrayValue, type, indexExpressions, indexedGetCall, indexedSetCall) + return IndexedLValue(statementGenerator, ktLeft, irOperator, + irArrayValue, type, indexExpressions, indexedGetCall, indexedSetCall) } val resolvedCall = getResolvedCall(ktLeft) ?: TODO("no resolved call for LHS") @@ -246,10 +248,10 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat if (descriptor.isDelegated) TODO("Delegated local variable") else - IrVariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator) + VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, irOperator) is PropertyDescriptor -> - IrCallGenerator(irStatementGenerator).run { - IrPropertyLValue( + CallGenerator(statementGenerator).run { + PropertyLValue( ktLeft, irOperator, descriptor, generateReceiver(ktLeft, resolvedCall.dispatchReceiver, descriptor.dispatchReceiverParameter), generateReceiver(ktLeft, resolvedCall.extensionReceiver, descriptor.extensionReceiverParameter), 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/StatementGenerator.kt similarity index 85% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrStatementGenerator.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index 7035f540790..abad9d8377e 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/StatementGenerator.kt @@ -26,7 +26,7 @@ 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.generators.values.IrVariableLValue +import org.jetbrains.kotlin.psi2ir.generators.values.VariableLValue import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContextUtils @@ -38,17 +38,17 @@ import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluat import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils -class IrStatementGenerator( - override val context: IrGeneratorContext, +class StatementGenerator( + override val context: GeneratorContext, val scopeOwner: DeclarationDescriptor, - val temporaryVariableFactory: IrTemporaryVariableFactory + val temporaryVariableFactory: TemporaryVariableFactory ) : KtVisitor(), IrGenerator { fun generateExpression(ktExpression: KtExpression): IrExpression = ktExpression.genExpr() private fun KtElement.genStmt(): IrStatement = - deparenthesize().accept(this@IrStatementGenerator, null) + deparenthesize().accept(this@StatementGenerator, null) private fun KtElement.genExpr(): IrExpression = genStmt().assertCast() @@ -77,8 +77,8 @@ class IrStatementGenerator( val irTmpInitializer = temporaryVariableFactory.createTemporaryVariable(ktInitializer.genExpr()) irBlock.addStatement(irTmpInitializer) - val irCallGenerator = IrCallGenerator(this) - irCallGenerator.putValue(ktInitializer, IrVariableLValue(irTmpInitializer)) + val irCallGenerator = CallGenerator(this) + irCallGenerator.putValue(ktInitializer, VariableLValue(irTmpInitializer)) for ((index, ktEntry) in multiDeclaration.entries.withIndex()) { val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) @@ -188,7 +188,7 @@ class IrStatementGenerator( companionObjectDescriptor) } is PropertyDescriptor -> { - IrCallGenerator(this).generateCall(expression, resolvedCall) + CallGenerator(this).generateCall(expression, resolvedCall) } is VariableDescriptor -> IrGetVariableExpressionImpl(expression.startOffset, expression.endOffset, descriptor) @@ -207,7 +207,7 @@ class IrStatementGenerator( TODO("VariableAsFunctionResolvedCall = variable call + invoke call") } - return IrCallGenerator(this).generateCall(expression, resolvedCall) + return CallGenerator(this).generateCall(expression, resolvedCall) } override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression, data: Nothing?): IrStatement = @@ -239,8 +239,38 @@ class IrStatementGenerator( } override fun visitBinaryExpression(expression: KtBinaryExpression, data: Nothing?): IrStatement = - IrOperatorExpressionGenerator(this).generateBinaryExpression(expression) + OperatorExpressionGenerator(this).generateBinaryExpression(expression) override fun visitPrefixExpression(expression: KtPrefixExpression, data: Nothing?): IrStatement = - IrOperatorExpressionGenerator(this).generatePrefixExpression(expression) + OperatorExpressionGenerator(this).generatePrefixExpression(expression) + + override fun visitPostfixExpression(expression: KtPostfixExpression, data: Nothing?): IrStatement = + OperatorExpressionGenerator(this).generatePostfixExpression(expression) + + override fun visitIfExpression(expression: KtIfExpression, data: Nothing?): IrStatement { + val resultType = getInferredTypeWithSmartcasts(expression) + val irWhen = IrWhenExpressionImpl(expression.startOffset, expression.endOffset, resultType) + + var ktBranch: KtIfExpression? = expression + branches@while (ktBranch != null) { + val irBranch = IrBranchImpl(ktBranch.startOffset, ktBranch.endOffset, + ktBranch.condition!!.genExpr().toExpectedType(context.builtIns.booleanType), + ktBranch.then!!.genExpr().toExpectedType(resultType)) + irWhen.addBranch(irBranch) + + val ktElse = ktBranch.`else` + + ktBranch = when (ktElse) { + is KtIfExpression -> ktElse + null -> + break@branches + else -> { + irWhen.elseExpression = ktElse.genExpr().toExpectedType(resultType) + break@branches + } + } + } + + return irWhen + } } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrTemporaryVariableFactory.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TemporaryVariableFactory.kt similarity index 96% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrTemporaryVariableFactory.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TemporaryVariableFactory.kt index 8d93777d6a9..0acf30bcd81 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrTemporaryVariableFactory.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TemporaryVariableFactory.kt @@ -26,7 +26,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType -class IrTemporaryVariableFactory(val scopeOwner: DeclarationDescriptor) { +class TemporaryVariableFactory(val scopeOwner: DeclarationDescriptor) { private var lastTemporaryIndex = 0 private fun nextTemporaryIndex() = lastTemporaryIndex++ diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrIndexedLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt similarity index 89% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrIndexedLValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt index 5b9f526427b..54ceab45324 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrIndexedLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IndexedLValue.kt @@ -22,13 +22,13 @@ 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.load -import org.jetbrains.kotlin.psi2ir.generators.IrCallGenerator -import org.jetbrains.kotlin.psi2ir.generators.IrStatementGenerator +import org.jetbrains.kotlin.psi2ir.generators.CallGenerator +import org.jetbrains.kotlin.psi2ir.generators.StatementGenerator import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.types.KotlinType -class IrIndexedLValue( - var irStatementGenerator: IrStatementGenerator, +class IndexedLValue( + var statementGenerator: StatementGenerator, val ktArrayAccessExpression: KtArrayAccessExpression, val irOperator: IrOperator?, val irArray: IrExpression, @@ -50,7 +50,7 @@ class IrIndexedLValue( } private fun generateGetOrSetCallAsDesugaredBlock(call: ResolvedCall<*>, irArgument: IrExpression? = null): IrExpression { - val callGenerator = IrCallGenerator(irStatementGenerator) + val callGenerator = CallGenerator(statementGenerator) setupCallGeneratorContext(callGenerator) if (irArgument != null) { @@ -64,19 +64,19 @@ class IrIndexedLValue( 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 callGenerator = IrCallGenerator(irStatementGenerator) + val callGenerator = CallGenerator(statementGenerator) val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, true, irOperator) val operatorCallReceiver = operatorCall.extensionReceiver ?: operatorCall.dispatchReceiver callGenerator.putValue(operatorCallReceiver!!, IrSingleExpressionValue(callGenerator.generateCall(ktArrayAccessExpression, indexedGetCall, irOperator))) - val irTmp = irStatementGenerator.temporaryVariableFactory.createTemporaryVariable( + val irTmp = statementGenerator.temporaryVariableFactory.createTemporaryVariable( callGenerator.generateCall(ktArrayAccessExpression, operatorCall, irOperator)) irBlock.addStatement(irTmp) callGenerator.putValue(indexedSetCall.resultingDescriptor.valueParameters.last(), - IrVariableLValue(irTmp)) + VariableLValue(irTmp)) irBlock.addStatement(callGenerator.generateCall(ktArrayAccessExpression, indexedSetCall, irOperator)) @@ -89,7 +89,7 @@ class IrIndexedLValue( 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 callGenerator = IrCallGenerator(irStatementGenerator) + val callGenerator = CallGenerator(statementGenerator) val irBlock = createDesugaredBlockWithTemporaries(indexedSetCall, callGenerator, false, irOperator) @@ -109,7 +109,7 @@ class IrIndexedLValue( private fun createDesugaredBlockWithTemporaries( call: ResolvedCall<*>, - callGenerator: IrCallGenerator, + callGenerator: CallGenerator, hasResult: Boolean, operator: IrOperator? ): IrBlockExpressionImpl { @@ -124,7 +124,7 @@ class IrIndexedLValue( hasResult, operator) } - private fun defineTemporaryVariables(irBlock: IrBlockExpression, callGenerator: IrCallGenerator) { + private fun defineTemporaryVariables(irBlock: IrBlockExpression, callGenerator: CallGenerator) { irBlock.addIfNotNull(callGenerator.introduceTemporary(ktArrayAccessExpression.arrayExpression!!, irArray, "array")) var index = 0 @@ -133,7 +133,7 @@ class IrIndexedLValue( } } - private fun setupCallGeneratorContext(callGenerator: IrCallGenerator) { + private fun setupCallGeneratorContext(callGenerator: CallGenerator) { callGenerator.putValue(ktArrayAccessExpression.arrayExpression!!, IrSingleExpressionValue(irArray)) for ((ktIndexExpression, irIndexValue) in indexValues) { diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrPropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt similarity index 91% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrPropertyLValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt index 6af6e62a832..509c82159a9 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrPropertyLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/PropertyLValue.kt @@ -24,7 +24,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.types.KotlinType -class IrPropertyLValue( +class PropertyLValue( val ktElement: KtElement, val irOperator: IrOperator?, val descriptor: PropertyDescriptor, @@ -37,8 +37,8 @@ class IrPropertyLValue( private fun IrPropertyAccessExpression.setReceivers() = apply { - dispatchReceiver = this@IrPropertyLValue.dispatchReceiver - extensionReceiver = this@IrPropertyLValue.extensionReceiver + dispatchReceiver = this@PropertyLValue.dispatchReceiver + extensionReceiver = this@PropertyLValue.extensionReceiver } override fun load(): IrExpression = diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrRematerializableValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt similarity index 100% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrRematerializableValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/RematerializableValue.kt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt similarity index 100% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/Value.kt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrVariableLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt similarity index 98% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrVariableLValue.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt index 9e74da9f69c..2e191604996 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/IrVariableLValue.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/values/VariableLValue.kt @@ -25,7 +25,7 @@ import org.jetbrains.kotlin.ir.expressions.IrSetVariableExpressionImpl import org.jetbrains.kotlin.psi2ir.toExpectedType import org.jetbrains.kotlin.types.KotlinType -class IrVariableLValue( +class VariableLValue( val startOffset: Int, val endOffset: Int, val descriptor: VariableDescriptor, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrSlots.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrSlots.kt index 0917f8d6acf..1807990acf4 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrSlots.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrSlots.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.ir -const val CHILD_DECLARATION_SLOT = 0 const val DETACHED_SLOT = Int.MIN_VALUE const val CHILD_EXPRESSION_SLOT = 0 const val ARGUMENT0_SLOT = 0 @@ -25,4 +24,8 @@ const val DISPATCH_RECEIVER_SLOT = -1 const val EXTENSION_RECEIVER_SLOT = -2 const val FUNCTION_BODY_SLOT = 0 const val MODULE_SLOT = 0 -const val INITIALIZER_SLOT = 0 \ No newline at end of file +const val INITIALIZER_SLOT = 0 +const val WHEN_SUBJECT_VARIABLE_SLOT = -1 +const val WHEN_ELSE_EXPRESSION_SLOT = -2 +const val BRANCH_CONDITION_SLOT = -1 +const val BRANCH_RESULT_SLOT = -2 \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlockExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlockExpression.kt index f04ae97d654..89e6541bf78 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlockExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlockExpression.kt @@ -16,10 +16,7 @@ package org.jetbrains.kotlin.ir.expressions -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.assertCast -import org.jetbrains.kotlin.ir.assertDetached +import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType import java.util.* @@ -56,8 +53,11 @@ class IrBlockExpressionImpl( statements.getOrNull(slot) override fun replaceChild(slot: Int, newChild: IrElement) { + newChild.assertDetached() if (0 <= slot && slot < statements.size) { + statements[slot].detach() statements[slot] = newChild.assertCast() + newChild.setTreeLocation(this, slot) } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhenExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhenExpression.kt new file mode 100644 index 00000000000..e481ef5123b --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrWhenExpression.kt @@ -0,0 +1,171 @@ +/* + * 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.expressions + +import org.jetbrains.kotlin.ir.* +import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.KotlinType +import java.util.* + +interface IrWhenExpression : IrExpression { + var subject: IrVariable? + val branches: List + var elseExpression: IrExpression? + + fun addBranch(branch: IrBranch) +} + +interface IrBranch : IrElement { + var condition: IrExpression + var result: IrExpression +} + +class IrWhenExpressionImpl( + startOffset: Int, + endOffset: Int, + type: KotlinType? +) : IrExpressionBase(startOffset, endOffset, type), IrWhenExpression { + constructor( + startOffset: Int, + endOffset: Int, + type: KotlinType?, + subject: IrVariable + ) : this(startOffset, endOffset, type) { + this.subject = subject + } + + override var subject: IrVariable? = null + set(value) { + value?.assertDetached() + subject?.detach() + field = value + field?.setTreeLocation(this, WHEN_SUBJECT_VARIABLE_SLOT) + } + + override val branches: MutableList = ArrayList() + + override var elseExpression: IrExpression? = null + set(value) { + value?.assertDetached() + subject?.detach() + field = value + field?.setTreeLocation(this, WHEN_ELSE_EXPRESSION_SLOT) + } + + override fun addBranch(branch: IrBranch) { + branch.assertDetached() + branch.setTreeLocation(this, branches.size) + branches.add(branch) + } + + override fun getChild(slot: Int): IrElement? = + when (slot) { + WHEN_SUBJECT_VARIABLE_SLOT -> subject + WHEN_ELSE_EXPRESSION_SLOT -> elseExpression + else -> branches.getOrNull(slot) + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + newChild.assertDetached() + when (slot) { + WHEN_SUBJECT_VARIABLE_SLOT -> + subject = newChild.assertCast() + WHEN_ELSE_EXPRESSION_SLOT -> + elseExpression = newChild.assertCast() + in branches.indices -> { + branches[slot].detach() + branches[slot] = newChild.assertCast() + newChild.setTreeLocation(this, slot) + } + } + } + + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitWhenExpression(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + subject?.accept(visitor, data) + branches.forEach { it.accept(visitor, data) } + elseExpression?.accept(visitor, data) + } + + companion object { + fun ifThen(startOffset: Int, endOffset: Int, type: KotlinType?, condition: IrExpression, thenExpression: IrExpression) = + IrWhenExpressionImpl(startOffset, endOffset, type).apply { + addBranch(IrBranchImpl(startOffset, endOffset, condition, thenExpression)) + } + + fun ifThenElse(startOffset: Int, endOffset: Int, type: KotlinType?, + condition: IrExpression, thenExpression: IrExpression, elseExpression: IrExpression) = + IrWhenExpressionImpl(startOffset, endOffset, type).apply { + addBranch(IrBranchImpl(startOffset, endOffset, condition, thenExpression)) + this.elseExpression = elseExpression + } + } +} + +class IrBranchImpl(startOffset: Int, endOffset: Int) : IrElementBase(startOffset, endOffset), IrBranch { + constructor(startOffset: Int, endOffset: Int, condition: IrExpression, result: IrExpression) : this(startOffset, endOffset) { + this.condition = condition + this.result = result + } + + private var conditionImpl: IrExpression? = null + override var condition: IrExpression + get() = conditionImpl!! + set(value) { + value.assertDetached() + conditionImpl?.detach() + conditionImpl = value + value.setTreeLocation(this, BRANCH_CONDITION_SLOT) + } + + private var resultImpl: IrExpression? = null + override var result: IrExpression + get() = resultImpl!! + set(value) { + value.assertDetached() + resultImpl?.detach() + resultImpl = value + value.setTreeLocation(this, BRANCH_RESULT_SLOT) + } + + override fun getChild(slot: Int): IrElement? = + when (slot) { + BRANCH_CONDITION_SLOT -> condition + BRANCH_RESULT_SLOT -> result + else -> null + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + when (slot) { + BRANCH_CONDITION_SLOT -> + condition = newChild.assertCast() + BRANCH_RESULT_SLOT -> + result = newChild.assertCast() + } + } + + override fun accept(visitor: IrElementVisitor, data: D): R = + visitor.visitBranch(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + conditionImpl?.accept(visitor, data) + resultImpl?.accept(visitor, data) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt index d9a6154ff4c..091e26ebe54 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.expressions.IrCallExpression import org.jetbrains.kotlin.ir.expressions.IrGetPropertyExpression import org.jetbrains.kotlin.ir.expressions.IrSetPropertyExpression +import org.jetbrains.kotlin.ir.expressions.IrWhenExpression import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.utils.Printer @@ -70,6 +71,19 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor { } } + override fun visitWhenExpression(expression: IrWhenExpression, data: String) { + expression.dumpLabeledElementWith(data) { + expression.subject?.let { subject -> + subject.accept(this, "\$subject:") + } + for (branch in expression.branches) { + branch.condition.accept(this, "if") + branch.result.accept(this, "then") + } + expression.elseExpression?.accept(this, "else") + } + } + private inline fun IrElement.dumpLabeledElementWith(label: String, body: () -> Unit) { printer.println(accept(elementRenderer, null).withLabel(label)) indented(body) 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 ac4284f218c..340254556b3 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 @@ -112,9 +112,14 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: Nothing?): String = "STRING_CONCATENATION type=${expression.renderType()}" - override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String { - return "TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}" - } + override fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: Nothing?): String = + "TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}" + + override fun visitWhenExpression(expression: IrWhenExpression, data: Nothing?): String = + "WHEN subject=${expression.subject?.descriptor?.name} type=${expression.type.render()}" + + override fun visitBranch(branch: IrBranch, data: Nothing?): String = + "BRANCH" override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String = "DUMMY ${declaration.descriptor.name}" diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt index fb60a01d0f6..b3e1d2cb4b6 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt @@ -63,8 +63,10 @@ interface IrElementVisitor { fun visitBinaryOperator(expression: IrBinaryOperatorExpression, data: D) = visitOperatorExpression(expression, data) fun visitTypeOperatorExpression(expression: IrTypeOperatorExpression, data: D) = visitExpression(expression, data) + fun visitWhenExpression(expression: IrWhenExpression, data: D) = visitExpression(expression, data) + fun visitBranch(branch: IrBranch, data: D): R = visitElement(branch, data) + // NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data) fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data) - } diff --git a/compiler/testData/ir/irText/elvis.txt b/compiler/testData/ir/irText/elvis.txt index 8b8f245c976..feaa34a9fc1 100644 --- a/compiler/testData/ir/irText/elvis.txt +++ b/compiler/testData/ir/irText/elvis.txt @@ -16,8 +16,14 @@ IrFile /elvis.kt IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.String IrExpressionBody BLOCK type= hasResult=false operator=null - DUMMY KtIfExpression type=kotlin.Unit - DUMMY KtIfExpression type=kotlin.Unit + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= + LITERAL String type=kotlin.String value='' + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= + LITERAL String type=kotlin.String value='' RETURN type= BINARY_OP operator=ELVIS type=kotlin.String related=null TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String? diff --git a/compiler/testData/ir/irText/ifElseIf.kt b/compiler/testData/ir/irText/ifElseIf.kt new file mode 100644 index 00000000000..855e3698846 --- /dev/null +++ b/compiler/testData/ir/irText/ifElseIf.kt @@ -0,0 +1,4 @@ +fun test(i: Int) = + if (i > 0) 1 + else if (i < 0) -1 + else 0 \ No newline at end of file diff --git a/compiler/testData/ir/irText/ifElseIf.txt b/compiler/testData/ir/irText/ifElseIf.txt new file mode 100644 index 00000000000..6a7d93dd803 --- /dev/null +++ b/compiler/testData/ir/irText/ifElseIf.txt @@ -0,0 +1,15 @@ +IrFile /ifElseIf.kt + IrFunction public fun test(/*0*/ i: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + WHEN subject=null type=kotlin.Int + if: BINARY_OP operator=GT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int + GET_VAR i type=kotlin.Int operator=null + LITERAL Int type=kotlin.Int value='0' + then: LITERAL Int type=kotlin.Int value='1' + if: BINARY_OP operator=LT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int + GET_VAR i type=kotlin.Int operator=null + LITERAL Int type=kotlin.Int value='0' + then: DUMMY MINUS type=kotlin.Int + else: LITERAL Int type=kotlin.Int value='0' diff --git a/compiler/testData/ir/irText/smartCasts.txt b/compiler/testData/ir/irText/smartCasts.txt index e576d3696eb..4624aa63961 100644 --- a/compiler/testData/ir/irText/smartCasts.txt +++ b/compiler/testData/ir/irText/smartCasts.txt @@ -18,7 +18,9 @@ IrFile /smartCasts.kt IrFunction public fun test1(/*0*/ x: kotlin.Any): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null - DUMMY KtIfExpression type=kotlin.Unit + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= CALL .println type=kotlin.Unit operator=null message: GET_PROPERTY .length type=kotlin.Int operator=null $this: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String @@ -37,7 +39,10 @@ IrFile /smartCasts.kt IrFunction public fun test2(/*0*/ x: kotlin.Any): kotlin.String IrExpressionBody BLOCK type= hasResult=false operator=null - DUMMY KtIfExpression type=kotlin.Unit + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= + LITERAL String type=kotlin.String value='' RETURN type= CALL .overloaded type=kotlin.String operator=null s: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String @@ -45,7 +50,10 @@ IrFile /smartCasts.kt IrFunction public fun test3(/*0*/ x: kotlin.Any): kotlin.String IrExpressionBody BLOCK type= hasResult=false operator=null - DUMMY KtIfExpression type=kotlin.Unit + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= + LITERAL String type=kotlin.String value='' RETURN type= TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String GET_VAR x type=kotlin.Any operator=null diff --git a/compiler/testData/ir/irText/smartCastsWithDestructuring.txt b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt index 65f91080dc8..d5010411248 100644 --- a/compiler/testData/ir/irText/smartCastsWithDestructuring.txt +++ b/compiler/testData/ir/irText/smartCastsWithDestructuring.txt @@ -14,7 +14,9 @@ IrFile /smartCastsWithDestructuring.kt IrFunction public fun test(/*0*/ x: I1): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false operator=null - DUMMY KtIfExpression type=kotlin.Unit + WHEN subject=null type=kotlin.Unit + if: DUMMY KtIsExpression type=kotlin.Boolean + then: RETURN type= BLOCK type= hasResult=false operator=SYNTHETIC_BLOCK VAR val tmp0: I1 GET_VAR x type=I1 operator=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 26bad8da276..9f5e45772cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -137,6 +137,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("ifElseIf.kt") + public void testIfElseIf() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/ifElseIf.kt"); + doTest(fileName); + } + @TestMetadata("implicitCastOnPlatformType.kt") public void testImplicitCastOnPlatformType() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/implicitCastOnPlatformType.kt");