defmt
defmt copied to clipboard
Derive macro for Format does not set generic bound correctly
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: https://github.com/knurling-rs/defmt/blob/06650b9cad2e5c8ce35b5694e1b74b15d7ecf4b9/macros/src/derives/format/codegen.rs#L47-L55
I think where_clause should be moved to EncodeData and constructed in encode_struct_data from the fields.
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.
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.
More details on this behaviour: https://smallcultfollowing.com/babysteps/blog/2022/04/12/implied-bounds-and-perfect-derive/
I see 2 non-exclusive options going forward:
- Provide a
#[defmt(bounds = "")]container-attribute to overwrite the inferred bounds. This is whatserdedoes. - Infer the perfect bounds. This is also what
serdedoes.
See https://github.com/jamesmunns/postcard/issues/153 for a similar issue in another crate.
I think the first option is pretty easy to implement and would provide a solid workaround.
Didn't PR #800 already fix this? I think this issue can be closed now.
Is #800 part of 0.3.8? If yes, then I'll extract a minimal example from my concrete problem.
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.
The next release will bump the minimum version.