Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generics type inline function call serializer<T>() fail in kotlin/native #2676

Closed
Gargantua7 opened this issue May 16, 2024 · 14 comments
Closed

Comments

@Gargantua7
Copy link

Describe the bug
Here is the sample

class Greeting {
    private val platform = getPlatform()

    fun greet(): String {
        foo<@Serializable(with = ValueSerializer::class) Value>()
        return "Hello, ${platform.name}!"
    }
}

inline fun <reified T> foo() {
    serializer<T>()
}

class Value(val value: Int)

object ValueSerializer : KSerializer<Value> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(Value::class.qualifiedName.toString(), PrimitiveKind.INT)

    override fun deserialize(decoder: Decoder): Value {
        return Value(decoder.decodeInt())
    }

    override fun serialize(encoder: Encoder, value: Value) {
        encoder.encodeInt(value.value)
    }
}

and call it in swift

Greeting().greet()

then crash

Uncaught Kotlin exception: kotlinx.serialization.SerializationException: Serializer for class 'Value' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
To get enum serializer on Kotlin/Native, it should be annotated with @Serializable annotation.
To get interface serializer on Kotlin/Native, use PolymorphicSerializer() constructor function.

    at 0   Shared                              0x10a90a38e        kfun:kotlinx.serialization.internal#platformSpecificSerializerNotRegistered__at__kotlin.reflect.KClass<*>(){}kotlin.Nothing + 174 
    at 1   Shared                              0x10a8ed95f        kfun:kotlinx.serialization#serializer__at__kotlinx.serialization.modules.SerializersModule(kotlin.reflect.KType){}kotlinx.serialization.KSerializer<kotlin.Any?> + 1631 
    at 2   Shared                              0x10a8ed2ca        kfun:kotlinx.serialization#serializer(kotlin.reflect.KType){}kotlinx.serialization.KSerializer<kotlin.Any?> + 138 
    at 3   Shared                              0x10a98132b        objc2kotlin_kfun:Greeting#greet(){}kotlin.String + 379 

Environment

  • Kotlin version: 1.9.23
  • Library version: 1.6.3
  • Kotlin platforms: KMP(K/N for iOS)
  • Gradle version: 8.4
  • IDE version (if bug is related to the IDE) Android Studio JellyFish 2023.3.1 & XCode 15.0.1
@Gargantua7
Copy link
Author

if defined a module like

SerializersModule {
    contextual(Value::class, ValueSerializer)
}

can fix this issue, but it is not flexible

@pdvrieze
Copy link
Contributor

@Gargantua7 You need to add @Serializable(ValueSerializer::class) to Value. At that point it will be available directly (as the default, you can still override it at the use site).

@Gargantua7
Copy link
Author

@Gargantua7 You need to add @Serializable(ValueSerializer::class) to Value. At that point it will be available directly (as the default, you can still override it at the use site).

Value just a sample. In my real project, the class defined in a third party lib, so i cant add any annotation to it.

And this sample work fine on Android or JVM, so i think it maybe a bug on Native

@xiaozhikang0916
Copy link
Contributor

foo<@Serializable(with = ValueSerializer::class) Value>() is it even a valid statement?

I ran the code snippet in jvm test but failed with Serializer for class 'kotlinx.serialization.Value' is not found.

@Gargantua7

This comment was marked as outdated.

@Gargantua7
Copy link
Author

Gargantua7 commented May 17, 2024

foo<@Serializable(with = ValueSerializer::class) Value>() is it even a valid statement?

I ran the code snippet in jvm test but failed with Serializer for class 'kotlinx.serialization.Value' is not found.

Sorry that I not apply org.jetbrains.kotlin.plugin.serialization plugin in my sample project, so it will failed on Android.
And after I apply plugin, this sample work fine.

So foo<@Serializable(with = ValueSerializer::class) Value>() is a valid statement.

It seems to be the effect of the plugin, and it does not take effect on Native

1715920388818

@Gargantua7 Gargantua7 reopened this May 17, 2024
@xiaozhikang0916
Copy link
Contributor

Can you share your demo project? As I am failing both in unit test and Android application.


foo<@Serializable(with = ValueSerializer::class) Value>() is it even a valid statement?

I doubt that if it is possible to add an annotation to an existing type when passing to an inline function, or create a new type for the plugin.

@Gargantua7
Copy link
Author

Can you share your demo project? As I am failing both in unit test and Android application.

foo<@Serializable(with = ValueSerializer::class) Value>() is it even a valid statement?

I doubt that if it is possible to add an annotation to an existing type when passing to an inline function, or create a new type for the plugin.

this is the sample project, maybe you should configuration the android sdk in local.properties:

EmptyProject 2.zip

@xiaozhikang0916
Copy link
Contributor

Intresting, if the statement foo<@Serializable(with = ValueSerializer::class) Value>(Value(42)) is in a library module ( shared in the demo project ), it runs fine. But if move it to the application module ( composeApp there ), SerializationException is thrown.

@Gargantua7
Copy link
Author

Gargantua7 commented May 17, 2024

Will plans to fix it or remove the feature ? This will determine if I end up keeping it as is or using SerializersModule

@pdvrieze
Copy link
Contributor

@Gargantua7 I meant to put that annotation at the class declaration. It doesn't work as part of a function type parameter (erasure). If you want to have the function work, have another (non-reified) version of the function that takes the serializer as parameter.

@Gargantua7
Copy link
Author

Gargantua7 commented May 17, 2024

I meant to put that annotation at the class declaration.

Sorry that the class is declaration at third party or standary library (eg. Number, BigDecimal), so I have no way to put to the annotation

It doesn't work as part of a function type parameter (erasure)

In fact it will work fine in the JVM's library module, discussed above

@sandwwraith
Copy link
Member

Hi, I see that the report is essentially is about foo<@Serializable(with = ValueSerializer::class) Value>() not finding serializer. The idea is, it was never meant to be working this way. @Serializable(...) annotation should be used on classes and properties/properties types that are inside these classes so the plugin can insert a proper serializer into generated code.

If you give it a second thought, this construction is kinda unnecessary. What is the point of calling serializer<@Serializable(with = ValueSerializer::class) Value>() if you already know that you should get a ValueSerializer?
For intermediate functions, like foo, it is also makes more sense to provide an overload that explicitly accepts KSerializer. All SerialFormat functions, such as Json.encodeToString, have these overloads, allowing users to provide the custom serializer they want.

Related issues and discussions:
https://youtrack.jetbrains.com/issue/KT-64920/Json.encodeToString-yields-different-results-depending-on-whether-typealias-is-used#focus=Comments-27-9092520.0-0
https://youtrack.jetbrains.com/issue/KT-67252

@sandwwraith sandwwraith closed this as not planned Won't fix, can't repro, duplicate, stale May 17, 2024
@Gargantua7
Copy link
Author

Caller passing in KSerializer is indeed a good alternative.

THX

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants