From 69ad68ca835cadc8e88c110e22e7a6aaf45d265d Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 13 Mar 2024 19:16:21 +0100 Subject: [PATCH] Make intrinsics more consistent with reflective serializer() lookup Intrinsic should not look into use-site `@Serializable(with)` annotations. #KT-64920 Fixed --- .../ir/SerializationJvmIrIntrinsicSupport.kt | 2 +- .../backend/ir/SerializerSearchUtil.kt | 10 +- .../testData/boxIr/intrinsicsConsistency.kt | 92 +++++++++++++++++++ ...tionFirLightTreeBlackBoxTestGenerated.java | 6 ++ .../SerializationIrBoxTestGenerated.java | 6 ++ 5 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt index ef702a7ffa6..b1dbdeccad0 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializationJvmIrIntrinsicSupport.kt @@ -165,7 +165,7 @@ class SerializationJvmIrIntrinsicSupport( get() = currentVersion != null && currentVersion!! >= ApiVersion.parse("1.2.0")!! private fun findTypeSerializerOrContext(argType: IrType): IrClassSymbol? = - emptyGenerator.findTypeSerializerOrContextUnchecked(this, argType) + emptyGenerator.findTypeSerializerOrContextUnchecked(this, argType, useTypeAnnotations = false) private fun instantiateObject(iv: InstructionAdapter, objectSymbol: IrClassSymbol) { val originalIrClass = objectSymbol.owner diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt index 702e6e8daf3..278ebc2bfb6 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt @@ -85,17 +85,17 @@ fun BaseIrGenerator?.findTypeSerializerOrContext( } fun BaseIrGenerator?.findTypeSerializerOrContextUnchecked( - context: SerializationBaseContext, kType: IrType + context: SerializationBaseContext, kType: IrType, useTypeAnnotations: Boolean = true ): IrClassSymbol? { - val annotations = kType.annotations if (kType.isTypeParameter()) return null + val annotations = if (useTypeAnnotations) kType.annotations else emptyList() annotations.serializableWith()?.let { return it } this?.additionalSerializersInScopeOfCurrentFile?.get(kType.classOrNull!! to kType.isNullable())?.let { return it } if (kType.isMarkedNullable()) return findTypeSerializerOrContextUnchecked(context, kType.makeNotNull()) if (this?.contextualKClassListInCurrentFile?.contains(kType.classOrNull) == true) return context.referenceClassId(contextSerializerId) - return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType) + return analyzeSpecialSerializers(context, annotations) ?: findTypeSerializer(context, kType, useTypeAnnotations) } fun analyzeSpecialSerializers( @@ -111,8 +111,8 @@ fun analyzeSpecialSerializers( } -fun findTypeSerializer(context: SerializationBaseContext, type: IrType): IrClassSymbol? { - type.overriddenSerializer?.let { return it } +fun findTypeSerializer(context: SerializationBaseContext, type: IrType, useTypeAnnotations: Boolean = true): IrClassSymbol? { + if (useTypeAnnotations) type.overriddenSerializer?.let { return it } if (type.isTypeParameter()) return null if (type.isArray()) return context.referenceClassId(referenceArraySerializerId) if (type.isGeneratedSerializableObject()) return context.referenceClassId(objectSerializerId) diff --git a/plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt b/plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt new file mode 100644 index 00000000000..76b7c3b842f --- /dev/null +++ b/plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt @@ -0,0 +1,92 @@ +// TARGET_BACKEND: JVM_IR + +// WITH_STDLIB + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlinx.serialization.json.internal.* +import kotlinx.serialization.modules.* +import kotlin.reflect.* +import kotlin.test.* + +class UUID + +typealias Uuid = @Serializable(UuidStringSerializer::class) UUID + +internal object UuidStringSerializer : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING) + override fun deserialize(decoder: Decoder): UUID = TODO() + override fun serialize(encoder: Encoder, value: UUID): Unit = TODO() +} + + +@Serializable +class Box + +internal class BoxSerializer(val tSer: KSerializer): KSerializer> { + override val descriptor: SerialDescriptor + get() = PrimitiveSerialDescriptor("Box@${tSer.descriptor.serialName}", PrimitiveKind.STRING) + + override fun serialize(encoder: Encoder, value: Box) = TODO() + + override fun deserialize(decoder: Decoder): Box = TODO() +} + +typealias BoxAlias = @Serializable(BoxSerializer::class) Box + + +@Serializable +class ByDefault + +internal object NotDefaultSerializer: KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("NotByDefault", PrimitiveKind.STRING) + override fun deserialize(decoder: Decoder): ByDefault = TODO() + override fun serialize(encoder: Encoder, value: ByDefault): Unit = TODO() +} + +typealias NotDefault = @Serializable(NotDefaultSerializer::class) ByDefault + +inline fun MutableList.isIntrinsicEqual(testName: String) { + val r1 = runCatching { serializer() } + val r2 = runCatching { serializer(typeOf()) } + val areEq = (r1.isFailure && r2.isFailure) || (r1.isSuccess && r2.isSuccess) + if (!areEq) { + add("Results are not equal for $testName: $r1 vs $r2") + } else if (r1.isSuccess) { + if (r1.getOrNull()?.descriptor != r2.getOrNull()?.descriptor) + add("Not equal descriptors: $r1 vs $r2") + } +} + +fun box(): String { + val res = buildList { + // Check that all variations of @Serializable do not change result of lookup + isIntrinsicEqual("ByDefault") + isIntrinsicEqual<@Serializable(NotDefaultSerializer::class) ByDefault>("@Serializable(with) ByDefault") + isIntrinsicEqual("typealias NotDefault") + isIntrinsicEqual>("List ByDefault") + isIntrinsicEqual>("List @Serializable(with) ByDefault") + isIntrinsicEqual>("List typealias NotDefault") + + // Same, but for generics + isIntrinsicEqual<@Serializable(BoxSerializer::class) Box>("@Serializable Box") + isIntrinsicEqual>("typealias Box") + isIntrinsicEqual>>("List typealias Box") + isIntrinsicEqual>>("List @Serializable Box") + + + // Same, but for non-serializable class — should be failures + isIntrinsicEqual("UUID") + isIntrinsicEqual<@Serializable(UuidStringSerializer::class) UUID>("@Serializable(with)") + isIntrinsicEqual("typealias") + isIntrinsicEqual>("List UUID") + isIntrinsicEqual>("List @Serializable(with)") + isIntrinsicEqual>("List typealias") + } + return if (res.isEmpty()) "OK" + else res.toString() +} diff --git a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java index 21b3eed0132..93c131232c8 100644 --- a/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java +++ b/plugins/kotlinx-serialization/tests-gen/org/jetbrains/kotlinx/serialization/runners/SerializationFirLightTreeBlackBoxTestGenerated.java @@ -171,6 +171,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsBox.kt"); } + @Test + @TestMetadata("intrinsicsConsistency.kt") + public void testIntrinsicsConsistency() { + runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt"); + } + @Test @TestMetadata("intrinsicsNonReified.kt") public void testIntrinsicsNonReified() { 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 4b7f5b54507..611ae5d76b3 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 @@ -168,6 +168,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsBox.kt"); } + @Test + @TestMetadata("intrinsicsConsistency.kt") + public void testIntrinsicsConsistency() { + runTest("plugins/kotlinx-serialization/testData/boxIr/intrinsicsConsistency.kt"); + } + @Test @TestMetadata("intrinsicsNonReified.kt") public void testIntrinsicsNonReified() {