Repository: godot-rust/godot-rust (rust)

Issue:
Can't Derive ToVariant on generic type with constraint
<!--
Feature requests add new functionality to the library. 
For ergonomic or internal improvements, open a 'Quality of life' issue instead.

In your request, please mention the motivation for the new feature,
as well as potential drawbacks and alternatives. 
-->
Hi ! 
I'm trying to get this to compile : 
```rust
#[derive(Debug, Clone, ToVariant)]
pub enum WorldChange<T: Entity> {
    EntityMoved {
        id: EntityId,
        from: Position,
        to: Position,
    },
    EntitySentMessage {
        from: EntityId,
        to: EntityId,
        msg: T::Message,
    },
    EntityStateChanged {
        id: EntityId,
        change: T::EntityChange,
    },
    EntityPlaced(EntityId, Position),
    EntityUnplaced(EntityId),
}
```
but it complains `this enum takes 1 generic argument but 0 generic arguments were supplied
expected 1 generic argument`.

Note that it doesn't complain for MyStruct<T>, but it complains for any constraint MyStruct<T : Debug> or MyStruct<T : ToVariant>
it doesn't matter as well if T::Message impl ToVariant or not (which it does)

It would be nice to be able to constrain the type because my only other option is to impl ToVariant by myself which is not ideal.

Pull request description:
Fixed ToVariant/FromVariant derive for generic types with bounds
Fixes part of #957; but there's a separate problem with `where` clauses (see description).

Patch:
diff --git a/gdnative-derive/src/variant/bounds.rs b/gdnative-derive/src/variant/bounds.rs
index 3f30d28..0907e40 100644
--- a/gdnative-derive/src/variant/bounds.rs
+++ b/gdnative-derive/src/variant/bounds.rs
@@ -1,5 +1,6 @@
+use syn::punctuated::Punctuated;
 use syn::visit::Visit;
-use syn::Generics;
+use syn::{GenericParam, Generics};
 
 use crate::extend_bounds::{with_visitor, BoundsVisitor};
 
@@ -50,3 +51,20 @@ pub(crate) fn extend_bounds(
         }
     })
 }
+
+pub(crate) fn remove_bounds(mut generics: Generics) -> Generics {
+    for param in generics.params.iter_mut() {
+        match param {
+            GenericParam::Type(ty) => {
+                ty.colon_token = None;
+                ty.bounds = Punctuated::new();
+            }
+            GenericParam::Lifetime(lt) => {
+                lt.colon_token = None;
+                lt.bounds = Punctuated::new();
+            }
+            GenericParam::Const(_) => {}
+        }
+    }
+    generics
+}
diff --git a/gdnative-derive/src/variant/from.rs b/gdnative-derive/src/variant/from.rs
index a726ff7..3054c89 100644
--- a/gdnative-derive/src/variant/from.rs
+++ b/gdnative-derive/src/variant/from.rs
@@ -1,5 +1,6 @@
 use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
 
+use crate::variant::bounds;
 use syn::Ident;
 
 use super::repr::Repr;
@@ -106,11 +107,12 @@ pub(crate) fn expand_from_variant(derive_data: DeriveData) -> Result<TokenStream
         }
     };
 
+    let generics_no_bounds = bounds::remove_bounds(generics.clone());
     let where_clause = &generics.where_clause;
 
     let result = quote! {
         #derived
-        impl #generics ::gdnative::core_types::FromVariant for #ident #generics #where_clause {
+        impl #generics ::gdnative::core_types::FromVariant for #ident #generics_no_bounds #where_clause {
             fn from_variant(
                 #input_ident: &::gdnative::core_types::Variant
             ) -> ::std::result::Result<Self, ::gdnative::core_types::FromVariantError> {
diff --git a/gdnative-derive/src/variant/to.rs b/gdnative-derive/src/variant/to.rs
index 01f3b10..ae16f84 100644
--- a/gdnative-derive/src/variant/to.rs
+++ b/gdnative-derive/src/variant/to.rs
@@ -1,3 +1,4 @@
+use crate::variant::bounds;
 use proc_macro2::{Literal, TokenStream as TokenStream2};
 
 use super::repr::Repr;
@@ -69,11 +70,12 @@ pub(crate) fn expand_to_variant(
         }
     };
 
+    let generics_no_bounds = bounds::remove_bounds(generics.clone());
     let where_clause = &generics.where_clause;
 
     let result = quote! {
         #derived
-        impl #generics #trait_path for #ident #generics #where_clause {
+        impl #generics #trait_path for #ident #generics_no_bounds #where_clause {
             fn #to_variant_fn(#to_variant_receiver) -> ::gdnative::core_types::Variant {
                 use #trait_path;
                 use ::gdnative::core_types::FromVariant;


Test patch:
diff --git a/test/src/test_derive.rs b/test/src/test_derive.rs
index 8139b22..e0447df 100644
--- a/test/src/test_derive.rs
+++ b/test/src/test_derive.rs
@@ -40,9 +40,8 @@ pub(crate) fn register(handle: InitHandle) {
 
 crate::godot_itest! { test_derive_to_variant {
     #[derive(Clone, Eq, PartialEq, Debug, ToVariant, FromVariant)]
-    struct ToVar<T, R>
+    struct ToVar<T: Associated, R>
     where
-        T: Associated,
         R: Default,
     {
         foo: T::A,
@@ -55,7 +54,7 @@ crate::godot_itest! { test_derive_to_variant {
     }
 
     #[derive(Clone, Eq, PartialEq, Debug, ToVariant, FromVariant)]
-    enum ToVarEnum<T> {
+    enum ToVarEnum<T: Bound> {
         Foo(T),
         Bar,
         Baz { baz: u8 },
@@ -67,9 +66,12 @@ crate::godot_itest! { test_derive_to_variant {
         T: Associated,
         R: Default;
 
+    trait Bound {}
+    impl Bound for bool {}
+
     trait Associated {
         type A;
-        type B;
+        type B : Bound;
     }
 
     impl Associated for f64 {