Skip to content

Commit b45a8f4

Browse files
committed
add support for tuple structs
TODO Signed-off-by: Benno Lossin <[email protected]>
1 parent 39e6ae6 commit b45a8f4

File tree

7 files changed

+197
-59
lines changed

7 files changed

+197
-59
lines changed

internal/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use proc_macro::TokenStream;
1414
use syn::parse_macro_input;
1515

1616
mod init;
17+
mod member;
1718
mod pin_data;
1819
mod pinned_drop;
1920
mod zeroable;

internal/src/member.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::fmt::Display;
2+
3+
use quote::{IdentFragment, ToTokens};
4+
use syn::{Ident, Index};
5+
6+
pub enum Member {
7+
Named(Ident),
8+
Unnamed(Index),
9+
}
10+
11+
impl Member {
12+
pub fn new(idx: usize, ident: Option<&Ident>) -> Self {
13+
ident.cloned().map(Self::Named).unwrap_or(idx.into())
14+
}
15+
}
16+
17+
impl ToTokens for Member {
18+
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
19+
match self {
20+
Self::Named(ident) => ToTokens::to_tokens(ident, tokens),
21+
Self::Unnamed(idx) => ToTokens::to_tokens(idx, tokens),
22+
}
23+
}
24+
}
25+
26+
impl From<usize> for Member {
27+
fn from(value: usize) -> Self {
28+
Self::Unnamed(value.into())
29+
}
30+
}
31+
32+
impl IdentFragment for Member {
33+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
34+
match self {
35+
Self::Named(ident) => IdentFragment::fmt(ident, f),
36+
Self::Unnamed(idx) => IdentFragment::fmt(idx, f),
37+
}
38+
}
39+
40+
fn span(&self) -> Option<proc_macro2::Span> {
41+
match self {
42+
Self::Named(ident) => Some(ident.span()),
43+
Self::Unnamed(idx) => Some(idx.span),
44+
}
45+
}
46+
}
47+
48+
impl Display for Member {
49+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50+
match self {
51+
Self::Named(ident) => write!(f, "{ident}"),
52+
Self::Unnamed(idx) => write!(f, "{}", idx.index),
53+
}
54+
}
55+
}

internal/src/pin_data.rs

Lines changed: 127 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ use syn::{
77
parse_quote, parse_quote_spanned,
88
spanned::Spanned,
99
visit_mut::VisitMut,
10-
Error, Field, Ident, Item, ItemStruct, PathSegment, Result, Type, TypePath, WhereClause,
10+
Error, Field, Fields, Ident, Item, ItemStruct, PathSegment, Result, Type, TypePath,
11+
WhereClause,
1112
};
1213

