From nobody Mon Feb 9 10:26:25 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A8FFE339B30; Sun, 11 Jan 2026 12:27:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768134463; cv=none; b=Su1J8ddqnzxt6nWAKgOjRLheZ41aN/YpR+5NBVLNJUpU2dEOwB8Knl6hOGHau6NCkLR7390SnZZYMHNfsEWsSlpqEVMFbLbf8ld6pH7I7s9CtC6IzpcjwhTe+9+rkyfViMDvdq5+WPnI+UVeJnfHistK0VsuB/XKkwgmlH5CXMU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768134463; c=relaxed/simple; bh=N/5s9AYuE+5kO4yqlD/qp1LnforDUkz9lZhYws95Cfg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=g5GCq4ZXcjymivFPTRK5du9zqpUUBnllf4jCwe5Vdpw83Hkj35ZMAmFzC/iH7TK7//Rerpu1F8CIrP0/74cVXPJTMP7jFjoPT9a3v/HRIqaj4ItgLo76lDQnqFJxOQn6/q4k8VyBtLH15BtCVH0/KlkSgZd+p+w1Rmrw5igWLFo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NPYdjtRP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="NPYdjtRP" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 019D7C19422; Sun, 11 Jan 2026 12:27:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1768134463; bh=N/5s9AYuE+5kO4yqlD/qp1LnforDUkz9lZhYws95Cfg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NPYdjtRPmkhQEeLaJHcu35EthOGf3ECf773/sCyInSpay2o06Mmkp+8QCmCTVDo9V vmEFKPSm6Tn4WK99U3+b4DRgElsxvzwgbAVY2ATpYcq9qFCcAu1OxRnVF1+Bdi9yGS 1jkoXmv91AS9QuAyBefnTyczPc9dM9XxH3rOilPvYmjJNs0+qeBWo1VbBupsjtIMyE ToS4qBhuxykeCEDXTMbmG6EzYT9ls5ZwqKdoN61Jgevt/5+xTWfgZdAqwJPiejxMJz sxWTqJ2qpnDiy1asKqHLp3I+E+joJ2xN3X6Sl5IA3WexZhYjdNRAiBCRCa+sH+0Q6h rFAb3eYBjJk5w== From: Benno Lossin To: Benno Lossin , Gary Guo , Miguel Ojeda , Boqun Feng , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 12/15] rust: pin-init: internal: init: add support for attributes on initializer fields Date: Sun, 11 Jan 2026 13:25:10 +0100 Message-ID: <20260111122554.2662175-13-lossin@kernel.org> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260111122554.2662175-1-lossin@kernel.org> References: <20260111122554.2662175-1-lossin@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- Changes in v2: * only attach cfg attributes to non-user controlled code for fields --- rust/pin-init/internal/src/init.rs | 69 ++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/rust/pin-init/internal/src/init.rs b/rust/pin-init/internal/sr= c/init.rs index 1e6f7aa1c1aa..c5e24ab1c3e0 100644 --- a/rust/pin-init/internal/src/init.rs +++ b/rust/pin-init/internal/src/init.rs @@ -25,7 +25,12 @@ struct This { _in_token: Token![in], } =20 -enum InitializerField { +struct InitializerField { + attrs: Vec, + kind: InitializerKind, +} + +enum InitializerKind { Value { ident: Ident, value: Option<(Token![:], Expr)>, @@ -42,7 +47,7 @@ enum InitializerField { }, } =20 -impl InitializerField { +impl InitializerKind { fn ident(&self) -> Option<&Ident> { match self { Self::Value { ident, .. } | Self::Init { ident, .. } =3D> Some= (ident), @@ -227,10 +232,16 @@ fn init_fields( slot: &Ident, ) -> TokenStream { let mut guards =3D vec![]; + let mut guard_attrs =3D vec![]; let mut res =3D TokenStream::new(); - for field in fields { - let init =3D match field { - InitializerField::Value { ident, value } =3D> { + for InitializerField { attrs, kind } in fields { + let cfgs =3D { + let mut cfgs =3D attrs.clone(); + cfgs.retain(|attr| attr.path().is_ident("cfg") || attr.path().= is_ident("cfg_attr")); + cfgs + }; + let init =3D match kind { + InitializerKind::Value { ident, value } =3D> { let mut value_ident =3D ident.clone(); let value_prep =3D value.as_ref().map(|value| &value.1).ma= p(|value| { // Setting the span of `value_ident` to `value`'s span= improves error messages @@ -253,21 +264,24 @@ fn init_fields( } }; quote! { + #(#attrs)* { #value_prep // SAFETY: TODO unsafe { #write(::core::ptr::addr_of_mut!((*#slot)= .#ident), #value_ident) }; } + #(#cfgs)* #[allow(unused_variables)] let #ident =3D #accessor; } } - InitializerField::Init { ident, value, .. } =3D> { + InitializerKind::Init { ident, value, .. } =3D> { // Again span for better diagnostics let init =3D format_ident!("init", span =3D value.span()); if pinned { let project_ident =3D format_ident!("__project_{ident}= "); quote! { + #(#attrs)* { let #init =3D #value; // SAFETY: @@ -277,12 +291,14 @@ fn init_fields( // for `#ident`. unsafe { #data.#ident(::core::ptr::addr_of_mut= !((*#slot).#ident), #init)? }; } + #(#cfgs)* // SAFETY: TODO #[allow(unused_variables)] let #ident =3D unsafe { #data.#project_ident(&mut = (*#slot).#ident) }; } } else { quote! { + #(#attrs)* { let #init =3D #value; // SAFETY: `slot` is valid, because we are ins= ide of an initializer @@ -294,20 +310,25 @@ fn init_fields( )? }; } + #(#cfgs)* // SAFETY: TODO #[allow(unused_variables)] let #ident =3D unsafe { &mut (*#slot).#ident }; } } } - InitializerField::Code { block: value, .. } =3D> quote!(#[allo= w(unused_braces)] #value), + InitializerKind::Code { block: value, .. } =3D> quote! { + #(#attrs)* + #[allow(unused_braces)] + #value + }, }; res.extend(init); - if let Some(ident) =3D field.ident() { + if let Some(ident) =3D kind.ident() { // `mixed_site` ensures that the guard is not accessible to th= e user-controlled code. let guard =3D format_ident!("__{ident}_guard", span =3D Span::= mixed_site()); - guards.push(guard.clone()); res.extend(quote! { + #(#cfgs)* // Create the drop guard: // // We rely on macro hygiene to make it impossible for user= s to access this local @@ -319,13 +340,18 @@ fn init_fields( ) }; }); + guards.push(guard); + guard_attrs.push(cfgs); } } quote! { #res // If execution reaches this point, all fields have been initializ= ed. Therefore we can now // dismiss the guards by forgetting them. - #(::core::mem::forget(#guards);)* + #( + #(#guard_attrs)* + ::core::mem::forget(#guards); + )* } } =20 @@ -335,7 +361,10 @@ fn make_field_check( init_kind: InitKind, path: &Path, ) -> TokenStream { - let fields =3D fields.iter().filter_map(|f| f.ident()); + let field_attrs =3D fields + .iter() + .filter_map(|f| f.kind.ident().map(|_| &f.attrs)); + let field_name =3D fields.iter().filter_map(|f| f.kind.ident()); match init_kind { InitKind::Normal =3D> quote! { // We use unreachable code to ensure that all fields have been= mentioned exactly once, @@ -346,7 +375,8 @@ fn make_field_check( let _ =3D || unsafe { ::core::ptr::write(slot, #path { #( - #fields: ::core::panic!(), + #(#field_attrs)* + #field_name: ::core::panic!(), )* }) }; @@ -366,7 +396,8 @@ fn make_field_check( zeroed =3D ::core::mem::zeroed(); ::core::ptr::write(slot, #path { #( - #fields: ::core::panic!(), + #(#field_attrs)* + #field_name: ::core::panic!(), )* ..zeroed }) @@ -387,7 +418,7 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::Re= sult { let lh =3D content.lookahead1(); if lh.peek(End) || lh.peek(Token![..]) { break; - } else if lh.peek(Ident) || lh.peek(Token![_]) { + } else if lh.peek(Ident) || lh.peek(Token![_]) || lh.peek(Toke= n![#]) { fields.push_value(content.parse()?); let lh =3D content.lookahead1(); if lh.peek(End) { @@ -449,6 +480,16 @@ fn parse(input: syn::parse::ParseStream<'_>) -> syn::R= esult { } =20 impl Parse for InitializerField { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let attrs =3D input.call(Attribute::parse_outer)?; + Ok(Self { + attrs, + kind: input.parse()?, + }) + } +} + +impl Parse for InitializerKind { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { let lh =3D input.lookahead1(); if lh.peek(Token![_]) { --=20 2.52.0