diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index 245e70e7b99..98ff37a6efd 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl import org.jetbrains.kotlin.ir.transformStatement import org.jetbrains.kotlin.ir.types.classOrNull import org.jetbrains.kotlin.ir.types.isNullable +import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name @@ -328,7 +329,7 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV allScopes.push(createScope(source)) source.body = context.createIrBuilder(source.symbol, source.startOffset, source.endOffset).run { if (inverted) { - irExprBody(irCall(target).apply { + irExprBody(irCall(target).apply { passTypeArgumentsFrom(source) val structure: List = replacements.bindingParameterTemplateStructure[original]!! var flattenedIndex = 0 @@ -490,9 +491,54 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } } - @Suppress("ControlFlowWithEmptyBody") - if (expression.isSpecializedInlineClassEqEq) { - // todo + if (expression.isSpecializedMFVCEqEq) { + val leftArgument = expression.getValueArgument(0)!!.transform(this, null) + val rightArgument = expression.getValueArgument(1)!!.transform(this, null) + val leftImplementation = valueDeclarationsRemapper.implementationAgnostic(leftArgument) + val rightImplementation = valueDeclarationsRemapper.implementationAgnostic(rightArgument) + if (leftImplementation != null) { + val leftClass = leftImplementation.regularDeclarations.valueClass + if (rightImplementation != null) { + val rightClass = rightImplementation.regularDeclarations.valueClass + require(leftClass == rightClass) { "Equals for different classes: $leftClass and $rightClass called" } + return context.createIrBuilder(expression.symbol).run { + irCall(leftImplementation.regularDeclarations.specializedEqualsMethod).apply { + val arguments = + (leftImplementation.virtualFields + rightImplementation.virtualFields).map { it.makeGetter(this@run, Unit) } + arguments.forEachIndexed { index, argument -> putValueArgument(index, argument) } + } + } + } else { + val equals = leftClass.functions.single { it.name.asString() == "equals" && it.overriddenSymbols.isNotEmpty() } + return super.visitCall(context.createIrBuilder(expression.symbol).run { + irCall(equals).apply { + copyTypeArgumentsFrom(expression) + dispatchReceiver = leftArgument + putValueArgument(0, rightArgument) + } as IrCall + }) + } + } else if (rightImplementation != null) { + if (leftArgument.isNullConst()) { + return context.createIrBuilder(expression.symbol).irBlock { + +rightArgument + +irFalse() + }.transform(this, null) + } + if (leftArgument.type.classOrNull == rightArgument.type.classOrNull && leftArgument.type.isNullable()) { + return context.createIrBuilder(expression.symbol).irBlock { + val leftValue = irTemporary(leftArgument) + +irIfNull(context.irBuiltIns.booleanType, irGet(leftValue), irFalse(), irBlock { + val nonNullLeftArgumentVariable = + irTemporary(irImplicitCast(irGet(leftValue), leftArgument.type.makeNotNull())) + +irCall(context.irBuiltIns.eqeqSymbol).apply { + putValueArgument(0, irGet(nonNullLeftArgumentVariable)) + putValueArgument(1, rightArgument) + } + }) + }.transform(this@JvmMultiFieldValueClassLowering, null) + } + } } return super.visitCall(expression) } @@ -542,11 +588,12 @@ private class JvmMultiFieldValueClassLowering(context: JvmBackendContext) : JvmV } } - // Note that reference equality (x === y) is not allowed on values of inline class type, + // Note that reference equality (x === y) is not allowed on values of MFVC class type, // so it is enough to check for eqeq. - private val IrCall.isSpecializedInlineClassEqEq: Boolean + private val IrCall.isSpecializedMFVCEqEq: Boolean get() = symbol == context.irBuiltIns.eqeqSymbol && - getValueArgument(0)?.type?.classOrNull?.owner?.takeIf { it.isMultiFieldValueClass } != null + listOf(getValueArgument(0)!!, getValueArgument(1)!!) + .any { it.type.classOrNull?.owner?.takeIf { it.isMultiFieldValueClass } != null } override fun visitGetField(expression: IrGetField): IrExpression { val field = expression.symbol.owner diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt index c071eb2180d..9b5714b54bf 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DataClassMembersGenerator.kt @@ -128,7 +128,7 @@ abstract class DataClassMembersGenerator( fun generateEqualsMethodBody(properties: List) { val irType = irClass.defaultType - if (!irClass.isSingleFieldValueClass) { + if (!irClass.isValue) { +irIfThenReturnTrue(irEqeqeq(irThis(), irOther())) } +irIfThenReturnFalse(irNotIs(irOther(), irType)) diff --git a/compiler/testData/codegen/box/valueClasses/simple.kt b/compiler/testData/codegen/box/valueClasses/simple.kt index 526b33b88ce..ea2c0c4edb6 100644 --- a/compiler/testData/codegen/box/valueClasses/simple.kt +++ b/compiler/testData/codegen/box/valueClasses/simple.kt @@ -1,3 +1,5 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// https://youtrack.jetbrains.com/issue/KT-52236/Different-modality-in-psi-and-fir // CHECK_BYTECODE_LISTING // WITH_STDLIB // TARGET_BACKEND: JVM_IR @@ -40,7 +42,7 @@ interface Base3 { } @JvmInline -value class R(val x: Int, val y: UInt, override val z: E, val t: A): Base1, Base3 +value class R(val x: Int, val y: UInt, override val z: E, val t: A) : Base1, Base3 fun > f(r: R) { println(r) @@ -89,14 +91,16 @@ interface Base1 { val fakeOverrideRegular: Int get() = TODO() } + interface Base2 { var l: R> } + interface Base4 { var l: T } -class NotInlined(override var l: R>, var y: Int): Base1, Base2, Base4>> { +class NotInlined(override var l: R>, var y: Int) : Base1, Base2, Base4>> { override fun toString(): String = l.toString() + l.z.x.x.z init { @@ -150,6 +154,22 @@ fun reuseBoxed(list: MutableList>>) { list.add(list.last()) } +fun supply(x: Boolean) {} +fun equalsChecks(left: R>, right: R>) { + supply(left == right) + supply(left as Any == right) + supply(left == right as Any) + supply(left as Any == right as Any) + supply(null == right) + supply(left == null) + supply(null as Any? == right) + supply(null as R>? == right) + supply(left == null as Any?) + supply(left == null as R>?) + supply(left as R>? == right) + supply(left == right as R>?) +} + // todo add default parameters -fun box() = "bad"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) } +fun box() = "OK"//.also { h(R(1, 2U, E(D(C(3, B(4U), "5"))), A(listOf(listOf(6))))) } diff --git a/compiler/testData/codegen/box/valueClasses/simple.txt b/compiler/testData/codegen/box/valueClasses/simple.txt index cebd031dc0b..760160e0fce 100644 --- a/compiler/testData/codegen/box/valueClasses/simple.txt +++ b/compiler/testData/codegen/box/valueClasses/simple.txt @@ -36,6 +36,42 @@ public final class B { public synthetic final method unbox-impl(): int } +@kotlin.Metadata +public final class Base1$DefaultImpls { + // source: 'simple.kt' + public static @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(@org.jetbrains.annotations.NotNull p0: Base1): R + public static method getFakeOverrideRegular(@org.jetbrains.annotations.NotNull p0: Base1): int + public final inner class Base1$DefaultImpls +} + +@kotlin.Metadata +public interface Base1 { + // source: 'simple.kt' + public abstract @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(): R + public abstract method getFakeOverrideRegular(): int + public final inner class Base1$DefaultImpls +} + +@kotlin.Metadata +public interface Base2 { + // source: 'simple.kt' + public abstract @org.jetbrains.annotations.NotNull method getL(): R + public abstract method setL-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void +} + +@kotlin.Metadata +public interface Base3 { + // source: 'simple.kt' + public abstract @org.jetbrains.annotations.NotNull method getZ(): E +} + +@kotlin.Metadata +public interface Base4 { + // source: 'simple.kt' + public abstract method getL(): java.lang.Object + public abstract method setL(p0: java.lang.Object): void +} + @kotlin.jvm.JvmInline @kotlin.Metadata public final class C { @@ -46,12 +82,15 @@ public final class C { private synthetic method (p0: int, p1: int, p2: java.lang.String): void public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): C public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean public final method getX(): int public final method getY(): int public final @org.jetbrains.annotations.NotNull method getZ(): java.lang.String + public method hashCode(): int public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String public synthetic final method unbox-impl0(): int public synthetic final method unbox-impl1(): int @@ -69,13 +108,16 @@ public final class D { public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): D public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void public final static @org.jetbrains.annotations.NotNull method constructor-impl(p0: int, p1: int, p2: int): D + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean public final method getX$x(): int public final method getX$y(): int public final @org.jetbrains.annotations.NotNull method getX$z(): java.lang.String public final @org.jetbrains.annotations.NotNull method getX(): C + public method hashCode(): int public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String public synthetic final method unbox-impl0(): int public synthetic final method unbox-impl1(): int @@ -92,14 +134,19 @@ public final class E { private synthetic method (p0: int, p1: int, p2: java.lang.String): void public synthetic final static method box-impl(p0: int, p1: int, p2: java.lang.String): E public final static method constructor-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean public static method equals-impl(p0: int, p1: int, p2: java.lang.String, p3: java.lang.Object): boolean public final static method equals-impl0(p0: int, p1: int, p2: java.lang.String, p3: int, p4: int, p5: java.lang.String): boolean + public final static @org.jetbrains.annotations.NotNull method getWithNonTrivialSetters-impl(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): D public final method getX$x$x(): int public final method getX$x$y(): int public final @org.jetbrains.annotations.NotNull method getX$x$z(): java.lang.String public final @org.jetbrains.annotations.NotNull method getX$x(): C public final @org.jetbrains.annotations.NotNull method getX(): D + public method hashCode(): int public static method hashCode-impl(p0: int, p1: int, p2: java.lang.String): int + public final static method setWithNonTrivialSetters-sUp7gFk(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String, p3: int, p4: int, @org.jetbrains.annotations.NotNull p5: java.lang.String): void + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: int, p1: int, p2: java.lang.String): java.lang.String public synthetic final method unbox-impl0(): int public synthetic final method unbox-impl1(): int @@ -115,9 +162,23 @@ public final class NotInlined { private field l$z$x$x$x: int private field l$z$x$x$y: int private @org.jetbrains.annotations.NotNull field l$z$x$x$z: java.lang.String + private @org.jetbrains.annotations.NotNull field withNonTrivialGettersWithBF$t: java.util.List + private field withNonTrivialGettersWithBF$y: int + private field withNonTrivialGettersWithBF$z$x$x$x$1: int + private field withNonTrivialGettersWithBF$z$x$x$x: int + private field withNonTrivialGettersWithBF$z$x$x$y: int + private @org.jetbrains.annotations.NotNull field withNonTrivialGettersWithBF$z$x$x$z: java.lang.String + private @org.jetbrains.annotations.NotNull field withNonTrivialSettersWithBF$t: java.util.List + private field withNonTrivialSettersWithBF$y: int + private field withNonTrivialSettersWithBF$z$x$x$x$1: int + private field withNonTrivialSettersWithBF$z$x$x$x: int + private field withNonTrivialSettersWithBF$z$x$x$y: int + private @org.jetbrains.annotations.NotNull field withNonTrivialSettersWithBF$z$x$x$z: java.lang.String private field y: int private method (p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int): void public synthetic method (p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int, p7: kotlin.jvm.internal.DefaultConstructorMarker): void + public @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(): R + public method getFakeOverrideRegular(): int public final @org.jetbrains.annotations.NotNull method getL$t(): java.util.List public final method getL$y(): int public final method getL$z$x$x$x(): int @@ -126,9 +187,16 @@ public final class NotInlined { public final @org.jetbrains.annotations.NotNull method getL$z$x$x(): C public final @org.jetbrains.annotations.NotNull method getL$z$x(): D public final @org.jetbrains.annotations.NotNull method getL$z(): E - public final @org.jetbrains.annotations.NotNull method getL(): R + public @org.jetbrains.annotations.NotNull method getL(): R + public synthetic bridge method getL(): java.lang.Object + public final @org.jetbrains.annotations.NotNull method getWithNonTrivialGettersWithBF(): R + public final @org.jetbrains.annotations.NotNull method getWithNonTrivialSetters(): R + public final @org.jetbrains.annotations.NotNull method getWithNonTrivialSettersWithBF(): R public final method getY(): int - public final method setL-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public synthetic bridge method setL(p0: java.lang.Object): void + public method setL-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public final method setWithNonTrivialSetters-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public final method setWithNonTrivialSettersWithBF-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void public final method setY(p0: int): void public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public final method trySetter(): void @@ -147,8 +215,13 @@ public final class R { private synthetic method (p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): void public synthetic final static method box-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): R public final static method constructor-impl(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean public static method equals-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: java.lang.Object): boolean public final static method equals-impl0(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List, p6: int, p7: int, p8: int, p9: int, p10: java.lang.String, p11: java.util.List): boolean + public @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC(): R + public static @org.jetbrains.annotations.NotNull method getFakeOverrideMFVC-impl(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): R + public method getFakeOverrideRegular(): int + public static method getFakeOverrideRegular-impl(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): int public final @org.jetbrains.annotations.NotNull method getT(): java.util.List public final method getY(): int public final method getZ$x$x$x(): int @@ -157,7 +230,9 @@ public final class R { public final @org.jetbrains.annotations.NotNull method getZ$x$x(): C public final @org.jetbrains.annotations.NotNull method getZ$x(): D public final @org.jetbrains.annotations.NotNull method getZ(): E + public method hashCode(): int public static method hashCode-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String public static method toString-impl(p0: int, p1: int, p2: int, p3: int, p4: java.lang.String, p5: java.util.List): java.lang.String public synthetic final method unbox-impl0(): int public synthetic final method unbox-impl1(): int @@ -171,11 +246,14 @@ public final class R { public final class SimpleKt { // source: 'simple.kt' public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String - public final static method ekeke(@org.jetbrains.annotations.NotNull p0: NotInlined): void + public final static method equalsChecks-GPBa7dw-yJnZWq8(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List, p6: int, p7: int, p8: int, p9: int, @org.jetbrains.annotations.NotNull p10: java.lang.String, @org.jetbrains.annotations.NotNull p11: java.util.List): void public final static method f-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void public final static method g-sUp7gFk-ZTiL54U(p0: int, p1: int, @org.jetbrains.annotations.NotNull p2: java.lang.String): void public final static method h-sUp7gFk-Y_3tvh0(p0: int, p1: int, p2: int, p3: int, @org.jetbrains.annotations.NotNull p4: java.lang.String, @org.jetbrains.annotations.NotNull p5: java.util.List): void public final static method h1(): void public final static @org.jetbrains.annotations.NotNull method inlined-OsBMiQA(p0: int, p1: int, p2: int): D public final static @org.jetbrains.annotations.NotNull method notInlined-OsBMiQA(p0: int, p1: int, p2: int): D + public final static method reuseBoxed(@org.jetbrains.annotations.NotNull p0: java.util.List): void + public final static method supply(p0: boolean): void + public final static method testVars(@org.jetbrains.annotations.NotNull p0: NotInlined): void }