diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt index 7a8b8ae62f7..fde272a2f7c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/Autoboxing.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.backend.konan.descriptors.target import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities @@ -243,26 +242,32 @@ private class InlineClassTransformer(private val context: Context) : IrBuildingT return declaration } - private fun IrField.isInlinedClassField(): Boolean { - val parentClass = this.parent as? IrClass - return parentClass != null && parentClass.isInlined() - } - override fun visitGetField(expression: IrGetField): IrExpression { super.visitGetField(expression) - return if (expression.symbol.owner.isInlinedClassField()) { - expression.receiver!! - } else { + val field = expression.symbol.owner + val parentClass = field.parentClassOrNull + return if (parentClass == null || !parentClass.isInlined()) expression + else { + builder.at(expression) + .irCall(symbols.reinterpret, field.type, + listOf(parentClass.defaultType, field.type) + ).apply { + extensionReceiver = expression.receiver!! + } } } override fun visitSetField(expression: IrSetField): IrExpression { super.visitSetField(expression) - return if (expression.symbol.owner.isInlinedClassField()) { + return if (expression.symbol.owner.parentClassOrNull?.isInlined() == true) { // TODO: it is better to get rid of functions setting such fields. + // Here we're trying to maintain all IR nodes as is, albeit the transformed IR isn't equivalent to the original. + // By far SET_FIELD can only be in the constructor which won't be codegened. + // Box functions use createUninitializedInstance instead of constructor calls + // and are placed separately so they won't be processed here. val startOffset = expression.startOffset val endOffset = expression.endOffset IrBlockImpl(startOffset, endOffset, irBuiltIns.unitType).apply { diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 7f18d802006..568b3ece109 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3110,6 +3110,10 @@ task inlineClass_customEquals(type: KonanLocalTest) { source = "codegen/inlineClass/customEquals.kt" } +task inlineClass_defaultEquals(type: KonanLocalTest) { + source = "codegen/inlineClass/defaultEquals.kt" +} + task deserialized_inline0(type: KonanLocalTest) { source = "serialization/deserialized_inline0.kt" } diff --git a/backend.native/tests/codegen/inlineClass/defaultEquals.kt b/backend.native/tests/codegen/inlineClass/defaultEquals.kt new file mode 100644 index 00000000000..8fa1792eb29 --- /dev/null +++ b/backend.native/tests/codegen/inlineClass/defaultEquals.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +package codegen.inlineClass.defaultEquals + +import kotlin.test.* + +inline class A(val x: Int) +inline class B(val a: A) +inline class C(val s: String) +inline class D(val c: C) + +@Test fun runTest() { + val a = A(42) + val b = B(a) + val c = C("zzz") + val d = D(c) + assertTrue(a.equals(a)) + assertTrue(b.equals(b)) + assertTrue(c.equals(c)) + assertTrue(d.equals(d)) +}