Support custom 'equals' for inline classes

This commit is contained in:
Svyatoslav Scherbina
2018-08-23 14:41:35 +03:00
committed by SvyatoslavScherbina
parent 8f1b94f38b
commit a52dd55d42
@@ -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,