Skip to content

Commit fe3d3fc

Browse files
committed
Relax Allocator bounds into pin-safe trait
Because `Allocator` requires that returned memory blocks remain valid until the instance and all clones are dropped, some types of allocators cannot fulfill the safety requirements for the trait. This change relaxes the safety requirements of `Allocator` to blocks remaining valid until the allocator becomes unreachable and moves the bound into a separate `PinSafeAllocator` trait. It also relaxes some overly cautious bounds on the `Unpin` impls for `Box` since unpinning a box doesn't require any special consideration.
1 parent b4151a4 commit fe3d3fc

File tree

11 files changed

+171
-34
lines changed

11 files changed

+171
-34
lines changed

library/alloc/src/alloc.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ extern "Rust" {
4444
/// to the allocator registered with the `#[global_allocator]` attribute
4545
/// if there is one, or the `std` crate’s default.
4646
///
47+
/// This type is always guaranteed to implement [`PinSafeAllocator`].
48+
///
4749
/// Note: while this type is unstable, the functionality it provides can be
4850
/// accessed through the [free functions in `alloc`](self#functions).
4951
#[unstable(feature = "allocator_api", issue = "32838")]
@@ -314,6 +316,11 @@ unsafe impl Allocator for Global {
314316
}
315317
}
316318

319+
// SAFETY: memory blocks allocated by `Global` are not invalidated when `Global` is dropped.
320+
#[unstable(feature = "allocator_api", issue = "32838")]
321+
#[cfg(not(test))]
322+
unsafe impl PinSafeAllocator for Global {}
323+
317324
/// The allocator for unique pointers.
318325
#[cfg(all(not(no_global_oom_handling), not(test)))]
319326
#[lang = "exchange_malloc"]

library/alloc/src/boxed.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ use core::task::{Context, Poll};
168168

169169
#[cfg(not(no_global_oom_handling))]
170170
use crate::alloc::{handle_alloc_error, WriteCloneIntoRaw};
171-
use crate::alloc::{AllocError, Allocator, Global, Layout};
171+
use crate::alloc::{AllocError, Allocator, Global, Layout, PinSafeAllocator};
172172
#[cfg(not(no_global_oom_handling))]
173173
use crate::borrow::Cow;
174174
use crate::raw_vec::RawVec;
@@ -1123,8 +1123,12 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
11231123
// recognized as "releasing" the unique pointer to permit aliased raw accesses,
11241124
// so all raw pointer methods have to go through `Box::leak`. Turning *that* to a raw pointer
11251125
// behaves correctly.
1126-
let alloc = unsafe { ptr::read(&b.1) };
1127-
(Unique::from(Box::leak(b)), alloc)
1126+
let manually_drop = mem::ManuallyDrop::new(b);
1127+
// SAFETY: unique ownership of the memory block moves into `ptr`
1128+
let ptr = unsafe { &mut *manually_drop.0.as_ptr() };
1129+
// SAFETY: moving the allocator will not invalidate `ptr`
1130+
let alloc = unsafe { ptr::read(&manually_drop.1) };
1131+
(Unique::from(ptr), alloc)
11281132
}
11291133

11301134
/// Returns a reference to the underlying allocator.
@@ -1179,9 +1183,13 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
11791183
#[inline]
11801184
pub const fn leak<'a>(b: Self) -> &'a mut T
11811185
where
1182-
A: 'a,
1186+
A: ~const PinSafeAllocator,
11831187
{
1184-
unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() }
1188+
let (ptr, alloc) = Box::into_unique(b);
1189+
mem::forget(alloc);
1190+
// SAFETY: ptr will remain valid for any lifetime since `alloc` is never
1191+
// dropped
1192+
unsafe { &mut *ptr.as_ptr() }
11851193
}
11861194

