[FIR2IR] More accurate equals intrinsic for double/float types

This commit is contained in:
Ivan Kochurkin
2021-10-18 20:29:42 +03:00
committed by TeamCityServer
parent b4fada82ae
commit 82591bb107
6 changed files with 47 additions and 29 deletions
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.fir.backend.generators
import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.expressions.* 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.fir.types.isNullable
import org.jetbrains.kotlin.ir.builders.primitiveOp1 import org.jetbrains.kotlin.ir.builders.primitiveOp1
import org.jetbrains.kotlin.ir.builders.primitiveOp2 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.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl 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.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol 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.classifierOrFail
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.isDoubleOrFloatWithoutNullability import org.jetbrains.kotlin.ir.types.isDoubleOrFloatWithoutNullability
import org.jetbrains.kotlin.ir.util.getSimpleFunction import org.jetbrains.kotlin.ir.util.getSimpleFunction
@@ -158,28 +161,44 @@ internal class OperatorExpressionGenerator(
comparisonInfo: PrimitiveConeNumericComparisonInfo?, comparisonInfo: PrimitiveConeNumericComparisonInfo?,
isLeftType: Boolean isLeftType: Boolean
): IrExpression { ): IrExpression {
return visitor.convertToIrExpression(this).asComparisonOperand( val isOriginalNullable = (this as? FirExpressionWithSmartcast)?.originalExpression?.typeRef?.isMarkedNullable ?: false
if (isLeftType) comparisonInfo?.leftType else comparisonInfo?.rightType, val irExpression = visitor.convertToIrExpression(this)
comparisonInfo?.comparisonType, val operandType = if (isLeftType) comparisonInfo?.leftType else comparisonInfo?.rightType
noImplicitCast = comparisonInfo?.leftType == 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 { fun eraseImplicitCast(): IrExpression {
return if (noImplicitCast && if (irExpression is IrTypeOperatorCall) {
this is IrTypeOperatorCall && val isDoubleOrFloatWithoutNullability = irExpression.type.isDoubleOrFloatWithoutNullability()
this.operator == IrTypeOperator.IMPLICIT_CAST && if (noImplicitCast && !isDoubleOrFloatWithoutNullability && irExpression.operator == IrTypeOperator.IMPLICIT_CAST) {
!this.type.isDoubleOrFloatWithoutNullability() return irExpression.argument
) { } else {
this.argument val simpleType = irExpression.type as? IrSimpleType
} else { if (isDoubleOrFloatWithoutNullability &&
this 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) { if (targetType == null) {
@@ -195,21 +214,20 @@ internal class OperatorExpressionGenerator(
typeConverter.classIdToSymbolMap[operandClassId]?.getSimpleFunction("to${targetType.lookupTag.classId.shortClassName.asString()}") typeConverter.classIdToSymbolMap[operandClassId]?.getSimpleFunction("to${targetType.lookupTag.classId.shortClassName.asString()}")
?: error("No conversion function for $operandType ~> $targetType") ?: error("No conversion function for $operandType ~> $targetType")
val dispatchReceiver = this@asComparisonOperand
val unsafeIrCall = IrCallImpl( val unsafeIrCall = IrCallImpl(
startOffset, endOffset, irExpression.startOffset, irExpression.endOffset,
conversionFunction.owner.returnType, conversionFunction.owner.returnType,
conversionFunction, conversionFunction,
valueArgumentsCount = 0, valueArgumentsCount = 0,
typeArgumentsCount = 0 typeArgumentsCount = 0
).also { ).also {
it.dispatchReceiver = dispatchReceiver it.dispatchReceiver = irExpression
} }
return if (operandType.isNullable) { return if (operandType.isNullable) {
val (receiverVariable, receiverVariableSymbol) = 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) components.createSafeCallConstruction(receiverVariable, receiverVariableSymbol, unsafeIrCall)
} else { } else {
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperIeee754Comparisons // !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 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!! fun equals6(a: Any?, b: Any?) = if (a is Double? && b is Double) a == b else null!!
@@ -1,5 +1,4 @@
// !LANGUAGE: +ProperIeee754Comparisons // !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 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!! fun equals6(a: Any?, b: Any?) = if (a is Float? && b is Float) a == b else null!!
@@ -113,3 +113,4 @@ fun Float.test6fr(x: Any): Boolean {
else -> false else -> false
} }
} }
@@ -94,7 +94,7 @@ FILE fqName:<root> fileName:/nullableFloatingPointEqeq.kt
if: CONST Boolean type=kotlin.Boolean value=true 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 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 <root>.testDI2' type=kotlin.Int? origin=null $this: GET_VAR 'val tmp_2: kotlin.Int? [val] declared in <root>.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 <root>.testDI2' type=kotlin.Any? origin=null GET_VAR 'y: kotlin.Any? declared in <root>.testDI2' type=kotlin.Any? origin=null
BRANCH BRANCH
if: CONST Boolean type=kotlin.Boolean value=true if: CONST Boolean type=kotlin.Boolean value=true
@@ -39,7 +39,8 @@ fun testDI2(x: Any?, y: Any?): Boolean {
EQEQ(arg0 = tmp2_safe_receiver, arg1 = null) -> null EQEQ(arg0 = tmp2_safe_receiver, arg1 = null) -> null
else -> tmp2_safe_receiver.toDouble() else -> tmp2_safe_receiver.toDouble()
} }
}, arg1 = y /*as Double */) }, arg1 = y /*as Double? */)
else -> false else -> false
} }
} }