Get rid of excess method in compiled inline class with custom equals

^KT-54501: Fixed
This commit is contained in:
vladislav.grechko
2022-10-17 16:42:18 +02:00
committed by teamcity
parent 70c2f2b86f
commit c990bbb26c
8 changed files with 204 additions and 34 deletions
@@ -5,6 +5,7 @@
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
@@ -28,8 +29,8 @@ 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
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
val jvmInlineClassPhase = makeIrFilePhase(
::JvmInlineClassLowering,
@@ -118,7 +119,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
buildPrimaryInlineClassConstructor(declaration, irConstructor)
buildBoxFunction(declaration)
buildUnboxFunction(declaration)
buildSpecializedEqualsMethod(declaration)
buildSpecializedEqualsMethodIfNeeded(declaration)
addJvmInlineAnnotation(declaration)
}
@@ -479,35 +480,29 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
irClass.declarations += function
}
fun buildSpecializedEqualsMethod(valueClass: IrClass) {
fun buildSpecializedEqualsMethodIfNeeded(valueClass: IrClass) {
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
// Return if we have already built specialized equals as static replacement of typed equals
if (function.body != null) return
val left = function.valueParameters[0]
val right = function.valueParameters[1]
val type = left.type.unboxInlineClass()
val typedEqualsStaticReplacement = findStaticReplacementForTypedEquals(valueClass)
val untypedEquals = valueClass.functions.single { it.overridesEqualsFromAny }
val untypedEquals = valueClass.functions.single { it.isEquals(context) }
function.body = context.createIrBuilder(valueClass.symbol).run {
irExprBody(
if (typedEqualsStaticReplacement == null) {
if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) {
val boxFunction = this@JvmInlineClassLowering.context.inlineClassReplacements.getBoxFunction(valueClass)
if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) {
val boxFunction = this@JvmInlineClassLowering.context.inlineClassReplacements.getBoxFunction(valueClass)
fun irBox(expr: IrExpression) = irCall(boxFunction).apply { putValueArgument(0, expr) }
fun irBox(expr: IrExpression) = irCall(boxFunction).apply { putValueArgument(0, expr) }
irCall(untypedEquals).apply {
dispatchReceiver = irBox(irGet(left))
putValueArgument(0, irBox(irGet(right)))
}
} else {
irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
irCall(untypedEquals).apply {
dispatchReceiver = irBox(irGet(left))
putValueArgument(0, irBox(irGet(right)))
}
} else {
irCall(typedEqualsStaticReplacement).apply {
putValueArgument(0, irGet(left))
putValueArgument(1, irGet(right))
}
irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
}
)
}
@@ -515,15 +510,4 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
valueClass.declarations += function
}
private fun findStaticReplacementForTypedEquals(valueClass: IrClass): IrFunction? {
fun isTypedEquals(irFunction: IrFunction): Boolean {
return irFunction.run {
name == EQUALS && returnType.isBoolean() && valueParameters.size == 1
&& (valueParameters[0].type.classFqName?.run { valueClass.hasEqualFqName(this) } ?: false)
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
}
}
return valueClass.functions
.singleOrNull { context.inlineClassReplacements.originalFunctionForStaticReplacement[it]?.run { isTypedEquals(this) } ?: false }
}
}
@@ -184,6 +184,7 @@ internal abstract class JvmValueClassAbstractLowering(val context: JvmBackendCon
function,
replacement,
when {
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)
@@ -64,6 +64,11 @@ 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.name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
specializedEqualsCache.computeIfAbsent(it.parentAsClass) { it }
}
it.isRemoveAtSpecialBuiltinStub() ->
null
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
@@ -1308,6 +1308,10 @@ fun IrBuiltIns.getKFunctionType(returnType: IrType, parameterTypes: List<IrType>
fun IdSignature?.isComposite(): Boolean =
this is IdSignature.CompositeSignature
val IrFunction.overridesEqualsFromAny
get() = name == OperatorNameConventions.EQUALS && valueParameters.size == 1 && valueParameters[0].type.isNullableAny()
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
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
}
@@ -1,7 +1,6 @@
// WITH_STDLIB
// CHECK_BYTECODE_LISTING
// FIR_IDENTICAL
// FIR_IDENTICAL
inline fun <T> useRef(value: T, f: (T) -> Boolean) = f(value)
@@ -0,0 +1,88 @@
@kotlin.Metadata
public interface I {
// source: 'inlineClassEqualsOverride.kt'
public abstract method equals-ACFGG4I(p0: int): boolean
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC1 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: double
private synthetic method <init>(p0: double): void
public synthetic final static method box-impl(p0: double): IC1
public static method constructor-impl(p0: double): double
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: double, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: double, p1: double): boolean
public final method getValue(): double
public method hashCode(): int
public static method hashCode-impl(p0: double): int
public method toString(): java.lang.String
public static method toString-impl(p0: double): java.lang.String
public synthetic final method unbox-impl(): double
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC2 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC2
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public final method equals-ACFGG4I(p0: int): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC3 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC3
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC4 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC4
public static method constructor-impl(p0: int): int
public final @org.jetbrains.annotations.NotNull method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Void
public final static @org.jetbrains.annotations.NotNull method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Void
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.Metadata
public final class InlineClassEqualsOverrideKt {
// source: 'inlineClassEqualsOverride.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}
@@ -2,6 +2,7 @@
// WORKS_WHEN_VALUE_CLASS
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
// TARGET_BACKEND: JVM_IR
// CHECK_BYTECODE_LISTING
import kotlin.math.abs
@@ -0,0 +1,88 @@
@kotlin.Metadata
public interface I {
// source: 'inlineClassEqualsOverride.kt'
public abstract method equals-ACFGG4I(p0: int): boolean
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC1 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: double
private synthetic method <init>(p0: double): void
public synthetic final static method box-impl(p0: double): IC1
public static method constructor-impl(p0: double): double
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: double, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: double, p1: double): boolean
public final method getValue(): double
public method hashCode(): int
public static method hashCode-impl(p0: double): int
public method toString(): java.lang.String
public static method toString-impl(p0: double): java.lang.String
public synthetic final method unbox-impl(): double
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC2 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC2
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public method equals-ACFGG4I(p0: int): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC3 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC3
public static method constructor-impl(p0: int): int
public method equals(p0: java.lang.Object): boolean
public static method equals-impl(p0: int, p1: java.lang.Object): boolean
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.jvm.JvmInline
@kotlin.Metadata
public final class IC4 {
// source: 'inlineClassEqualsOverride.kt'
private final field value: int
private synthetic method <init>(p0: int): void
public synthetic final static method box-impl(p0: int): IC4
public static method constructor-impl(p0: int): int
public @org.jetbrains.annotations.NotNull method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Void
public static @org.jetbrains.annotations.NotNull method equals-impl(p0: int, @org.jetbrains.annotations.Nullable p1: java.lang.Object): java.lang.Void
public final static method equals-impl0(p0: int, p1: int): boolean
public final method getValue(): int
public method hashCode(): int
public static method hashCode-impl(p0: int): int
public method toString(): java.lang.String
public static method toString-impl(p0: int): java.lang.String
public synthetic final method unbox-impl(): int
}
@kotlin.Metadata
public final class InlineClassEqualsOverrideKt {
// source: 'inlineClassEqualsOverride.kt'
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
}