11871195
/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
@@ -1218,7 +1226,7 @@ impl<T: ?Sized, A: Allocator> Box<T, A> {
12181226
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
12191227
pub const fn into_pin(boxed: Self) -> Pin<Self>
12201228
where
1221-
A: 'static,
1229+
A: ~const PinSafeAllocator,
12221230
{
12231231
// It's not possible to move or replace the insides of a `Pin<Box<T>>`
12241232
// when `T: !Unpin`, so it's safe to pin it directly without any
@@ -1454,9 +1462,9 @@ impl<T> From<T> for Box<T> {
14541462

14551463
#[stable(feature = "pin", since = "1.33.0")]
14561464
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
1457-
impl<T: ?Sized, A: Allocator> const From<Box<T, A>> for Pin<Box<T, A>>
1465+
impl<T: ?Sized, A> const From<Box<T, A>> for Pin<Box<T, A>>
14581466
where
1459-
A: 'static,
1467+
A: ~const PinSafeAllocator,
14601468
{
14611469
/// Converts a `Box<T>` into a `Pin<Box<T>>`. If `T` does not implement [`Unpin`], then
14621470
/// `*boxed` will be pinned in memory and unable to be moved.
@@ -2033,13 +2041,10 @@ impl<T: ?Sized, A: Allocator> AsMut<T> for Box<T, A> {
20332041
*/
20342042
#[stable(feature = "pin", since = "1.33.0")]
20352043
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
2036-
impl<T: ?Sized, A: Allocator> const Unpin for Box<T, A> where A: 'static {}
2044+
impl<T: ?Sized, A: Allocator> const Unpin for Box<T, A> {}
20372045

20382046
#[unstable(feature = "generator_trait", issue = "43122")]
2039-
impl<G: ?Sized + Generator<R> + Unpin, R, A: Allocator> Generator<R> for Box<G, A>
2040-
where
2041-
A: 'static,
2042-
{
2047+
impl<G: ?Sized + Generator<R> + Unpin, R, A: Allocator> Generator<R> for Box<G, A> {
20432048
type Yield = G::Yield;
20442049
type Return = G::Return;
20452050

@@ -2049,10 +2054,7 @@ where
20492054
}
20502055

20512056
#[unstable(feature = "generator_trait", issue = "43122")]
2052-
impl<G: ?Sized + Generator<R>, R, A: Allocator> Generator<R> for Pin<Box<G, A>>
2053-
where
2054-
A: 'static,
2055-
{
2057+
impl<G: ?Sized + Generator<R>, R, A: Allocator> Generator<R> for Pin<Box<G, A>> {
20562058
type Yield = G::Yield;
20572059
type Return = G::Return;
20582060

@@ -2062,10 +2064,7 @@ where
20622064
}
20632065

20642066
#[stable(feature = "futures_api", since = "1.36.0")]
2065-
impl<F: ?Sized + Future + Unpin, A: Allocator> Future for Box<F, A>
2066-
where
2067-
A: 'static,
2068-
{
2067+
impl<F: ?Sized + Future + Unpin, A: Allocator> Future for Box<F, A> {
20692068
type Output = F::Output;
20702069

20712070
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {

library/alloc/src/rc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ impl<T> Rc<T> {
630630
#[stable(feature = "pin", since = "1.33.0")]
631631
#[must_use]
632632
pub fn pin(value: T) -> Pin<Rc<T>> {
633+
// SAFETY: Global is a pin-safe allocator.
633634
unsafe { Pin::new_unchecked(Rc::new(value)) }
634635
}
635636

library/alloc/src/sync.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,13 +530,15 @@ impl<T> Arc<T> {
530530
#[stable(feature = "pin", since = "1.33.0")]
531531
#[must_use]
532532
pub fn pin(data: T) -> Pin<Arc<T>> {
533+
// SAFETY: Global is a pin-safe allocator.
533534
unsafe { Pin::new_unchecked(Arc::new(data)) }
534535
}
535536

536537
/// Constructs a new `Pin<Arc<T>>`, return an error if allocation fails.
537538
#[unstable(feature = "allocator_api", issue = "32838")]
538539
#[inline]
539540
pub fn try_pin(data: T) -> Result<Pin<Arc<T>>, AllocError> {
541+
// SAFETY: Global is a pin-safe allocator.
540542
unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) }
541543
}
542544

library/alloc/tests/boxed.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::alloc::{AllocError, Allocator, Layout};
1+
use core::alloc::{AllocError, Allocator, Layout, PinSafeAllocator};
22
use core::cell::Cell;
33
use core::mem::MaybeUninit;
44
use core::ptr::NonNull;
@@ -151,6 +151,9 @@ unsafe impl const Allocator for ConstAllocator {
151151
}
152152
}
153153

154+
// SAFETY: Memory allocated by `ConstAllocator` is never invalidated.
155+
unsafe impl const PinSafeAllocator for ConstAllocator {}
156+
154157
#[test]
155158
fn const_box() {
156159
const VALUE: u32 = {

library/core/src/alloc/mod.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,14 @@ impl fmt::Display for AllocError {
8787
/// # Safety
8888
///
8989
/// * Memory blocks returned from an allocator must point to valid memory and retain their validity
90-
/// until the instance and all of its clones are dropped,
90+
/// until the instance and all of its clones are dropped, forgotten, or otherwise rendered
91+
/// inaccessible. The validity of memory block is tied directly to the validity of the allocator
92+
/// group that it is allocated from.
9193
///
92-
/// * cloning or moving the allocator must not invalidate memory blocks returned from this
93-
/// allocator. A cloned allocator must behave like the same allocator, and
94+
/// * Cloning or moving the allocator must not invalidate memory blocks returned from this
95+
/// allocator. A cloned allocator must behave like the same allocator.
9496
///
95-
/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
97+
/// * Any pointer to a memory block which is [*currently allocated*] may be passed to any other
9698
/// method of the allocator.
9799
///
98100
/// [*currently allocated*]: #currently-allocated-memory
@@ -408,3 +410,21 @@ where
408410
unsafe { (**self).shrink(ptr, old_layout, new_layout) }
409411
}
410412
}
413+
414+
/// An [`Allocator`] which returns memory blocks that can safely be pinned.
415+
///
416+
/// Unlike `Allocator`, `PinSafeAllocator` guarantees that forgetting an instance will cause any
417+
/// allocated memory to remain valid indefinitely.
418+
///
419+
/// # Safety
420+
///
421+
/// In addition to the safety guarantees of `Allocator`, memory blocks returned from a
422+
/// `PinSafeAllocator` must retain their validity until the instance and all of its clones are
423+
/// dropped.
424+
#[unstable(feature = "allocator_api", issue = "32838")]
425+
pub unsafe trait PinSafeAllocator: Allocator {}
426+
427+
#[unstable(feature = "allocator_api", issue = "32838")]
428+
// SAFETY: Allocators that live forever never become unreachable, and so never invalidate their
429+
// allocated memory blocks.
430+
unsafe impl<A: Allocator + ?Sized> PinSafeAllocator for &'static A {}

src/test/ui/box/leak-alloc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fn use_value(_: u32) {}
2222
fn main() {
2323
let alloc = Alloc {};
2424
let boxed = Box::new_in(10, alloc.by_ref());
25+
//~^ ERROR `alloc` does not live long enough
2526
let theref = Box::leak(boxed);
2627
drop(alloc);
2728
//~^ ERROR cannot move out of `alloc` because it is borrowed

src/test/ui/box/leak-alloc.stderr

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
1+
error[E0597]: `alloc` does not live long enough
2+
--> $DIR/leak-alloc.rs:24:33
3+
|
4+
LL | let boxed = Box::new_in(10, alloc.by_ref());
5+
| ^^^^^^^^^^^^^^
6+
| |
7+
| borrowed value does not live long enough
8+
| argument requires that `alloc` is borrowed for `'static`
9+
...
10+
LL | }
11+
| - `alloc` dropped here while still borrowed
12+
113
error[E0505]: cannot move out of `alloc` because it is borrowed
2-
--> $DIR/leak-alloc.rs:26:10
14+
--> $DIR/leak-alloc.rs:27:10
315
|
416
LL | let boxed = Box::new_in(10, alloc.by_ref());
5-
| -------------- borrow of `alloc` occurs here
6-
LL | let theref = Box::leak(boxed);
17+
| --------------
18+
| |
19+
| borrow of `alloc` occurs here
20+
| argument requires that `alloc` is borrowed for `'static`
21+
...
722
LL | drop(alloc);
823
| ^^^^^ move out of `alloc` occurs here
9-
LL |
10-
LL | use_value(*theref)
11-
| ------- borrow later used here
1224

13-
error: aborting due to previous error
25+
error: aborting due to 2 previous errors
1426

15-
For more information about this error, try `rustc --explain E0505`.
27+
Some errors have detailed explanations: E0505, E0597.
28+
For more information about an error, try `rustc --explain E0505`.

src/test/ui/box/pin-safe-alloc.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// run-pass
2+
#![feature(allocator_api)]
3+
#![feature(box_into_pin)]
4+
5+
use std::alloc::{AllocError, Allocator, Layout, PinSafeAllocator, System};
6+
use std::ptr::NonNull;
7+
use std::marker::PhantomPinned;
8+
use std::boxed::Box;
9+
10+
struct Alloc {}
11+
12+
unsafe impl Allocator for Alloc {
13+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
14+
System.allocate(layout)
15+
}
16+
17+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
18+
System.deallocate(ptr, layout)
19+
}
20+
}
21+
22+
unsafe impl PinSafeAllocator for Alloc {}
23+
24+
fn main() {
25+
struct MyPinned {
26+
_value: u32,
27+
_pinned: PhantomPinned,
28+
}
29+
30+
let value = MyPinned {
31+
_value: 0,
32+
_pinned: PhantomPinned,
33+
};
34+
let alloc = Alloc {};
35+
let _ = Box::pin_in(value, alloc);
36+
}

src/test/ui/box/pin-unsafe-alloc.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#![feature(allocator_api)]
2+
#![feature(box_into_pin)]
3+
4+
use std::alloc::{AllocError, Allocator, Layout, System};
5+
use std::ptr::NonNull;
6+
use std::marker::PhantomPinned;
7+
use std::boxed::Box;
8+
9+
struct Alloc {}
10+
11+
unsafe impl Allocator for Alloc {
12+
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
13+
System.allocate(layout)
14+
}
15+
16+
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
17+
System.deallocate(ptr, layout)
18+
}
19+
}
20+
21+
fn main() {
22+
struct MyPinned {
23+
_value: u32,
24+
_pinned: PhantomPinned,
25+
}
26+
27+
let value = MyPinned {
28+
_value: 0,
29+
_pinned: PhantomPinned,
30+
};
31+
let alloc = Alloc {};
32+
let _ = Box::pin_in(value, alloc);
33+
//~^ ERROR the trait bound `Alloc: PinSafeAllocator` is not satisfied
34+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0277]: the trait bound `Alloc: PinSafeAllocator` is not satisfied
2+
--> $DIR/pin-unsafe-alloc.rs:32:32
3+
|
4+
LL | let _ = Box::pin_in(value, alloc);
5+
| ----------- ^^^^^ expected an implementor of trait `PinSafeAllocator`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `Box::<T, A>::pin_in`
10+
--> $SRC_DIR/alloc/src/boxed.rs:LL:COL
11+
|
12+
LL | A: ~const Allocator + ~const PinSafeAllocator + ~const Drop + ~const Destruct,
13+
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Box::<T, A>::pin_in`
14+
help: consider borrowing here
15+
|
16+
LL | let _ = Box::pin_in(value, &alloc);
17+
| +
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)