Get rid of excess method in compiled inline class with custom equals
^KT-54501: Fixed
This commit is contained in:
committed by
teamcity
parent
70c2f2b86f
commit
c990bbb26c
+14
-30
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+5
@@ -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)
|
||||
|
||||
|
||||
+88
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user