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 37f65bfe7b6..f2deff25f93 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 @@ -22,12 +22,14 @@ 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.transformations.collapseDesugaredBlocks +import org.jetbrains.kotlin.psi2ir.transformations.foldStringConcatenation +import org.jetbrains.kotlin.psi2ir.transformations.inlineDesugaredBlocks import org.jetbrains.kotlin.resolve.BindingContext class Psi2IrTranslator(val configuration: Configuration = Configuration()) { class Configuration( - val shouldCollapseDesugaredBlocks: Boolean = true + val shouldInlineDesugaredBlocks: Boolean = true, + val shouldFoldStringConcatenation: Boolean = true ) fun generateModule(moduleDescriptor: ModuleDescriptor, ktFiles: List, bindingContext: BindingContext): IrModule { @@ -38,6 +40,7 @@ class Psi2IrTranslator(val configuration: Configuration = Configuration()) { } private fun postprocess(irElement: IrElement) { - if (configuration.shouldCollapseDesugaredBlocks) collapseDesugaredBlocks(irElement) + if (configuration.shouldInlineDesugaredBlocks) inlineDesugaredBlocks(irElement) + if (configuration.shouldFoldStringConcatenation) foldStringConcatenation(irElement) } } 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 6bb8eca155d..3425f682fbe 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 @@ -40,7 +40,9 @@ val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf( KtTokens.PLUS to IrOperator.PLUS, KtTokens.MINUS to IrOperator.MINUS, KtTokens.MUL to IrOperator.MUL, - KtTokens.DIV to IrOperator.DIV + KtTokens.DIV to IrOperator.DIV, + KtTokens.PERC to IrOperator.PERC, + KtTokens.RANGE to IrOperator.RANGE ) val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS @@ -55,10 +57,17 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat return when (ktOperator) { KtTokens.EQ -> generateAssignment(expression) in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator) + in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator) else -> createDummyExpression(expression, ktOperator.toString()) } } + private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression { + val irOperator = getIrOperator(ktOperator) + val operatorCall = getResolvedCall(expression)!! + return IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator) + } + private fun generateAugmentedAssignment(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression { val ktLeft = expression.left!! diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/FoldStringConcatenation.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/FoldStringConcatenation.kt new file mode 100644 index 00000000000..d1e0c81c130 --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/FoldStringConcatenation.kt @@ -0,0 +1,78 @@ +/* + * 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.psi2ir.transformations + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.detach +import org.jetbrains.kotlin.ir.expressions.IrCallExpression +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStringConcatenationExpression +import org.jetbrains.kotlin.ir.expressions.IrStringConcatenationExpressionImpl +import org.jetbrains.kotlin.ir.replaceWith +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.util.OperatorNameConventions +import java.util.* + +fun foldStringConcatenation(element: IrElement) { + element.accept(FoldStringConcatenation(), null) +} + +class FoldStringConcatenation : IrElementVisitor { + override fun visitElement(element: IrElement, data: Nothing?) { + element.acceptChildren(this, data) + } + + override fun visitCallExpression(expression: IrCallExpression, data: Nothing?) { + if (!isStringPlus(expression.descriptor)) { + visitElement(expression, data) + return + } + + val arguments = ArrayList() + collectStringConcatenationArguments(expression, arguments) + val irStringConcatenation = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, expression.type) + arguments.forEach { irStringConcatenation.addArgument(it) } + + expression.replaceWith(irStringConcatenation) + } + + private fun collectStringConcatenationArguments(expression: IrExpression, arguments: ArrayList) { + when { + expression is IrCallExpression && isStringPlus(expression.descriptor)-> { + collectStringConcatenationArguments(expression.dispatchReceiver!!, arguments) + collectStringConcatenationArguments(expression.getArgument(0)!!, arguments) + } + expression is IrStringConcatenationExpression -> { + arguments.addAll(expression.arguments) + expression.arguments.forEach { it.detach() } + } + else -> { + arguments.add(expression) + expression.detach() + } + } + } + + private fun isStringPlus(descriptor: CallableDescriptor): Boolean { + if (descriptor.name != OperatorNameConventions.PLUS) return false + val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false + if (!KotlinBuiltIns.isString(dispatchReceiverType)) return false + return true + } +} diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/CollapseDesugaredBlocks.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InlineDesugaredBlocks.kt similarity index 91% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/CollapseDesugaredBlocks.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InlineDesugaredBlocks.kt index 8711ef9df69..c7a8313bdeb 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/CollapseDesugaredBlocks.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InlineDesugaredBlocks.kt @@ -23,11 +23,11 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockExpressionImpl import org.jetbrains.kotlin.ir.replaceWith import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -fun collapseDesugaredBlocks(element: IrElement) { - element.accept(CollapseDesugaredBlocks(), null) +fun inlineDesugaredBlocks(element: IrElement) { + element.accept(InlineDesugaredBlocks(), null) } -class CollapseDesugaredBlocks : IrElementVisitor { +class InlineDesugaredBlocks : IrElementVisitor { override fun visitElement(element: IrElement, data: Nothing?) { element.acceptChildren(this, data) } 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 0bbb374eaae..902e62ea7cd 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 @@ -48,7 +48,7 @@ sealed class IrOperator(val debugName: String) { object MINUS : IrOperator("MINUS") object MUL : IrOperator("MUL") object DIV : IrOperator("DIV") - object MOD : IrOperator("MOD") + object PERC : IrOperator("PERC") object EQ : IrOperator("EQ") object PLUSEQ : IrOperator("PLUSEQ") diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStringConcatenationExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStringConcatenationExpression.kt index d250542a6ee..023942e101b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStringConcatenationExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStringConcatenationExpression.kt @@ -55,7 +55,7 @@ class IrStringConcatenationExpressionImpl( } override fun accept(visitor: IrElementVisitor, data: D): R = - visitor.visitStringTemplate(this, data) + visitor.visitStringConcatenation(this, data) override fun acceptChildren(visitor: IrElementVisitor, data: D) { arguments.forEach { it.accept(visitor, data) } 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 3602914db86..5763f992dc6 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 @@ -99,6 +99,9 @@ class RenderIrElementVisitor : IrElementVisitor { "SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" + "type=${expression.renderType()}" + 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()}" } 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 82ae67b1a35..fb60a01d0f6 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 @@ -43,7 +43,7 @@ interface IrElementVisitor { fun visitLiteral(expression: IrLiteralExpression, data: D): R = visitExpression(expression, data) fun visitReturnExpression(expression: IrReturnExpression, data: D): R = visitExpression(expression, data) fun visitBlockExpression(expression: IrBlockExpression, data: D): R = visitExpression(expression, data) - fun visitStringTemplate(expression: IrStringConcatenationExpression, data: D) = visitExpression(expression, data) + fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: D) = visitExpression(expression, data) fun visitThisExpression(expression: IrThisExpression, data: D) = visitExpression(expression, data) fun visitDeclarationReference(expression: IrDeclarationReference, data: D) = visitExpression(expression, data) diff --git a/compiler/testData/ir/irText/assignments.txt b/compiler/testData/ir/irText/assignments.txt index c0a9a17b8cd..c0229f64f63 100644 --- a/compiler/testData/ir/irText/assignments.txt +++ b/compiler/testData/ir/irText/assignments.txt @@ -8,7 +8,9 @@ IrFile /assignments.kt SET_VAR x type= LITERAL Int type=kotlin.Int value='1' SET_VAR x type= - DUMMY PLUS type=kotlin.Int + CALL .plus type=kotlin.Int operator=PLUS + $this: GET_VAR x type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='1' IrFunction public fun test2(/*0*/ r: Ref): kotlin.Unit IrExpressionBody BLOCK type= hasResult=false isDesugared=false diff --git a/compiler/testData/ir/irText/simpleOperators.kt b/compiler/testData/ir/irText/simpleOperators.kt new file mode 100644 index 00000000000..a64691ec594 --- /dev/null +++ b/compiler/testData/ir/irText/simpleOperators.kt @@ -0,0 +1,6 @@ +fun test1(a: Int, b: Int) = a + b +fun test2(a: Int, b: Int) = a - b +fun test3(a: Int, b: Int) = a * b +fun test4(a: Int, b: Int) = a / b +fun test5(a: Int, b: Int) = a % b +fun test6(a: Int, b: Int) = a .. b diff --git a/compiler/testData/ir/irText/simpleOperators.txt b/compiler/testData/ir/irText/simpleOperators.txt new file mode 100644 index 00000000000..632601b8ad8 --- /dev/null +++ b/compiler/testData/ir/irText/simpleOperators.txt @@ -0,0 +1,43 @@ +IrFile /simpleOperators.kt + IrFunction public fun test1(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .plus type=kotlin.Int operator=PLUS + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int + IrFunction public fun test2(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .minus type=kotlin.Int operator=MINUS + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int + IrFunction public fun test3(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .times type=kotlin.Int operator=MUL + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int + IrFunction public fun test4(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .div type=kotlin.Int operator=DIV + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int + IrFunction public fun test5(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .mod type=kotlin.Int operator=PERC + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int + IrFunction public fun test6(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.ranges.IntRange + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + CALL .rangeTo type=kotlin.ranges.IntRange operator=RANGE + $this: GET_VAR a type=kotlin.Int + other: GET_VAR b type=kotlin.Int diff --git a/compiler/testData/ir/irText/stringPlus.kt b/compiler/testData/ir/irText/stringPlus.kt new file mode 100644 index 00000000000..90776bb3785 --- /dev/null +++ b/compiler/testData/ir/irText/stringPlus.kt @@ -0,0 +1,3 @@ +fun test1(a: String, b: Any) = a + b +fun test2(a: String, b: Int) = a + "+" + b +fun test3(a: String, b: Int) = (a + "+") + (b + 1) + a \ No newline at end of file diff --git a/compiler/testData/ir/irText/stringPlus.txt b/compiler/testData/ir/irText/stringPlus.txt new file mode 100644 index 00000000000..62254ccc8c2 --- /dev/null +++ b/compiler/testData/ir/irText/stringPlus.txt @@ -0,0 +1,27 @@ +IrFile /stringPlus.kt + IrFunction public fun test1(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Any): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + STRING_CONCATENATION type=kotlin.String + GET_VAR a type=kotlin.String + GET_VAR b type=kotlin.Any + IrFunction public fun test2(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Int): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + STRING_CONCATENATION type=kotlin.String + GET_VAR a type=kotlin.String + LITERAL String type=kotlin.String value='+' + GET_VAR b type=kotlin.Int + IrFunction public fun test3(/*0*/ a: kotlin.String, /*1*/ b: kotlin.Int): kotlin.String + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN type= + STRING_CONCATENATION type=kotlin.String + GET_VAR a type=kotlin.String + LITERAL String type=kotlin.String value='+' + CALL .plus type=kotlin.Int operator=PLUS + $this: GET_VAR b type=kotlin.Int + other: LITERAL Int type=kotlin.Int value='1' + GET_VAR a type=kotlin.String diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 32ad3e749c5..5b1bc08924a 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -113,6 +113,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("simpleOperators.kt") + public void testSimpleOperators() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/simpleOperators.kt"); + doTest(fileName); + } + @TestMetadata("smartCasts.kt") public void testSmartCasts() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smartCasts.kt"); @@ -130,4 +136,10 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/smoke.kt"); doTest(fileName); } + + @TestMetadata("stringPlus.kt") + public void testStringPlus() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/stringPlus.kt"); + doTest(fileName); + } }