Skip to content

Commit 4c09883

Browse files
committed
rust: pin-init: internal: init: add support for attributes on initializer fields
Initializer fields ought to support the same attributes that are allowed in struct initializers on fields. For example, `cfg` or lint levels such as `expect`, `allow` etc. Add parsing support for these attributes using syn to initializer fields and adjust the macro expansion accordingly. Signed-off-by: Benno Lossin <lossin@kernel.org>
1 parent 1cc3e01 commit 4c09883

1 file changed

Lines changed: 55 additions & 14 deletions

File tree

rust/pin-init/internal/src/init.rs

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ struct This {
2727
_in_token: Token![in],
2828
}
2929

30-
enum InitializerField {
30+
struct InitializerField {
31+
attrs: Vec<Attribute>,
32+
kind: InitializerKind,
33+
}
34+
35+
enum InitializerKind {
3136
Value {
3237
ident: Ident,
3338
value: Option<(Token![:], Expr)>,
@@ -44,7 +49,7 @@ enum InitializerField {
4449
},
4550
}
4651

47-
impl InitializerField {
52+
impl InitializerKind {
4853
fn ident(&self) -> Option<&Ident> {
4954
match self {
5055
Self::Value { ident, .. } | Self::Init { ident, .. } => Some(ident),
@@ -229,10 +234,16 @@ fn init_fields(
229234
slot: &Ident,
230235
) -> TokenStream {
231236
let mut guards = vec![];
237+
let mut guard_attrs = vec![];
232238
let mut res = TokenStream::new();
233-
for field in fields {
234-
let init = match field {
235-
InitializerField::Value { ident, value } => {
239+
for InitializerField { attrs, kind } in fields {
240+
let cfgs = {
241+
let mut cfgs = attrs.clone();
242+
cfgs.retain(|attr| attr.path().is_ident("cfg") || attr.path().is_ident("cfg_attr"));
243+
cfgs
244+
};
245+
let init = match kind {
246+
InitializerKind::Value { ident, value } => {
236247
let mut value_ident = ident.clone();
237248
let value_prep = value.as_ref().map(|value| &value.1).map(|value| {
238249
// Setting the span of `value_ident` to `value`'s span improves error messages
@@ -255,21 +266,24 @@ fn init_fields(
255266
}
256267
};
257268
quote! {
269+
#(#attrs)*
258270
{
259271
#value_prep
260272
// SAFETY: TODO
261273
unsafe { #write(::core::ptr::addr_of_mut!((*#slot).#ident), #value_ident) };
262274
}
275+
#(#cfgs)*
263276
#[allow(unused_variables)]
264277
let #ident = #accessor;
265278
}
266279
}
267-
InitializerField::Init { ident, value, .. } => {
280+
InitializerKind::Init { ident, value, .. } => {
268281
// Again span for better diagnostics
269282
let init = format_ident!("init", span = value.span());
270283
if pinned {
271284
let project_ident = format_ident!("__project_{ident}");
272285
quote! {
286+
#(#attrs)*
273287
{
274288
let #init = #value;
275289
// SAFETY:
@@ -279,12 +293,14 @@ fn init_fields(
279293
// for `#ident`.
280294
unsafe { #data.#ident(::core::ptr::addr_of_mut!((*#slot).#ident), #init)? };
281295
}
296+
#(#cfgs)*
282297
// SAFETY: TODO
283298
#[allow(unused_variables)]
284299
let #ident = unsafe { #data.#project_ident(&mut (*#slot).#ident) };
285300
}
286301
} else {
287302
quote! {
303+
#(#attrs)*
288304
{
289305
let #init = #value;
290306
// SAFETY: `slot` is valid, because we are inside of an initializer
@@ -296,20 +312,25 @@ fn init_fields(
296312
)?
297313
};
298314
}
315+
#(#cfgs)*
299316
// SAFETY: TODO
300317
#[allow(unused_variables)]
301318
let #ident = unsafe { &mut (*#slot).#ident };
302319
}
303320
}
304321
}
305-
InitializerField::Code { block: value, .. } => quote!(#[allow(unused_braces)] #value),
322+
InitializerKind::Code { block: value, .. } => quote! {
323+
#(#attrs)*
324+
#[allow(unused_braces)]
325+
#value
326+
},
306327
};
307328
res.extend(init);
308-
if let Some(ident) = field.ident() {
329+
if let Some(ident) = kind.ident() {
309330
// `mixed_site` ensures that the guard is not accessible to the user-controlled code.
310331
let guard = format_ident!("__{ident}_guard", span = Span::mixed_site());
311-
guards.push(guard.clone());
312332
res.extend(quote! {
333+
#(#cfgs)*
313334
// Create the drop guard:
314335
//
315336
// We rely on macro hygiene to make it impossible for users to access this local
@@ -321,13 +342,18 @@ fn init_fields(
321342
)
322343
};
323344
});
345+
guards.push(guard);
346+
guard_attrs.push(cfgs);
324347
}
325348
}
326349
quote! {
327350
#res
328351
// If execution reaches this point, all fields have been initialized. Therefore we can now
329352
// dismiss the guards by forgetting them.
330-
#(::core::mem::forget(#guards);)*
353+
#(
354+
#(#guard_attrs)*
355+
::core::mem::forget(#guards);
356+
)*
331357
}
332358
}
333359

@@ -337,7 +363,10 @@ fn make_field_check(
337363
init_kind: InitKind,
338364
path: &Path,
339365
) -> TokenStream {
340-
let fields = fields.iter().filter_map(|f| f.ident());
366+
let field_attrs = fields
367+
.iter()
368+
.filter_map(|f| f.kind.ident().map(|_| &f.attrs));
369+
let field_name = fields.iter().filter_map(|f| f.kind.ident());
341370
match init_kind {
342371
InitKind::Normal => quote! {
343372
// We use unreachable code to ensure that all fields have been mentioned exactly once,
@@ -348,7 +377,8 @@ fn make_field_check(
348377
let _ = || unsafe {
349378
::core::ptr::write(slot, #path {
350379
#(
351-
#fields: ::core::panic!(),
380+
#(#field_attrs)*
381+
#field_name: ::core::panic!(),
352382
)*
353383
})
354384
};
@@ -368,7 +398,8 @@ fn make_field_check(
368398
zeroed = ::core::mem::zeroed();
369399
::core::ptr::write(slot, #path {
370400
#(
371-
#fields: ::core::panic!(),
401+
#(#field_attrs)*
402+
#field_name: ::core::panic!(),
372403
)*
373404
..zeroed
374405
})
@@ -389,7 +420,7 @@ impl Parse for Initializer {
389420
let lh = content.lookahead1();
390421
if lh.peek(End) || lh.peek(Token![..]) {
391422
break;
392-
} else if lh.peek(Ident) || lh.peek(Token![_]) {
423+
} else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Token![#]) {
393424
fields.push_value(content.parse()?);
394425
let lh = content.lookahead1();
395426
if lh.peek(End) {
@@ -451,6 +482,16 @@ impl Parse for This {
451482
}
452483

453484
impl Parse for InitializerField {
485+
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
486+
let attrs = input.call(Attribute::parse_outer)?;
487+
Ok(Self {
488+
attrs,
489+
kind: input.parse()?,
490+
})
491+
}
492+
}
493+
494+
impl Parse for InitializerKind {
454495
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
455496
let lh = input.lookahead1();
456497
if lh.peek(Token![_]) {

0 commit comments

Comments
 (0)