taiki-e / pin-project-lite

A lightweight version of pin-project written with declarative macros.
https://docs.rs/pin-project-lite
Apache License 2.0
216 stars 15 forks source link

Cannot interoperate with other field attributes #3

Open taiki-e opened 4 years ago

taiki-e commented 4 years ago

In the current implementation, an error will occur if the field has an attribute other than #[pin].

pin_project! {
   #[derive(serde::Deserialize)]
   struct Struct1<T, U> {
       #[serde(default)] //~ ERROR 
       pinned: Option<T>,
       unpinned: U,
    }
}
taiki-e commented 4 years ago

This is a bug, but I'm not sure if it can be fully fixed.

taiki-e commented 3 years ago

This also affects doc-comment on fields (as that is actually#[doc = "..."] attributes).

taiki-e commented 3 years ago

Another case: https://github.com/taiki-e/pin-project-lite/pull/28#issuecomment-702176944

  • The #[project] (and #[project_ref]) attribute must precede the other attributes except for #[doc]:

    pin_project! {
      /// documents (`#[doc]`) can be placed before `#[project]`.
      #[derive(Clone)] // <--- Error
      #[project = EnumProj] 
      #[derive(Debug)] // <--- Ok
      enum Enum<T, U> {
          Struct {
              #[pin]
              pinned: T,
              unpinned: U,
          },
          Unit,
      }
    }
taiki-e commented 3 years ago

Workaround: If the attribute is #[doc] (doc comment), this can be avoided by changing it to normal comment.

  pin_project! {
     #[derive(serde::Deserialize)]
     struct Struct1<T, U> {
-        /// description
+        // description
         pinned: Option<T>,
         unpinned: U,
      }
  }
taiki-e commented 3 years ago

Note: This issue is about attributes that apply only to the original struct/enum.

Proper handling of cfg/cfg_attr on fields and variants requires a more complex approach than the fix for this (it needs to be aware of the kind of attribute and propagate some of it to projected type). Even pin-project could not find the correct solution other than "leave to the compiler". And it is impossible to use the same trick in pin-project-lite.

See https://github.com/taiki-e/pin-project/issues/68#issuecomment-528318870 and https://github.com/hyperium/hyper/issues/2388#issuecomment-758304097 for a known workaround of the cfg issue.

(The cfg issue is not a "limitation that may be fixed" but a "limitation that is considered impossible to fix" and is a similar category as "wontfix". Actually, pin-project-lite cannot handle it properly on both fields and variants.)

tesaguri commented 3 years ago

I have written a macro as a workaround for #[cfg] attributes on enum variants and fields.

https://gist.github.com/tesaguri/2a1c0790a48bbda3dd7f71c26d02a793

What this does is to rewrite something like this:

enum Foo {
    #[cfg(a)]
    A { /* ... */ }
    #[cfg(b)]
    B { /* ... */ }
}

to this:

#[cfg(all(a, b))]
enum Foo {
    A { /* ... */ }
    B { /* ... */ }
}

#[cfg(all(a, not(b)))]
enum Foo {
    A { /* ... */ }
}

#[cfg(all(not(a), b))]
enum Foo {
    B { /* ... */ }
}

#[cfg(all(not(a), not(b)))]
enum Foo {
}

and pass the token tree to pin_project!.

tmccombs commented 2 years ago

Workaround: If the attribute is #[doc] (doc comment), this can be avoided by changing it to normal comment.

This doesn't work if you actually want the comment in the generated documentation. And if you have #![deny(missing_docs)] enabled on your crate, you are really in a pickle, because if the field is public, it will give you an error if it isn't documented, and you can't use #[allow(missing_docs)] or #[doc(hidden)] either.