diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index c66dd900c86..8ab1131b9fc 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -21647,6 +21647,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/inlineClasses/inlineInCompanionGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("inlineOverInlineWithCustomEquals.kt") + public void testInlineOverInlineWithCustomEquals() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("inlineToString.kt") public void testInlineToString() throws Exception { @@ -22265,6 +22271,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/inlineClasses/kt54455.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("kt54536.kt") + public void testKt54536() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/kt54536.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("kt54603.kt") public void testKt54603() throws Exception { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 5db4f804020..9dd4c46cb1a 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt @@ -491,19 +491,31 @@ private class JvmInlineClassLowering(context: JvmBackendContext) : JvmValueClass val untypedEquals = valueClass.functions.single { it.isEquals(context) } function.body = context.createIrBuilder(valueClass.symbol).run { + val context = this@JvmInlineClassLowering.context + val underlyingType = getInlineClassUnderlyingType(valueClass) irExprBody( if (untypedEquals.origin == IrDeclarationOrigin.DEFINED) { - val boxFunction = this@JvmInlineClassLowering.context.inlineClassReplacements.getBoxFunction(valueClass) + val boxFunction = context.inlineClassReplacements.getBoxFunction(valueClass) fun irBox(expr: IrExpression) = irCall(boxFunction).apply { putValueArgument(0, expr) } - val underlyingType = getInlineClassUnderlyingType(valueClass) irCall(untypedEquals).apply { dispatchReceiver = irBox(coerceInlineClasses(irGet(left), left.type, underlyingType)) putValueArgument(0, irBox(coerceInlineClasses(irGet(right), right.type, underlyingType))) } } else { - irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type)) + val underlyingClass = underlyingType.getClass() + // We can't directly compare unboxed values of underlying inline class as this class can have custom equals + if (underlyingClass?.isSingleFieldValueClass == true && !underlyingType.isNullable()) { + val underlyingClassEq = + context.inlineClassReplacements.getSpecializedEqualsMethod(underlyingClass, context.irBuiltIns) + irCall(underlyingClassEq).apply { + putValueArgument(0, coerceInlineClasses(irGet(left), left.type, underlyingType)) + putValueArgument(1, coerceInlineClasses(irGet(right), right.type, underlyingType)) + } + } else { + irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type)) + } } ) } diff --git a/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt new file mode 100644 index 00000000000..f039e51c3cd --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt @@ -0,0 +1,69 @@ +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// TARGET_BACKEND: JVM_IR + + +OPTIONAL_JVM_INLINE_ANNOTATION +value class A(val x: Int) { + fun equals(other: A) = true +} + +class C + +OPTIONAL_JVM_INLINE_ANNOTATION +value class B1(val x: A) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class B2(val x: A?) + + +OPTIONAL_JVM_INLINE_ANNOTATION +value class D1(val x: C) { + fun equals(other: D1) = true +} + + +OPTIONAL_JVM_INLINE_ANNOTATION +value class D2(val x: C?) { + fun equals(other: D2) = true +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class E1(val x: D1) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class E2(val x: D2) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class F(val x: T) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class G(val x: T) + +OPTIONAL_JVM_INLINE_ANNOTATION +value class H(val x: F) + +fun box(): String { + if (E1(D1(C())) != E1(D1(C()))) return "Fail 1" + + if (E2(D2(C())) != E2(D2(C()))) return "Fail 2.1" + if (E2(D2(null)) != E2(D2(C()))) return "Fail 2.2" + if (E2(D2(C())) != E2(D2(null))) return "Fail 2.3" + if (E2(D2(null)) != E2(D2(null))) return "Fail 2.4" + + if (B1(A(0)) != B1(A(5))) return "Fail 3" + + if (B2(A(0)) != B2(A(5))) return "Fail 4.1" + if (B2(null) == B2(A(5))) return "Fail 4.2" + if (B2(A(0)) == B2(null)) return "Fail 4.3" + if (B2(null) != B2(null)) return "Fail 4.4" + + if (F(D1(C())) != F(D1(C()))) return "Fail 5" + + if (G(D1(C())) != G(D1(C()))) return "Fail 6" + + if (H(F(1)) == H(F(2))) return "Fail 7" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/inlineClasses/kt54536.kt b/compiler/testData/codegen/box/inlineClasses/kt54536.kt new file mode 100644 index 00000000000..ba5d880ba4b --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/kt54536.kt @@ -0,0 +1,18 @@ +// WITH_STDLIB +// WORKS_WHEN_VALUE_CLASS +// LANGUAGE: +ValueClasses, +CustomEqualsInInlineClasses +// TARGET_BACKEND: JVM_IR + +OPTIONAL_JVM_INLINE_ANNOTATION +value class A(val x: Int) { + fun equals(other: A) = x % 5 == other.x % 5 +} + +OPTIONAL_JVM_INLINE_ANNOTATION +value class B(val x: A) + +fun box() = if (B(A(0)) == B(A(5))) "OK" else "Fail" + +// CHECK_BYTECODE_TEXT +// 0 INVOKESTATIC B.box-impl +// 0 INVOKESTATIC A.box-impl \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index bf6c01842b8..569da439324 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -21647,6 +21647,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inlineClasses/inlineInCompanionGeneric.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("inlineOverInlineWithCustomEquals.kt") + public void testInlineOverInlineWithCustomEquals() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("inlineToString.kt") public void testInlineToString() throws Exception { @@ -22265,6 +22271,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/inlineClasses/kt54455.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); } + @Test + @TestMetadata("kt54536.kt") + public void testKt54536() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/kt54536.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal()); + } + @Test @TestMetadata("kt54603.kt") public void testKt54603() throws Exception {