From 82591bb1079c71fd09701758680ddc80114d3d4a Mon Sep 17 00:00:00 2001 From: Ivan Kochurkin Date: Mon, 18 Oct 2021 20:29:42 +0300 Subject: [PATCH] [FIR2IR] More accurate equals intrinsic for double/float types --- .../generators/OperatorExpressionGenerator.kt | 68 ++++++++++++------- .../ieee754/smartCastsForDouble.kt | 1 - .../ieee754/smartCastsForFloat.kt | 1 - .../floatingPointEquals.fir.kt.txt | 1 + .../nullableFloatingPointEqeq.fir.ir.txt | 2 +- .../nullableFloatingPointEqeq.fir.kt.txt | 3 +- 6 files changed, 47 insertions(+), 29 deletions(-) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt index ef4fbef090f..0fedad0cb04 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt @@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.backend.generators import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.isMarkedNullable import org.jetbrains.kotlin.fir.types.isNullable import org.jetbrains.kotlin.ir.builders.primitiveOp1 import org.jetbrains.kotlin.ir.builders.primitiveOp2 @@ -15,9 +15,12 @@ import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.isDoubleOrFloatWithoutNullability import org.jetbrains.kotlin.ir.util.getSimpleFunction @@ -158,28 +161,44 @@ internal class OperatorExpressionGenerator( comparisonInfo: PrimitiveConeNumericComparisonInfo?, isLeftType: Boolean ): IrExpression { - return visitor.convertToIrExpression(this).asComparisonOperand( - if (isLeftType) comparisonInfo?.leftType else comparisonInfo?.rightType, - comparisonInfo?.comparisonType, - noImplicitCast = comparisonInfo?.leftType == comparisonInfo?.rightType - ) - } + val isOriginalNullable = (this as? FirExpressionWithSmartcast)?.originalExpression?.typeRef?.isMarkedNullable ?: false + val irExpression = visitor.convertToIrExpression(this) + val operandType = if (isLeftType) comparisonInfo?.leftType else comparisonInfo?.rightType + val targetType = comparisonInfo?.comparisonType + val noImplicitCast = comparisonInfo?.leftType == comparisonInfo?.rightType - private fun IrExpression.asComparisonOperand( - operandType: ConeClassLikeType?, - targetType: ConeClassLikeType?, - noImplicitCast: Boolean - ): IrExpression { fun eraseImplicitCast(): IrExpression { - return if (noImplicitCast && - this is IrTypeOperatorCall && - this.operator == IrTypeOperator.IMPLICIT_CAST && - !this.type.isDoubleOrFloatWithoutNullability() - ) { - this.argument - } else { - this + if (irExpression is IrTypeOperatorCall) { + val isDoubleOrFloatWithoutNullability = irExpression.type.isDoubleOrFloatWithoutNullability() + if (noImplicitCast && !isDoubleOrFloatWithoutNullability && irExpression.operator == IrTypeOperator.IMPLICIT_CAST) { + return irExpression.argument + } else { + val simpleType = irExpression.type as? IrSimpleType + if (isDoubleOrFloatWithoutNullability && + isOriginalNullable && + simpleType?.hasQuestionMark == false + ) { + // Make it compatible with IR lowering + val nullableDoubleOrFloatType = IrSimpleTypeImpl( + simpleType.classifier, + true, + simpleType.arguments, + simpleType.annotations, + simpleType.abbreviation + ) + return IrTypeOperatorCallImpl( + irExpression.startOffset, + irExpression.endOffset, + nullableDoubleOrFloatType, + irExpression.operator, + nullableDoubleOrFloatType, + irExpression.argument + ) + } + } } + + return irExpression } if (targetType == null) { @@ -195,21 +214,20 @@ internal class OperatorExpressionGenerator( typeConverter.classIdToSymbolMap[operandClassId]?.getSimpleFunction("to${targetType.lookupTag.classId.shortClassName.asString()}") ?: error("No conversion function for $operandType ~> $targetType") - val dispatchReceiver = this@asComparisonOperand val unsafeIrCall = IrCallImpl( - startOffset, endOffset, + irExpression.startOffset, irExpression.endOffset, conversionFunction.owner.returnType, conversionFunction, valueArgumentsCount = 0, typeArgumentsCount = 0 ).also { - it.dispatchReceiver = dispatchReceiver + it.dispatchReceiver = irExpression } return if (operandType.isNullable) { val (receiverVariable, receiverVariableSymbol) = - components.createTemporaryVariableForSafeCallConstruction(dispatchReceiver, conversionScope) + components.createTemporaryVariableForSafeCallConstruction(irExpression, conversionScope) - unsafeIrCall.dispatchReceiver = IrGetValueImpl(startOffset, endOffset, receiverVariableSymbol) + unsafeIrCall.dispatchReceiver = IrGetValueImpl(irExpression.startOffset, irExpression.endOffset, receiverVariableSymbol) components.createSafeCallConstruction(receiverVariable, receiverVariableSymbol, unsafeIrCall) } else { diff --git a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt index 82e4db9e94e..e279293dd8e 100644 --- a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt +++ b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForDouble.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +ProperIeee754Comparisons -// IGNORE_BACKEND_FIR: JVM_IR fun equals5(a: Any?, b: Any?) = if (a is Double && b is Double?) a == b else null!! fun equals6(a: Any?, b: Any?) = if (a is Double? && b is Double) a == b else null!! diff --git a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt index 13c33280ed4..526f626e3f3 100644 --- a/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt +++ b/compiler/testData/codegen/bytecodeText/ieee754/smartCastsForFloat.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +ProperIeee754Comparisons -// IGNORE_BACKEND_FIR: JVM_IR fun equals5(a: Any?, b: Any?) = if (a is Float && b is Float?) a == b else null!! fun equals6(a: Any?, b: Any?) = if (a is Float? && b is Float) a == b else null!! diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/floatingPointEquals.fir.kt.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/floatingPointEquals.fir.kt.txt index a1ed83271ac..a8045172c87 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/floatingPointEquals.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/floatingPointEquals.fir.kt.txt @@ -113,3 +113,4 @@ fun Float.test6fr(x: Any): Boolean { else -> false } } + diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.ir.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.ir.txt index 1ab58c34d7d..0237b3a6b20 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.ir.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.ir.txt @@ -94,7 +94,7 @@ FILE fqName: fileName:/nullableFloatingPointEqeq.kt if: CONST Boolean type=kotlin.Boolean value=true then: CALL 'public open fun toDouble (): kotlin.Double declared in kotlin.Int' type=kotlin.Double origin=null $this: GET_VAR 'val tmp_2: kotlin.Int? [val] declared in .testDI2' type=kotlin.Int? origin=null - arg1: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double + arg1: TYPE_OP type=kotlin.Double? origin=IMPLICIT_CAST typeOperand=kotlin.Double? GET_VAR 'y: kotlin.Any? declared in .testDI2' type=kotlin.Any? origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.kt.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.kt.txt index e9599a84b7d..2321e225eae 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.kt.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.fir.kt.txt @@ -39,7 +39,8 @@ fun testDI2(x: Any?, y: Any?): Boolean { EQEQ(arg0 = tmp2_safe_receiver, arg1 = null) -> null else -> tmp2_safe_receiver.toDouble() } - }, arg1 = y /*as Double */) + }, arg1 = y /*as Double? */) else -> false } } +