Skip to content

Commit 135c024

Browse files
committed
internal: add syn version of pinned_drop proc macro
Implement the `pinned_drop` attribute macro using syn to simplify parsing by not going through an additional declarative macro. This not only simplifies the code by a lot, increasing maintainability and making it easier to implement new features. But also improves the user experience by improving the error messages one gets when giving incorrect inputs to the macro. For example in this piece of code, there is a `drop` function missing: use pin_init::*; #[pin_data(PinnedDrop)] struct Foo {} #[pinned_drop] impl PinnedDrop for Foo {} But this error is communicated very poorly in the declarative macro version: error: no rules expected `)` | 6 | #[pinned_drop] | ^^^^^^^^^^^^^^ no rules expected this token in macro call | note: while trying to match keyword `fn` --> src/macros.rs | | fn drop($($sig:tt)*) { | ^^ = note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied | 3 | #[pin_data(PinnedDrop)] | ^^^^^^^^^^^^^^^^^^^^^^^ | | | the trait `PinnedDrop` is not implemented for `Foo` | required by a bound introduced by this call | = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) The syn version is much more concise and right to the point: error[E0046]: not all trait items implemented, missing: `drop` | 7 | impl PinnedDrop for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation | = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }` Another example is the following: use pin_init::*; use std::pin::Pin; #[pin_data(PinnedDrop)] struct Foo {} #[pinned_drop] impl PinnedDrop for Foo { fn drop(self: Pin<&mut Self>) {} const BAZ: usize = 0; } It produces this error in the declarative macro version: error: no rules expected keyword `const` | 10 | const BAZ: usize = 0; | ^^^^^ no rules expected this token in macro call | note: while trying to match `)` --> src/macros.rs | | ), | ^ error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied | 3 | #[pin_data(PinnedDrop)] | ^^^^^^^^^^^^^^^^^^^^^^^ | | | the trait `PinnedDrop` is not implemented for `Foo` | required by a bound introduced by this call | = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) In the syn version, we get instead: error[E0438]: const `BAZ` is not a member of trait `pinned_init::PinnedDrop` | 11 | const BAZ: usize = 0; | ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pinned_init::PinnedDrop` The syn version is only enabled in the user-space version and disabled in the kernel until syn becomes available there. Signed-off-by: Benno Lossin <[email protected]>
1 parent 9a72924 commit 135c024

File tree

5 files changed

+103
-53
lines changed

5 files changed

+103
-53
lines changed

internal/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@ extern crate quote;
3030

3131
mod helpers;
3232
mod pin_data;
33+
#[cfg(kernel)]
3334
mod pinned_drop;
3435
#[cfg(kernel)]
3536
mod zeroable;
3637

38+
#[cfg(not(kernel))]
39+
#[path = "syn_pinned_drop.rs"]
40+
mod pinned_drop;
3741
#[cfg(not(kernel))]
3842
#[path = "syn_zeroable.rs"]
3943
mod zeroable;

