Improve support of custom equals in inline classes
- Ensure that typed equals parameter's type is a star projection of corresponding inline class - Make possible to declare typed equals that returns 'Nothing' - Forbid type parameters in typed equals operator declaration ^KT-54909 fixed ^KT-54910 fixed
This commit is contained in:
+2
-15
@@ -6,11 +6,10 @@
|
||||
package org.jetbrains.kotlin.backend.common.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.IrGeneratorContextBase
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -38,23 +37,11 @@ class MethodsFromAnyGeneratorForLowerings(val context: BackendContext, val irCla
|
||||
|
||||
fun createEqualsMethodDeclaration(): IrSimpleFunction =
|
||||
irClass.addSyntheticFunction("equals", context.irBuiltIns.booleanType).apply {
|
||||
overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals(context) }
|
||||
overriddenSymbols = irClass.collectOverridenSymbols { it.isEquals() }
|
||||
addValueParameter("other", context.irBuiltIns.anyNType)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun IrFunction.isToString(): Boolean =
|
||||
name.asString() == "toString" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isHashCode() =
|
||||
name.asString() == "hashCode" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isEquals(context: BackendContext) =
|
||||
name.asString() == "equals" &&
|
||||
extensionReceiverParameter == null && contextReceiverParametersCount == 0 &&
|
||||
valueParameters.singleOrNull()?.type == context.irBuiltIns.anyNType
|
||||
|
||||
|
||||
fun IrClass.collectOverridenSymbols(predicate: (IrFunction) -> Boolean): List<IrSimpleFunctionSymbol> =
|
||||
superTypes.mapNotNull { it.getClass()?.functions?.singleOrNull(predicate)?.symbol }
|
||||
}
|
||||
|
||||
-1
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
|
||||
import org.jetbrains.kotlin.backend.common.ScopeWithIr
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isHashCode
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||
|
||||
+1
-3
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isEquals
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
||||
@@ -29,7 +28,6 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
|
||||
|
||||
val jvmInlineClassPhase = makeIrFilePhase(
|
||||
@@ -488,7 +486,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
||||
val right = function.valueParameters[1]
|
||||
val type = left.type.unboxInlineClass()
|
||||
|
||||
val untypedEquals = valueClass.functions.single { it.isEquals(context) }
|
||||
val untypedEquals = valueClass.functions.single { it.isEquals() }
|
||||
|
||||
function.body = context.createIrBuilder(valueClass.symbol).run {
|
||||
val context = this@JvmInlineClassLowering.context
|
||||
|
||||
+1
-4
@@ -5,8 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isEquals
|
||||
import org.jetbrains.kotlin.backend.common.lower.MethodsFromAnyGeneratorForLowerings.Companion.isToString
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.common.lower.irCatch
|
||||
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
|
||||
@@ -705,7 +703,6 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
||||
}
|
||||
}
|
||||
if (expression.isSpecializedMFVCEqEq) {
|
||||
val backendContext = context
|
||||
return context.createIrBuilder(getCurrentScopeSymbol()).irBlock {
|
||||
val leftArgument = expression.getValueArgument(0)!!
|
||||
val rightArgument = expression.getValueArgument(1)!!
|
||||
@@ -736,7 +733,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV
|
||||
}
|
||||
} else {
|
||||
// left one is unboxed, right is not
|
||||
val equals = leftClass.functions.single { it.isEquals(backendContext) }
|
||||
val equals = leftClass.functions.single { it.isEquals() }
|
||||
+irCall(equals).apply {
|
||||
copyTypeArgumentsFrom(expression)
|
||||
dispatchReceiver = leftArgument
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
|
||||
function,
|
||||
replacement,
|
||||
when {
|
||||
function.isTypedEquals -> InlineClassAbi.mangledNameFor(function, mangleReturnTypes = false, useOldMangleRules = false)
|
||||
function.isTypedEquals() -> InlineClassAbi.mangledNameFor(function, mangleReturnTypes = false, useOldMangleRules = false)
|
||||
// If the original function has signature which need mangling we still need to replace it with a mangled version.
|
||||
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) && when (specificMangle) {
|
||||
SpecificMangle.Inline -> function.signatureRequiresMangling(includeInline = true, includeMFVC = false)
|
||||
|
||||
+2
-5
@@ -5,10 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.classFileContainsMethod
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.extensionReceiverName
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isStaticValueClassReplacement
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.parentClassId
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.*
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -64,7 +61,7 @@ class MemoizedInlineClassReplacements(
|
||||
// Mangle all functions in the body of an inline class
|
||||
it.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true ->
|
||||
when {
|
||||
it.isTypedEquals -> createStaticReplacement(it).also {
|
||||
it.isTypedEquals() -> createStaticReplacement(it).also {
|
||||
it.name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
||||
specializedEqualsCache.computeIfAbsent(it.parentAsClass) { it }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.overrides.FakeOverrideBuilderStrategy
|
||||
import org.jetbrains.kotlin.ir.overrides.IrOverridingUtil
|
||||
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy
|
||||
import org.jetbrains.kotlin.ir.overrides.IrUnimplementedOverridesStrategy.ProcessAsFakeOverrides
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl
|
||||
@@ -1311,10 +1310,24 @@ fun IrBuiltIns.getKFunctionType(returnType: IrType, parameterTypes: List<IrType>
|
||||
fun IdSignature?.isComposite(): Boolean =
|
||||
this is IdSignature.CompositeSignature
|
||||
|
||||
val IrFunction.isTypedEquals: Boolean
|
||||
get() {
|
||||
val parentClass = parent as? IrClass ?: return false
|
||||
return name == OperatorNameConventions.EQUALS && returnType.isBoolean() && valueParameters.size == 1
|
||||
&& (valueParameters[0].type.classFqName?.run { parentClass.hasEqualFqName(this) } ?: false)
|
||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null && parentClass.isValue
|
||||
}
|
||||
fun IrFunction.isToString(): Boolean =
|
||||
name.asString() == "toString" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isHashCode() =
|
||||
name.asString() == "hashCode" && extensionReceiverParameter == null && contextReceiverParametersCount == 0 && valueParameters.isEmpty()
|
||||
|
||||
fun IrFunction.isEquals() =
|
||||
name.asString() == "equals" &&
|
||||
extensionReceiverParameter == null && contextReceiverParametersCount == 0 &&
|
||||
valueParameters.singleOrNull()?.type?.isNullableAny() == true
|
||||
|
||||
fun IrFunction.isTypedEquals(): Boolean {
|
||||
val parentClass = parent as? IrClass ?: return false
|
||||
val enclosingClassStartProjection = parentClass.symbol.starProjectedType
|
||||
return name == OperatorNameConventions.EQUALS
|
||||
&& (returnType.isBoolean() || returnType.isNothing())
|
||||
&& valueParameters.size == 1
|
||||
&& (valueParameters[0].type == enclosingClassStartProjection)
|
||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
|
||||
&& parentClass.isValue
|
||||
}
|
||||
Reference in New Issue
Block a user