Skip to content

Commit 0c52973

Browse files
committed
add decl_generics to parse_generics() and #[pin_data]
The generic parameters on a type definition can specify default values. Currently `parse_generics()` cannot handle this though. For example when parsing the following generics: <T: Clone, const N: usize = 0> The `impl_generics` will be set to `T: Clone, const N: usize = 0` and `ty_generics` will be set to `T, N`. Now using the `impl_generics` on an impl block: impl<$($impl_generics)*> Foo {} will result in invalid Rust code, because default values are only available on type definitions. Therefore add parsing support for generic parameter default values using a new kind of generics called `decl_generics` and change the old behavior of `impl_generics` to not contain the generic parameter default values. Now `Generics` has three fields: - `impl_generics`: the generics with bounds (e.g. `T: Clone, const N: usize`) - `decl_generics`: the generics with bounds and default values (e.g. `T: Clone, const N: usize = 0`) - `ty_generics`: contains the generics without bounds and without default values (e.g. `T, N`) `impl_generics` is designed to be used on `impl<$impl_generics>`, `decl_generics` for the type definition, so `struct Foo<$decl_generics>` and `ty_generics` whenever you use the type, so `Foo<$ty_generics>`. Here is an example that uses all three different types of generics: let (Generics { decl_generics, impl_generics, ty_generics }, rest) = parse_generics(input); quote! { struct Foo<$($decl_generics)*> { // ... } impl<$impl_generics> Foo<$ty_generics> { fn foo() { // ... } } }
1 parent 83b08cb commit 0c52973

2 files changed

Lines changed: 95 additions & 37 deletions

File tree

pinned-init-macro/src/pin_data.rs

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree};
44
use quote::quote;
55

66
struct Generics {
7+
decl_generics: Vec<TokenTree>,
78
impl_generics: Vec<TokenTree>,
89
ty_generics: Vec<TokenTree>,
910
}
@@ -12,6 +13,8 @@ struct Generics {
1213
///
1314
/// The generics are not present in the rest, but a where clause might remain.
1415
fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
16+
// The generics with bounds and default values.
17+
let mut decl_generics = vec![];
1518
// `impl_generics`, the declared generics with their bounds.
1619
let mut impl_generics = vec![];
1720
// Only the names of the generics, without any bounds.
@@ -23,10 +26,17 @@ fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
2326
let mut toks = input.into_iter();
2427
// If we are at the beginning of a generic parameter.
2528
let mut at_start = true;
26-
for tt in &mut toks {
29+
let mut skip_until_comma = false;
30+
while let Some(tt) = toks.next() {
31+
if nesting == 1 && matches!(&tt, TokenTree::Punct(p) if p.as_char() == '>') {
32+
// Found the end of the generics.
33+
break;
34+
} else if nesting >= 1 {
35+
decl_generics.push(tt.clone());
36+
}
2737
match tt.clone() {
2838
TokenTree::Punct(p) if p.as_char() == '<' => {
29-
if nesting >= 1 {
39+
if nesting >= 1 && !skip_until_comma {
3040
// This is inside of the generics and part of some bound.
3141
impl_generics.push(tt);
3242
}
@@ -38,49 +48,70 @@ fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
3848
break;
3949
} else {
4050
nesting -= 1;
41-
if nesting >= 1 {
51+
if nesting >= 1 && !skip_until_comma {
4252
// We are still inside of the generics and part of some bound.
4353
impl_generics.push(tt);
4454
}
45-
if nesting == 0 {
46-
break;
47-
}
4855
}
4956
}
50-
tt => {
57+
TokenTree::Punct(p) if skip_until_comma && p.as_char() == ',' => {
5158
if nesting == 1 {
52-
// Here depending on the token, it might be a generic variable name.
53-
match &tt {
54-
// Ignore const.
55-
TokenTree::Ident(i) if i.to_string() == "const" => {}
56-
TokenTree::Ident(_) if at_start => {
57-
ty_generics.push(tt.clone());
58-
// We also already push the `,` token, this makes it easier to append
59-
// generics.
60-
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
61-
at_start = false;
62-
}
63-
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
64-
// Lifetimes begin with `'`.
65-
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
66-
ty_generics.push(tt.clone());
67-
}
68-
_ => {}
69-
}
70-
}
71-
if nesting >= 1 {
59+
impl_generics.push(tt.clone());
7260
impl_generics.push(tt);
73-
} else if nesting == 0 {
61+
skip_until_comma = false;
62+
}
63+
}
64+
_ if !skip_until_comma => {
65+
match nesting {
7466
// If we haven't entered the generics yet, we still want to keep these tokens.
75-
rest.push(tt);
67+
0 => rest.push(tt),
68+
1 => {
69+
// Here depending on the token, it might be a generic variable name.
70+
match tt.clone() {
71+
TokenTree::Ident(i) if at_start && i.to_string() == "const" => {
72+
let Some(name) = toks.next() else {
73+
// Parsing error.
74+
break;
75+
};
76+
impl_generics.push(tt);
77+
impl_generics.push(name.clone());
78+
ty_generics.push(name.clone());
79+
decl_generics.push(name);
80+
at_start = false;
81+
}
82+
TokenTree::Ident(_) if at_start => {
83+
impl_generics.push(tt.clone());
84+
ty_generics.push(tt);
85+
at_start = false;
86+
}
87+
TokenTree::Punct(p) if p.as_char() == ',' => {
88+
impl_generics.push(tt.clone());
89+
ty_generics.push(tt);
90+
at_start = true;
91+
}
92+
// Lifetimes begin with `'`.
93+
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
94+
impl_generics.push(tt.clone());
95+
ty_generics.push(tt);
96+
}
97+
// Generics can have default values, we skip these.
98+
TokenTree::Punct(p) if p.as_char() == '=' => {
99+
skip_until_comma = true;
100+
}
101+
_ => impl_generics.push(tt),
102+
}
103+
}
104+
_ => impl_generics.push(tt),
76105
}
77106
}
107+
_ => {}
78108
}
79109
}
80110
rest.extend(toks);
81111
(
82112
Generics {
83113
impl_generics,
114+
decl_generics,
84115
ty_generics,
85116
},
86117
rest,
@@ -97,6 +128,7 @@ pub(crate) fn pin_data(
97128

98129
let (
99130
Generics {
131+
decl_generics,
100132
impl_generics,
101133
ty_generics,
102134
},
@@ -164,6 +196,7 @@ pub(crate) fn pin_data(
164196
@sig(#(#rest)*),
165197
@impl_generics(#(#impl_generics)*),
166198
@ty_generics(#(#ty_generics)*),
199+
@decl_generics(#(#decl_generics)*),
167200
@body(#last),
168201
});
169202
quoted.extend(errs);

src/macros.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,10 @@
171171
//! t: T,
172172
//! }
173173
//! #[doc(hidden)]
174-
//! impl<'__pin, T>
175-
//! ::core::marker::Unpin for Bar<T> where __Unpin<'__pin, T>: ::core::marker::Unpin {}
174+
//! impl<'__pin, T> ::core::marker::Unpin for Bar<T>
175+
//! where
176+
//! __Unpin<'__pin, T>: ::core::marker::Unpin,
177+
//! {}
176178
//! // Now we need to ensure that `Bar` does not implement `Drop`, since that would give users
177179
//! // access to `&mut self` inside of `drop` even if the struct was pinned. This could lead to
178180
//! // UB with only safe code, so we disallow this by giving a trait implementation error using
@@ -189,8 +191,9 @@
189191
//! // for safety, but a good sanity check, since no normal code calls `PinnedDrop::drop`.
190192
//! #[allow(non_camel_case_types)]
191193
//! trait UselessPinnedDropImpl_you_need_to_specify_PinnedDrop {}
192-
//! impl<T: ::pinned_init::PinnedDrop>
193-
//! UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
194+
//! impl<
195+
//! T: ::pinned_init::PinnedDrop,
196+
//! > UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for T {}
194197
//! impl<T> UselessPinnedDropImpl_you_need_to_specify_PinnedDrop for Bar<T> {}
195198
//! };
196199
//! ```
@@ -375,7 +378,10 @@
375378
//! b: Bar<u32>,
376379
//! }
377380
//! #[doc(hidden)]
378-
//! impl<'__pin> ::core::marker::Unpin for Foo where __Unpin<'__pin>: ::core::marker::Unpin {}
381+
//! impl<'__pin> ::core::marker::Unpin for Foo
382+
//! where
383+
//! __Unpin<'__pin>: ::core::marker::Unpin,
384+
//! {}
379385
//! // Since we specified `PinnedDrop` as the argument to `#[pin_data]`, we expect `Foo` to
380386
//! // implement `PinnedDrop`. Thus we do not need to prevent `Drop` implementations like
381387
//! // before, instead we implement `Drop` here and delegate to `PinnedDrop`.
@@ -526,6 +532,7 @@ macro_rules! __pin_data {
526532
),
527533
@impl_generics($($impl_generics:tt)*),
528534
@ty_generics($($ty_generics:tt)*),
535+
@decl_generics($($decl_generics:tt)*),
529536
@body({ $($fields:tt)* }),
530537
) => {
531538
// We now use token munching to iterate through all of the fields. While doing this we
@@ -548,6 +555,9 @@ macro_rules! __pin_data {
548555
@impl_generics($($impl_generics)*),
549556
// The 'ty generics', the generics that will need to be specified on the impl blocks.
550557
@ty_generics($($ty_generics)*),
558+
// The 'decl generics', the generics that need to be specified on the struct
559+
// definition.
560+
@decl_generics($($decl_generics)*),
551561
// The where clause of any impl block and the declaration.
552562
@where($($($whr)*)?),
553563
// The remaining fields tokens that need to be processed.
@@ -573,6 +583,7 @@ macro_rules! __pin_data {
573583
@name($name:ident),
574584
@impl_generics($($impl_generics:tt)*),
575585
@ty_generics($($ty_generics:tt)*),
586+
@decl_generics($($decl_generics:tt)*),
576587
@where($($whr:tt)*),
577588
// We found a PhantomPinned field, this should generally be pinned!
578589
@fields_munch($field:ident : $($($(::)?core::)?marker::)?PhantomPinned, $($rest:tt)*),
@@ -595,6 +606,7 @@ macro_rules! __pin_data {
595606
@name($name),
596607
@impl_generics($($impl_generics)*),
597608
@ty_generics($($ty_generics)*),
609+
@decl_generics($($decl_generics)*),
598610
@where($($whr)*),
599611
@fields_munch($($rest)*),
600612
@pinned($($pinned)* $($accum)* $field: ::core::marker::PhantomPinned,),
@@ -611,6 +623,7 @@ macro_rules! __pin_data {
611623
@name($name:ident),
612624
@impl_generics($($impl_generics:tt)*),
613625
@ty_generics($($ty_generics:tt)*),
626+
@decl_generics($($decl_generics:tt)*),
614627
@where($($whr:tt)*),
615628
// We reached the field declaration.
616629
@fields_munch($field:ident : $type:ty, $($rest:tt)*),
@@ -628,6 +641,7 @@ macro_rules! __pin_data {
628641
@name($name),
629642
@impl_generics($($impl_generics)*),
630643
@ty_generics($($ty_generics)*),
644+
@decl_generics($($decl_generics)*),
631645
@where($($whr)*),
632646
@fields_munch($($rest)*),
633647
@pinned($($pinned)* $($accum)* $field: $type,),
@@ -644,6 +658,7 @@ macro_rules! __pin_data {
644658
@name($name:ident),
645659
@impl_generics($($impl_generics:tt)*),
646660
@ty_generics($($ty_generics:tt)*),
661+
@decl_generics($($decl_generics:tt)*),
647662
@where($($whr:tt)*),
648663
// We reached the field declaration.
649664
@fields_munch($field:ident : $type:ty, $($rest:tt)*),
@@ -661,6 +676,7 @@ macro_rules! __pin_data {
661676
@name($name),
662677
@impl_generics($($impl_generics)*),
663678
@ty_generics($($ty_generics)*),
679+
@decl_generics($($decl_generics)*),
664680
@where($($whr)*),
665681
@fields_munch($($rest)*),
666682
@pinned($($pinned)*),
@@ -677,6 +693,7 @@ macro_rules! __pin_data {
677693
@name($name:ident),
678694
@impl_generics($($impl_generics:tt)*),
679695
@ty_generics($($ty_generics:tt)*),
696+
@decl_generics($($decl_generics:tt)*),
680697
@where($($whr:tt)*),
681698
// We found the `#[pin]` attr.
682699
@fields_munch(#[pin] $($rest:tt)*),
@@ -693,6 +710,7 @@ macro_rules! __pin_data {
693710
@name($name),
694711
@impl_generics($($impl_generics)*),
695712
@ty_generics($($ty_generics)*),
713+
@decl_generics($($decl_generics)*),
696714
@where($($whr)*),
697715
@fields_munch($($rest)*),
698716
// We do not include `#[pin]` in the list of attributes, since it is not actually an
@@ -712,6 +730,7 @@ macro_rules! __pin_data {
712730
@name($name:ident),
713731
@impl_generics($($impl_generics:tt)*),
714732
@ty_generics($($ty_generics:tt)*),
733+
@decl_generics($($decl_generics:tt)*),
715734
@where($($whr:tt)*),
716735
// We reached the field declaration with visibility, for simplicity we only munch the
717736
// visibility and put it into `$accum`.
@@ -729,6 +748,7 @@ macro_rules! __pin_data {
729748
@name($name),
730749
@impl_generics($($impl_generics)*),
731750
@ty_generics($($ty_generics)*),
751+
@decl_generics($($decl_generics)*),
732752
@where($($whr)*),
733753
@fields_munch($field $($rest)*),
734754
@pinned($($pinned)*),
@@ -745,6 +765,7 @@ macro_rules! __pin_data {
745765
@name($name:ident),
746766
@impl_generics($($impl_generics:tt)*),
747767
@ty_generics($($ty_generics:tt)*),
768+
@decl_generics($($decl_generics:tt)*),
748769
@where($($whr:tt)*),
749770
// Some other attribute, just put it into `$accum`.
750771
@fields_munch(#[$($attr:tt)*] $($rest:tt)*),
@@ -761,6 +782,7 @@ macro_rules! __pin_data {
761782
@name($name),
762783
@impl_generics($($impl_generics)*),
763784
@ty_generics($($ty_generics)*),
785+
@decl_generics($($decl_generics)*),
764786
@where($($whr)*),
765787
@fields_munch($($rest)*),
766788
@pinned($($pinned)*),
@@ -777,6 +799,7 @@ macro_rules! __pin_data {
777799
@name($name:ident),
778800
@impl_generics($($impl_generics:tt)*),
779801
@ty_generics($($ty_generics:tt)*),
802+
@decl_generics($($decl_generics:tt)*),
780803
@where($($whr:tt)*),
781804
// We reached the end of the fields, plus an optional additional comma, since we added one
782805
// before and the user is also allowed to put a trailing comma.
@@ -790,7 +813,7 @@ macro_rules! __pin_data {
790813
) => {
791814
// Declare the struct with all fields in the correct order.
792815
$($struct_attrs)*
793-
$vis struct $name <$($impl_generics)*>
816+
$vis struct $name <$($decl_generics)*>
794817
where $($whr)*
795818
{
796819
$($fields)*
@@ -832,7 +855,8 @@ macro_rules! __pin_data {
832855

833856
// SAFETY: We have added the correct projection functions above to `__ThePinData` and
834857
// we also use the least restrictive generics possible.
835-
unsafe impl<$($impl_generics)*> $crate::__internal::HasPinData for $name<$($ty_generics)*>
858+
unsafe impl<$($impl_generics)*>
859+
$crate::__internal::HasPinData for $name<$($ty_generics)*>
836860
where $($whr)*
837861
{
838862
type PinData = __ThePinData<$($ty_generics)*>;
@@ -842,7 +866,8 @@ macro_rules! __pin_data {
842866
}
843867
}
844868

845-
unsafe impl<$($impl_generics)*> $crate::__internal::PinData for __ThePinData<$($ty_generics)*>
869+
unsafe impl<$($impl_generics)*>
870+
$crate::__internal::PinData for __ThePinData<$($ty_generics)*>
846871
where $($whr)*
847872
{
848873
type Datee = $name<$($ty_generics)*>;

0 commit comments

Comments
 (0)