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?) =
|
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? =
|
fun generateReceiver(ktExpression: KtExpression, receiver: ReceiverValue?): IrExpression? =
|
||||||
if (receiver == null)
|
if (receiver == null)
|
||||||
@@ -223,12 +226,13 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun generateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
|
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)
|
doGenerateValueArgument(valueArgument, valueParameterDescriptor)
|
||||||
}
|
else
|
||||||
else {
|
doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(expectedType)
|
||||||
doGenerateValueArgument(valueArgument, valueParameterDescriptor)?.toExpectedType(valueParameterDescriptor.type)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
|
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument, valueParameterDescriptor: ValueParameterDescriptor): IrExpression? =
|
||||||
if (valueArgument is DefaultValueArgument)
|
if (valueArgument is DefaultValueArgument)
|
||||||
@@ -236,7 +240,6 @@ class IrCallGenerator(val irStatementGenerator: IrStatementGenerator) : IrGenera
|
|||||||
else
|
else
|
||||||
valueArgumentValues[valueParameterDescriptor]?.load() ?: doGenerateValueArgument(valueArgument)
|
valueArgumentValues[valueParameterDescriptor]?.load() ?: doGenerateValueArgument(valueArgument)
|
||||||
|
|
||||||
|
|
||||||
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
|
private fun doGenerateValueArgument(valueArgument: ResolvedValueArgument): IrExpression? =
|
||||||
when (valueArgument) {
|
when (valueArgument) {
|
||||||
is ExpressionValueArgument ->
|
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.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
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.psi2ir.toExpectedType
|
||||||
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.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.PLUSEQ to IrOperator.PLUSEQ,
|
||||||
KtTokens.MINUSEQ to IrOperator.MINUSEQ,
|
KtTokens.MINUSEQ to IrOperator.MINUSEQ,
|
||||||
KtTokens.MULTEQ to IrOperator.MULTEQ,
|
KtTokens.MULTEQ to IrOperator.MULTEQ,
|
||||||
@@ -51,51 +54,104 @@ val KT_OPERATOR_TO_IR_OPERATOR: Map<IElementType, IrOperator> =
|
|||||||
KtTokens.LT to IrOperator.LT,
|
KtTokens.LT to IrOperator.LT,
|
||||||
KtTokens.LTEQ to IrOperator.LTEQ,
|
KtTokens.LTEQ to IrOperator.LTEQ,
|
||||||
KtTokens.GT to IrOperator.GT,
|
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
|
internal val AUGMENTED_ASSIGNMENTS =
|
||||||
val BINARY_OPERATORS_WITH_CALLS = OperatorConventions.BINARY_OPERATION_NAMES.keys
|
setOf(IrOperator.PLUSEQ, IrOperator.MINUSEQ, IrOperator.MULTEQ, IrOperator.DIVEQ, IrOperator.PERCEQ)
|
||||||
val COMPARISON_OPERATORS = OperatorConventions.COMPARISON_OPERATIONS
|
|
||||||
|
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 {
|
class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerator): IrGenerator {
|
||||||
override val context: IrGeneratorContext get() = irStatementGenerator.context
|
override val context: IrGeneratorContext get() = irStatementGenerator.context
|
||||||
|
|
||||||
fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression {
|
fun generateBinaryExpression(expression: KtBinaryExpression): IrExpression {
|
||||||
val ktOperator = expression.operationReference.getReferencedNameElementType()
|
val ktOperator = expression.operationReference.getReferencedNameElementType()
|
||||||
|
val irOperator = getIrOperator(ktOperator)
|
||||||
|
|
||||||
return when (ktOperator) {
|
return when (irOperator) {
|
||||||
KtTokens.EQ -> generateAssignment(expression)
|
null -> createDummyExpression(expression, ktOperator.toString())
|
||||||
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, ktOperator)
|
IrOperator.EQ -> generateAssignment(expression)
|
||||||
in BINARY_OPERATORS_WITH_CALLS -> generateBinaryOperatorWithConventionalCall(expression, ktOperator)
|
in AUGMENTED_ASSIGNMENTS -> generateAugmentedAssignment(expression, irOperator)
|
||||||
in COMPARISON_OPERATORS -> generateComparisonOperator(expression, ktOperator)
|
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())
|
else -> createDummyExpression(expression, ktOperator.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateComparisonOperator(expression: KtBinaryExpression, ktOperator: IElementType): IrExpression {
|
private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||||
val irOperator = getIrOperator(ktOperator)
|
val irArgument0 = irStatementGenerator.generateExpression(expression.left!!)
|
||||||
|
val irArgument1 = irStatementGenerator.generateExpression(expression.right!!)
|
||||||
val compareToCall = getResolvedCall(expression)!!
|
return IrBinaryOperatorExpressionImpl(
|
||||||
val compareToDescriptor = compareToCall.resultingDescriptor
|
expression.startOffset, expression.endOffset, context.builtIns.booleanType,
|
||||||
|
irOperator, null, irArgument0, irArgument1
|
||||||
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 generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrOperator): IrExpression {
|
||||||
val irOperator = getIrOperator(ktOperator)
|
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)!!
|
val operatorCall = getResolvedCall(expression)!!
|
||||||
return IrCallGenerator(irStatementGenerator).generateCall(expression, operatorCall, irOperator)
|
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 ktLeft = expression.left!!
|
||||||
|
|
||||||
val irOperator = getIrOperator(ktOperator)
|
|
||||||
val irLhs = generateLValue(ktLeft, irOperator)
|
val irLhs = generateLValue(ktLeft, irOperator)
|
||||||
|
|
||||||
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false
|
val isSimpleAssignment = get(BindingContext.VARIABLE_REASSIGNMENT, expression) ?: false
|
||||||
@@ -119,8 +175,8 @@ class IrOperatorExpressionGenerator(val irStatementGenerator: IrStatementGenerat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getIrOperator(ktOperator: IElementType): IrOperator =
|
private fun getIrOperator(ktOperator: IElementType): IrOperator? =
|
||||||
KT_OPERATOR_TO_IR_OPERATOR[ktOperator] ?: TODO("Operator: $ktOperator")
|
KT_TOKEN_TO_IR_OPERATOR[ktOperator]
|
||||||
|
|
||||||
private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
|
private fun generateAssignment(expression: KtBinaryExpression): IrExpression {
|
||||||
val ktLeft = expression.left!!
|
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);
|
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")
|
@TestMetadata("extensionPropertyGetterCall.kt")
|
||||||
public void testExtensionPropertyGetterCall() throws Exception {
|
public void testExtensionPropertyGetterCall() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/extensionPropertyGetterCall.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/extensionPropertyGetterCall.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("primitiveComparisons.kt")
|
||||||
public void testPrimitiveComparisons() throws Exception {
|
public void testPrimitiveComparisons() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/primitiveComparisons.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/primitiveComparisons.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user