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
|
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.createIrBuilder
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
|
||||||
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
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.util.*
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.name.Name
|
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.resolve.JVM_INLINE_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions.EQUALS
|
|
||||||
|
|
||||||
val jvmInlineClassPhase = makeIrFilePhase(
|
val jvmInlineClassPhase = makeIrFilePhase(
|
||||||
::JvmInlineClassLowering,
|
::JvmInlineClassLowering,
|
||||||
@@ -118,7 +119,7 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
buildPrimaryInlineClassConstructor(declaration, irConstructor)
|
buildPrimaryInlineClassConstructor(declaration, irConstructor)
|
||||||
buildBoxFunction(declaration)
|
buildBoxFunction(declaration)
|
||||||
buildUnboxFunction(declaration)
|
buildUnboxFunction(declaration)
|
||||||
buildSpecializedEqualsMethod(declaration)
|
buildSpecializedEqualsMethodIfNeeded(declaration)
|
||||||
addJvmInlineAnnotation(declaration)
|
addJvmInlineAnnotation(declaration)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,35 +480,29 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
irClass.declarations += function
|
irClass.declarations += function
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildSpecializedEqualsMethod(valueClass: IrClass) {
|
fun buildSpecializedEqualsMethodIfNeeded(valueClass: IrClass) {
|
||||||
val function = context.inlineClassReplacements.getSpecializedEqualsMethod(valueClass, context.irBuiltIns)
|
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 left = function.valueParameters[0]
|
||||||
val right = function.valueParameters[1]
|
val right = function.valueParameters[1]
|
||||||
val type = left.type.unboxInlineClass()
|
val type = left.type.unboxInlineClass()
|
||||||
|
|
||||||
val typedEqualsStaticReplacement = findStaticReplacementForTypedEquals(valueClass)
|
val untypedEquals = valueClass.functions.single { it.isEquals(context) }
|
||||||
val untypedEquals = valueClass.functions.single { it.overridesEqualsFromAny }
|
|
||||||
|
|
||||||
function.body = context.createIrBuilder(valueClass.symbol).run {
|
function.body = context.createIrBuilder(valueClass.symbol).run {
|
||||||
irExprBody(
|
irExprBody(
|
||||||
if (typedEqualsStaticReplacement == null) {
|
if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) {
|
||||||
if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) {
|
val boxFunction = this@JvmInlineClassLowering.context.inlineClassReplacements.getBoxFunction(valueClass)
|
||||||
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 {
|
irCall(untypedEquals).apply {
|
||||||
dispatchReceiver = irBox(irGet(left))
|
dispatchReceiver = irBox(irGet(left))
|
||||||
putValueArgument(0, irBox(irGet(right)))
|
putValueArgument(0, irBox(irGet(right)))
|
||||||
}
|
|
||||||
} else {
|
|
||||||
irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
irCall(typedEqualsStaticReplacement).apply {
|
irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
|
||||||
putValueArgument(0, irGet(left))
|
|
||||||
putValueArgument(1, irGet(right))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -515,15 +510,4 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass
|
|||||||
valueClass.declarations += function
|
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,
|
function,
|
||||||
replacement,
|
replacement,
|
||||||
when {
|
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.
|
// 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) {
|
(!function.isFakeOverride || function.findInterfaceImplementation(context.state.jvmDefaultMode) != null) && when (specificMangle) {
|
||||||
SpecificMangle.Inline -> function.signatureRequiresMangling(includeInline = true, includeMFVC = false)
|
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
|
// Mangle all functions in the body of an inline class
|
||||||
it.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true ->
|
it.parent.safeAs<IrClass>()?.isSingleFieldValueClass == true ->
|
||||||
when {
|
when {
|
||||||
|
it.isTypedEquals -> createStaticReplacement(it).also {
|
||||||
|
it.name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
|
||||||
|
specializedEqualsCache.computeIfAbsent(it.parentAsClass) { it }
|
||||||
|
}
|
||||||
|
|
||||||
it.isRemoveAtSpecialBuiltinStub() ->
|
it.isRemoveAtSpecialBuiltinStub() ->
|
||||||
null
|
null
|
||||||
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
it.isValueClassMemberFakeOverriddenFromJvmDefaultInterfaceMethod() ||
|
||||||
|
|||||||
@@ -1308,6 +1308,10 @@ fun IrBuiltIns.getKFunctionType(returnType: IrType, parameterTypes: List<IrType>
|
|||||||
fun IdSignature?.isComposite(): Boolean =
|
fun IdSignature?.isComposite(): Boolean =
|
||||||
this is IdSignature.CompositeSignature
|
this is IdSignature.CompositeSignature
|
||||||
|
|
||||||
val IrFunction.overridesEqualsFromAny
|
val IrFunction.isTypedEquals: Boolean
|
||||||
get() = name == OperatorNameConventions.EQUALS && valueParameters.size == 1 && valueParameters[0].type.isNullableAny()
|
get() {
|
||||||
&& contextReceiverParametersCount == 0 && extensionReceiverParameter == null
|
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
|
// WITH_STDLIB
|
||||||
// CHECK_BYTECODE_LISTING
|
// CHECK_BYTECODE_LISTING
|
||||||
// FIR_IDENTICAL
|
// FIR_IDENTICAL
|
||||||
// FIR_IDENTICAL
|
|
||||||
|
|
||||||
inline fun <T> useRef(value: T, f: (T) -> Boolean) = f(value)
|
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
|
// WORKS_WHEN_VALUE_CLASS
|
||||||
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses
|
||||||
// TARGET_BACKEND: JVM_IR
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
// CHECK_BYTECODE_LISTING
|
||||||
|
|
||||||
import kotlin.math.abs
|
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