-
Notifications
You must be signed in to change notification settings - Fork 613
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
Maybe throw a compile error when using serializer<T>() for a non-Serializable type #2522
Comments
Nice idea, would be nice to produce either string warning or plugin-induced error. |
I think we discussed this during the intrinsic design process. The general agreement was that compiler intrinsics are optimization and not a separate feature; therefore, they should not affect users' experience (in a negative AND positive way). So, regarding this particular suggestion: it is a nice idea, but doing so would also result in different experiences between |
Also there would be differences between |
Here is my experience with interface StateKeeper {
fun <T : Any> save(value: T, strategy: SerializationStrategy<T>)
fun <T : Any> restore(strategy: DeserializationStrategy<T>): T?
} This works just fine. For convenience, I would like to also some extensions, something like this. inline fun <reified T : Any> StateKeeper.save(value: T) {
save(value = value, strategy = serializer<T>())
}
inline fun <reified T : Any> StateKeeper.restore(): T? =
restore(strategy = serializer<T>()) After reading the docs for It would be much safer if we could have a compile time safe option for such use cases. To me, just documenting both |
For the intrinsic case it would make sense to generate a warning, and fall back to the default (reflective) approach. This would only really affect compilations that treat all warnings as errors, which would not likely be "stable" anyway (as new conditions trigger warnings). But I agree the semantics should remain. |
I believe in the specific case of the intrinsic Anyway, to me it looks beneficial to have a way of intrinsic-only behaviour, even if implemented via a separate dedicated API. I.e. I want to be sure that if the code compiled, then there is a guaranteed serializer for me. So that I could use this API in a library (as mentioned above), and the user's compilation would fail if they pass a non-serializable type by accident. |
@arkivanov It could succeed if |
So you actually want to report this diagnostic on |
Perhaps an approach that could be used to the same effect is to allow the |
Nice idea @pdvrieze! However, I don't understand what prevents the compiler plugin from throwing an exception instead of generating the With the latest example API above, if I write the following: fun foo(stateKeeper: StateKeeper) {
val some = stateKeeper.restore<Some>()
}
data class Some(val value: Int) Then I have the following in public final class FooKt {
public static final void foo(@NotNull StateKeeper stateKeeper) {
Intrinsics.checkNotNullParameter(stateKeeper, "stateKeeper");
int $i$f$restore = false;
Some some = (Some)stateKeeper.consume("some", (DeserializationStrategy)SerializersKt.noCompiledSerializer("com.arkivanov.essenty.statekeeper.Some"));
}
} So the code being generated is 100% failing at runtime. I understand that we can't support this |
@arkivanov As Leonid alluded, the way that inlining works is that inlined code is precompiled and then injected into the caller by the inliner. When the inliner is called, the actual value of |
Thanks! I might be missing something, but please consider the following fact. In a library I have the following API. inline fun <reified T : Any> StateKeeper.restore(): T? =
restore(strategy = serializer<T>()) This produces the following in the library jar. public static final <T> T restore(StateKeeper $this$restore) {
Intrinsics.checkNotNullParameter($this$restore, "<this>");
int $i$f$restore = false;
Intrinsics.reifiedOperationMarker(6, "T");
MagicApiIntrinsics.voidMagicApiCall("kotlinx.serialization.serializer.simple");
return $this$restore.restore((DeserializationStrategy)SerializersKt.serializer((KType)null));
} You can see Then in my project that depends on the library I have the following code. fun foo(stateKeeper: StateKeeper) {
val some = stateKeeper.restore<Some>()
}
data class Some(val value: Int)
fun bar(stateKeeper: StateKeeper) {
val some = stateKeeper.restore<SomeSerializable>()
}
@Serializable
data class SomeSerializable(val value: Int) After compiling the project into public final class FooKt {
public static final void foo(@NotNull StateKeeper stateKeeper) {
Intrinsics.checkNotNullParameter(stateKeeper, "stateKeeper");
int $i$f$restore = false;
Some some = (Some)stateKeeper.restore((DeserializationStrategy)SerializersKt.noCompiledSerializer("com.arkivanov.essenty.statekeeper.Some"));
}
public static final void bar(@NotNull StateKeeper stateKeeper) {
Intrinsics.checkNotNullParameter(stateKeeper, "stateKeeper");
int $i$f$restore = false;
SomeSerializable some = (SomeSerializable)stateKeeper.restore((DeserializationStrategy)SomeSerializable.Companion.serializer());
}
} As you can see, in case of |
We can throw |
What is your use-case and why do you need this feature?
Consider the following code:
Currently, it produces the following bytecode for Java (decompiled):
Describe the solution you'd like
If I understand it correctly, the generated code just throws an exception at runtime. Maybe it would be better to fail the compilation instead?
The text was updated successfully, but these errors were encountered: