Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 60 additions & 18 deletions internal/src/pin_data.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use quote::{format_ident, quote, ToTokens};
use syn::{
parse::{End, Nothing, Parse},
parse_quote, parse_quote_spanned,
spanned::Spanned,
visit_mut::VisitMut,
Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
Field, Fields, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
};

use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
Expand Down Expand Up @@ -35,10 +35,18 @@ impl Parse for Args {
}
}

impl ToTokens for Args {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Nothing(_) => (),
Self::PinnedDrop(kw) => kw.to_tokens(tokens),
}
}
}

struct FieldInfo<'a> {
field: &'a Field,
pinned: bool,
cfg_attrs: Vec<&'a Attribute>,
}

pub(crate) fn pin_data(
Expand Down Expand Up @@ -68,6 +76,51 @@ pub(crate) fn pin_data(
}
};

// Handling cfg can gets very complicated, especially for tuple structs.
// Therefore, resolve all field cfgs first before continuing.
for (field_idx, field) in struct_.fields.iter_mut().enumerate() {
let cfg: Vec<_> = field
.attrs
.iter()
.filter(|a| a.path().is_ident("cfg"))
.map(|a| {
a.parse_args::<TokenStream>()
.expect("parse as token stream cannot fail")
})
.collect();

if cfg.is_empty() {
continue;
}

field.attrs.retain(|a| !a.path().is_ident("cfg"));
let cfg_true_struct = quote!(#struct_);

let punctuated = match &mut struct_.fields {
Fields::Named(fields) => &mut fields.named,
Fields::Unnamed(fields) => &mut fields.unnamed,
Fields::Unit => unreachable!(),
};
*punctuated = std::mem::take(punctuated)
.into_pairs()
.enumerate()
.filter(|&(i, _)| i != field_idx)
.map(|(_, p)| p)
.collect();
let cfg_false_struct = quote!(#struct_);

// Resolve one field at a time until we've got no more field cfgs.
return Ok(quote!(
#[cfg(all(#(#cfg,)*))]
#[::pin_init::pin_data(#args)]
#cfg_true_struct

#[cfg(not(all(#(#cfg,)*)))]
#[::pin_init::pin_data(#args)]
#cfg_false_struct
));
}

// The generics might contain the `Self` type. Since this macro will define a new type with the
// same generics and bounds, this poses a problem: `Self` will refer to the new type as opposed
// to this struct definition. Therefore we have to replace `Self` with the concrete name.
Expand All @@ -90,16 +143,14 @@ pub(crate) fn pin_data(
dcx.error(&field, "#[pin] attribute specified more than once");
}

let cfg_attrs = field
.attrs
.iter()
.filter(|a| a.path().is_ident("cfg"))
.collect();
assert!(
!field.attrs.iter().any(|a| a.path().is_ident("cfg")),
"cfgs should be all resolved at this point"
);

FieldInfo {
field: &*field,
pinned: pinned_count != 0,
cfg_attrs,
}
})
.collect();
Expand Down Expand Up @@ -185,9 +236,7 @@ fn generate_unpin_impl(
let pinned_fields = fields.iter().filter(|f| f.pinned).map(|f| {
let ident = f.field.ident.as_ref().unwrap();
let ty = &f.field.ty;
let cfg_attrs = &f.cfg_attrs;
quote!(
#(#cfg_attrs)*
#ident: #ty
)
});
Expand Down Expand Up @@ -280,31 +329,26 @@ fn generate_projections(
.iter()
.map(|field| {
let Field { vis, ident, ty, .. } = &field.field;
let cfg_attrs = &field.cfg_attrs;

let ident = ident
.as_ref()
.expect("only structs with named fields are supported");
if field.pinned {
(
quote!(
#(#cfg_attrs)*
#vis #ident: ::core::pin::Pin<&'__pin mut #ty>,
),
quote!(
#(#cfg_attrs)*
// SAFETY: this field is structurally pinned.
#ident: unsafe { ::core::pin::Pin::new_unchecked(&mut #this.#ident) },
),
)
} else {
(
quote!(
#(#cfg_attrs)*
#vis #ident: &'__pin mut #ty,
),
quote!(
#(#cfg_attrs)*
#ident: &mut #this.#ident,
),
)
Expand Down Expand Up @@ -374,7 +418,6 @@ fn generate_the_pin_data(
.iter()
.map(|f| {
let Field { vis, ident, ty, .. } = f.field;
let cfg_attrs = &f.cfg_attrs;

let field_name = ident
.as_ref()
Expand All @@ -391,7 +434,6 @@ fn generate_the_pin_data(
/// - `(*slot).#field_name` is properly aligned.
/// - `(*slot).#field_name` points to uninitialized and exclusively accessed
/// memory.
#(#cfg_attrs)*
// Allow `non_snake_case` since the same warning will be emitted on
// the struct definition.
#[allow(non_snake_case)]
Expand Down
Loading