Commit 83ac2870 authored by Gary Guo's avatar Gary Guo Committed by Miguel Ojeda
Browse files

rust: pin-init: internal: move alignment check to `make_field_check`



Instead of having the reference creation serving dual-purpose as both for
let bindings and alignment check, detangle them so that the alignment check
is done explicitly in `make_field_check`. This is more robust against
refactors that may change the way let bindings are created.

Cc: stable@vger.kernel.org
Reviewed-by: default avatarAlice Ryhl <aliceryhl@google.com>
Signed-off-by: default avatarGary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260427-pin-init-fix-v3-1-496a699674dd@garyguo.net


[ Reworded for typo. - Miguel ]
Signed-off-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent ba6b3285
Loading
Loading
Loading
Loading
+37 −41
Original line number Diff line number Diff line
@@ -249,10 +249,6 @@ fn init_fields(
                });
                // Again span for better diagnostics
                let write = quote_spanned!(ident.span()=> ::core::ptr::write);
                // NOTE: the field accessor ensures that the initialized field is properly aligned.
                // Unaligned fields will cause the compiler to emit E0793. We do not support
                // unaligned fields since `Init::__init` requires an aligned pointer; the call to
                // `ptr::write` below has the same requirement.
                let accessor = if pinned {
                    let project_ident = format_ident!("__project_{ident}");
                    quote! {
@@ -367,49 +363,49 @@ fn init_fields(
    }
}

/// Generate the check for ensuring that every field has been initialized.
/// Generate the check for ensuring that every field has been initialized and aligned.
fn make_field_check(
    fields: &Punctuated<InitializerField, Token![,]>,
    init_kind: InitKind,
    path: &Path,
) -> TokenStream {
    let field_attrs = fields
    let field_attrs: Vec<_> = fields
        .iter()
        .filter_map(|f| f.kind.ident().map(|_| &f.attrs));
    let field_name = fields.iter().filter_map(|f| f.kind.ident());
    match init_kind {
        InitKind::Normal => quote! {
            // We use unreachable code to ensure that all fields have been mentioned exactly once,
            // this struct initializer will still be type-checked and complain with a very natural
            // error message if a field is forgotten/mentioned more than once.
        .filter_map(|f| f.kind.ident().map(|_| &f.attrs))
        .collect();
    let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect();
    let zeroing_trailer = match init_kind {
        InitKind::Normal => None,
        InitKind::Zeroing => Some(quote! {
            ..::core::mem::zeroed()
        }),
    };
    quote! {
        #[allow(unreachable_code, clippy::diverging_sub_expression)]
        // We use unreachable code to perform field checks. They're still checked by the compiler.
        // SAFETY: this code is never executed.
        let _ = || unsafe {
                ::core::ptr::write(slot, #path {
            // Create references to ensure that the initialized field is properly aligned.
            // Unaligned fields will cause the compiler to emit E0793. We do not support
            // unaligned fields since `Init::__init` requires an aligned pointer; the call to
            // `ptr::write` for value-initialization case has the same requirement.
            #(
                #(#field_attrs)*
                        #field_name: ::core::panic!(),
                let _ = &(*slot).#field_name;
            )*
                })
            };
        },
        InitKind::Zeroing => quote! {
            // We use unreachable code to ensure that all fields have been mentioned at most once.
            // Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
            // be zeroed. This struct initializer will still be type-checked and complain with a
            // very natural error message if a field is mentioned more than once, or doesn't exist.
            #[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
            // SAFETY: this code is never executed.
            let _ = || unsafe {

            // If the zeroing trailer is not present, this checks that all fields have been
            // mentioned exactly once. If the zeroing trailer is present, all missing fields will be
            // zeroed, so this checks that all fields have been mentioned at most once. The use of
            // struct initializer will still generate very natural error messages for any misuse.
            ::core::ptr::write(slot, #path {
                #(
                    #(#field_attrs)*
                    #field_name: ::core::panic!(),
                )*
                    ..::core::mem::zeroed()
                #zeroing_trailer
            })
        };
        },
    }
}