14+
use crate::member::Member;
15+
1316
pub(crate) mod kw {
1417
syn::custom_keyword!(PinnedDrop);
1518
}
@@ -171,8 +174,13 @@ fn generate_unpin_impl(
171174
let (_, ty_generics, _) = generics.split_for_impl();
172175
let mut pinned_fields = fields
173176
.iter()
174-
.filter(|f| is_field_structurally_pinned(f))
175-
.cloned()
177+
.enumerate()
178+
.filter(|(_, f)| is_field_structurally_pinned(f))
179+
.map(|(i, f)| {
180+
let mut f = f.clone();
181+
f.ident.get_or_insert_with(|| format_ident!("_{i}"));
182+
f
183+
})
176184
.collect::<Vec<_>>();
177185
for field in &mut pinned_fields {
178186
field.attrs.retain(|a| !a.path().is_ident("pin"));
@@ -271,42 +279,55 @@ fn generate_projections(
271279
let projection = format_ident!("{ident}Projection");
272280
let this = format_ident!("this");
273281

274-
let (fields_decl, fields_proj) = collect_tuple(fields.iter().map(
275-
|f @ Field {
276-
vis,
277-
ident,
278-
ty,
279-
attrs,
280-
..
281-
}| {
282+
let (fields_decl, fields_proj) = collect_tuple(fields.iter().enumerate().map(
283+
|(
284+
idx,
285+
f @ Field {
286+
vis,
287+
ident,
288+
ty,
289+
attrs,
290+
..
291+
},
292+
)| {
282293
let mut attrs = attrs.clone();
283294
attrs.retain(|a| !a.path().is_ident("pin"));
284295
let mut no_doc_attrs = attrs.clone();
285296
no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
286-
let ident = ident
287-
.as_ref()
288-
.expect("only structs with named fields are supported");
289-
if is_field_structurally_pinned(f) {
297+
let ty = if is_field_structurally_pinned(f) {
298+
quote!(::core::pin::Pin<&'__pin mut #ty>)
299+
} else {
300+
quote!(&'__pin mut #ty)
301+
};
302+
let m = Member::new(idx, ident.as_ref());
303+
let access = if is_field_structurally_pinned(f) {
304+
quote!(
305+
// SAFETY: this field is structurally pinned.
306+
unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#m) }
307+
)
308+
} else {
309+
quote!(&mut #this.#m)
310+
};
311+
if ident.is_some() {
290312
(
291313
quote!(
292314
#(#attrs)*
293-
#vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
315+
#vis #ident: #ty,
294316
),
295317
quote!(
296318
#(#no_doc_attrs)*
297-
// SAFETY: this field is structurally pinned.
298-
#ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
319+
#ident: #access,
299320
),
300321
)
301322
} else {
302323
(
303324
quote!(
304325
#(#attrs)*
305-
#vis #ident: &'__pin mut #ty,
326+
#vis #ty,
306327
),
307328
quote!(
308329
#(#no_doc_attrs)*
309-
#ident: &mut #this.#ident,
330+
#access,
310331
),
311332
)
312333
}
@@ -315,49 +336,91 @@ fn generate_projections(
315336
let structurally_pinned_fields_docs = fields
316337
.iter()
317338
.filter(|f| is_field_structurally_pinned(f))
318-
.map(|Field { ident, .. }| {
319-
let doc = format!(" - `{}`", ident.as_ref().expect(""));
339+
.enumerate()
340+
.map(|(i, Field { ident, .. })| {
341+
let doc = match ident.as_ref() {
342+
Some(ident) => format!(" - `{ident}`"),
343+
None => format!("- `{i}`"),
344+
};
320345
quote!(#[doc = #doc])
321346
});
322347
let not_structurally_pinned_fields_docs = fields
323348
.iter()
324349
.filter(|f| !is_field_structurally_pinned(f))
325-
.map(|Field { ident, .. }| {
326-
let doc = format!(" - `{}`", ident.as_ref().expect(""));
350+
.enumerate()
351+
.map(|(i, Field { ident, .. })| {
352+
let doc = match ident.as_ref() {
353+
Some(ident) => format!(" - `{ident}`"),
354+
None => format!("- `{i}`"),
355+
};
327356
quote!(#[doc = #doc])
328357
});
329358
let docs = format!(" Pin-projections of [`{ident}`]");
330-
quote! {
331-
#[doc = #docs]
332-
#[allow(dead_code)]
333-
#[doc(hidden)]
334-
#vis struct #projection #generics {
335-
#(#fields_decl)*
336-
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
337-
}
359+
match fields {
360+
Fields::Unit | Fields::Named(_) => quote! {
361+
#[doc = #docs]
362+
#[allow(dead_code)]
363+
#[doc(hidden)]
364+
#vis struct #projection #generics {
365+
#(#fields_decl)*
366+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
367+
}
338368

339-
impl #og_impl_gen #ident #og_ty_gen
340-
#whr
341-
{
342-
/// Pin-projects all fields of `Self`.
343-
///
344-
/// These fields are structurally pinned:
345-
#(#structurally_pinned_fields_docs)*
346-
///
347-
/// These fields are **not** structurally pinned:
348-
#(#not_structurally_pinned_fields_docs)*
349-
#[inline]
350-
#vis fn project<'__pin>(
351-
self: ::core::pin::Pin<&'__pin mut Self>,
352-
) -> #projection #ty_gen {
353-
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
354-
let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
355-
#projection {
356-
#(#fields_proj)*
357-
___pin_phantom_data: ::core::marker::PhantomData,
369+
impl #og_impl_gen #ident #og_ty_gen
370+
#whr
371+
{
372+
/// Pin-projects all fields of `Self`.
373+
///
374+
/// These fields are structurally pinned:
375+
#(#structurally_pinned_fields_docs)*
376+
///
377+
/// These fields are **not** structurally pinned:
378+
#(#not_structurally_pinned_fields_docs)*
379+
#[inline]
380+
#vis fn project<'__pin>(
381+
self: ::core::pin::Pin<&'__pin mut Self>,
382+
) -> #projection #ty_gen {
383+
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
384+
let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
385+
#projection {
386+
#(#fields_proj)*
387+
___pin_phantom_data: ::core::marker::PhantomData,
388+
}
358389
}
359390
}
360-
}
391+
},
392+
Fields::Unnamed(_) => quote! {
393+
#[doc = #docs]
394+
#[allow(dead_code)]
395+
#[doc(hidden)]
396+
#vis struct #projection #generics (
397+
#(#fields_decl)*
398+
::core::marker::PhantomData<&'__pin mut ()>,
399+
);
400+
401+
impl #og_impl_gen #ident #og_ty_gen
402+
#whr
403+
{
404+
/// Pin-projects all fields of `Self`.
405+
///
406+
/// These fields are structurally pinned:
407+
#(#structurally_pinned_fields_docs)*
408+
///
409+
/// These fields are **not** structurally pinned:
410+
#(#not_structurally_pinned_fields_docs)*
411+
#[inline]
412+
#vis fn project<'__pin>(
413+
self: ::core::pin::Pin<&'__pin mut Self>,
414+
) -> #projection #ty_gen {
415+
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
416+
let #this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
417+
#projection(
418+
#(#fields_proj)*
419+
::core::marker::PhantomData,
420+
)
421+
}
422+
}
423+
},
361424
}
362425
}
363426

@@ -378,6 +441,7 @@ fn generate_the_pin_data(
378441
//
379442
// The functions are `unsafe` to prevent accidentally calling them.
380443
fn handle_field(
444+
idx: usize,
381445
Field {
382446
vis,
383447
ident,
@@ -390,10 +454,12 @@ fn generate_the_pin_data(
390454
) -> TokenStream {
391455
let mut attrs = attrs.clone();
392456
attrs.retain(|a| !a.path().is_ident("pin"));
393-
let ident = ident
394-
.as_ref()
395-
.expect("only structs with named fields are supported");
396-
let project_ident = format_ident!("__project_{ident}");
457+
let m = Member::new(idx, ident.as_ref());
458+
let fun_ident = match &m {
459+
Member::Named(ident) => ident.clone(),
460+
Member::Unnamed(idx) => format_ident!("_{}", idx),
461+
};
462+
let project_ident = format_ident!("__project_{}", m);
397463
let (init_ty, init_fn, project_ty, project_body, pin_safety) = if pinned {
398464
(
399465
quote!(PinInit),
@@ -415,7 +481,7 @@ fn generate_the_pin_data(
415481
)
416482
};
417483
let slot_safety = format!(
418-
" `slot` points at the field `{ident}` inside of `{struct_ident}`, which is pinned.",
484+
" `slot` points at the field `{m}` inside of `{struct_ident}`, which is pinned.",
419485
);
420486
quote! {
421487
/// # Safety
@@ -425,7 +491,7 @@ fn generate_the_pin_data(
425491
/// to deallocate.
426492
#pin_safety
427493
#(#attrs)*
428-
#vis unsafe fn #ident<E>(
494+
#vis unsafe fn #fun_ident<E>(
429495
self,
430496
slot: *mut #ty,
431497
init: impl ::pin_init::#init_ty<#ty, E>,
@@ -439,6 +505,7 @@ fn generate_the_pin_data(
439505
///
440506
#[doc = #slot_safety]
441507
#(#attrs)*
508+
#[allow(non_snake_case)]
442509
#vis unsafe fn #project_ident<'__slot>(
443510
self,
444511
slot: &'__slot mut #ty,
@@ -450,7 +517,8 @@ fn generate_the_pin_data(
450517

451518
let field_accessors = fields
452519
.iter()
453-
.map(|f| handle_field(f, ident, is_field_structurally_pinned(f)))
520+
.enumerate()
521+
.map(|(i, f)| handle_field(i, f, ident, is_field_structurally_pinned(f)))
454522
.collect::<TokenStream>();
455523
quote! {
456524
// We declare this struct which will host all of the projection function for our type. It

tests/tuple-struct.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use pin_init::pin_data;
2+
3+
#[pin_data]
4+
pub struct Bar;
5+
6+
#[pin_data]
7+
pub struct Foo(#[pin] Bar);

tests/ui/expand/many_generics.expanded.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const _: () = {
103103
/// # Safety
104104
///
105105
/// `slot` points at the field `array` inside of `Foo`, which is pinned.
106+
#[allow(non_snake_case)]
106107
unsafe fn __project_array<'__slot>(
107108
self,
108109
slot: &'__slot mut [u8; 1024 * 1024],
@@ -124,6 +125,7 @@ const _: () = {
124125
/// # Safety
125126
///
126127
/// `slot` points at the field `r` inside of `Foo`, which is pinned.
128+
#[allow(non_snake_case)]
127129
unsafe fn __project_r<'__slot>(
128130
self,
129131
slot: &'__slot mut &'b mut [&'a mut T; SIZE],
@@ -146,6 +148,7 @@ const _: () = {
146148
/// # Safety
147149
///
148150
/// `slot` points at the field `_pin` inside of `Foo`, which is pinned.
151+
#[allow(non_snake_case)]
149152
unsafe fn __project__pin<'__slot>(
150153
self,
151154
slot: &'__slot mut PhantomPinned,

tests/ui/expand/pin-data.expanded.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const _: () = {
6161
/// # Safety
6262
///
6363
/// `slot` points at the field `array` inside of `Foo`, which is pinned.
64+
#[allow(non_snake_case)]
6465
unsafe fn __project_array<'__slot>(
6566
self,
6667
slot: &'__slot mut [u8; 1024 * 1024],
@@ -83,6 +84,7 @@ const _: () = {
8384
/// # Safety
8485
///
8586
/// `slot` points at the field `_pin` inside of `Foo`, which is pinned.
87+
#[allow(non_snake_case)]
8688
unsafe fn __project__pin<'__slot>(
8789
self,
8890
slot: &'__slot mut PhantomPinned,

0 commit comments

Comments
 (0)