diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index 86de0b5eb9285..3286f30d2ce48 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -464,13 +464,15 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override serializableProperties: List ): List { return DeclarationIrBuilder(compilerContext, symbol).run { - serializableProperties.map { cacheableChildSerializerInstance(serializableClass, it) } + val hasKeepGeneratedSerializerAnnotation = serializableClass.hasKeepGeneratedSerializerAnnotation + serializableProperties.map { cacheableChildSerializerInstance(serializableClass, it, hasKeepGeneratedSerializerAnnotation) } } } private fun IrBuilderWithScope.cacheableChildSerializerInstance( serializableClass: IrClass, - property: IrSerializableProperty + property: IrSerializableProperty, + hasKeepGeneratedSerializerAnnotation: Boolean ): IrExpression? { // to avoid a cyclical dependency between the serializer cache and the cache of child serializers, // the class should not cache its serializer as a child @@ -486,7 +488,12 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override val serializer = getIrSerialTypeInfo(property, compilerContext).serializer ?: return null if (serializer.owner.kind == ClassKind.OBJECT) return null - return serializerInstance( + // disable caching for sealed serializer because of initialization loop, see https://github.com/Kotlin/kotlinx.serialization/issues/2759 + if (hasKeepGeneratedSerializerAnnotation && serializer.owner.classId == sealedSerializerId) { + return null + } + + val serializerInstance = serializerInstance( serializer, compilerContext, property.type, @@ -494,6 +501,8 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override serializableClass, null ) + + return serializerInstance } private fun IrSimpleType.checkTypeArgumentsHasSelf(itselfClass: IrClassSymbol): Boolean { diff --git a/plugins/kotlinx-serialization/testData/boxIr/KeepGeneratedSerializer.kt b/plugins/kotlinx-serialization/testData/boxIr/KeepGeneratedSerializer.kt index b499aeebd2433..799510379c1d2 100644 --- a/plugins/kotlinx-serialization/testData/boxIr/KeepGeneratedSerializer.kt +++ b/plugins/kotlinx-serialization/testData/boxIr/KeepGeneratedSerializer.kt @@ -190,6 +190,19 @@ object ObjectSerializer: KSerializer { } } +// == sealed loop == +@Serializable +public sealed interface SealedInterface + +@Serializable(with = SealedChild.CustomSerializer::class) +@SerialName("child") +@KeepGeneratedSerializer +data class SealedChild( + val child: SealedInterface, +) : SealedInterface { + internal object CustomSerializer : KSerializer by generatedSerializer() +} + fun box(): String = boxWrapper { val value = Value(42) val data = Data(42) @@ -213,6 +226,8 @@ fun box(): String = boxWrapper { assertEquals("Object()", Object.generatedSerializer().descriptor.toString(), "Object.generatedSerializer() illegal") assertSame(Object.generatedSerializer(), Object.generatedSerializer(), "Object.generatedSerializer() instance differs") + + assertEquals("SealedInterface", SealedInterface.serializer().descriptor.serialName, "SealedInterface.serializer() illegal") } inline fun test(