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

Derive macro for Format does not set generic bound correctly #799

Closed
Sympatron opened this issue Dec 15, 2023 · 8 comments
Closed

Derive macro for Format does not set generic bound correctly #799

Sympatron opened this issue Dec 15, 2023 · 8 comments
Labels
type: bug Something isn't working

Comments

@Sympatron
Copy link
Contributor

Sympatron commented Dec 15, 2023

The currently implementation of the derive macro does set T: defmt::Format bounds for all generic parameters T. This is not allways correct. Consider the following example:

trait Foo {
    type Bar;
}
#[derive(defmt::Format)]
struct Baz<T: Foo> {
    field: T::Bar
}

This will generate the trait bound T: defmt::Format instead of T::Bar: defmt::Format.
I am not quite sure what the general rule is in this case, but the easiest thing to do would be to require : defmt::Format for all field types whether they are generic or not.

I could try to work on this, but I never wrote procedural macros before, so I am not quite sure how to solve this.
Currently this code is responsible for the where clause:

for param in &generics.params {
if let GenericParam::Type(ty) = param {
let ident = &ty.ident;
where_clause
.predicates
.push(parse_quote!(#ident: defmt::Format));
}
}

I think where_clause should be moved to EncodeData and constructed in encode_struct_data from the fields.

@jonathanpallant
Copy link
Contributor

OK, interesting! We're going to need to think about this one, and we're also just about to head into Christmas break. We'll get back to this in 2024.

@jonathanpallant
Copy link
Contributor

My colleague offers this expansion of some built-in derives:

trait Foo {
    type Bar;
}
#[derive(Clone, Debug)]
struct Baz<T: Foo> {
    field: T::Bar,
}

expands to

#[automatically_derived]
impl<T: ::core::clone::Clone + Foo> ::core::clone::Clone for Baz<T>
where
    T::Bar: ::core::clone::Clone,
{
    #[inline]
    fn clone(&self) -> Baz<T> {
        Baz {
            field: ::core::clone::Clone::clone(&self.field),
        }
    }
}

#[automatically_derived]
impl<T: ::core::fmt::Debug + Foo> ::core::fmt::Debug for Baz<T>
where
    T::Bar: ::core::fmt::Debug,
{
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f, "Baz", "field", &&self.field)
    }
}

It probably makes sense to emit something similar. I don't fully understand why T: Debug and T: Clone in this expansion, but if that's what the standard library does, it probably makes sense.

@jonathanpallant
Copy link
Contributor

Sympatron added a commit to Sympatron/defmt that referenced this issue Dec 15, 2023
Sympatron added a commit to Sympatron/defmt that referenced this issue Dec 15, 2023
@Urhengulas Urhengulas added the type: bug Something isn't working label Mar 5, 2024
github-merge-queue bot pushed a commit that referenced this issue Mar 6, 2024
@ia0
Copy link
Contributor

ia0 commented Aug 2, 2024

I see 2 non-exclusive options going forward:

  1. Provide a #[defmt(bounds = "")] container-attribute to overwrite the inferred bounds. This is what serde does.
  2. Infer the perfect bounds. This is also what serde does.

See jamesmunns/postcard#153 for a similar issue in another crate.

I think the first option is pretty easy to implement and would provide a solid workaround.

@Sympatron
Copy link
Contributor Author

Didn't PR #800 already fix this? I think this issue can be closed now.

@ia0
Copy link
Contributor

ia0 commented Aug 5, 2024

Is #800 part of 0.3.8? If yes, then I'll extract a minimal example from my concrete problem.

@ia0
Copy link
Contributor

ia0 commented Aug 5, 2024

While trying to create a minimal example, I found the issue. The defmt:0.3.8 crate depends on defmt-macros:0.3.2 and my Cargo.lock was using 0.3.6 which doesn't seem to have the fix. I've run cargo update --precise=0.3.9 defmt-macros and now it works. It might be useful to bump the minimum version of defmt-macros in defmt as a minor bump since it adds a new feature.

@Urhengulas
Copy link
Member

The next release will bump the minimum version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants