Correctly handle star projections according to logic of the old FE.
#KT-54297 Fixed Properly substitute surrogate UnitSerializer in the Companion.serializer() function generated on classes that use polymorphic or sealed serializer by default. (Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1692)
This commit is contained in:
committed by
Space Team
parent
2a626b27d3
commit
596949a501
+32
-27
@@ -266,9 +266,10 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun IrBuilderWithScope.callSerializerFromCompanion(
|
fun IrBuilderWithScope.callSerializerFromCompanion(
|
||||||
thisIrType: IrType,
|
thisIrType: IrSimpleType,
|
||||||
typeArgs: List<IrType>,
|
typeArgs: List<IrType>,
|
||||||
args: List<IrExpression>
|
args: List<IrExpression>,
|
||||||
|
expectedSerializer: ClassId?
|
||||||
): IrExpression? {
|
): IrExpression? {
|
||||||
val baseClass = thisIrType.getClass() ?: return null
|
val baseClass = thisIrType.getClass() ?: return null
|
||||||
val companionClass = baseClass.companionObject() ?: return null
|
val companionClass = baseClass.companionObject() ?: return null
|
||||||
@@ -276,22 +277,25 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
it is IrFunction && it.name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && it.valueParameters.size == baseClass.typeParameters.size
|
it is IrFunction && it.name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && it.valueParameters.size == baseClass.typeParameters.size
|
||||||
} ?: return null
|
} ?: return null
|
||||||
|
|
||||||
|
// workaround for sealed and abstract classes - the `Companion.serializer()` function expects non-null serializers, but does not use them, so serializers of any type can be passed
|
||||||
|
val replaceArgsWithUnitSerializer = expectedSerializer == polymorphicSerializerId || expectedSerializer == sealedSerializerId
|
||||||
|
|
||||||
val adjustedArgs: List<IrExpression> =
|
val adjustedArgs: List<IrExpression> =
|
||||||
// if typeArgs.size == args.size then the serializer is custom - we need to use the actual serializers from the arguments
|
if (replaceArgsWithUnitSerializer) {
|
||||||
if ((typeArgs.size != args.size) && (baseClass.modality == Modality.SEALED || baseClass.modality == Modality.ABSTRACT)) {
|
|
||||||
val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!!
|
val serializer = findStandardKotlinTypeSerializer(compilerContext, context.irBuiltIns.unitType)!!
|
||||||
// workaround for sealed and abstract classes - the `serializer` function expects non-null serializers, but does not use them, so serializers of any type can be passed
|
|
||||||
List(baseClass.typeParameters.size) { irGetObject(serializer) }
|
List(baseClass.typeParameters.size) { irGetObject(serializer) }
|
||||||
} else {
|
} else {
|
||||||
args
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val adjustedTypeArgs: List<IrType> = if (replaceArgsWithUnitSerializer) thisIrType.argumentTypesOrUpperBounds() else typeArgs
|
||||||
|
|
||||||
with(serializerProviderFunction as IrFunction) {
|
with(serializerProviderFunction as IrFunction) {
|
||||||
// Note that [typeArgs] may be unused if we short-cut to e.g. SealedClassSerializer
|
// Note that [typeArgs] may be unused if we short-cut to e.g. SealedClassSerializer
|
||||||
return irInvoke(
|
return irInvoke(
|
||||||
irGetObject(companionClass),
|
irGetObject(companionClass),
|
||||||
symbol,
|
symbol,
|
||||||
typeArgs.takeIf { it.size == typeParameters.size }.orEmpty(),
|
adjustedTypeArgs.takeIf { it.size == typeParameters.size }.orEmpty(),
|
||||||
adjustedArgs.takeIf { it.size == valueParameters.size }.orEmpty()
|
adjustedArgs.takeIf { it.size == valueParameters.size }.orEmpty()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -384,18 +388,19 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
var serializerClass = serializerClassOriginal
|
var serializerClass = serializerClassOriginal
|
||||||
var args: List<IrExpression>
|
var args: List<IrExpression>
|
||||||
var typeArgs: List<IrType>
|
var typeArgs: List<IrType>
|
||||||
val thisIrType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}")
|
@Suppress("NAME_SHADOWING") val kType = (kType as? IrSimpleType) ?: error("Don't know how to work with type ${kType::class}")
|
||||||
|
val typeArgumentsAsTypes = kType.argumentTypesOrUpperBounds()
|
||||||
var needToCopyAnnotations = false
|
var needToCopyAnnotations = false
|
||||||
|
|
||||||
when (serializerClassOriginal.owner.classId) {
|
when (serializerClassOriginal.owner.classId) {
|
||||||
polymorphicSerializerId -> {
|
polymorphicSerializerId -> {
|
||||||
needToCopyAnnotations = true
|
needToCopyAnnotations = true
|
||||||
args = listOf(classReference(kType.classOrUpperBound()!!))
|
args = listOf(classReference(kType.classOrUpperBound()!!))
|
||||||
typeArgs = listOf(thisIrType)
|
typeArgs = listOf(kType)
|
||||||
}
|
}
|
||||||
contextSerializerId -> {
|
contextSerializerId -> {
|
||||||
args = listOf(classReference(kType.classOrUpperBound()!!))
|
args = listOf(classReference(kType.classOrUpperBound()!!))
|
||||||
typeArgs = listOf(thisIrType)
|
typeArgs = listOf(kType)
|
||||||
|
|
||||||
val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 }
|
val hasNewCtxSerCtor = compilerContext.referenceConstructors(contextSerializerId).any { it.owner.valueParameters.size == 3 }
|
||||||
|
|
||||||
@@ -408,15 +413,15 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
add(
|
add(
|
||||||
createArrayOfExpression(
|
createArrayOfExpression(
|
||||||
wrapIrTypeIntoKSerializerIrType(
|
wrapIrTypeIntoKSerializerIrType(
|
||||||
thisIrType,
|
kType,
|
||||||
variance = Variance.OUT_VARIANCE
|
variance = Variance.OUT_VARIANCE
|
||||||
),
|
),
|
||||||
thisIrType.arguments.map {
|
typeArgumentsAsTypes.map {
|
||||||
val argSer = findTypeSerializerOrContext(
|
val argSer = findTypeSerializerOrContext(
|
||||||
compilerContext,
|
compilerContext,
|
||||||
it.typeOrNull!! //todo: handle star projections here?
|
it
|
||||||
)
|
)
|
||||||
instantiate(argSer, it.typeOrNull!!)!!
|
instantiate(argSer, it)!!
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -425,7 +430,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
objectSerializerId -> {
|
objectSerializerId -> {
|
||||||
needToCopyAnnotations = true
|
needToCopyAnnotations = true
|
||||||
args = listOf(irString(kType.serialName()), irGetObject(kType.classOrUpperBound()!!))
|
args = listOf(irString(kType.serialName()), irGetObject(kType.classOrUpperBound()!!))
|
||||||
typeArgs = listOf(thisIrType)
|
typeArgs = listOf(kType)
|
||||||
}
|
}
|
||||||
sealedSerializerId -> {
|
sealedSerializerId -> {
|
||||||
needToCopyAnnotations = true
|
needToCopyAnnotations = true
|
||||||
@@ -438,7 +443,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
)
|
)
|
||||||
val projectedOutCurrentKClass =
|
val projectedOutCurrentKClass =
|
||||||
compilerContext.irBuiltIns.kClassClass.typeWithArguments(
|
compilerContext.irBuiltIns.kClassClass.typeWithArguments(
|
||||||
listOf(makeTypeProjection(thisIrType, Variance.OUT_VARIANCE))
|
listOf(makeTypeProjection(kType, Variance.OUT_VARIANCE))
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
createArrayOfExpression(
|
createArrayOfExpression(
|
||||||
@@ -448,7 +453,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
createArrayOfExpression(
|
createArrayOfExpression(
|
||||||
wrapIrTypeIntoKSerializerIrType(thisIrType, variance = Variance.OUT_VARIANCE),
|
wrapIrTypeIntoKSerializerIrType(kType, variance = Variance.OUT_VARIANCE),
|
||||||
subSerializers.mapIndexed { i, serializer ->
|
subSerializers.mapIndexed { i, serializer ->
|
||||||
val type = subclasses[i]
|
val type = subclasses[i]
|
||||||
val expr = serializerInstance(
|
val expr = serializerInstance(
|
||||||
@@ -468,20 +473,20 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
typeArgs = listOf(thisIrType)
|
typeArgs = listOf(kType)
|
||||||
}
|
}
|
||||||
enumSerializerId -> {
|
enumSerializerId -> {
|
||||||
serializerClass = pluginContext.referenceClass(enumSerializerId)
|
serializerClass = pluginContext.referenceClass(enumSerializerId)
|
||||||
val enumDescriptor = kType.classOrNull!!
|
val enumDescriptor = kType.classOrNull!!
|
||||||
typeArgs = listOf(thisIrType)
|
typeArgs = listOf(kType)
|
||||||
// instantiate serializer only inside enum Companion
|
// instantiate serializer only inside enum Companion
|
||||||
if (this@BaseIrGenerator !is SerializableCompanionIrGenerator) {
|
if (this@BaseIrGenerator !is SerializableCompanionIrGenerator) {
|
||||||
// otherwise call Companion.serializer()
|
// otherwise call Companion.serializer()
|
||||||
callSerializerFromCompanion(thisIrType, typeArgs, emptyList())?.let { return it }
|
callSerializerFromCompanion(kType, typeArgs, emptyList(), enumSerializerId)?.let { return it }
|
||||||
}
|
}
|
||||||
|
|
||||||
val enumArgs = mutableListOf(
|
val enumArgs = mutableListOf(
|
||||||
irString(thisIrType.serialName()),
|
irString(kType.serialName()),
|
||||||
irCall(enumDescriptor.owner.findEnumValuesMethod()),
|
irCall(enumDescriptor.owner.findEnumValuesMethod()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -523,19 +528,19 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
args = kType.arguments.map {
|
args = typeArgumentsAsTypes.map {
|
||||||
val argSer = findTypeSerializerOrContext(
|
val argSer = findTypeSerializerOrContext(
|
||||||
pluginContext,
|
pluginContext,
|
||||||
it.typeOrNull!! // todo: stars?
|
it
|
||||||
)
|
)
|
||||||
instantiate(argSer, it.typeOrNull!!) ?: return null
|
instantiate(argSer, it) ?: return null
|
||||||
}
|
}
|
||||||
typeArgs = kType.arguments.map { it.typeOrNull!! }
|
typeArgs = typeArgumentsAsTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (serializerClassOriginal.owner.classId == referenceArraySerializerId) {
|
if (serializerClassOriginal.owner.classId == referenceArraySerializerId) {
|
||||||
args = listOf(wrapperClassReference(kType.arguments.single().typeOrNull!!)) + args
|
args = listOf(wrapperClassReference(typeArgumentsAsTypes.single())) + args
|
||||||
typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs
|
typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,7 +548,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
if (!kType.isInterface() && serializerClassOriginal == kType.classOrUpperBound()?.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()
|
// This is default type serializer, we can shortcut through Companion.serializer()
|
||||||
// BUT not during generation of this method itself
|
// BUT not during generation of this method itself
|
||||||
callSerializerFromCompanion(thisIrType, typeArgs, args)?.let { return it }
|
callSerializerFromCompanion(kType, typeArgs, args, serializerClassOriginal.owner.classId)?.let { return it }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -571,7 +576,7 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
|||||||
assert(ctor.isBound)
|
assert(ctor.isBound)
|
||||||
val ctorDecl = ctor.owner
|
val ctorDecl = ctor.owner
|
||||||
if (needToCopyAnnotations) {
|
if (needToCopyAnnotations) {
|
||||||
val classAnnotations = copyAnnotationsFrom(thisIrType.getClass()?.let { collectSerialInfoAnnotations(it) }.orEmpty())
|
val classAnnotations = copyAnnotationsFrom(kType.getClass()?.let { collectSerialInfoAnnotations(it) }.orEmpty())
|
||||||
args = args + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, classAnnotations)
|
args = args + createArrayOfExpression(compilerContext.irBuiltIns.annotationType, classAnnotations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
@@ -236,5 +236,16 @@ fun IrType.classOrUpperBound(): IrClassSymbol? = when(val cls = classifierOrNull
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces star projections with representativeUpperBound of respective type parameter
|
||||||
|
* to mimic behaviour of old FE (see StarProjectionImpl.getType())
|
||||||
|
*/
|
||||||
|
fun IrSimpleType.argumentTypesOrUpperBounds(): List<IrType> {
|
||||||
|
val params = this.classOrUpperBound()!!.owner.typeParameters
|
||||||
|
return arguments.mapIndexed { index, argument ->
|
||||||
|
argument.typeOrNull ?: params[index].representativeUpperBound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
|
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
|
||||||
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
|
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
|
||||||
|
interface SomeInterface
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed class SealedMultiple<T1 : SomeInterface, T2 : SomeInterface>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
abstract class AbstractMultiple<T1 : SomeInterface, T2 : SomeInterface>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface SealedInterfaceMultiple<T1 : SomeInterface, T2 : SomeInterface>
|
||||||
|
|
||||||
|
interface InterfaceMultiple<T1 : SomeInterface, T2 : SomeInterface>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class ToSerialize(
|
||||||
|
val a: SealedMultiple<SomeInterface, SomeInterface>,
|
||||||
|
val b: AbstractMultiple<*, *>,
|
||||||
|
val c: SealedInterfaceMultiple<*, *>,
|
||||||
|
val d: InterfaceMultiple<*, *>
|
||||||
|
)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val s = ToSerialize.serializer().descriptor.elementDescriptors.joinToString { it.kind.toString() }
|
||||||
|
return if (s == "SEALED, OPEN, SEALED, OPEN") "OK" else s
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
// WITH_STDLIB
|
||||||
|
|
||||||
|
import kotlinx.serialization.*
|
||||||
|
import kotlinx.serialization.json.*
|
||||||
|
import kotlinx.serialization.descriptors.*
|
||||||
|
|
||||||
|
interface E
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Box<T: E>(val boxed: T)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Wrapper(val boxed: Box<*>)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val s = Wrapper.serializer().descriptor.elementDescriptors.joinToString()
|
||||||
|
return if (s == "Box(boxed: kotlinx.serialization.Polymorphic<E>)") "OK" else s
|
||||||
|
}
|
||||||
+12
@@ -93,11 +93,23 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
|
|||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/multimoduleInheritance.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/multimoduleInheritance.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("multipleGenericsPolymorphic.kt")
|
||||||
|
public void testMultipleGenericsPolymorphic() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/multipleGenericsPolymorphic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("sealedInterfaces.kt")
|
@TestMetadata("sealedInterfaces.kt")
|
||||||
public void testSealedInterfaces() throws Exception {
|
public void testSealedInterfaces() throws Exception {
|
||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjections.kt")
|
||||||
|
public void testStarProjections() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/starProjections.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
|||||||
+12
@@ -91,9 +91,21 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
|
|||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/multimoduleInheritance.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/multimoduleInheritance.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("multipleGenericsPolymorphic.kt")
|
||||||
|
public void testMultipleGenericsPolymorphic() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/multipleGenericsPolymorphic.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("sealedInterfaces.kt")
|
@TestMetadata("sealedInterfaces.kt")
|
||||||
public void testSealedInterfaces() throws Exception {
|
public void testSealedInterfaces() throws Exception {
|
||||||
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
runTest("plugins/kotlinx-serialization/testData/boxIr/sealedInterfaces.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjections.kt")
|
||||||
|
public void testStarProjections() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/starProjections.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user