From 263430305532e2140c759ed56cde2d1ade1de951 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 21 Dec 2022 18:13:11 +0100 Subject: [PATCH] Fix compilation error when @Serializable expect class has explicit companion as it may have no primary constructor (only actual class has it). Refactor & rename Fir2IrGeneratorUtils.kt as having 'Fir' in stacktrace when actual K2 compiler is not used is very confusing. Fixes https://github.com/Kotlin/kotlinx.serialization/issues/2134 #KT-55683 Fixed --- ...rGeneratorUtils.kt => IrGeneratorUtils.kt} | 7 ++-- .../compiler/backend/ir/IrPredicates.kt | 3 ++ .../ir/SerializableCompanionIrGenerator.kt | 2 +- .../backend/ir/SerializerIrGenerator.kt | 6 ++-- .../testData/boxIr/expectActual.kt | 35 +++++++++++++++++++ ...SerializationFirBlackBoxTestGenerated.java | 6 ++++ .../SerializationIrBoxTestGenerated.java | 6 ++++ 7 files changed, 56 insertions(+), 9 deletions(-) rename plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/{Fir2IrGeneratorUtils.kt => IrGeneratorUtils.kt} (85%) create mode 100644 plugins/kotlinx-serialization/testData/boxIr/expectActual.kt diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrGeneratorUtils.kt similarity index 85% rename from plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt rename to plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrGeneratorUtils.kt index a2c2a57a388..fea48c6ddb5 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/Fir2IrGeneratorUtils.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrGeneratorUtils.kt @@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.primaryConstructor // TODO KT-53096 @@ -38,9 +37,7 @@ fun IrPluginContext.generateBodyForDefaultConstructor(declaration: IrConstructor return irFactory.createBlockBody(-1, -1, listOf(delegatingAnyCall, initializerCall)) } -val Sequence.primary get() = find { it.isPrimary } ?: error("Expected to have a primary constructor") - -fun IrClass.addDefaultConstructorIfAbsent(ctx: IrPluginContext) { - val declaration = constructors.primary +fun IrClass.addDefaultConstructorBodyIfAbsent(ctx: IrPluginContext) { + val declaration = primaryConstructor ?: return if (declaration.body == null) declaration.body = ctx.generateBodyForDefaultConstructor(declaration) } \ No newline at end of file diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt index 8c4f5366890..d672da3ab71 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt @@ -208,6 +208,9 @@ internal val List.hasAnySerialAnnotation: Boolean internal val List.serialNameValue: String? get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)?.getStringConstArgument(0) // @SerialName("foo") + +val IrClass.primaryConstructorOrFail get() = primaryConstructor ?: error("$this is expected to have a primary constructor") + /** * True — ALWAYS * False — NEVER diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt index b780031f1a3..71840936ac3 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableCompanionIrGenerator.kt @@ -53,7 +53,7 @@ class SerializableCompanionIrGenerator( val serializableClass = getSerializableClassByCompanion(companionDescriptor) ?: return if (serializableClass.shouldHaveGeneratedMethodsInCompanion) { SerializableCompanionIrGenerator(irClass, getSerializableClassByCompanion(irClass)!!, context).generate() - irClass.addDefaultConstructorIfAbsent(context) + irClass.addDefaultConstructorBodyIfAbsent(context) irClass.patchDeclarationParents(irClass.parent) } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index 8e3d215987b..020cbfc2790 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -199,7 +199,7 @@ open class SerializerIrGenerator( fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: IrConstructor) = addFunctionBody(typedConstructorDescriptor) { ctor -> // generate call to primary ctor to init serialClassDesc and super() - val primaryCtor = irClass.constructors.primary + val primaryCtor = irClass.primaryConstructorOrFail +IrDelegatingConstructorCallImpl.fromSymbolOwner( startOffset, endOffset, @@ -518,7 +518,7 @@ open class SerializerIrGenerator( generateGoldenMaskCheck(bitMasks, properties, localSerialDesc.get()) - val ctor: IrConstructorSymbol = serializableIrClass.constructors.primary.symbol + val ctor: IrConstructorSymbol = serializableIrClass.primaryConstructorOrFail.symbol val params = ctor.owner.valueParameters val variableByParamReplacer: (ValueParameterDescriptor) -> IrExpression? = { vpd -> @@ -642,7 +642,7 @@ open class SerializerIrGenerator( // replace origin only for plugin generated serializers irClass.origin = SERIALIZATION_PLUGIN_ORIGIN } - irClass.addDefaultConstructorIfAbsent(context) + irClass.addDefaultConstructorBodyIfAbsent(context) irClass.patchDeclarationParents(irClass.parent) } } diff --git a/plugins/kotlinx-serialization/testData/boxIr/expectActual.kt b/plugins/kotlinx-serialization/testData/boxIr/expectActual.kt new file mode 100644 index 00000000000..04da6642e59 --- /dev/null +++ b/plugins/kotlinx-serialization/testData/boxIr/expectActual.kt @@ -0,0 +1,35 @@ +// IGNORE_BACKEND_K2: JVM_IR +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects +// WITH_STDLIB + +// FILE: common.kt + +package foo + +import kotlinx.serialization.* + +@Serializable +expect class Expected { + companion object { fun factoryMethod(): Expected } +} + +// FILE: jvm.kt + +package foo + +import kotlinx.serialization.* + +@Serializable +actual class Expected { + actual companion object { actual fun factoryMethod(): Expected = Expected() } +} + + +fun box(): String { + val b = Expected.factoryMethod() + if (b !is Expected) return "Incorrect factory method" + val desc = Expected.serializer().descriptor + if (desc.toString() != "foo.Expected()") return "Incorrect descriptor $desc" + return "OK" +} \ No newline at end of file diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java index b0db2c69dbe..32fb3327b9c 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirBlackBoxTestGenerated.java @@ -57,6 +57,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization runTest("plugins/kotlinx-serialization/testData/boxIr/enumsAreCached.kt"); } + @Test + @TestMetadata("expectActual.kt") + public void testExpectActual() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/expectActual.kt"); + } + @Test @TestMetadata("externalSerialierJava.kt") public void testExternalSerialierJava() throws Exception { diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java index f8d70a3ea86..6a36d0729cf 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationIrBoxTestGenerated.java @@ -55,6 +55,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlinx-serialization/testData/boxIr/enumsAreCached.kt"); } + @Test + @TestMetadata("expectActual.kt") + public void testExpectActual() throws Exception { + runTest("plugins/kotlinx-serialization/testData/boxIr/expectActual.kt"); + } + @Test @TestMetadata("externalSerialierJava.kt") public void testExternalSerialierJava() throws Exception {