diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt similarity index 73% rename from compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt rename to compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt index 40fe8c6c7ee..aa7a1024ef3 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGeneration.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ArgumentsGenerationUtils.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.psi2ir.generators +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -24,6 +25,7 @@ import org.jetbrains.kotlin.psi2ir.intermediate.* import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.scopes.receivers.* +import java.lang.AssertionError fun StatementGenerator.generateReceiverOrNull(ktDefaultElement: KtElement, receiver: ReceiverValue?): IntermediateValue? = receiver?.let { generateReceiver(ktDefaultElement, receiver) } @@ -82,14 +84,47 @@ fun StatementGenerator.generateCallReceiver( } } -fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? = +fun StatementGenerator.generateVarargExpression(varargArgument: VarargValueArgument, valueParameter: ValueParameterDescriptor) : IrExpression? { + if (varargArgument.arguments.isEmpty()) { + return null + } + + val varargStartOffset = varargArgument.arguments.fold(Int.MAX_VALUE) { minStartOffset, argument -> + Math.min(minStartOffset, argument.asElement().startOffset) + } + val varargEndOffset = varargArgument.arguments.fold(Int.MIN_VALUE) { maxEndOffset, argument -> + Math.max(maxEndOffset, argument.asElement().endOffset) + } + + val varargElementType = valueParameter.varargElementType ?: + throw AssertionError("Vararg argument for non-vararg parameter $valueParameter") + + val irVararg = IrVarargImpl(varargStartOffset, varargEndOffset, valueParameter.type, varargElementType) + + for (argument in varargArgument.arguments) { + val ktArgumentExpression = argument.getArgumentExpression() ?: + throw AssertionError("No argument expression for vararg element ${argument.asElement().text}") + val irVarargElement = + if (argument.getSpreadElement() != null) + IrSpreadElementImpl(ktArgumentExpression.startOffset, ktArgumentExpression.endOffset, + generateExpression(ktArgumentExpression)) + else + generateExpression(ktArgumentExpression) + + irVararg.addElement(irVarargElement) + } + + return irVararg +} + +fun StatementGenerator.generateValueArgument(valueArgument: ResolvedValueArgument, valueParameter: ValueParameterDescriptor): IrExpression? = when (valueArgument) { is DefaultValueArgument -> null is ExpressionValueArgument -> generateExpression(valueArgument.valueArgument!!.getArgumentExpression()!!) is VarargValueArgument -> - createDummyExpression(valueArgument.arguments[0].getArgumentExpression()!!, "vararg") + generateVarargExpression(valueArgument, valueParameter) else -> TODO("Unexpected valueArgument: ${valueArgument.javaClass.simpleName}") } @@ -98,7 +133,8 @@ fun StatementGenerator.pregenerateCall(resolvedCall: ResolvedCall<*>): CallBuild val call = pregenerateCallReceivers(resolvedCall) resolvedCall.valueArgumentsByIndex!!.forEachIndexed { index, valueArgument -> - call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument) + val valueParameter = call.descriptor.valueParameters[index] + call.irValueArgumentsByIndex[index] = generateValueArgument(valueArgument, valueParameter) } return call diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index fd993effeeb..8c0fc62c9e0 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -115,6 +115,16 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor element.expression.replaceWithCast(expression.type) + is IrExpression -> element.replaceWithCast(expression.varargElementType) + } + } + } + private fun IrExpression.replaceWithCast(expectedType: KotlinType?) { replaceWith { it.wrapWithImplicitCast(expectedType) } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt index 7a7234f220b..72898318d1b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrExpression.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.ir.throwNoSuchSlot import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.types.KotlinType -interface IrExpression : IrStatement { +interface IrExpression : IrStatement, IrVarargElement { val type: KotlinType } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVararg.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVararg.kt new file mode 100644 index 00000000000..5471db3b31a --- /dev/null +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrVararg.kt @@ -0,0 +1,109 @@ +/* + * 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.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.utils.SmartList + +interface IrVarargElement : IrElement + +interface IrVararg : IrExpression { + val varargElementType : KotlinType + val elements: List +} + +interface IrSpreadElement : IrVarargElement { + var expression: IrExpression +} + +class IrVarargImpl( + startOffset: Int, + endOffset: Int, + type: KotlinType, + override val varargElementType: KotlinType +) : IrVararg, IrExpressionBase(startOffset, endOffset, type) { + override val elements: MutableList = SmartList() + + fun addElement(varargElement: IrVarargElement) { + varargElement.assertDetached() + varargElement.setTreeLocation(this, elements.size) + elements.add(varargElement) + } + + override fun getChild(slot: Int): IrElement? = + elements.getOrNull(slot) + + override fun replaceChild(slot: Int, newChild: IrElement) { + if (slot < 0 || slot >= elements.size) throwNoSuchSlot(slot) + newChild.assertDetached() + elements[slot].detach() + elements[slot] = newChild.assertCast() + newChild.setTreeLocation(this, slot) + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitVararg(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + elements.forEach { it.accept(visitor, data) } + } +} + +class IrSpreadElementImpl( + startOffset: Int, + endOffset: Int +) : IrElementBase(startOffset, endOffset), IrSpreadElement { + constructor(startOffset: Int, endOffset: Int, expression: IrExpression) : this(startOffset, endOffset) { + this.expression = expression + } + + private var expressionImpl: IrExpression? = null + override var expression: IrExpression + get() = expressionImpl!! + set(value) { + value.assertDetached() + expressionImpl?.detach() + expressionImpl = value + value.setTreeLocation(this, CHILD_EXPRESSION_SLOT) + } + + + override fun getChild(slot: Int): IrElement? = + when (slot) { + CHILD_EXPRESSION_SLOT -> expression + else -> null + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + when (slot) { + CHILD_EXPRESSION_SLOT -> expression = newChild.assertCast() + } + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitSpreadElement(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + expression.accept(visitor, data) + } + + +} \ No newline at end of file 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 3aa33a6ee88..31f48439a12 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 @@ -66,6 +66,12 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitConst(expression: IrConst, data: Nothing?): String = "CONST ${expression.kind} type=${expression.renderType()} value='${expression.value}'" + override fun visitVararg(expression: IrVararg, data: Nothing?): String = + "VARARG type=${expression.type} varargElementType=${expression.varargElementType}" + + override fun visitSpreadElement(spread: IrSpreadElement, data: Nothing?): String = + "SPREAD_ELEMENT" + override fun visitBlock(expression: IrBlock, data: Nothing?): String = "BLOCK type=${expression.renderType()} operator=${expression.operator}" 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 ef2c02e82a9..2ed2a2ef210 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 @@ -41,6 +41,8 @@ interface IrElementVisitor { fun visitExpression(expression: IrExpression, data: D): R = visitElement(expression, data) fun visitConst(expression: IrConst, data: D): R = visitExpression(expression, data) + fun visitVararg(expression: IrVararg, data: D): R = visitExpression(expression, data) + fun visitSpreadElement(spread: IrSpreadElement, data: D): R = visitElement(spread, data) fun visitBlock(expression: IrBlock, data: D): R = visitExpression(expression, data) fun visitStringConcatenation(expression: IrStringConcatenation, data: D) = visitExpression(expression, data) @@ -73,4 +75,5 @@ interface IrElementVisitor { // 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/arrayAssignment.txt b/compiler/testData/ir/irText/arrayAssignment.txt index bd360b517ba..7acc3a25856 100644 --- a/compiler/testData/ir/irText/arrayAssignment.txt +++ b/compiler/testData/ir/irText/arrayAssignment.txt @@ -3,8 +3,10 @@ FILE /arrayAssignment.kt BLOCK_BODY VAR val x: kotlin.IntArray CALL .intArrayOf type=kotlin.IntArray operator=null - elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray - DUMMY vararg type=kotlin.Int + elements: VARARG type=IntArray varargElementType=Int + CONST Int type=kotlin.Int value='1' + CONST Int type=kotlin.Int value='2' + CONST Int type=kotlin.Int value='3' CALL .set type=kotlin.Unit operator=EQ $this: GET_VAR x type=kotlin.IntArray operator=null index: CONST Int type=kotlin.Int value='1' @@ -17,7 +19,9 @@ FILE /arrayAssignment.kt BLOCK_BODY CALL .set type=kotlin.Unit operator=EQ $this: CALL .intArrayOf type=kotlin.IntArray operator=null - elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray - DUMMY vararg type=kotlin.Int + elements: VARARG type=IntArray varargElementType=Int + CONST Int type=kotlin.Int value='1' + CONST Int type=kotlin.Int value='2' + CONST Int type=kotlin.Int value='3' index: CALL .foo type=kotlin.Int operator=null value: CONST Int type=kotlin.Int value='1' diff --git a/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt b/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt index 86a1a6f715f..1b8e2407016 100644 --- a/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt +++ b/compiler/testData/ir/irText/arrayAugmentedAssignment1.txt @@ -3,8 +3,10 @@ FILE /arrayAugmentedAssignment1.kt BLOCK_BODY RETURN type=kotlin.Nothing CALL .intArrayOf type=kotlin.IntArray operator=null - elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray - DUMMY vararg type=kotlin.Int + elements: VARARG type=IntArray varargElementType=Int + CONST Int type=kotlin.Int value='1' + CONST Int type=kotlin.Int value='2' + CONST Int type=kotlin.Int value='3' FUN public fun bar(): kotlin.Int BLOCK_BODY RETURN type=kotlin.Nothing diff --git a/compiler/testData/ir/irText/incrementDecrement.txt b/compiler/testData/ir/irText/incrementDecrement.txt index b4de85fa190..469601a4e60 100644 --- a/compiler/testData/ir/irText/incrementDecrement.txt +++ b/compiler/testData/ir/irText/incrementDecrement.txt @@ -5,8 +5,10 @@ FILE /incrementDecrement.kt PROPERTY public val arr: kotlin.IntArray getter=null setter=null EXPRESSION_BODY CALL .intArrayOf type=kotlin.IntArray operator=null - elements: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray - DUMMY vararg type=kotlin.Int + elements: VARARG type=IntArray varargElementType=Int + CONST Int type=kotlin.Int value='1' + CONST Int type=kotlin.Int value='2' + CONST Int type=kotlin.Int value='3' FUN public fun testVarPrefix(): kotlin.Unit BLOCK_BODY VAR var x: kotlin.Int diff --git a/compiler/testData/ir/irText/vararg.kt b/compiler/testData/ir/irText/vararg.kt new file mode 100644 index 00000000000..c2931299b79 --- /dev/null +++ b/compiler/testData/ir/irText/vararg.kt @@ -0,0 +1,3 @@ +val test1 = arrayOf() +val test2 = arrayOf("1", "2", "3") +val test3 = arrayOf("0", *test2, *test1, "4") \ No newline at end of file diff --git a/compiler/testData/ir/irText/vararg.txt b/compiler/testData/ir/irText/vararg.txt new file mode 100644 index 00000000000..25c413eec92 --- /dev/null +++ b/compiler/testData/ir/irText/vararg.txt @@ -0,0 +1,21 @@ +FILE /vararg.kt + PROPERTY public val test1: kotlin.Array getter=null setter=null + EXPRESSION_BODY + CALL .arrayOf type=kotlin.Array operator=null + PROPERTY public val test2: kotlin.Array getter=null setter=null + EXPRESSION_BODY + CALL .arrayOf type=kotlin.Array operator=null + elements: VARARG type=Array varargElementType=String + CONST String type=kotlin.String value='1' + CONST String type=kotlin.String value='2' + CONST String type=kotlin.String value='3' + PROPERTY public val test3: kotlin.Array getter=null setter=null + EXPRESSION_BODY + CALL .arrayOf type=kotlin.Array operator=null + elements: VARARG type=Array varargElementType=String + CONST String type=kotlin.String value='0' + SPREAD_ELEMENT + CALL . type=kotlin.Array operator=GET_PROPERTY + SPREAD_ELEMENT + CALL . type=kotlin.Array operator=GET_PROPERTY + CONST String type=kotlin.String value='4' diff --git a/compiler/testData/ir/irText/varargWithImplicitCast.kt b/compiler/testData/ir/irText/varargWithImplicitCast.kt new file mode 100644 index 00000000000..d32a2e916cc --- /dev/null +++ b/compiler/testData/ir/irText/varargWithImplicitCast.kt @@ -0,0 +1,9 @@ +fun testScalar(a: Any): IntArray { + if (a !is Int) return intArrayOf() + return intArrayOf(a) +} + +fun testSpread(a: Any): IntArray { + if (a !is IntArray) return intArrayOf() + return intArrayOf(*a) +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/varargWithImplicitCast.txt b/compiler/testData/ir/irText/varargWithImplicitCast.txt new file mode 100644 index 00000000000..cc10d25f245 --- /dev/null +++ b/compiler/testData/ir/irText/varargWithImplicitCast.txt @@ -0,0 +1,26 @@ +FILE /varargWithImplicitCast.kt + FUN public fun testScalar(/*0*/ a: kotlin.Any): kotlin.IntArray + BLOCK_BODY + WHEN type=kotlin.Unit operator=IF + if: TYPE_OP operator=NOT_INSTANCEOF typeOperand=kotlin.Int + GET_VAR a type=kotlin.Any operator=null + then: RETURN type=kotlin.Nothing + CALL .intArrayOf type=kotlin.IntArray operator=null + RETURN type=kotlin.Nothing + CALL .intArrayOf type=kotlin.IntArray operator=null + elements: VARARG type=IntArray varargElementType=Int + TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Int + GET_VAR a type=kotlin.Any operator=null + FUN public fun testSpread(/*0*/ a: kotlin.Any): kotlin.IntArray + BLOCK_BODY + WHEN type=kotlin.Unit operator=IF + if: TYPE_OP operator=NOT_INSTANCEOF typeOperand=kotlin.IntArray + GET_VAR a type=kotlin.Any operator=null + then: RETURN type=kotlin.Nothing + CALL .intArrayOf type=kotlin.IntArray operator=null + RETURN type=kotlin.Nothing + CALL .intArrayOf type=kotlin.IntArray operator=null + elements: VARARG type=IntArray varargElementType=Int + SPREAD_ELEMENT + TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.IntArray + GET_VAR a type=kotlin.Any operator=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 3ccc019e7f5..fd904561a1d 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -299,6 +299,18 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("vararg.kt") + public void testVararg() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/vararg.kt"); + doTest(fileName); + } + + @TestMetadata("varargWithImplicitCast.kt") + public void testVarargWithImplicitCast() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/varargWithImplicitCast.kt"); + doTest(fileName); + } + @TestMetadata("variableAsFunctionCall.kt") public void testVariableAsFunctionCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/variableAsFunctionCall.kt");