Desugar comparison operators as IrBinaryOperatorExpression.

They are not exactly 'compareTo' calls (although annotated with a related 'compareTo' function),
and require special treatment in BEs.
This commit is contained in:
Dmitry Petrov
2016-08-17 15:59:00 +03:00
committed by Dmitry Petrov
parent 1b018a6ead
commit db5843adc8
10 changed files with 142 additions and 32 deletions
@@ -19,34 +19,44 @@ package org.jetbrains.kotlin.psi2ir.generators
import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.ir.expressions.IrBinaryOperatorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.ir.expressions.IrOperator
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtArrayAccessExpression import org.jetbrains.kotlin.psi.KtArrayAccessExpression
import org.jetbrains.kotlin.psi.KtBinaryExpression import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtExpression 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.* import org.jetbrains.kotlin.psi2ir.generators.values.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
val KT_OPERATOR_TO_IR_OPERATOR = hashMapOf( val KT_OPERATOR_TO_IR_OPERATOR: Map<IElementType, IrOperator> =
KtTokens.PLUSEQ to IrOperator.PLUSEQ, hashMapOf(
KtTokens.MINUSEQ to IrOperator.MINUSEQ, KtTokens.PLUSEQ to IrOperator.PLUSEQ,
KtTokens.MULTEQ to IrOperator.MULTEQ, KtTokens.MINUSEQ to IrOperator.MINUSEQ,
KtTokens.DIVEQ to IrOperator.DIVEQ, KtTokens.MULTEQ to IrOperator.MULTEQ,
KtTokens.PERCEQ to IrOperator.PERCEQ, KtTokens.DIVEQ to IrOperator.DIVEQ,
KtTokens.PERCEQ to IrOperator.PERCEQ,
KtTokens.PLUS to IrOperator.PLUS, KtTokens.PLUS to IrOperator.PLUS,
KtTokens.MINUS to IrOperator.MINUS, KtTokens.MINUS to IrOperator.MINUS,
KtTokens.MUL to IrOperator.MUL, KtTokens.MUL to IrOperator.MUL,
KtTokens.DIV to IrOperator.DIV, KtTokens.DIV to IrOperator.DIV,
KtTokens.PERC to IrOperator.PERC, KtTokens.PERC to IrOperator.PERC,
KtTokens.RANGE to IrOperator.RANGE KtTokens.RANGE to IrOperator.RANGE,
)
KtTokens.LT to IrOperator.LT,
KtTokens.LTEQ to IrOperator.LTEQ,
KtTokens.GT to IrOperator.GT,
KtTokens.GTEQ to IrOperator.GTEQ
)
val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS
val BINARY_OPERATORS_WITH_CALLS = OperatorConventions.BINARY_OPERATION_NAMES.keys val BINARY_OPERATORS_WITH_CALLS = OperatorConventions.BINARY_OPERATION_NAMES.keys
val COMPARISON_OPERATORS = OperatorConventions.COMPARISON_OPERATIONS
class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator { class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator {
override val context: IrGeneratorContext get() = irStatementGenerator.context override val context: IrGeneratorContext get() = irStatementGenerator.context
@@ -58,10 +68,24 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
KtTokens.EQ -> generateAssignment(expression) KtTokens.EQ -> generateAssignment(expression)
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator) in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator) in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator)
in COMPARISON_OPERATORS -> generateComparisonOperator(expression, ktOperator)
else -> createDummyExpression(expression, ktOperator.toString()) else -> createDummyExpression(expression, ktOperator.toString())
} }
} }
private fun generateComparisonOperator(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
val irOperator = getIrOperator(ktOperator)
val compareToCall = getResolvedCall(expression)!!
val compareToDescriptor = compareToCall.resultingDescriptor
val irCallGenerator = IrCallGenerator(irStatementGenerator)
val irArgument0 = irCallGenerator.generateReceiver(expression.left!!, compareToCall.dispatchReceiver, compareToDescriptor.dispatchReceiverParameter)!!
val irArgument1 = irCallGenerator.generateValueArgument(compareToCall.valueArgumentsByIndex!![0], compareToDescriptor.valueParameters[0])!!
return IrBinaryOperatorExpressionImpl(expression.startOffset, expression.endOffset, context.builtIns.booleanType,
irOperator, compareToDescriptor, irArgument0, irArgument1)
}
private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression { private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
val irOperator = getIrOperator(ktOperator) val irOperator = getIrOperator(ktOperator)
val operatorCall = getResolvedCall(expression)!! val operatorCall = getResolvedCall(expression)!!
@@ -95,9 +119,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
} }
} }
private fun getIrOperator(ktOperator: IElementType): IrOperator { private fun getIrOperator(ktOperator: IElementType): IrOperator =
return KT_OPERATOR_TO_IR_OPERATOR[ktOperator] ?: TODO("Operator: $ktOperator") KT_OPERATOR_TO_IR_OPERATOR[ktOperator] ?: TODO("Operator: $ktOperator")
}
private fun generateAssignment(expression: KtBinaryExpression): IrExpression { private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
val ktLeft = expression.left!! val ktLeft = expression.left!!
@@ -157,9 +157,7 @@ class IrStatementGenerator(
} }
val irStringTemplate = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression)) val irStringTemplate = IrStringConcatenationExpressionImpl(expression.startOffset, expression.endOffset, getInferredTypeWithSmartcasts(expression))
entries.forEach { it.expression!!.let { entries.forEach { it.expression!!.let { irStringTemplate.addArgument(it.genExpr()) } }
irStringTemplate.addArgument(TODO())
} }
return irStringTemplate return irStringTemplate
} }
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.ir.expressions package org.jetbrains.kotlin.ir.expressions
sealed class IrOperator(val debugName: String) { abstract class IrOperator(val debugName: String) {
override fun toString(): String = debugName override fun toString(): String = debugName
object INVOKE : IrOperator("INVOKE") object INVOKE : IrOperator("INVOKE")
@@ -28,16 +28,17 @@ sealed class IrOperator(val debugName: String) {
object EXCL : IrOperator("EXCL") object EXCL : IrOperator("EXCL")
object EXCLEXCL : IrOperator("EXCLEXCL") object EXCLEXCL : IrOperator("EXCLEXCL")
object ELVIS : IrOperator("ELVIS") object ELVIS : IrOperator("ELVIS")
object LT : IrOperator("LT") object LT : IrOperator("LT")
object GT : IrOperator("GT") object GT : IrOperator("GT")
object LTEQ : IrOperator("LTEQ") object LTEQ : IrOperator("LTEQ")
object GTEQ : IrOperator("GTEQ") object GTEQ : IrOperator("GTEQ")
object EQEQ : IrOperator("EQEQ") object EQEQ : IrOperator("EQEQ")
object EQEQEQ : IrOperator("EQEQEQ") object EQEQEQ : IrOperator("EQEQEQ")
object EXCLEQ : IrOperator("EXCLEQ") object EXCLEQ : IrOperator("EXCLEQ")
object EXCLEQEQ : IrOperator("EXCLEQEQ") object EXCLEQEQ : IrOperator("EXCLEQEQ")
object IN : IrOperator("IN") object IN : IrOperator("IN")
object NOT_IN : IrOperator("NOT_IN") object NOT_IN : IrOperator("NOT_IN")
object ANDAND : IrOperator("ANDAND") object ANDAND : IrOperator("ANDAND")
@@ -16,14 +16,14 @@
package org.jetbrains.kotlin.ir.expressions package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
interface IrOperatorExpression : IrExpression { interface IrOperatorExpression : IrExpression {
val operator: IrOperator val operator: IrOperator
val relatedDescriptor: FunctionDescriptor? val relatedDescriptor: CallableDescriptor?
} }
interface IrUnaryOperatorExpression : IrOperatorExpression { interface IrUnaryOperatorExpression : IrOperatorExpression {
@@ -40,14 +40,14 @@ class IrUnaryOperatorExpressionImpl(
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
override val operator: IrOperator, override val operator: IrOperator,
override val relatedDescriptor: FunctionDescriptor? override val relatedDescriptor: CallableDescriptor?
) : IrExpressionBase(startOffset, endOffset, type), IrUnaryOperatorExpression { ) : IrExpressionBase(startOffset, endOffset, type), IrUnaryOperatorExpression {
constructor( constructor(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
operator: IrOperator, operator: IrOperator,
relatedDescriptor: FunctionDescriptor?, relatedDescriptor: CallableDescriptor?,
argument: IrExpression argument: IrExpression
) : this(startOffset, endOffset, type, operator, relatedDescriptor) { ) : this(startOffset, endOffset, type, operator, relatedDescriptor) {
this.argument = argument this.argument = argument
@@ -90,14 +90,14 @@ class IrBinaryOperatorExpressionImpl(
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
override val operator: IrOperator, override val operator: IrOperator,
override val relatedDescriptor: FunctionDescriptor? override val relatedDescriptor: CallableDescriptor?
) : IrExpressionBase(startOffset, endOffset, type), IrBinaryOperatorExpression { ) : IrExpressionBase(startOffset, endOffset, type), IrBinaryOperatorExpression {
constructor( constructor(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
type: KotlinType?, type: KotlinType?,
operator: IrOperator, operator: IrOperator,
relatedDescriptor: FunctionDescriptor?, relatedDescriptor: CallableDescriptor?,
argument0: IrExpression, argument0: IrExpression,
argument1: IrExpression argument1: IrExpression
) : this(startOffset, endOffset, type, operator, relatedDescriptor) { ) : this(startOffset, endOffset, type, operator, relatedDescriptor) {
@@ -144,7 +144,7 @@ class IrBinaryOperatorExpressionImpl(
visitor.visitBinaryOperator(this, data) visitor.visitBinaryOperator(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) { override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
argument0.acceptChildren(visitor, data) argument0.accept(visitor, data)
argument1.acceptChildren(visitor, data) argument1.accept(visitor, data)
} }
} }
@@ -99,6 +99,16 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" + "SET_PROPERTY ${if (expression.isSafe) "?." else "."}${expression.descriptor.name}" +
"type=${expression.renderType()}" "type=${expression.renderType()}"
override fun visitUnaryOperator(expression: IrUnaryOperatorExpression, data: Nothing?): String =
"UNARY_OP operator=${expression.operator} " +
"type=${expression.renderType()} " +
"related=${expression.relatedDescriptor?.render()}"
override fun visitBinaryOperator(expression: IrBinaryOperatorExpression, data: Nothing?): String =
"BINARY_OP operator=${expression.operator} " +
"type=${expression.renderType()} " +
"related=${expression.relatedDescriptor?.render()}"
override fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: Nothing?): String = override fun visitStringConcatenation(expression: IrStringConcatenationExpression, data: Nothing?): String =
"STRING_CONCATENATION type=${expression.renderType()}" "STRING_CONCATENATION type=${expression.renderType()}"
@@ -125,8 +135,8 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
internal fun IrDeclaration.name(): String = internal fun IrDeclaration.name(): String =
descriptor?.let { it.name.toString() } ?: "<none>" descriptor?.let { it.name.toString() } ?: "<none>"
internal fun DeclarationDescriptor.render(): String = internal fun DeclarationDescriptor?.render(): String =
DESCRIPTOR_RENDERER.render(this) this?.let { DESCRIPTOR_RENDERER.render(it) } ?: "<none>"
internal fun IrExpression.renderType(): String = internal fun IrExpression.renderType(): String =
type.render() type.render()
+4
View File
@@ -0,0 +1,4 @@
fun test1(a: String, b: String) = a > b
fun test2(a: String, b: String) = a < b
fun test3(a: String, b: String) = a >= b
fun test4(a: String, b: String) = a <= b
+29
View File
@@ -0,0 +1,29 @@
IrFile /conventionComparisons.kt
IrFunction public fun test1(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=GT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.String): kotlin.Int
GET_VAR a type=kotlin.String
GET_VAR b type=kotlin.String
IrFunction public fun test2(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=LT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.String): kotlin.Int
GET_VAR a type=kotlin.String
GET_VAR b type=kotlin.String
IrFunction public fun test3(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=GTEQ type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.String): kotlin.Int
GET_VAR a type=kotlin.String
GET_VAR b type=kotlin.String
IrFunction public fun test4(/*0*/ a: kotlin.String, /*1*/ b: kotlin.String): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=LTEQ type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.String): kotlin.Int
GET_VAR a type=kotlin.String
GET_VAR b type=kotlin.String
+4
View File
@@ -0,0 +1,4 @@
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
+29
View File
@@ -0,0 +1,29 @@
IrFile /primitiveComparisons.kt
IrFunction public fun test1(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=GT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int
GET_VAR a type=kotlin.Int
GET_VAR b type=kotlin.Int
IrFunction public fun test2(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=LT type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int
GET_VAR a type=kotlin.Int
GET_VAR b type=kotlin.Int
IrFunction public fun test3(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=GTEQ type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int
GET_VAR a type=kotlin.Int
GET_VAR b type=kotlin.Int
IrFunction public fun test4(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean
IrExpressionBody
BLOCK type=<no-type> hasResult=false isDesugared=true
RETURN type=<no-type>
BINARY_OP operator=LTEQ type=kotlin.Boolean related=public open override /*1*/ fun compareTo(/*0*/ other: kotlin.Int): kotlin.Int
GET_VAR a type=kotlin.Int
GET_VAR b type=kotlin.Int
@@ -89,6 +89,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("conventionComparisons.kt")
public void testConventionComparisons() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/conventionComparisons.kt");
doTest(fileName);
}
@TestMetadata("destructuring1.kt") @TestMetadata("destructuring1.kt")
public void testDestructuring1() throws Exception { public void testDestructuring1() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/destructuring1.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/destructuring1.kt");
@@ -107,6 +113,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("primitiveComparisons.kt")
public void testPrimitiveComparisons() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/primitiveComparisons.kt");
doTest(fileName);
}
@TestMetadata("references.kt") @TestMetadata("references.kt")
public void testReferences() throws Exception { public void testReferences() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/references.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/references.kt");