diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt index fe768da7688..970bfbbb72c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt @@ -24,6 +24,8 @@ import org.jetbrains.kotlin.backend.common.lower.irNot import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.irasdescriptors.containsNull import org.jetbrains.kotlin.backend.konan.irasdescriptors.isSubtypeOf +import org.jetbrains.kotlin.backend.konan.irasdescriptors.overrides +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.IrFile @@ -37,11 +39,13 @@ import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.builders.irGet +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.defaultOrNullableType import org.jetbrains.kotlin.ir.util.isNullConst +import org.jetbrains.kotlin.ir.util.simpleFunctions import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid /** @@ -116,6 +120,8 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, extensionReceiver = expression } + private val anyEquals = irBuiltins.anyClass.owner.simpleFunctions().single { it.name.asString() == "equals" } + private fun lowerEqeq(expression: IrCall): IrExpression { // TODO: optimize boxing? @@ -133,7 +139,7 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, if (expression.symbol == irBuiltins.eqeqSymbol) { lhs.type.getInlinedClass()?.let { - if (it == rhs.type.getInlinedClass()) { + if (it == rhs.type.getInlinedClass() && inlinedClassHasDefaultEquals(it)) { return genInlineClassEquals(expression.descriptor, rhs, lhs) } } @@ -143,6 +149,22 @@ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, } } + private fun inlinedClassHasDefaultEquals(irClass: IrClass): Boolean { + if (!irClass.descriptor.isInline) { + // Implicitly-inlined class, e.g. primitive one. + return true + } + + val equals = irClass.simpleFunctions() + .single { it.name.asString() == "equals" && it.valueParameters.size == 1 && it.overrides(anyEquals) } + + // Note: this is not absolutely correct for inline class from other module + // since custom equals implementation can be added after compilation but before linking. + // TODO: reimplement after inline classes get properly designed `equals` operator. + // see https://youtrack.jetbrains.com/issue/KT-26354. + return equals.descriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED + } + fun IrBuilderWithScope.genInlineClassEquals( descriptor: FunctionDescriptor, rhs: IrExpression,