Varargs.
This commit is contained in:
committed by
Dmitry Petrov
parent
d45811a5da
commit
b17e3b0299
+39
-3
@@ -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
|
||||
+10
@@ -115,6 +115,16 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor<Unit,
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVararg(expression: IrVararg, data: Nothing?) {
|
||||
expression.acceptChildren(this, data)
|
||||
for (element in expression.elements) {
|
||||
when (element) {
|
||||
is IrSpreadElement -> element.expression.replaceWithCast(expression.type)
|
||||
is IrExpression -> element.replaceWithCast(expression.varargElementType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrExpression.replaceWithCast(expectedType: KotlinType?) {
|
||||
replaceWith { it.wrapWithImplicitCast(expectedType) }
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IrVarargElement>
|
||||
}
|
||||
|
||||
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<IrVarargElement> = 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 <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
|
||||
return visitor.visitVararg(this, data)
|
||||
}
|
||||
|
||||
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, 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 <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
|
||||
return visitor.visitSpreadElement(this, data)
|
||||
}
|
||||
|
||||
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
|
||||
expression.accept(visitor, data)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -66,6 +66,12 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
|
||||
override fun <T> visitConst(expression: IrConst<T>, 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}"
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ interface IrElementVisitor<out R, in D> {
|
||||
|
||||
fun visitExpression(expression: IrExpression, data: D): R = visitElement(expression, data)
|
||||
fun <T> visitConst(expression: IrConst<T>, 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<out R, in D> {
|
||||
// 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)
|
||||
|
||||
}
|
||||
|
||||
+8
-4
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
+4
-2
@@ -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
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
val test1 = arrayOf<String>()
|
||||
val test2 = arrayOf("1", "2", "3")
|
||||
val test3 = arrayOf("0", *test2, *test1, "4")
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
FILE /vararg.kt
|
||||
PROPERTY public val test1: kotlin.Array<kotlin.String> getter=null setter=null
|
||||
EXPRESSION_BODY
|
||||
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
|
||||
PROPERTY public val test2: kotlin.Array<kotlin.String> getter=null setter=null
|
||||
EXPRESSION_BODY
|
||||
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
|
||||
elements: VARARG type=Array<out String> 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<kotlin.String> getter=null setter=null
|
||||
EXPRESSION_BODY
|
||||
CALL .arrayOf type=kotlin.Array<kotlin.String> operator=null
|
||||
elements: VARARG type=Array<out String> varargElementType=String
|
||||
CONST String type=kotlin.String value='0'
|
||||
SPREAD_ELEMENT
|
||||
CALL .<get-test2> type=kotlin.Array<kotlin.String> operator=GET_PROPERTY
|
||||
SPREAD_ELEMENT
|
||||
CALL .<get-test1> type=kotlin.Array<kotlin.String> operator=GET_PROPERTY
|
||||
CONST String type=kotlin.String value='4'
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user