Skip to content

Commit 1f5ec9c

Browse files
committed
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 again refactors that may change the way let bindings are created. Reviewed-by: Alice Ryhl <aliceryhl@google.com> Signed-off-by: Gary Guo <gary@garyguo.net>
1 parent 6fcc4f2 commit 1f5ec9c

3 files changed

Lines changed: 59 additions & 43 deletions

File tree

internal/src/init.rs

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -249,10 +249,6 @@ fn init_fields(
249249
});
250250
// Again span for better diagnostics
251251
let write = quote_spanned!(ident.span()=> ::core::ptr::write);
252-
// NOTE: the field accessor ensures that the initialized field is properly aligned.
253-
// Unaligned fields will cause the compiler to emit E0793. We do not support
254-
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
255-
// `ptr::write` below has the same requirement.
256252
let accessor = if pinned {
257253
let project_ident = format_ident!("__project_{ident}");
258254
quote! {
@@ -367,49 +363,49 @@ fn init_fields(
367363
}
368364
}
369365

370-
/// Generate the check for ensuring that every field has been initialized.
366+
/// Generate the check for ensuring that every field has been initialized and aligned.
371367
fn make_field_check(
372368
fields: &Punctuated<InitializerField, Token![,]>,
373369
init_kind: InitKind,
374370
path: &Path,
375371
) -> TokenStream {
376-
let field_attrs = fields
372+
let field_attrs: Vec<_> = fields
377373
.iter()
378-
.filter_map(|f| f.kind.ident().map(|_| &f.attrs));
379-
let field_name = fields.iter().filter_map(|f| f.kind.ident());
380-
match init_kind {
381-
InitKind::Normal => quote! {
382-
// We use unreachable code to ensure that all fields have been mentioned exactly once,
383-
// this struct initializer will still be type-checked and complain with a very natural
384-
// error message if a field is forgotten/mentioned more than once.
385-
#[allow(unreachable_code, clippy::diverging_sub_expression)]
386-
// SAFETY: this code is never executed.
387-
let _ = || unsafe {
388-
::core::ptr::write(slot, #path {
389-
#(
390-
#(#field_attrs)*
391-
#field_name: ::core::panic!(),
392-
)*
393-
})
394-
};
395-
},
396-
InitKind::Zeroing => quote! {
397-
// We use unreachable code to ensure that all fields have been mentioned at most once.
398-
// Since the user specified `..Zeroable::zeroed()` at the end, all missing fields will
399-
// be zeroed. This struct initializer will still be type-checked and complain with a
400-
// very natural error message if a field is mentioned more than once, or doesn't exist.
401-
#[allow(unreachable_code, clippy::diverging_sub_expression, unused_assignments)]
402-
// SAFETY: this code is never executed.
403-
let _ = || unsafe {
404-
::core::ptr::write(slot, #path {
405-
#(
406-
#(#field_attrs)*
407-
#field_name: ::core::panic!(),
408-
)*
409-
..::core::mem::zeroed()
410-
})
411-
};
412-
},
374+
.filter_map(|f| f.kind.ident().map(|_| &f.attrs))
375+
.collect();
376+
let field_name: Vec<_> = fields.iter().filter_map(|f| f.kind.ident()).collect();
377+
let zeroing_trailer = match init_kind {
378+
InitKind::Normal => None,
379+
InitKind::Zeroing => Some(quote! {
380+
..::core::mem::zeroed()
381+
}),
382+
};
383+
quote! {
384+
#[allow(unreachable_code, clippy::diverging_sub_expression)]
385+
// We use unreachable code to perform field checks. They're still checked by the compiler.
386+
// SAFETY: this code is never executed.
387+
let _ = || unsafe {
388+
// Create references to ensure that the initialized field is properly aligned.
389+
// Unaligned fields will cause the compiler to emit E0793. We do not support
390+
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
391+
// `ptr::write` for value-initialization case has the same requirement.
392+
#(
393+
#(#field_attrs)*
394+
let _ = &(*slot).#field_name;
395+
)*
396+
397+
// If the zeroing trailer is not present, this checks that all fields have been
398+
// mentioned exactly once. If the zeroing trailer is present, all missing fields will be
399+
// zeroed, so this checks that all fields have been mentioned at most once. The use of
400+
// struct initializer will still generate very natural error messages for any misuse.
401+
::core::ptr::write(slot, #path {
402+
#(
403+
#(#field_attrs)*
404+
#field_name: ::core::panic!(),
405+
)*
406+
#zeroing_trailer
407+
})
408+
};
413409
}
414410
}
415411

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use pin_init::*;
22

33
#[repr(C, packed)]
4+
#[derive(Zeroable)]
45
struct Foo {
56
a: i8,
67
b: i32,
78
}
89

910
fn main() {
1011
let _ = init!(Foo { a: -42, b: 42 });
12+
let _ = init!(Foo {
13+
b: 42,
14+
..Zeroable::init_zeroed()
15+
});
1116
}
Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
error[E0793]: reference to field of packed struct is unaligned
2-
--> tests/ui/compile-fail/init/packed_struct.rs:10:13
2+
--> tests/ui/compile-fail/init/packed_struct.rs:11:13
33
|
4-
10 | let _ = init!(Foo { a: -42, b: 42 });
4+
11 | let _ = init!(Foo { a: -42, b: 42 });
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: this struct is 1-byte aligned, but the type of this field may require higher alignment
88
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
99
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
1010
= note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error[E0793]: reference to field of packed struct is unaligned
13+
--> tests/ui/compile-fail/init/packed_struct.rs:12:13
14+
|
15+
12 | let _ = init!(Foo {
16+
| _____________^
17+
13 | | b: 42,
18+
14 | | ..Zeroable::init_zeroed()
19+
15 | | });
20+
| |______^
21+
|
22+
= note: this struct is 1-byte aligned, but the type of this field may require higher alignment
23+
= note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
24+
= help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
25+
= note: this error originates in the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info)

0 commit comments

Comments
 (0)