Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CI and sync with Linux 6.12 #23

Merged
merged 12 commits into from
Nov 22, 2024
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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)'] }
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 0 additions & 4 deletions examples/big_struct_in_place.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#![feature(allocator_api)]

use std::convert::Infallible;

use pinned_init::*;

// Struct with size over 1GiB
Expand Down
9 changes: 6 additions & 3 deletions examples/error.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
#![feature(allocator_api)]
#![cfg_attr(feature = "alloc", feature(allocator_api))]

use core::convert::Infallible;

#[cfg(feature = "alloc")]
use std::alloc::AllocError;

#[derive(Debug)]
pub struct Error;

impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
Self
fn from(e: Infallible) -> Self {
match e {}
}
bonzini marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(feature = "alloc")]
impl From<AllocError> for Error {
fn from(_: AllocError) -> Self {
Self
Expand Down
29 changes: 29 additions & 0 deletions src/__internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,32 @@ impl OnlyCallFromDrop {
Self(())
}
}

/// Initializer that always fails.
///
/// Used by [`assert_pinned!`].
///
/// [`assert_pinned!`]: crate::assert_pinned
pub struct AlwaysFail<T: ?Sized> {
_t: PhantomData<T>,
}

impl<T: ?Sized> AlwaysFail<T> {
/// Creates a new initializer that always fails.
pub fn new() -> Self {
Self { _t: PhantomData }
}
}

impl<T: ?Sized> Default for AlwaysFail<T> {
fn default() -> Self {
Self::new()
}
}

// SAFETY: `__pinned_init` always fails, which is always okay.
unsafe impl<T: ?Sized> PinInit<T, ()> for AlwaysFail<T> {
unsafe fn __pinned_init(self, _slot: *mut T) -> Result<(), ()> {
Err(())
}
}
172 changes: 136 additions & 36 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -747,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<T> {
/// #[pin]
/// elem: T,
/// }
///
/// impl<T> Foo<T> {
/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
/// assert_pinned!(Foo<T>, 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
Expand Down Expand Up @@ -826,11 +893,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) })
}
}

Expand Down Expand Up @@ -924,11 +988,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) })
}
}

Expand Down Expand Up @@ -1144,27 +1206,15 @@ impl<T> InPlaceInit<T> for Box<T> {
where
E: From<AllocError>,
{
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]
fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
where
E: From<AllocError>,
{
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)
}
}

Expand All @@ -1175,29 +1225,79 @@ impl<T> InPlaceInit<T> for Arc<T> {
where
E: From<AllocError>,
{
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]
fn try_init<E>(init: impl Init<T, E>) -> Result<Self, E>
where
E: From<AllocError>,
{
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<T> {
/// 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<E>(self, init: impl Init<T, E>) -> Result<Self::Initialized, E>;

/// 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<E>(self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, E>;
}

#[cfg(feature = "alloc")]
impl<T> InPlaceWrite<T> for Box<MaybeUninit<T>> {
type Initialized = Box<T>;

fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, E> {
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<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, 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<T> InPlaceWrite<T> for Arc<MaybeUninit<T>> {
type Initialized = Arc<T>;

fn write_init<E>(mut self, init: impl Init<T, E>) -> Result<Self::Initialized, 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.
unsafe { init.__init(slot)? };
// SAFETY: All fields have been initialized.
Ok(unsafe { this.assume_init() })
Ok(unsafe { self.assume_init() })
}

fn write_pin_init<E>(mut self, init: impl PinInit<T, E>) -> Result<Pin<Self::Initialized>, 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()) })
}
}

Expand Down
Loading
Loading