diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt index 1eac2b64a33..e81c244430f 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrCallGenerator.kt @@ -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 -> diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt index ef873cacf06..902a4e45bf6 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/IrOperatorExpressionGenerator.kt @@ -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 = - 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 = 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!! diff --git a/compiler/testData/ir/irText/equality.kt b/compiler/testData/ir/irText/equality.kt new file mode 100644 index 00000000000..bc3b2ccfb80 --- /dev/null +++ b/compiler/testData/ir/irText/equality.kt @@ -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 diff --git a/compiler/testData/ir/irText/equality.txt b/compiler/testData/ir/irText/equality.txt new file mode 100644 index 00000000000..045af2c09b5 --- /dev/null +++ b/compiler/testData/ir/irText/equality.txt @@ -0,0 +1,22 @@ +IrFile /equality.kt + IrFunction public fun test1(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN 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= hasResult=false isDesugared=true + RETURN 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= hasResult=false isDesugared=true + RETURN 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? diff --git a/compiler/testData/ir/irText/identity.kt b/compiler/testData/ir/irText/identity.kt new file mode 100644 index 00000000000..c527823783b --- /dev/null +++ b/compiler/testData/ir/irText/identity.kt @@ -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 diff --git a/compiler/testData/ir/irText/identity.txt b/compiler/testData/ir/irText/identity.txt new file mode 100644 index 00000000000..e4c7abd37dc --- /dev/null +++ b/compiler/testData/ir/irText/identity.txt @@ -0,0 +1,22 @@ +IrFile /identity.kt + IrFunction public fun test1(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int): kotlin.Boolean + IrExpressionBody + BLOCK type= hasResult=false isDesugared=true + RETURN 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= hasResult=false isDesugared=true + RETURN 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= hasResult=false isDesugared=true + RETURN type= + BINARY_OP operator=EQEQEQ type=kotlin.Boolean related=null + GET_VAR a type=kotlin.Any? + GET_VAR b type=kotlin.Any? diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 0658c4226ce..67f879ddf68 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -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");