internal/src/syn_pinned_drop.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
3+
use proc_macro2::TokenStream;
4+
use quote::quote;
5+
use syn::{
6+
parse_macro_input, parse_quote, spanned::Spanned, Error, ImplItem, ImplItemFn, ItemImpl,
7+
Result, Token,
8+
};
9+
10+
pub(crate) fn pinned_drop(
11+
args: proc_macro::TokenStream,
12+
input: proc_macro::TokenStream,
13+
) -> proc_macro::TokenStream {
14+
parse_macro_input!(args as syn::parse::Nothing);
15+
do_impl(parse_macro_input!(input as ItemImpl))
16+
.unwrap_or_else(|e| e.into_compile_error())
17+
.into()
18+
}
19+
20+
fn do_impl(mut input: ItemImpl) -> Result<TokenStream> {
21+
let Some((_, path, _)) = &mut input.trait_ else {
22+
return Err(Error::new_spanned(
23+
input,
24+
"expected an `impl` block implementing `PinnedDrop`",
25+
));
26+
};
27+
if !is_pinned_drop(path) {
28+
return Err(Error::new_spanned(
29+
input,
30+
"expected an `impl` block implementing `PinnedDrop`",
31+
));
32+
}
33+
let mut error = None;
34+
if let Some(unsafety) = input.unsafety.take() {
35+
error = Some(
36+
Error::new_spanned(
37+
unsafety,
38+
"implementing the trait `PinnedDrop` via `#[pinned_drop]` is not unsafe",
39+
)
40+
.into_compile_error(),
41+
);
42+
}
43+
input.unsafety = Some(Token![unsafe](input.impl_token.span()));
44+
if path.segments.len() != 2 {
45+
path.segments.insert(0, parse_quote!(pin_init));
46+
}
47+
path.leading_colon.get_or_insert(Token![::](path.span()));
48+
for item in &mut input.items {
49+
match item {
50+
ImplItem::Fn(ImplItemFn { sig, .. }) if sig.ident == "drop" => {
51+
sig.inputs
52+
.push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop));
53+
}
54+
_ => {}
55+
}
56+
}
57+
Ok(quote! {
58+
#error
59+
#input
60+
})
61+
}
62+
63+
fn is_pinned_drop(path: &syn::Path) -> bool {
64+
if path.segments.len() > 2 {
65+
return false;
66+
}
67+
// If there is a `::`, then the path needs to be `::pin_init::PinnedDrop`.
68+
if path.leading_colon.is_some() && path.segments.len() != 2 {
69+
return false;
70+
}
71+
for (actual, expected) in path.segments.iter().rev().zip(["PinnedDrop", "pin_init"]) {
72+
if actual.ident != expected {
73+
return false;
74+
}
75+
}
76+
true
77+
}
Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,7 @@
1-
error: no rules expected `)`
2-
--> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1
1+
error[E0046]: not all trait items implemented, missing: `drop`
2+
--> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1
33
|
4-
6 | #[pinned_drop]
5-
| ^^^^^^^^^^^^^^ no rules expected this token in macro call
4+
7 | impl PinnedDrop for Foo {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
66
|
7-
note: while trying to match keyword `fn`
8-
--> src/macros.rs
9-
|
10-
| fn drop($($sig:tt)*) {
11-
| ^^
12-
= note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info)
13-
14-
error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied
15-
--> tests/ui/compile-fail/pinned_drop/no_fn.rs:3:1
16-
|
17-
3 | #[pin_data(PinnedDrop)]
18-
| ^^^^^^^^^^^^^^^^^^^^^^^
19-
| |
20-
| the trait `PinnedDrop` is not implemented for `Foo`
21-
| required by a bound introduced by this call
22-
|
23-
= note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)
7+
= help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`
Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,16 @@
1-
error: no rules expected keyword `const`
1+
error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop`
22
--> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:10:5
33
|
44
10 | const BAZ: usize = 0;
5-
| ^^^^^ no rules expected this token in macro call
6-
|
7-
note: while trying to match `)`
8-
--> src/macros.rs
9-
|
10-
| ),
11-
| ^
5+
| ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop`
126

13-
error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied
14-
--> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:3:1
7+
error[E0412]: cannot find type `Pin` in this scope
8+
--> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:8:19
9+
|
10+
8 | fn drop(self: Pin<&mut Self>) {}
11+
| ^^^ not found in this scope
12+
|
13+
help: consider importing this struct
1514
|
16-
3 | #[pin_data(PinnedDrop)]
17-
| ^^^^^^^^^^^^^^^^^^^^^^^
18-
| |
19-
| the trait `PinnedDrop` is not implemented for `Foo`
20-
| required by a bound introduced by this call
15+
1 + use std::pin::Pin;
2116
|
22-
= note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)
Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
1-
error: no rules expected keyword `const`
1+
error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop`
22
--> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:8:5
33
|
44
8 | const BAZ: usize = 0;
5-
| ^^^^^ no rules expected this token in macro call
6-
|
7-
note: while trying to match keyword `fn`
8-
--> src/macros.rs
9-
|
10-
| fn drop($($sig:tt)*) {
11-
| ^^
5+
| ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop`
126

13-
error[E0277]: the trait bound `Foo: PinnedDrop` is not satisfied
14-
--> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:3:1
7+
error[E0046]: not all trait items implemented, missing: `drop`
8+
--> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:7:1
159
|
16-
3 | #[pin_data(PinnedDrop)]
17-
| ^^^^^^^^^^^^^^^^^^^^^^^
18-
| |
19-
| the trait `PinnedDrop` is not implemented for `Foo`
20-
| required by a bound introduced by this call
10+
7 | impl PinnedDrop for Foo {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
2112
|
22-
= note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
= help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`

0 commit comments

Comments
 (0)