Fix equality comparison for inline class over inline class
^KT-54536: Fixed
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 63d4007..c59661b 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
@@ -21642,6 +21642,12 @@
}
@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 {
runTest("compiler/testData/codegen/box/inlineClasses/inlineToString.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
@@ -22260,6 +22266,12 @@
}
@Test
+ @TestMetadata("kt54536.kt")
+ public void testKt54536() throws Exception {
+ runTest("compiler/testData/codegen/box/inlineClasses/kt54536.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
+ }
+
+ @Test
@TestMetadata("mangledDefaultParameterFunction.kt")
public void testMangledDefaultParameterFunction() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/mangledDefaultParameterFunction.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
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 8e70511..efaaba0 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
@@ -489,10 +489,11 @@
val untypedEquals = valueClass.functions.single { it.overridesEqualsFromAny }
function.body = context.createIrBuilder(valueClass.symbol).run {
+ val context = this@JvmInlineClassLowering.context
irExprBody(
if (typedEqualsStaticReplacement == null) {
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) }
@@ -501,7 +502,19 @@
putValueArgument(0, irBox(irGet(right)))
}
} else {
- irEquals(coerceInlineClasses(irGet(left), left.type, type), coerceInlineClasses(irGet(right), right.type, type))
+ val underlyingType = getInlineClassUnderlyingType(valueClass)
+ val underlyingClass = underlyingType.getClass()
+ // We can't directly compare unboxed values of underlying inline class since 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))
+ }
}
} else {
irCall(typedEqualsStaticReplacement).apply {
diff --git a/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt
new file mode 100644
index 0000000..1adf89b
--- /dev/null
+++ b/compiler/testData/codegen/box/inlineClasses/inlineOverInlineWithCustomEquals.kt
@@ -0,0 +1,64 @@
+// 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<T>(val x: T)
+
+OPTIONAL_JVM_INLINE_ANNOTATION
+value class G<T : D1>(val x: T)
+
+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"
+
+ 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 0000000..ba5d880
--- /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 019d4eb..5b00788 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
@@ -21642,6 +21642,12 @@
}
@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 {
runTest("compiler/testData/codegen/box/inlineClasses/inlineToString.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
@@ -22260,6 +22266,12 @@
}
@Test
+ @TestMetadata("kt54536.kt")
+ public void testKt54536() throws Exception {
+ runTest("compiler/testData/codegen/box/inlineClasses/kt54536.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());
+ }
+
+ @Test
@TestMetadata("mangledDefaultParameterFunction.kt")
public void testMangledDefaultParameterFunction() throws Exception {
runTest("compiler/testData/codegen/box/inlineClasses/mangledDefaultParameterFunction.kt", TransformersFunctions.getReplaceOptionalJvmInlineAnnotationWithReal());