From b17ead97bcdc14666931c70006dd2296451786ec Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Mon, 8 Aug 2022 13:15:06 +0200 Subject: [PATCH] Fix incorrect class reference in polymorphic and contextual case Upper bound should be used for type parameters there --- .../compiler/backend/ir/BaseIrGenerator.kt | 14 +++++------ .../backend/ir/IrBuilderWithPluginContext.kt | 12 ++++++---- .../compiler/backend/ir/IrPredicates.kt | 11 ++++++++- .../compiler/fir/FirBuildersUtils.kt | 24 ------------------- 4 files changed, 24 insertions(+), 37 deletions(-) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index 0a6c9f13d8c..f63a1c481ad 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -379,11 +379,11 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override when (serializerClassOriginal.owner.classId) { polymorphicSerializerId -> { needToCopyAnnotations = true - args = listOf(classReference(kType)) + args = listOf(classReference(kType.classOrUpperBound()!!)) typeArgs = listOf(thisIrType) } contextSerializerId -> { - args = listOf(classReference(kType)) + args = listOf(classReference(kType.classOrUpperBound()!!)) typeArgs = listOf(thisIrType) val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 } @@ -412,16 +412,16 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override } objectSerializerId -> { needToCopyAnnotations = true - args = listOf(irString(kType.serialName()), irGetObject(kType.classOrNull!!)) + args = listOf(irString(kType.serialName()), irGetObject(kType.classOrUpperBound()!!)) typeArgs = listOf(thisIrType) } sealedSerializerId -> { needToCopyAnnotations = true args = mutableListOf().apply { add(irString(kType.serialName())) - add(classReference(kType)) + add(classReference(kType.classOrUpperBound()!!)) val (subclasses, subSerializers) = allSealedSerializableSubclassesFor( - kType.classOrNull!!.owner, + kType.classOrUpperBound()!!.owner, pluginContext ) val projectedOutCurrentKClass = @@ -431,7 +431,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override add( createArrayOfExpression( projectedOutCurrentKClass, - subclasses.map { classReference(it) } + subclasses.map { classReference(it.classOrUpperBound()!!) } ) ) add( @@ -528,7 +528,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override } // If KType is interface, .classSerializer always yields PolymorphicSerializer, which may be unavailable for interfaces from other modules - if (!kType.isInterface() && serializerClassOriginal == kType.classOrNull!!.owner.classSerializer(pluginContext) && this@BaseIrGenerator !is SerializableCompanionIrGenerator) { + if (!kType.isInterface() && serializerClassOriginal == kType.classOrUpperBound()?.owner.classSerializer(pluginContext) && this@BaseIrGenerator !is SerializableCompanionIrGenerator) { // This is default type serializer, we can shortcut through Companion.serializer() // BUT not during generation of this method itself callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt index 9a658ccc21e..2fa6740bedd 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrBuilderWithPluginContext.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.deepCopyWithVariables import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol @@ -506,8 +507,8 @@ interface IrBuilderWithPluginContext { ) } - fun IrBuilderWithScope.classReference(classSymbol: IrType): IrClassReference = - createClassReference(classSymbol, startOffset, endOffset) + fun IrBuilderWithScope.classReference(classSymbol: IrClassSymbol): IrClassReference = + createClassReference(classSymbol.starProjectedType, startOffset, endOffset) fun collectSerialInfoAnnotations(irClass: IrClass): List { if (!(irClass.isInterface || irClass.descriptor.hasSerializableOrMetaAnnotation)) return emptyList() @@ -555,14 +556,15 @@ interface IrBuilderWithPluginContext { if (compilerContext.platform.isJvm()) { // "Byte::class" -> "java.lang.Byte::class" // TODO: get rid of descriptor - val wrapperFqName = KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName + val wrapperFqName = + KotlinBuiltIns.getPrimitiveType(classType.classOrNull!!.descriptor)?.let(JvmPrimitiveType::get)?.wrapperFqName if (wrapperFqName != null) { val wrapperClass = compilerContext.referenceClass(ClassId.topLevel(wrapperFqName)) ?: error("Primitive wrapper class for $classType not found: $wrapperFqName") - return classReference(wrapperClass.defaultType) + return createClassReference(wrapperClass.defaultType, startOffset, endOffset) } } - return classReference(classType) + return createClassReference(classType, startOffset, endOffset) } fun IrClass.getSuperClassOrAny(): IrClass = getSuperClassNotAny() ?: compilerContext.irBuiltIns.anyClass.owner diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt index 4c7f718da67..e3a228725e0 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/IrPredicates.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.ir import org.jetbrains.kotlin.backend.jvm.ir.getStringConstArgument +import org.jetbrains.kotlin.backend.jvm.ir.representativeUpperBound import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality @@ -17,6 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol +import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* @@ -108,7 +110,7 @@ internal fun IrClass.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLAS internal val IrType.genericIndex: Int? get() = (this.classifierOrNull as? IrTypeParameterSymbol)?.owner?.index -fun IrType.serialName(): String = this.classOrNull!!.owner.serialName() +fun IrType.serialName(): String = this.classOrUpperBound()!!.owner.serialName() fun IrClass.serialName(): String { return annotations.serialNameValue ?: fqNameWhenAvailable?.asString() ?: error("${this.render()} does not have fqName") @@ -199,4 +201,11 @@ fun findSerializerConstructorForTypeArgumentsSerializers(serializer: IrClass): I return serializer.constructors.singleOrNull { it.valueParameters.let { vps -> vps.size == typeParamsCount && vps.all { vp -> vp.type.isKSerializer() } } }?.symbol +} + +fun IrType.classOrUpperBound(): IrClassSymbol? = when(val cls = classifierOrNull) { + is IrClassSymbol -> cls + is IrScriptSymbol -> cls.owner.targetClass + is IrTypeParameterSymbol -> cls.owner.representativeUpperBound.classOrUpperBound() + else -> null } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt index fd5c3c2fa65..9deb1b1e9d7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirBuildersUtils.kt @@ -63,28 +63,4 @@ fun FirDeclarationGenerationExtension.buildConstructor(classId: ClassId, isInner }.also { it.containingClassForStaticMemberAttr = lookupTag } -} - -// FIXME: copied from Parcelize plugin -inline fun FirSession.createFunction( - owner: FirRegularClassSymbol, - callableId: CallableId, - init: FirSimpleFunctionBuilder.() -> Unit -): FirNamedFunctionSymbol { - val function = buildSimpleFunction { - moduleData = this@createFunction.moduleData - origin = SerializationPluginKey.origin - status = FirResolvedDeclarationStatusImpl( - Visibilities.Public, - if (owner.modality == Modality.FINAL) Modality.FINAL else Modality.OPEN, - EffectiveVisibility.Public - ).apply { - isOverride = true - } - name = callableId.callableName - symbol = FirNamedFunctionSymbol(callableId) - dispatchReceiverType = owner.defaultType() - init() - } - return function.symbol } \ No newline at end of file