Equality and identity operators.
This commit is contained in:
committed by
Dmitry Petrov
parent
db5843adc8
commit
96e3c52878
+10
-7
@@ -192,7 +192,10 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
|
||||
}
|
||||
|
||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?, receiverParameterDescriptor: ReceiverParameterDescriptor?) =
|
||||
generateReceiver(ktExpression, receiver)?.toExpectedType(receiverParameterDescriptor?.type)
|
||||
generateReceiver(ktExpression, receiver, receiverParameterDescriptor?.type)
|
||||
|
||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?, expectedType: KotlinType?) =
|
||||
generateReceiver(ktExpression, receiver)?.toExpectedType(expectedType)
|
||||
|
||||
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
|
||||
if (receiver == null)
|
||||
@@ -223,12 +226,13 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
|
||||
}
|
||||
|
||||
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
|
||||
if (valueParameterDescriptor.varargElementType != null) {
|
||||
generateValueArgument(valueArgument, valueParameterDescriptor, valueParameterDescriptor.type)
|
||||
|
||||
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor, expectedType: KotlinType): IrExpression? =
|
||||
if (valueParameterDescriptor.varargElementType != null)
|
||||
doGenerateValueArgument(valueArgument, valueParameterDescriptor)
|
||||
}
|
||||
else {
|
||||
doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(valueParameterDescriptor.type)
|
||||
}
|
||||
else
|
||||
doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(expectedType)
|
||||
|
||||
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
|
||||
if (valueArgument is DefaultValueArgument)
|
||||
@@ -236,7 +240,6 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
|
||||
else
|
||||
valueArgumentValues[valueParameterDescriptor]?.load() ?: doGenerateValueArgument(valueArgument)
|
||||
|
||||
|
||||
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
|
||||
when (valueArgument) {
|
||||
is ExpressionValueArgument ->
|
||||
|
||||
+85
-29
@@ -29,12 +29,15 @@ 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.toExpectedType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
|
||||
internal val KT_TOKEN_TO_IR_OPERATOR =
|
||||
mapOf(
|
||||
KtTokens.EQ to IrOperator.EQ,
|
||||
|
||||
val KT_OPERATOR_TO_IR_OPERATOR: Map<IElementType, IrOperator> =
|
||||
hashMapOf(
|
||||
KtTokens.PLUSEQ to IrOperator.PLUSEQ,
|
||||
KtTokens.MINUSEQ to IrOperator.MINUSEQ,
|
||||
KtTokens.MULTEQ to IrOperator.MULTEQ,
|
||||
@@ -51,51 +54,104 @@ val KT_OPERATOR_TO_IR_OPERATOR: Map<IElementType, IrOperator> =
|
||||
KtTokens.LT to IrOperator.LT,
|
||||
KtTokens.LTEQ to IrOperator.LTEQ,
|
||||
KtTokens.GT to IrOperator.GT,
|
||||
KtTokens.GTEQ to IrOperator.GTEQ
|
||||
KtTokens.GTEQ to IrOperator.GTEQ,
|
||||
|
||||
KtTokens.EQEQ to IrOperator.EQEQ,
|
||||
KtTokens.EXCLEQ to IrOperator.EXCLEQ,
|
||||
KtTokens.EQEQEQ to IrOperator.EQEQEQ,
|
||||
KtTokens.EXCLEQEQEQ to IrOperator.EXCLEQEQ
|
||||
)
|
||||
|
||||
val AUGMENTED_ASSIGNMENTS = KtTokens.AUGMENTED_ASSIGNMENTS
|
||||
val BINARY_OPERATORS_WITH_CALLS = OperatorConventions.BINARY_OPERATION_NAMES.keys
|
||||
val COMPARISON_OPERATORS = OperatorConventions.COMPARISON_OPERATIONS
|
||||
internal val AUGMENTED_ASSIGNMENTS =
|
||||
setOf(IrOperator.PLUSEQ, IrOperator.MINUSEQ, IrOperator.MULTEQ, IrOperator.DIVEQ, IrOperator.PERCEQ)
|
||||
|
||||
internal val BINARY_OPERATORS_DESUGARED_TO_CALLS =
|
||||
setOf(IrOperator.PLUS, IrOperator.MINUS, IrOperator.MUL, IrOperator.DIV, IrOperator.PERC, IrOperator.RANGE)
|
||||
|
||||
internal val COMPARISON_OPERATORS =
|
||||
setOf(IrOperator.LT, IrOperator.LTEQ, IrOperator.GT, IrOperator.GTEQ)
|
||||
|
||||
internal val EQUALITY_OPERATORS =
|
||||
setOf(IrOperator.EQEQ, IrOperator.EXCLEQ)
|
||||
|
||||
internal val IDENTITY_OPERATORS =
|
||||
setOf(IrOperator.EQEQEQ, IrOperator.EXCLEQEQ)
|
||||
|
||||
class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator {
|
||||
override val context: IrGeneratorContext get() = irStatementGenerator.context
|
||||
|
||||
fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression {
|
||||
val ktOperator = expression.operationReference.getReferencedNameElementType()
|
||||
val irOperator = getIrOperator(ktOperator)
|
||||
|
||||
return when (ktOperator) {
|
||||
KtTokens.EQ -> generateAssignment(expression)
|
||||
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
|
||||
in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator)
|
||||
in COMPARISON_OPERATORS -> generateComparisonOperator(expression, ktOperator)
|
||||
return when (irOperator) {
|
||||
null -> createDummyExpression(expression, ktOperator.toString())
|
||||
IrOperator.EQ -> generateAssignment(expression)
|
||||
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, irOperator)
|
||||
in BINARY_OPERATORS_DESUGARED_TO_CALLS -> generateBinaryOperatorWithConventionalCall(expression, irOperator)
|
||||
in COMPARISON_OPERATORS -> generateComparisonOperator(expression, irOperator)
|
||||
in EQUALITY_OPERATORS -> generateEqualityOperator(expression, irOperator)
|
||||
in IDENTITY_OPERATORS -> generateIdentityOperator(expression, irOperator)
|
||||
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 generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||
val irArgument0 = irStatementGenerator.generateExpression(expression.left!!)
|
||||
val irArgument1 = irStatementGenerator.generateExpression(expression.right!!)
|
||||
return IrBinaryOperatorExpressionImpl(
|
||||
expression.startOffset, expression.endOffset, context.builtIns.booleanType,
|
||||
irOperator, null, irArgument0, irArgument1
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
||||
val irOperator = getIrOperator(ktOperator)
|
||||
private fun generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||
val relatedCall = getResolvedCall(expression)!!
|
||||
val relatedDescriptor = relatedCall.resultingDescriptor
|
||||
|
||||
val irCallGenerator = IrCallGenerator(irStatementGenerator)
|
||||
|
||||
// NB special typing rules for equality operators: both arguments are nullable
|
||||
|
||||
val irArgument0 =
|
||||
irCallGenerator.generateReceiver(expression.left!!, relatedCall.dispatchReceiver)!!
|
||||
.toExpectedType(relatedDescriptor.dispatchReceiverParameter!!.type.makeNullable())
|
||||
|
||||
val valueParameter0 = relatedDescriptor.valueParameters[0]
|
||||
val irArgument1 =
|
||||
irCallGenerator.generateValueArgument(
|
||||
relatedCall.valueArgumentsByIndex!![0],
|
||||
valueParameter0, valueParameter0.type.makeNullable()
|
||||
)!!
|
||||
|
||||
return IrBinaryOperatorExpressionImpl(
|
||||
expression.startOffset, expression.endOffset, context.builtIns.booleanType,
|
||||
irOperator, relatedDescriptor, irArgument0, irArgument1
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateComparisonOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||
val relatedCall = getResolvedCall(expression)!!
|
||||
val relatedDescriptor = relatedCall.resultingDescriptor
|
||||
|
||||
val irCallGenerator = IrCallGenerator(irStatementGenerator)
|
||||
val irArgument0 = irCallGenerator.generateReceiver(expression.left!!, relatedCall.dispatchReceiver, relatedDescriptor.dispatchReceiverParameter!!)!!
|
||||
val valueParameter0 = relatedDescriptor.valueParameters[0]
|
||||
val irArgument1 = irCallGenerator.generateValueArgument(relatedCall.valueArgumentsByIndex!![0], valueParameter0)!!
|
||||
return IrBinaryOperatorExpressionImpl(
|
||||
expression.startOffset, expression.endOffset, context.builtIns.booleanType,
|
||||
irOperator, relatedDescriptor, irArgument0, irArgument1
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateBinaryOperatorWithConventionalCall(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||
val operatorCall = getResolvedCall(expression)!!
|
||||
return IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator)
|
||||
}
|
||||
|
||||
private fun generateAugmentedAssignment(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
||||
private fun generateAugmentedAssignment(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||
val ktLeft = expression.left!!
|
||||
|
||||
val irOperator = getIrOperator(ktOperator)
|
||||
val irLhs = generateLValue(ktLeft, irOperator)
|
||||
|
||||
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false
|
||||
@@ -119,8 +175,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIrOperator(ktOperator: IElementType): IrOperator =
|
||||
KT_OPERATOR_TO_IR_OPERATOR[ktOperator] ?: TODO("Operator: $ktOperator")
|
||||
private fun getIrOperator(ktOperator: IElementType): IrOperator? =
|
||||
KT_TOKEN_TO_IR_OPERATOR[ktOperator]
|
||||
|
||||
private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
|
||||
val ktLeft = expression.left!!
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun test1(a: Int, b: Int) = a == b
|
||||
fun test2(a: Int, b: Int) = a != b
|
||||
fun test3(a: Any?, b: Any?) = a == b
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
IrFile /equality.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=EQEQ type=kotlin.Boolean related=public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
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=EXCLEQ type=kotlin.Boolean related=public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
GET_VAR a type=kotlin.Int
|
||||
GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.Boolean
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
BINARY_OP operator=EQEQ type=kotlin.Boolean related=public open operator fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
GET_VAR a type=kotlin.Any?
|
||||
GET_VAR b type=kotlin.Any?
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fun test1(a: Int, b: Int) = a === b
|
||||
fun test2(a: Int, b: Int) = a !== b
|
||||
fun test3(a: Any?, b: Any?) = a === b
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
IrFile /identity.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=EQEQEQ type=kotlin.Boolean related=null
|
||||
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=EXCLEQEQ type=kotlin.Boolean related=null
|
||||
GET_VAR a type=kotlin.Int
|
||||
GET_VAR b type=kotlin.Int
|
||||
IrFunction public fun test3(/*0*/ a: kotlin.Any?, /*1*/ b: kotlin.Any?): kotlin.Boolean
|
||||
IrExpressionBody
|
||||
BLOCK type=<no-type> hasResult=false isDesugared=true
|
||||
RETURN type=<no-type>
|
||||
BINARY_OP operator=EQEQEQ type=kotlin.Boolean related=null
|
||||
GET_VAR a type=kotlin.Any?
|
||||
GET_VAR b type=kotlin.Any?
|
||||
@@ -107,12 +107,24 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equality.kt")
|
||||
public void testEquality() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/equality.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("extensionPropertyGetterCall.kt")
|
||||
public void testExtensionPropertyGetterCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/extensionPropertyGetterCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("identity.kt")
|
||||
public void testIdentity() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/identity.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("primitiveComparisons.kt")
|
||||
public void testPrimitiveComparisons() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/primitiveComparisons.kt");
|
||||
|
||||
Reference in New Issue
Block a user