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
This commit is contained in:
Leonid Startsev
2022-12-21 18:13:11 +01:00
committed by Space Team
parent 6d3812c259
commit 2634303055
7 changed files with 56 additions and 9 deletions
@@ -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<IrConstructor>.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)
}
@@ -208,6 +208,9 @@ internal val List<IrConstructorCall>.hasAnySerialAnnotation: Boolean
internal val List<IrConstructorCall>.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
@@ -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)
}
}
@@ -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)
}
}
@@ -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"
}
@@ -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 {
@@ -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 {