From 377782101cb7e59aeebc23ce5aac23f5f5852cd9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 10:17:47 +0100 Subject: [PATCH 01/12] lib: do not mark new_uninit as required new_uninit is stable as of 1.82.0, there is no need to require it. Signed-off-by: Paolo Bonzini --- README.md | 1 - src/lib.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/README.md b/README.md index c931474..f3fdc12 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ This library allows you to do in-place initialization safely. This library requires unstable features when the `alloc` or `std` features are enabled and thus can only be used with a nightly compiler. The internally used features are: - `allocator_api` -- `new_uninit` - `get_mut_unchecked` When enabling the `alloc` or `std` feature, the user will be required to activate these features: diff --git a/src/lib.rs b/src/lib.rs index 3dc2c6b..ccfe96c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ //! This library requires unstable features when the `alloc` or `std` features are enabled and thus //! can only be used with a nightly compiler. The internally used features are: //! - `allocator_api` -//! - `new_uninit` //! - `get_mut_unchecked` //! //! When enabling the `alloc` or `std` feature, the user will be required to activate these features: @@ -237,7 +236,6 @@ #![forbid(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "alloc", feature(allocator_api))] -#![cfg_attr(feature = "alloc", feature(new_uninit))] #![cfg_attr(feature = "alloc", feature(get_mut_unchecked))] #[cfg(feature = "alloc")] From e15b5163371570f2b4528fd7caf6ec3ef069a7ae Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 11:22:49 +0100 Subject: [PATCH 02/12] cargo: add unexpected_cfgs support Signed-off-by: Paolo Bonzini --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3d003ea..b46acae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,3 +28,6 @@ trybuild = { version = "1.0", features = ["diff"] } macrotest = "1.0" # needed for macrotest, have to enable verbatim feature to be able to format `&raw` expressions. prettyplease = { version = "0.2", features = ["verbatim"] } + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(NO_UI_TESTS)', 'cfg(NO_ALLOC_FAIL_TESTS)'] } From 3f55f764811e75a5bf98688198fb89f2306b585b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 11:31:47 +0100 Subject: [PATCH 03/12] tests: update output for compile-fail tests Some of the error messages have changed in nightly Rust, since the .stderr files were last checked. Update everything. Signed-off-by: Paolo Bonzini --- tests/ui/compile-fail/init/missing_comma.stderr | 4 ++-- .../init/missing_comma_with_zeroable.stderr | 10 ++-------- tests/ui/compile-fail/init/no_error_coercion.stderr | 2 +- tests/ui/compile-fail/init/wrong_generics.stderr | 2 +- tests/ui/compile-fail/pin_data/missing_pin.stderr | 3 ++- tests/ui/compile-fail/pin_data/unexpected_item.stderr | 4 ++-- tests/ui/compile-fail/pinned_drop/no_fn.stderr | 4 ++-- .../pinned_drop/unexpected_additional_item.stderr | 2 +- .../ui/compile-fail/pinned_drop/unexpected_item.stderr | 4 ++-- tests/ui/compile-fail/zeroable/with_comma.stderr | 4 ++-- 10 files changed, 17 insertions(+), 22 deletions(-) diff --git a/tests/ui/compile-fail/init/missing_comma.stderr b/tests/ui/compile-fail/init/missing_comma.stderr index 8a3c63e..9e4c25d 100644 --- a/tests/ui/compile-fail/init/missing_comma.stderr +++ b/tests/ui/compile-fail/init/missing_comma.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `c` +error: no rules expected `c` --> tests/ui/compile-fail/init/missing_comma.rs:16:9 | 16 | c: Bar, @@ -10,7 +10,7 @@ note: while trying to match `,` | @munch_fields($field:ident $(: $val:expr)?, $($rest:tt)*), | ^ -error: no rules expected the token `c` +error: no rules expected `c` --> tests/ui/compile-fail/init/missing_comma.rs:16:9 | 16 | c: Bar, diff --git a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr index 245de12..4ffb89c 100644 --- a/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr +++ b/tests/ui/compile-fail/init/missing_comma_with_zeroable.stderr @@ -1,20 +1,14 @@ -error[E0782]: trait objects must include the `dyn` keyword +error[E0782]: expected a type, found a trait --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:15 | 12 | a: 0..Zeroable::zeroed() | ^^^^^^^^ | -help: add `dyn` keyword before this trait +help: you can add the `dyn` keyword if you want a trait object | 12 | a: 0..::zeroed() | ++++ + -error[E0599]: no function or associated item named `zeroed` found for trait `pinned_init::Zeroable` - --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:12:25 - | -12 | a: 0..Zeroable::zeroed() - | ^^^^^^ function or associated item not found in `Zeroable` - error[E0308]: mismatched types --> tests/ui/compile-fail/init/missing_comma_with_zeroable.rs:11:13 | diff --git a/tests/ui/compile-fail/init/no_error_coercion.stderr b/tests/ui/compile-fail/init/no_error_coercion.stderr index 3feba12..832f13e 100644 --- a/tests/ui/compile-fail/init/no_error_coercion.stderr +++ b/tests/ui/compile-fail/init/no_error_coercion.stderr @@ -5,7 +5,7 @@ error[E0277]: `?` couldn't convert the error to `std::alloc::AllocError` 17 | | a: Box::new(42), 18 | | bar <- init!(Bar { b: 42 }), 19 | | }? AllocError) - | |______________________^ the trait `From` is not implemented for `std::alloc::AllocError`, which is required by `Result: FromResidual>` + | |______________________^ the trait `From` is not implemented for `std::alloc::AllocError` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = help: the trait `FromResidual>` is implemented for `Result` diff --git a/tests/ui/compile-fail/init/wrong_generics.stderr b/tests/ui/compile-fail/init/wrong_generics.stderr index e70f0c0..cb1b1ef 100644 --- a/tests/ui/compile-fail/init/wrong_generics.stderr +++ b/tests/ui/compile-fail/init/wrong_generics.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `<` +error: no rules expected `<` --> tests/ui/compile-fail/init/wrong_generics.rs:7:22 | 7 | let _ = init!(Foo<()> { diff --git a/tests/ui/compile-fail/pin_data/missing_pin.stderr b/tests/ui/compile-fail/pin_data/missing_pin.stderr index e8f0307..5d98a04 100644 --- a/tests/ui/compile-fail/pin_data/missing_pin.stderr +++ b/tests/ui/compile-fail/pin_data/missing_pin.stderr @@ -7,7 +7,8 @@ error[E0277]: the trait bound `impl PinInit: Init` is not satis 13 | | }) | |__________^ the trait `Init` is not implemented for `impl PinInit` | - = help: the trait `Init, _>` is implemented for `impl PinInit` + = help: the trait `Init` is not implemented for `impl PinInit` + but trait `Init, _>` is implemented for it = help: for that trait implementation, expected `impl PinInit`, found `usize` note: required by a bound in `__ThePinData::a` --> tests/ui/compile-fail/pin_data/missing_pin.rs:4:1 diff --git a/tests/ui/compile-fail/pin_data/unexpected_item.stderr b/tests/ui/compile-fail/pin_data/unexpected_item.stderr index 0548653..1772f22 100644 --- a/tests/ui/compile-fail/pin_data/unexpected_item.stderr +++ b/tests/ui/compile-fail/pin_data/unexpected_item.stderr @@ -1,10 +1,10 @@ -error: no rules expected the token `fn` +error: no rules expected keyword `fn` --> tests/ui/compile-fail/pin_data/unexpected_item.rs:4:1 | 4 | fn foo() {} | ^^ no rules expected this token in macro call | -note: while trying to match `struct` +note: while trying to match keyword `struct` --> src/macros.rs | | $vis:vis struct $name:ident diff --git a/tests/ui/compile-fail/pinned_drop/no_fn.stderr b/tests/ui/compile-fail/pinned_drop/no_fn.stderr index 913bfa1..f2520ad 100644 --- a/tests/ui/compile-fail/pinned_drop/no_fn.stderr +++ b/tests/ui/compile-fail/pinned_drop/no_fn.stderr @@ -1,10 +1,10 @@ -error: no rules expected the token `)` +error: no rules expected `)` --> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1 | 6 | #[pinned_drop] | ^^^^^^^^^^^^^^ no rules expected this token in macro call | -note: while trying to match `fn` +note: while trying to match keyword `fn` --> src/macros.rs | | fn drop($($sig:tt)*) { diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr index 3438aa2..5a1ad50 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr +++ b/tests/ui/compile-fail/pinned_drop/unexpected_additional_item.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `const` +error: no rules expected keyword `const` --> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:10:5 | 10 | const BAZ: usize = 0; diff --git a/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr b/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr index 4c9c2c4..5b8fbb7 100644 --- a/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr +++ b/tests/ui/compile-fail/pinned_drop/unexpected_item.stderr @@ -1,10 +1,10 @@ -error: no rules expected the token `const` +error: no rules expected keyword `const` --> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:8:5 | 8 | const BAZ: usize = 0; | ^^^^^ no rules expected this token in macro call | -note: while trying to match `fn` +note: while trying to match keyword `fn` --> src/macros.rs | | fn drop($($sig:tt)*) { diff --git a/tests/ui/compile-fail/zeroable/with_comma.stderr b/tests/ui/compile-fail/zeroable/with_comma.stderr index 986006d..df6263a 100644 --- a/tests/ui/compile-fail/zeroable/with_comma.stderr +++ b/tests/ui/compile-fail/zeroable/with_comma.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `,` +error: no rules expected `,` --> tests/ui/compile-fail/zeroable/with_comma.rs:11:13 | 11 | let _ = init!(Foo { @@ -15,7 +15,7 @@ note: while trying to match `)` | ^ = note: this error originates in the macro `$crate::__init_internal` which comes from the expansion of the macro `init` (in Nightly builds, run with -Z macro-backtrace for more info) -error: no rules expected the token `,` +error: no rules expected `,` --> tests/ui/compile-fail/zeroable/with_comma.rs:11:13 | 11 | let _ = init!(Foo { From 19261939aac33a0eaf2ab18a0b360e49ef9cb0b1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 11:26:03 +0100 Subject: [PATCH 04/12] treewide: remove unused imports Signed-off-by: Paolo Bonzini --- examples/big_struct_in_place.rs | 2 -- tests/const-generic-default.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/examples/big_struct_in_place.rs b/examples/big_struct_in_place.rs index b8861f1..5330f78 100644 --- a/examples/big_struct_in_place.rs +++ b/examples/big_struct_in_place.rs @@ -1,7 +1,5 @@ #![feature(allocator_api)] -use std::convert::Infallible; - use pinned_init::*; // Struct with size over 1GiB diff --git a/tests/const-generic-default.rs b/tests/const-generic-default.rs index 6f99654..3585274 100644 --- a/tests/const-generic-default.rs +++ b/tests/const-generic-default.rs @@ -1,5 +1,3 @@ -use std::convert::Infallible; - use pinned_init::*; #[pin_data] From bcaf8e858cd6f4ba23feed92edff6ce0be806eb2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 11:00:45 +0100 Subject: [PATCH 05/12] examples: converting from Infallible is unreachable Use the same implementation for both examples/error.rs and examples/pthread_mutex.rs. Signed-off-by: Paolo Bonzini --- examples/error.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/error.rs b/examples/error.rs index 08e9ddc..4976dec 100644 --- a/examples/error.rs +++ b/examples/error.rs @@ -7,8 +7,8 @@ use std::alloc::AllocError; pub struct Error; impl From for Error { - fn from(_: Infallible) -> Self { - Self + fn from(e: Infallible) -> Self { + match e {} } } From 8f7064905a52b3c4bab9c0de4997f051099f4603 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 11:24:16 +0100 Subject: [PATCH 06/12] tests: ring_buf: remove dependency on allocator_api Reuse examples/error.rs so that the error returned by EvenU64::new2() is not core::alloc::AllocError. This will allow running the test without the allocator_api feature. Signed-off-by: Paolo Bonzini --- examples/error.rs | 5 ++++- tests/ring_buf.rs | 28 +++++++++++++--------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/examples/error.rs b/examples/error.rs index 4976dec..174e405 100644 --- a/examples/error.rs +++ b/examples/error.rs @@ -1,6 +1,8 @@ -#![feature(allocator_api)] +#![cfg_attr(feature = "alloc", feature(allocator_api))] use core::convert::Infallible; + +#[cfg(feature = "alloc")] use std::alloc::AllocError; #[derive(Debug)] @@ -12,6 +14,7 @@ impl From for Error { } } +#[cfg(feature = "alloc")] impl From for Error { fn from(_: AllocError) -> Self { Self diff --git a/tests/ring_buf.rs b/tests/ring_buf.rs index d6b7d7e..8837d15 100644 --- a/tests/ring_buf.rs +++ b/tests/ring_buf.rs @@ -1,6 +1,6 @@ -#![feature(allocator_api)] +#![cfg_attr(feature = "alloc", feature(allocator_api))] + use core::{ - alloc::AllocError, convert::Infallible, marker::PhantomPinned, mem::MaybeUninit, @@ -14,6 +14,10 @@ use std::sync::Arc; mod mutex; use mutex::*; +#[path = "../examples/error.rs"] +mod error; +use error::Error; + #[pin_data(PinnedDrop)] pub struct RingBuffer { buffer: [MaybeUninit; SIZE], @@ -152,15 +156,15 @@ pub struct EvenU64 { } impl EvenU64 { - pub fn new2(value: u64) -> impl Init { + pub fn new2(value: u64) -> impl Init { try_init!(Self { info: "Hello world!".to_owned(), data: if value % 2 == 0 { value } else { - return Err(AllocError); + return Err(Error); }, - }? AllocError) + }? Error) } pub fn new(value: u64) -> impl Init { try_init!(Self { @@ -190,16 +194,10 @@ fn even_stack() { #[test] fn even_failing() { - assert!(matches!( - Box::try_pin_init(EvenU64::new2(3)), - Err(AllocError) - )); - assert!(matches!( - Arc::try_pin_init(EvenU64::new2(5)), - Err(AllocError) - )); - assert!(matches!(Box::try_init(EvenU64::new2(3)), Err(AllocError))); - assert!(matches!(Arc::try_init(EvenU64::new2(5)), Err(AllocError))); + assert!(matches!(Box::try_pin_init(EvenU64::new2(3)), Err(Error))); + assert!(matches!(Arc::try_pin_init(EvenU64::new2(5)), Err(Error))); + assert!(matches!(Box::try_init(EvenU64::new2(3)), Err(Error))); + assert!(matches!(Arc::try_init(EvenU64::new2(5)), Err(Error))); } #[test] From 24acb56de8773bf4da13fc9f930e885b8af1ec49 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 12:09:45 +0100 Subject: [PATCH 07/12] examples: remove unnecessary feature The big_struct_in_place example only uses init!, which has no dependency on the allocator API (which provides the functionality of fallible initialization). Do not enable allocator_api. Signed-off-by: Paolo Bonzini --- examples/big_struct_in_place.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/big_struct_in_place.rs b/examples/big_struct_in_place.rs index 5330f78..3024de6 100644 --- a/examples/big_struct_in_place.rs +++ b/examples/big_struct_in_place.rs @@ -1,5 +1,3 @@ -#![feature(allocator_api)] - use pinned_init::*; // Struct with size over 1GiB From 0385db46088066399b573e383a211592e2db2920 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 4 Nov 2024 16:38:36 +0100 Subject: [PATCH 08/12] rust: init: simplify from `map_err` to `inspect_err` A new complexity lint, `manual_inspect` [1], has been introduced in the upcoming Rust 1.81 (currently in nightly), which checks for uses of `map*` which return the original item: error: --> rust/kernel/init.rs:846:23 | 846 | (self.1)(val).map_err(|e| { | ^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect = note: `-D clippy::manual-inspect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_inspect)]` help: try | 846 ~ (self.1)(val).inspect_err(|e| { 847 | // SAFETY: `slot` was initialized above. 848 ~ unsafe { core::ptr::drop_in_place(slot) }; | Thus clean them up. Link: https://rust-lang.github.io/rust-clippy/master/index.html#/manual_inspect [1] Tested-by: Benno Lossin Tested-by: Andreas Hindborg Link: https://lore.kernel.org/r/20240709160615.998336-3-ojeda@kernel.org Signed-off-by: Miguel Ojeda Signed-off-by: Paolo Bonzini (cherry picked from commit dee1396a486cf2b6e7840322f6d104680649f2ff) --- src/lib.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ccfe96c..186a2fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -824,11 +824,8 @@ where let val = unsafe { &mut *slot }; // SAFETY: `slot` is considered pinned. let val = unsafe { Pin::new_unchecked(val) }; - (self.1)(val).map_err(|e| { - // SAFETY: `slot` was initialized above. - unsafe { core::ptr::drop_in_place(slot) }; - e - }) + // SAFETY: `slot` was initialized above. + (self.1)(val).inspect_err(|_| unsafe { core::ptr::drop_in_place(slot) }) } } @@ -922,11 +919,9 @@ where // SAFETY: All requirements fulfilled since this function is `__init`. unsafe { self.0.__pinned_init(slot)? }; // SAFETY: The above call initialized `slot` and we still have unique access. - (self.1)(unsafe { &mut *slot }).map_err(|e| { + (self.1)(unsafe { &mut *slot }).inspect_err(|_| // SAFETY: `slot` was initialized above. - unsafe { core::ptr::drop_in_place(slot) }; - e - }) + unsafe { core::ptr::drop_in_place(slot) }) } } From 307b2bf65753c81e5780b6dccca6e0115905c481 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 3 Apr 2024 19:43:37 +0000 Subject: [PATCH 09/12] rust: init: change the generated name of guard variables The initializers created by the `[try_][pin_]init!` macros utilize the guard pattern to drop already initialized fields, when initialization fails mid-way. These guards are generated to have the same name as the field that they handle. To prevent namespacing issues [1] when the field name is the same as e.g. a constant name, add `__` as a prefix and `_guard` as the suffix. [ Gary says: "Here's the simplified example: ``` macro_rules! f { () => { let a = 1; let _: u32 = a; } } const a: u64 = 1; fn main() { f!(); } ``` The `a` in `f` have a different hygiene so normally it is scoped to the macro expansion and wouldn't escape. Interestingly a constant is still preferred despite the hygiene so constants escaped into the macro, leading to the error." - Miguel ] Signed-off-by: Benno Lossin Reviewed-by: Boqun Feng Reviewed-by: Alice Ryhl Link: https://lore.kernel.org/rust-for-linux/1e8a2a1f-abbf-44ba-8344-705a9cbb1627@proton.me/ [1] Link: https://lore.kernel.org/r/20240403194321.88716-1-benno.lossin@proton.me [ Added Benno's link and Gary's simplified example. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Paolo Bonzini (cherry picked from commit 9218cf826f1dbacbb857e6eabfae164d8ba05dea) --- src/macros.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index fbd66b5..6cc6a65 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -250,7 +250,7 @@ //! // error type is `Infallible`) we will need to drop this field if there //! // is an error later. This `DropGuard` will drop the field when it gets //! // dropped and has not yet been forgotten. -//! let t = unsafe { +//! let __t_guard = unsafe { //! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).t)) //! }; //! // Expansion of `x: 0,`: @@ -261,14 +261,14 @@ //! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).x), x) }; //! } //! // We again create a `DropGuard`. -//! let x = unsafe { +//! let __x_guard = unsafe { //! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).x)) //! }; //! // Since initialization has successfully completed, we can now forget //! // the guards. This is not `mem::forget`, since we only have //! // `&DropGuard`. -//! ::core::mem::forget(x); -//! ::core::mem::forget(t); +//! ::core::mem::forget(__x_guard); +//! ::core::mem::forget(__t_guard); //! // Here we use the type checker to ensure that every field has been //! // initialized exactly once, since this is `if false` it will never get //! // executed, but still type-checked. @@ -461,16 +461,16 @@ //! { //! unsafe { ::core::ptr::write(::core::addr_of_mut!((*slot).a), a) }; //! } -//! let a = unsafe { +//! let __a_guard = unsafe { //! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).a)) //! }; //! let init = Bar::new(36); //! unsafe { data.b(::core::addr_of_mut!((*slot).b), b)? }; -//! let b = unsafe { +//! let __b_guard = unsafe { //! ::pinned_init::__internal::DropGuard::new(::core::addr_of_mut!((*slot).b)) //! }; -//! ::core::mem::forget(b); -//! ::core::mem::forget(a); +//! ::core::mem::forget(__b_guard); +//! ::core::mem::forget(__a_guard); //! #[allow(unreachable_code, clippy::diverging_sub_expression)] //! let _ = || { //! unsafe { @@ -1209,14 +1209,14 @@ macro_rules! __init_internal { // We use `paste!` to create new hygiene for `$field`. $crate::macros::paste! { // SAFETY: We forget the guard later when initialization has succeeded. - let [<$field>] = unsafe { + let [< __ $field _guard >] = unsafe { $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot($use_data): @data($data), @slot($slot), - @guards([<$field>], $($guards,)*), + @guards([< __ $field _guard >], $($guards,)*), @munch_fields($($rest)*), ); } @@ -1240,14 +1240,14 @@ macro_rules! __init_internal { // We use `paste!` to create new hygiene for `$field`. $crate::macros::paste! { // SAFETY: We forget the guard later when initialization has succeeded. - let [<$field>] = unsafe { + let [< __ $field _guard >] = unsafe { $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot(): @data($data), @slot($slot), - @guards([<$field>], $($guards,)*), + @guards([< __ $field _guard >], $($guards,)*), @munch_fields($($rest)*), ); } @@ -1272,14 +1272,14 @@ macro_rules! __init_internal { // We use `paste!` to create new hygiene for `$field`. $crate::macros::paste! { // SAFETY: We forget the guard later when initialization has succeeded. - let [<$field>] = unsafe { + let [< __ $field _guard >] = unsafe { $crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field)) }; $crate::__init_internal!(init_slot($($use_data)?): @data($data), @slot($slot), - @guards([<$field>], $($guards,)*), + @guards([< __ $field _guard >], $($guards,)*), @munch_fields($($rest)*), ); } From e360d87cb5edc56e32c466d856a1072906b13a38 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Mon, 19 Aug 2024 22:57:31 +0200 Subject: [PATCH 10/12] rust: kernel: fix typos in code comments Fix spelling mistakes in code comments. Signed-off-by: Michael Vetter Reviewed-by: Alice Ryhl Reviewed-by: Benno Lossin Link: https://lore.kernel.org/r/20240819205731.2163-1-jubalh@iodoru.org [ Reworded slightly. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Paolo Bonzini (cherry picked from commit 0ff8f3f0979559b0d7494d580f2597beab3f159b) --- src/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 6cc6a65..04d4048 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -145,7 +145,7 @@ //! } //! } //! // Implement the internal `PinData` trait that marks the pin-data struct as a pin-data -//! // struct. This is important to ensure that no user can implement a rouge `__pin_data` +//! // struct. This is important to ensure that no user can implement a rogue `__pin_data` //! // function without using `unsafe`. //! unsafe impl ::pinned_init::__internal::PinData for __ThePinData { //! type Datee = Bar; @@ -156,7 +156,7 @@ //! // case no such fields exist, hence this is almost empty. The two phantomdata fields exist //! // for two reasons: //! // - `__phantom`: every generic must be used, since we cannot really know which generics -//! // are used, we declere all and then use everything here once. +//! // are used, we declare all and then use everything here once. //! // - `__phantom_pin`: uses the `'__pin` lifetime and ensures that this struct is invariant //! // over it. The lifetime is needed to work around the limitation that trait bounds must //! // not be trivial, e.g. the user has a `#[pin] PhantomPinned` field -- this is From f3197bbc552beae3006c1c16d6ce4dfc8fa06634 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Mon, 19 Aug 2024 11:24:39 +0000 Subject: [PATCH 11/12] rust: init: add `write_[pin_]init` functions Sometimes it is necessary to split allocation and initialization into two steps. One such situation is when reusing existing allocations obtained via `Box::drop_contents`. See [1] for an example. In order to support this use case add `write_[pin_]init` functions to the pin-init API. These functions operate on already allocated smart pointers that wrap `MaybeUninit`. Link: https://lore.kernel.org/rust-for-linux/f026532f-8594-4f18-9aa5-57ad3f5bc592@proton.me/ [1] Signed-off-by: Benno Lossin Reviewed-by: Boqun Feng Reviewed-by: Alice Ryhl Reviewed-by: Gary Guo Link: https://lore.kernel.org/r/20240819112415.99810-2-benno.lossin@proton.me Signed-off-by: Miguel Ojeda Signed-off-by: Paolo Bonzini (cherry picked from commit 6d1c22d0ace31d096b0dab5318c6a0d3219d6456) --- src/lib.rs | 88 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 186a2fb..81701c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1137,13 +1137,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = Box::try_new_uninit()?; - let slot = this.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }.into()) + Box::try_new_uninit()?.write_pin_init(init) } #[inline] @@ -1151,13 +1145,7 @@ impl InPlaceInit for Box { where E: From, { - let mut this = Box::try_new_uninit()?; - let slot = this.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid. - unsafe { init.__init(slot)? }; - // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }) + Box::try_new_uninit()?.write_init(init) } } @@ -1168,14 +1156,7 @@ impl InPlaceInit for Arc { where E: From, { - let mut this = Arc::try_new_uninit()?; - let slot = unsafe { Arc::get_mut_unchecked(&mut this) }; - let slot = slot.as_mut_ptr(); - // SAFETY: When init errors/panics, slot will get deallocated but not dropped, - // slot is valid and will not be moved, because we pin it later. - unsafe { init.__pinned_init(slot)? }; - // SAFETY: All fields have been initialized and this is the only `Arc` to that data. - Ok(unsafe { Pin::new_unchecked(this.assume_init()) }) + Arc::try_new_uninit()?.write_pin_init(init) } #[inline] @@ -1183,14 +1164,71 @@ impl InPlaceInit for Arc { where E: From, { - let mut this = Arc::try_new_uninit()?; - let slot = unsafe { Arc::get_mut_unchecked(&mut this) }; + Arc::try_new_uninit()?.write_init(init) + } +} + +/// Smart pointer containing uninitialized memory and that can write a value. +pub trait InPlaceWrite { + /// The type `Self` turns into when the contents are initialized. + type Initialized; + + /// Use the given initializer to write a value into `self`. + /// + /// Does not drop the current value and considers it as uninitialized memory. + fn write_init(self, init: impl Init) -> Result; + + /// Use the given pin-initializer to write a value into `self`. + /// + /// Does not drop the current value and considers it as uninitialized memory. + fn write_pin_init(self, init: impl PinInit) -> Result, E>; +} + +#[cfg(feature = "alloc")] +impl InPlaceWrite for Box> { + type Initialized = Box; + + fn write_init(mut self, init: impl Init) -> Result { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid. + unsafe { init.__init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { self.assume_init() }) + } + + fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { + let slot = self.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid and will not be moved, because we pin it later. + unsafe { init.__pinned_init(slot)? }; + // SAFETY: All fields have been initialized. + Ok(unsafe { self.assume_init() }.into()) + } +} + +#[cfg(feature = "alloc")] +impl InPlaceWrite for Arc> { + type Initialized = Arc; + + fn write_init(mut self, init: impl Init) -> Result { + let slot = unsafe { Arc::get_mut_unchecked(&mut self) }; let slot = slot.as_mut_ptr(); // SAFETY: When init errors/panics, slot will get deallocated but not dropped, // slot is valid. unsafe { init.__init(slot)? }; // SAFETY: All fields have been initialized. - Ok(unsafe { this.assume_init() }) + Ok(unsafe { self.assume_init() }) + } + + fn write_pin_init(mut self, init: impl PinInit) -> Result, E> { + let slot = unsafe { Arc::get_mut_unchecked(&mut self) }; + let slot = slot.as_mut_ptr(); + // SAFETY: When init errors/panics, slot will get deallocated but not dropped, + // slot is valid and will not be moved, because we pin it later. + unsafe { init.__pinned_init(slot)? }; + // SAFETY: All fields have been initialized and this is the only `Arc` to that data. + Ok(unsafe { Pin::new_unchecked(self.assume_init()) }) } } From aba442284887ebac4d4a2e00bd9fe1487abc86c1 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 14 Aug 2024 08:05:20 +0000 Subject: [PATCH 12/12] rust: init: add `assert_pinned` macro Add a macro to statically check if a field of a struct is marked with `#[pin]` ie that it is structurally pinned. This can be used when `unsafe` code needs to rely on fields being structurally pinned. The macro has a special "inline" mode for the case where the type depends on generic parameters from the surrounding scope. Signed-off-by: Benno Lossin Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl Link: https://lore.kernel.org/r/20240814-linked-list-v5-1-f5f5e8075da0@google.com [ Replaced `compile_fail` with `ignore` and a TODO note. Removed `pub` from example to clean `unreachable_pub` lint. - Miguel ] Signed-off-by: Miguel Ojeda Signed-off-by: Paolo Bonzini (cherry picked from commit 0528ca0a4f858da3369d405af8c76b8248dfeb7b) --- src/__internal.rs | 29 ++++++++++++++++++++ src/lib.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/src/__internal.rs b/src/__internal.rs index e63228d..b65c64e 100644 --- a/src/__internal.rs +++ b/src/__internal.rs @@ -261,3 +261,32 @@ impl OnlyCallFromDrop { Self(()) } } + +/// Initializer that always fails. +/// +/// Used by [`assert_pinned!`]. +/// +/// [`assert_pinned!`]: crate::assert_pinned +pub struct AlwaysFail { + _t: PhantomData, +} + +impl AlwaysFail { + /// Creates a new initializer that always fails. + pub fn new() -> Self { + Self { _t: PhantomData } + } +} + +impl Default for AlwaysFail { + fn default() -> Self { + Self::new() + } +} + +// SAFETY: `__pinned_init` always fails, which is always okay. +unsafe impl PinInit for AlwaysFail { + unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> { + Err(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 81701c7..5a823b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -745,6 +745,75 @@ macro_rules! try_init { }; } +/// Asserts that a field on a struct using `#[pin_data]` is marked with `#[pin]` ie. that it is +/// structurally pinned. +/// +/// # Example +/// +/// This will succeed: +/// ``` +/// use pinned_init::*; +/// #[pin_data] +/// struct MyStruct { +/// #[pin] +/// some_field: u64, +/// } +/// +/// assert_pinned!(MyStruct, some_field, u64); +/// ``` +/// +/// This will fail: +// TODO: replace with `compile_fail` when supported. +/// ```ignore +/// # use pinned_init::*; +/// #[pin_data] +/// struct MyStruct { +/// some_field: u64, +/// } +/// +/// assert_pinned!(MyStruct, some_field, u64); +/// ``` +/// +/// Some uses of the macro may trigger the `can't use generic parameters from outer item` error. To +/// work around this, you may pass the `inline` parameter to the macro. The `inline` parameter can +/// only be used when the macro is invoked from a function body. +/// ``` +/// # use pinned_init::*; +/// # use core::pin::Pin; +/// #[pin_data] +/// struct Foo { +/// #[pin] +/// elem: T, +/// } +/// +/// impl Foo { +/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> { +/// assert_pinned!(Foo, elem, T, inline); +/// +/// // SAFETY: The field is structurally pinned. +/// unsafe { self.map_unchecked_mut(|me| &mut me.elem) } +/// } +/// } +/// ``` +#[macro_export] +macro_rules! assert_pinned { + ($ty:ty, $field:ident, $field_ty:ty, inline) => { + let _ = move |ptr: *mut $field_ty| { + // SAFETY: This code is unreachable. + let data = unsafe { <$ty as $crate::__internal::HasPinData>::__pin_data() }; + let init = $crate::__internal::AlwaysFail::<$field_ty>::new(); + // SAFETY: This code is unreachable. + unsafe { data.$field(ptr, init) }.ok(); + }; + }; + + ($ty:ty, $field:ident, $field_ty:ty) => { + const _: () = { + $crate::assert_pinned!($ty, $field, $field_ty, inline); + }; + }; +} + /// A pin-initializer for the type `T`. /// /// To use this initializer, you will need a suitable memory location that can hold a `T`. This can