Skip to content

Commit 127a11a

Browse files
committedApr 2, 2020
Auto merge of #70362 - TimDiekmann:alloc-overhaul, r=Amanieu
Overhaul of the `AllocRef` trait to match allocator-wg's latest consens; Take 2 GitHub won't let me reopen #69889 so I make a new PR. In addition to #69889 this fixes the unsoundness of `RawVec::into_box` when using allocators supporting overallocating. Also it uses `MemoryBlock` in `AllocRef` to unify `_in_place` methods by passing `&mut MemoryBlock`. Additionally, `RawVec` now checks for `size_of::<T>()` again and ignore every ZST. The internal capacity of `RawVec` isn't used by ZSTs anymore, as `into_box` now requires a length to be specified. r? @Amanieu fixes rust-lang/wg-allocators#38 fixes rust-lang/wg-allocators#41 fixes rust-lang/wg-allocators#44 fixes rust-lang/wg-allocators#51
2 parents b793f40 + 89ed59d commit 127a11a

File tree

21 files changed

+1445
-1619
lines changed

21 files changed

+1445
-1619
lines changed
 

‎src/liballoc/alloc.rs

+76-36
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![stable(feature = "alloc_module", since = "1.28.0")]
44

5-
use core::intrinsics::{min_align_of_val, size_of_val};
5+
use core::intrinsics::{self, min_align_of_val, size_of_val};
66
use core::ptr::{NonNull, Unique};
77
use core::usize;
88

@@ -165,11 +165,19 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 {
165165
#[unstable(feature = "allocator_api", issue = "32838")]
166166
unsafe impl AllocRef for Global {
167167
#[inline]
168-
fn alloc(&mut self, layout: Layout) -> Result<(NonNull<u8>, usize), AllocErr> {
169-
if layout.size() == 0 {
170-
Ok((layout.dangling(), 0))
171-
} else {
172-
unsafe { NonNull::new(alloc(layout)).ok_or(AllocErr).map(|p| (p, layout.size())) }
168+
fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
169+
unsafe {
170+
let size = layout.size();
171+
if size == 0 {
172+
Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
173+
} else {
174+
let raw_ptr = match init {
175+
AllocInit::Uninitialized => alloc(layout),
176+
AllocInit::Zeroed => alloc_zeroed(layout),
177+
};
178+
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
179+
Ok(MemoryBlock { ptr, size })
180+
}
173181
}
174182
}
175183

@@ -181,32 +189,71 @@ unsafe impl AllocRef for Global {
181189
}
182190

183191
#[inline]
184-
unsafe fn realloc(
192+
unsafe fn grow(
185193
&mut self,
186194
ptr: NonNull<u8>,
187195
layout: Layout,
188196
new_size: usize,
189-
) -> Result<(NonNull<u8>, usize), AllocErr> {
190-
match (layout.size(), new_size) {
191-
(0, 0) => Ok((layout.dangling(), 0)),
192-
(0, _) => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())),
193-
(_, 0) => {
194-
self.dealloc(ptr, layout);
195-
Ok((layout.dangling(), 0))
197+
placement: ReallocPlacement,
198+
init: AllocInit,
199+
) -> Result<MemoryBlock, AllocErr> {
200+
let size = layout.size();
201+
debug_assert!(
202+
new_size >= size,
203+
"`new_size` must be greater than or equal to `memory.size()`"
204+
);
205+
206+
if size == new_size {
207+
return Ok(MemoryBlock { ptr, size });
208+
}
209+
210+
match placement {
211+
ReallocPlacement::InPlace => Err(AllocErr),
212+
ReallocPlacement::MayMove if layout.size() == 0 => {
213+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
214+
self.alloc(new_layout, init)
215+
}
216+
ReallocPlacement::MayMove => {
217+
// `realloc` probably checks for `new_size > size` or something similar.
218+
intrinsics::assume(new_size > size);
219+
let ptr = realloc(ptr.as_ptr(), layout, new_size);
220+
let memory =
221+
MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
222+
init.init_offset(memory, size);
223+
Ok(memory)
196224
}
197-
(_, _) => NonNull::new(realloc(ptr.as_ptr(), layout, new_size))
198-
.ok_or(AllocErr)
199-
.map(|p| (p, new_size)),
200225
}
201226
}
202227

203228
#[inline]
204-
fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull<u8>, usize), AllocErr> {
205-
if layout.size() == 0 {
206-
Ok((layout.dangling(), 0))
207-
} else {
208-
unsafe {
209-
NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr).map(|p| (p, layout.size()))
229+
unsafe fn shrink(
230+
&mut self,
231+
ptr: NonNull<u8>,
232+
layout: Layout,
233+
new_size: usize,
234+
placement: ReallocPlacement,
235+
) -> Result<MemoryBlock, AllocErr> {
236+
let size = layout.size();
237+
debug_assert!(
238+
new_size <= size,
239+
"`new_size` must be smaller than or equal to `memory.size()`"
240+
);
241+
242+
if size == new_size {
243+
return Ok(MemoryBlock { ptr, size });
244+
}
245+
246+
match placement {
247+
ReallocPlacement::InPlace => Err(AllocErr),
248+
ReallocPlacement::MayMove if new_size == 0 => {
249+
self.dealloc(ptr, layout);
250+
Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
251+
}
252+
ReallocPlacement::MayMove => {
253+
// `realloc` probably checks for `new_size < size` or something similar.
254+
intrinsics::assume(new_size < size);
255+
let ptr = realloc(ptr.as_ptr(), layout, new_size);
256+
Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size })
210257
}
211258
}
212259
}
@@ -218,14 +265,10 @@ unsafe impl AllocRef for Global {
218265
#[lang = "exchange_malloc"]
219266
#[inline]
220267
unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
221-
if size == 0 {
222-
align as *mut u8
223-
} else {
224-
let layout = Layout::from_size_align_unchecked(size, align);
225-
match Global.alloc(layout) {
226-
Ok((ptr, _)) => ptr.as_ptr(),
227-
Err(_) => handle_alloc_error(layout),
228-
}
268+
let layout = Layout::from_size_align_unchecked(size, align);
269+
match Global.alloc(layout, AllocInit::Uninitialized) {
270+
Ok(memory) => memory.ptr.as_ptr(),
271+
Err(_) => handle_alloc_error(layout),
229272
}
230273
}
231274

@@ -239,11 +282,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
239282
pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
240283
let size = size_of_val(ptr.as_ref());
241284
let align = min_align_of_val(ptr.as_ref());
242-
// We do not allocate for Box<T> when T is ZST, so deallocation is also not necessary.
243-
if size != 0 {
244-
let layout = Layout::from_size_align_unchecked(size, align);
245-
Global.dealloc(ptr.cast().into(), layout);
246-
}
285+
let layout = Layout::from_size_align_unchecked(size, align);
286+
Global.dealloc(ptr.cast().into(), layout)
247287
}
248288

249289
/// Abort on memory allocation error or failure.

‎src/liballoc/alloc/tests.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ use test::Bencher;
88
fn allocate_zeroed() {
99
unsafe {
1010
let layout = Layout::from_size_align(1024, 1).unwrap();
11-
let (ptr, _) =
12-
Global.alloc_zeroed(layout.clone()).unwrap_or_else(|_| handle_alloc_error(layout));
11+
let memory = Global
12+
.alloc(layout.clone(), AllocInit::Zeroed)
13+
.unwrap_or_else(|_| handle_alloc_error(layout));
1314

14-
let mut i = ptr.cast::<u8>().as_ptr();
15+
let mut i = memory.ptr.cast::<u8>().as_ptr();
1516
let end = i.add(layout.size());
1617
while i < end {
1718
assert_eq!(*i, 0);
1819
i = i.offset(1);
1920
}
20-
Global.dealloc(ptr, layout);
21+
Global.dealloc(memory.ptr, layout);
2122
}
2223
}
2324

‎src/liballoc/boxed.rs

+16-25
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ use core::ops::{
143143
};
144144
use core::pin::Pin;
145145
use core::ptr::{self, NonNull, Unique};
146-
use core::slice;
147146
use core::task::{Context, Poll};
148147

149-
use crate::alloc::{self, AllocRef, Global};
148+
use crate::alloc::{self, AllocInit, AllocRef, Global};
150149
use crate::raw_vec::RawVec;
151150
use crate::str::from_boxed_utf8_unchecked;
152151
use crate::vec::Vec;
@@ -196,14 +195,12 @@ impl<T> Box<T> {
196195
#[unstable(feature = "new_uninit", issue = "63291")]
197196
pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
198197
let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
199-
unsafe {
200-
let ptr = if layout.size() == 0 {
201-
NonNull::dangling()
202-
} else {
203-
Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).0.cast()
204-
};
205-
Box::from_raw(ptr.as_ptr())
206-
}
198+
let ptr = Global
199+
.alloc(layout, AllocInit::Uninitialized)
200+
.unwrap_or_else(|_| alloc::handle_alloc_error(layout))
201+
.ptr
202+
.cast();
203+
unsafe { Box::from_raw(ptr.as_ptr()) }
207204
}
208205

209206
/// Constructs a new `Box` with uninitialized contents, with the memory
@@ -226,11 +223,13 @@ impl<T> Box<T> {
226223
/// [zeroed]: ../../std/mem/union.MaybeUninit.html#method.zeroed
227224
#[unstable(feature = "new_uninit", issue = "63291")]
228225
pub fn new_zeroed() -> Box<mem::MaybeUninit<T>> {
229-
unsafe {
230-
let mut uninit = Self::new_uninit();
231-
ptr::write_bytes::<T>(uninit.as_mut_ptr(), 0, 1);
232-
uninit
233-
}
226+
let layout = alloc::Layout::new::<mem::MaybeUninit<T>>();
227+
let ptr = Global
228+
.alloc(layout, AllocInit::Zeroed)
229+
.unwrap_or_else(|_| alloc::handle_alloc_error(layout))
230+
.ptr
231+
.cast();
232+
unsafe { Box::from_raw(ptr.as_ptr()) }
234233
}
235234

236235
/// Constructs a new `Pin<Box<T>>`. If `T` does not implement `Unpin`, then
@@ -265,15 +264,7 @@ impl<T> Box<[T]> {
265264
/// ```
266265
#[unstable(feature = "new_uninit", issue = "63291")]
267266
pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {
268-
let layout = alloc::Layout::array::<mem::MaybeUninit<T>>(len).unwrap();
269-
unsafe {
270-
let ptr = if layout.size() == 0 {
271-
NonNull::dangling()
272-
} else {
273-
Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).0.cast()
274-
};
275-
Box::from_raw(slice::from_raw_parts_mut(ptr.as_ptr(), len))
276-
}
267+
unsafe { RawVec::with_capacity(len).into_box(len) }
277268
}
278269
}
279270

@@ -778,7 +769,7 @@ impl<T: Copy> From<&[T]> for Box<[T]> {
778769
let buf = RawVec::with_capacity(len);
779770
unsafe {
780771
ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len);
781-
buf.into_box()
772+
buf.into_box(slice.len()).assume_init()
782773
}
783774
}
784775
}

‎src/liballoc/collections/btree/node.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
11421142

11431143
(*left_node.as_leaf_mut()).len += right_len as u16 + 1;
11441144

1145-
if self.node.height > 1 {
1145+
let layout = if self.node.height > 1 {
11461146
ptr::copy_nonoverlapping(
11471147
right_node.cast_unchecked().as_internal().edges.as_ptr(),
11481148
left_node
@@ -1159,10 +1159,11 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Internal>, marker::
11591159
.correct_parent_link();
11601160
}
11611161

1162-
Global.dealloc(right_node.node.cast(), Layout::new::<InternalNode<K, V>>());
1162+
Layout::new::<InternalNode<K, V>>()
11631163
} else {
1164-
Global.dealloc(right_node.node.cast(), Layout::new::<LeafNode<K, V>>());
1165-
}
1164+
Layout::new::<LeafNode<K, V>>()
1165+
};
1166+
Global.dealloc(right_node.node.cast(), layout);
11661167

11671168
Handle::new_edge(self.node, self.idx)
11681169
}

‎src/liballoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
#![feature(lang_items)]
101101
#![feature(libc)]
102102
#![cfg_attr(not(bootstrap), feature(negative_impls))]
103+
#![feature(new_uninit)]
103104
#![feature(nll)]
104105
#![feature(optin_builtin_traits)]
105106
#![feature(pattern)]

‎src/liballoc/raw_vec.rs

+263-392
Large diffs are not rendered by default.

‎src/liballoc/raw_vec/tests.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn allocator_param() {
1212
//
1313
// Instead, this just checks that the `RawVec` methods do at
1414
// least go through the Allocator API when it reserves
15+
1516
// storage.
1617

1718
// A dumb allocator that consumes a fixed amount of fuel
@@ -20,12 +21,12 @@ fn allocator_param() {
2021
fuel: usize,
2122
}
2223
unsafe impl AllocRef for BoundedAlloc {
23-
fn alloc(&mut self, layout: Layout) -> Result<(NonNull<u8>, usize), AllocErr> {
24+
fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
2425
let size = layout.size();
2526
if size > self.fuel {
2627
return Err(AllocErr);
2728
}
28-
match Global.alloc(layout) {
29+
match Global.alloc(layout, init) {
2930
ok @ Ok(_) => {
3031
self.fuel -= size;
3132
ok
@@ -40,9 +41,9 @@ fn allocator_param() {
4041

4142
let a = BoundedAlloc { fuel: 500 };
4243
let mut v: RawVec<u8, _> = RawVec::with_capacity_in(50, a);
43-
assert_eq!(v.a.fuel, 450);
44+
assert_eq!(v.alloc.fuel, 450);
4445
v.reserve(50, 150); // (causes a realloc, thus using 50 + 150 = 200 units of fuel)
45-
assert_eq!(v.a.fuel, 250);
46+
assert_eq!(v.alloc.fuel, 250);
4647
}
4748

4849
#[test]

‎src/liballoc/rc.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ use core::ptr::{self, NonNull};
252252
use core::slice::{self, from_raw_parts_mut};
253253
use core::usize;
254254

255-
use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout};
255+
use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout};
256256
use crate::string::String;
257257
use crate::vec::Vec;
258258

@@ -936,10 +936,12 @@ impl<T: ?Sized> Rc<T> {
936936
let layout = Layout::new::<RcBox<()>>().extend(value_layout).unwrap().0.pad_to_align();
937937

938938
// Allocate for the layout.
939-
let (mem, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
939+
let mem = Global
940+
.alloc(layout, AllocInit::Uninitialized)
941+
.unwrap_or_else(|_| handle_alloc_error(layout));
940942

941943
// Initialize the RcBox
942-
let inner = mem_to_rcbox(mem.as_ptr());
944+
let inner = mem_to_rcbox(mem.ptr.as_ptr());
943945
debug_assert_eq!(Layout::for_value(&*inner), layout);
944946

945947
ptr::write(&mut (*inner).strong, Cell::new(1));

‎src/liballoc/sync.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use core::sync::atomic;
2525
use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
2626
use core::{isize, usize};
2727

28-
use crate::alloc::{box_free, handle_alloc_error, AllocRef, Global, Layout};
28+
use crate::alloc::{box_free, handle_alloc_error, AllocInit, AllocRef, Global, Layout};
2929
use crate::boxed::Box;
3030
use crate::rc::is_dangling;
3131
use crate::string::String;
@@ -814,10 +814,12 @@ impl<T: ?Sized> Arc<T> {
814814
// reference (see #54908).
815815
let layout = Layout::new::<ArcInner<()>>().extend(value_layout).unwrap().0.pad_to_align();
816816

817-
let (mem, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
817+
let mem = Global
818+
.alloc(layout, AllocInit::Uninitialized)
819+
.unwrap_or_else(|_| handle_alloc_error(layout));
818820

819821
// Initialize the ArcInner
820-
let inner = mem_to_arcinner(mem.as_ptr());
822+
let inner = mem_to_arcinner(mem.ptr.as_ptr());
821823
debug_assert_eq!(Layout::for_value(&*inner), layout);
822824

823825
ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1));

‎src/liballoc/tests/heap.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::alloc::{AllocRef, Global, Layout, System};
1+
use std::alloc::{AllocInit, AllocRef, Global, Layout, System};
22

33
/// Issue #45955 and #62251.
44
#[test]
@@ -20,7 +20,13 @@ fn check_overalign_requests<T: AllocRef>(mut allocator: T) {
2020
unsafe {
2121
let pointers: Vec<_> = (0..iterations)
2222
.map(|_| {
23-
allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap().0
23+
allocator
24+
.alloc(
25+
Layout::from_size_align(size, align).unwrap(),
26+
AllocInit::Uninitialized,
27+
)
28+
.unwrap()
29+
.ptr
2430
})
2531
.collect();
2632
for &ptr in &pointers {

‎src/liballoc/vec.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,9 @@ impl<T> Vec<T> {
679679
unsafe {
680680
self.shrink_to_fit();
681681
let buf = ptr::read(&self.buf);
682+
let len = self.len();
682683
mem::forget(self);
683-
buf.into_box()
684+
buf.into_box(len).assume_init()
684685
}
685686
}
686687

‎src/libcore/alloc.rs

-1,043
This file was deleted.

‎src/libcore/alloc/global.rs

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
use crate::alloc::Layout;
2+
use crate::cmp;
3+
use crate::ptr;
4+
5+
/// A memory allocator that can be registered as the standard library’s default
6+
/// through the `#[global_allocator]` attribute.
7+
///
8+
/// Some of the methods require that a memory block be *currently
9+
/// allocated* via an allocator. This means that:
10+
///
11+
/// * the starting address for that memory block was previously
12+
/// returned by a previous call to an allocation method
13+
/// such as `alloc`, and
14+
///
15+
/// * the memory block has not been subsequently deallocated, where
16+
/// blocks are deallocated either by being passed to a deallocation
17+
/// method such as `dealloc` or by being
18+
/// passed to a reallocation method that returns a non-null pointer.
19+
///
20+
///
21+
/// # Example
22+
///
23+
/// ```no_run
24+
/// use std::alloc::{GlobalAlloc, Layout, alloc};
25+
/// use std::ptr::null_mut;
26+
///
27+
/// struct MyAllocator;
28+
///
29+
/// unsafe impl GlobalAlloc for MyAllocator {
30+
/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() }
31+
/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
32+
/// }
33+
///
34+
/// #[global_allocator]
35+
/// static A: MyAllocator = MyAllocator;
36+
///
37+
/// fn main() {
38+
/// unsafe {
39+
/// assert!(alloc(Layout::new::<u32>()).is_null())
40+
/// }
41+
/// }
42+
/// ```
43+
///
44+
/// # Safety
45+
///
46+
/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and
47+
/// implementors must ensure that they adhere to these contracts:
48+
///
49+
/// * It's undefined behavior if global allocators unwind. This restriction may
50+
/// be lifted in the future, but currently a panic from any of these
51+
/// functions may lead to memory unsafety.
52+
///
53+
/// * `Layout` queries and calculations in general must be correct. Callers of
54+
/// this trait are allowed to rely on the contracts defined on each method,
55+
/// and implementors must ensure such contracts remain true.
56+
#[stable(feature = "global_alloc", since = "1.28.0")]
57+
pub unsafe trait GlobalAlloc {
58+
/// Allocate memory as described by the given `layout`.
59+
///
60+
/// Returns a pointer to newly-allocated memory,
61+
/// or null to indicate allocation failure.
62+
///
63+
/// # Safety
64+
///
65+
/// This function is unsafe because undefined behavior can result
66+
/// if the caller does not ensure that `layout` has non-zero size.
67+
///
68+
/// (Extension subtraits might provide more specific bounds on
69+
/// behavior, e.g., guarantee a sentinel address or a null pointer
70+
/// in response to a zero-size allocation request.)
71+
///
72+
/// The allocated block of memory may or may not be initialized.
73+
///
74+
/// # Errors
75+
///
76+
/// Returning a null pointer indicates that either memory is exhausted
77+
/// or `layout` does not meet this allocator's size or alignment constraints.
78+
///
79+
/// Implementations are encouraged to return null on memory
80+
/// exhaustion rather than aborting, but this is not
81+
/// a strict requirement. (Specifically: it is *legal* to
82+
/// implement this trait atop an underlying native allocation
83+
/// library that aborts on memory exhaustion.)
84+
///
85+
/// Clients wishing to abort computation in response to an
86+
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
87+
/// rather than directly invoking `panic!` or similar.
88+
///
89+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
90+
#[stable(feature = "global_alloc", since = "1.28.0")]
91+
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
92+
93+
/// Deallocate the block of memory at the given `ptr` pointer with the given `layout`.
94+
///
95+
/// # Safety
96+
///
97+
/// This function is unsafe because undefined behavior can result
98+
/// if the caller does not ensure all of the following:
99+
///
100+
/// * `ptr` must denote a block of memory currently allocated via
101+
/// this allocator,
102+
///
103+
/// * `layout` must be the same layout that was used
104+
/// to allocate that block of memory,
105+
#[stable(feature = "global_alloc", since = "1.28.0")]
106+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
107+
108+
/// Behaves like `alloc`, but also ensures that the contents
109+
/// are set to zero before being returned.
110+
///
111+
/// # Safety
112+
///
113+
/// This function is unsafe for the same reasons that `alloc` is.
114+
/// However the allocated block of memory is guaranteed to be initialized.
115+
///
116+
/// # Errors
117+
///
118+
/// Returning a null pointer indicates that either memory is exhausted
119+
/// or `layout` does not meet allocator's size or alignment constraints,
120+
/// just as in `alloc`.
121+
///
122+
/// Clients wishing to abort computation in response to an
123+
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
124+
/// rather than directly invoking `panic!` or similar.
125+
///
126+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
127+
#[stable(feature = "global_alloc", since = "1.28.0")]
128+
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
129+
let size = layout.size();
130+
let ptr = self.alloc(layout);
131+
if !ptr.is_null() {
132+
ptr::write_bytes(ptr, 0, size);
133+
}
134+
ptr
135+
}
136+
137+
/// Shrink or grow a block of memory to the given `new_size`.
138+
/// The block is described by the given `ptr` pointer and `layout`.
139+
///
140+
/// If this returns a non-null pointer, then ownership of the memory block
141+
/// referenced by `ptr` has been transferred to this allocator.
142+
/// The memory may or may not have been deallocated,
143+
/// and should be considered unusable (unless of course it was
144+
/// transferred back to the caller again via the return value of
145+
/// this method). The new memory block is allocated with `layout`, but
146+
/// with the `size` updated to `new_size`.
147+
///
148+
/// If this method returns null, then ownership of the memory
149+
/// block has not been transferred to this allocator, and the
150+
/// contents of the memory block are unaltered.
151+
///
152+
/// # Safety
153+
///
154+
/// This function is unsafe because undefined behavior can result
155+
/// if the caller does not ensure all of the following:
156+
///
157+
/// * `ptr` must be currently allocated via this allocator,
158+
///
159+
/// * `layout` must be the same layout that was used
160+
/// to allocate that block of memory,
161+
///
162+
/// * `new_size` must be greater than zero.
163+
///
164+
/// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
165+
/// must not overflow (i.e., the rounded value must be less than `usize::MAX`).
166+
///
167+
/// (Extension subtraits might provide more specific bounds on
168+
/// behavior, e.g., guarantee a sentinel address or a null pointer
169+
/// in response to a zero-size allocation request.)
170+
///
171+
/// # Errors
172+
///
173+
/// Returns null if the new layout does not meet the size
174+
/// and alignment constraints of the allocator, or if reallocation
175+
/// otherwise fails.
176+
///
177+
/// Implementations are encouraged to return null on memory
178+
/// exhaustion rather than panicking or aborting, but this is not
179+
/// a strict requirement. (Specifically: it is *legal* to
180+
/// implement this trait atop an underlying native allocation
181+
/// library that aborts on memory exhaustion.)
182+
///
183+
/// Clients wishing to abort computation in response to a
184+
/// reallocation error are encouraged to call the [`handle_alloc_error`] function,
185+
/// rather than directly invoking `panic!` or similar.
186+
///
187+
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
188+
#[stable(feature = "global_alloc", since = "1.28.0")]
189+
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
190+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
191+
let new_ptr = self.alloc(new_layout);
192+
if !new_ptr.is_null() {
193+
ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
194+
self.dealloc(ptr, layout);
195+
}
196+
new_ptr
197+
}
198+
}

‎src/libcore/alloc/layout.rs

+346
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
// ignore-tidy-undocumented-unsafe
2+
3+
use crate::cmp;
4+
use crate::fmt;
5+
use crate::mem;
6+
use crate::num::NonZeroUsize;
7+
use crate::ptr::NonNull;
8+
9+
const fn size_align<T>() -> (usize, usize) {
10+
(mem::size_of::<T>(), mem::align_of::<T>())
11+
}
12+
13+
/// Layout of a block of memory.
14+
///
15+
/// An instance of `Layout` describes a particular layout of memory.
16+
/// You build a `Layout` up as an input to give to an allocator.
17+
///
18+
/// All layouts have an associated size and a power-of-two alignment.
19+
///
20+
/// (Note that layouts are *not* required to have non-zero size,
21+
/// even though `GlobalAlloc` requires that all memory requests
22+
/// be non-zero in size. A caller must either ensure that conditions
23+
/// like this are met, use specific allocators with looser
24+
/// requirements, or use the more lenient `AllocRef` interface.)
25+
#[stable(feature = "alloc_layout", since = "1.28.0")]
26+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27+
#[lang = "alloc_layout"]
28+
pub struct Layout {
29+
// size of the requested block of memory, measured in bytes.
30+
size_: usize,
31+
32+
// alignment of the requested block of memory, measured in bytes.
33+
// we ensure that this is always a power-of-two, because API's
34+
// like `posix_memalign` require it and it is a reasonable
35+
// constraint to impose on Layout constructors.
36+
//
37+
// (However, we do not analogously require `align >= sizeof(void*)`,
38+
// even though that is *also* a requirement of `posix_memalign`.)
39+
align_: NonZeroUsize,
40+
}
41+
42+
impl Layout {
43+
/// Constructs a `Layout` from a given `size` and `align`,
44+
/// or returns `LayoutErr` if any of the following conditions
45+
/// are not met:
46+
///
47+
/// * `align` must not be zero,
48+
///
49+
/// * `align` must be a power of two,
50+
///
51+
/// * `size`, when rounded up to the nearest multiple of `align`,
52+
/// must not overflow (i.e., the rounded value must be less than
53+
/// or equal to `usize::MAX`).
54+
#[stable(feature = "alloc_layout", since = "1.28.0")]
55+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
56+
#[inline]
57+
pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutErr> {
58+
if !align.is_power_of_two() {
59+
return Err(LayoutErr { private: () });
60+
}
61+
62+
// (power-of-two implies align != 0.)
63+
64+
// Rounded up size is:
65+
// size_rounded_up = (size + align - 1) & !(align - 1);
66+
//
67+
// We know from above that align != 0. If adding (align - 1)
68+
// does not overflow, then rounding up will be fine.
69+
//
70+
// Conversely, &-masking with !(align - 1) will subtract off
71+
// only low-order-bits. Thus if overflow occurs with the sum,
72+
// the &-mask cannot subtract enough to undo that overflow.
73+
//
74+
// Above implies that checking for summation overflow is both
75+
// necessary and sufficient.
76+
if size > usize::MAX - (align - 1) {
77+
return Err(LayoutErr { private: () });
78+
}
79+
80+
unsafe { Ok(Layout::from_size_align_unchecked(size, align)) }
81+
}
82+
83+
/// Creates a layout, bypassing all checks.
84+
///
85+
/// # Safety
86+
///
87+
/// This function is unsafe as it does not verify the preconditions from
88+
/// [`Layout::from_size_align`](#method.from_size_align).
89+
#[stable(feature = "alloc_layout", since = "1.28.0")]
90+
#[rustc_const_stable(feature = "alloc_layout", since = "1.28.0")]
91+
#[inline]
92+
pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self {
93+
Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) }
94+
}
95+
96+
/// The minimum size in bytes for a memory block of this layout.
97+
#[stable(feature = "alloc_layout", since = "1.28.0")]
98+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
99+
#[inline]
100+
pub const fn size(&self) -> usize {
101+
self.size_
102+
}
103+
104+
/// The minimum byte alignment for a memory block of this layout.
105+
#[stable(feature = "alloc_layout", since = "1.28.0")]
106+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
107+
#[inline]
108+
pub const fn align(&self) -> usize {
109+
self.align_.get()
110+
}
111+
112+
/// Constructs a `Layout` suitable for holding a value of type `T`.
113+
#[stable(feature = "alloc_layout", since = "1.28.0")]
114+
#[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")]
115+
#[inline]
116+
pub const fn new<T>() -> Self {
117+
let (size, align) = size_align::<T>();
118+
// Note that the align is guaranteed by rustc to be a power of two and
119+
// the size+align combo is guaranteed to fit in our address space. As a
120+
// result use the unchecked constructor here to avoid inserting code
121+
// that panics if it isn't optimized well enough.
122+
unsafe { Layout::from_size_align_unchecked(size, align) }
123+
}
124+
125+
/// Produces layout describing a record that could be used to
126+
/// allocate backing structure for `T` (which could be a trait
127+
/// or other unsized type like a slice).
128+
#[stable(feature = "alloc_layout", since = "1.28.0")]
129+
#[inline]
130+
pub fn for_value<T: ?Sized>(t: &T) -> Self {
131+
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
132+
// See rationale in `new` for why this is using an unsafe variant below
133+
debug_assert!(Layout::from_size_align(size, align).is_ok());
134+
unsafe { Layout::from_size_align_unchecked(size, align) }
135+
}
136+
137+
/// Creates a `NonNull` that is dangling, but well-aligned for this Layout.
138+
///
139+
/// Note that the pointer value may potentially represent a valid pointer,
140+
/// which means this must not be used as a "not yet initialized"
141+
/// sentinel value. Types that lazily allocate must track initialization by
142+
/// some other means.
143+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
144+
#[inline]
145+
pub const fn dangling(&self) -> NonNull<u8> {
146+
// align is non-zero and a power of two
147+
unsafe { NonNull::new_unchecked(self.align() as *mut u8) }
148+
}
149+
150+
/// Creates a layout describing the record that can hold a value
151+
/// of the same layout as `self`, but that also is aligned to
152+
/// alignment `align` (measured in bytes).
153+
///
154+
/// If `self` already meets the prescribed alignment, then returns
155+
/// `self`.
156+
///
157+
/// Note that this method does not add any padding to the overall
158+
/// size, regardless of whether the returned layout has a different
159+
/// alignment. In other words, if `K` has size 16, `K.align_to(32)`
160+
/// will *still* have size 16.
161+
///
162+
/// Returns an error if the combination of `self.size()` and the given
163+
/// `align` violates the conditions listed in
164+
/// [`Layout::from_size_align`](#method.from_size_align).
165+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
166+
#[inline]
167+
pub fn align_to(&self, align: usize) -> Result<Self, LayoutErr> {
168+
Layout::from_size_align(self.size(), cmp::max(self.align(), align))
169+
}
170+
171+
/// Returns the amount of padding we must insert after `self`
172+
/// to ensure that the following address will satisfy `align`
173+
/// (measured in bytes).
174+
///
175+
/// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)`
176+
/// returns 3, because that is the minimum number of bytes of
177+
/// padding required to get a 4-aligned address (assuming that the
178+
/// corresponding memory block starts at a 4-aligned address).
179+
///
180+
/// The return value of this function has no meaning if `align` is
181+
/// not a power-of-two.
182+
///
183+
/// Note that the utility of the returned value requires `align`
184+
/// to be less than or equal to the alignment of the starting
185+
/// address for the whole allocated block of memory. One way to
186+
/// satisfy this constraint is to ensure `align <= self.align()`.
187+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
188+
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
189+
#[inline]
190+
pub const fn padding_needed_for(&self, align: usize) -> usize {
191+
let len = self.size();
192+
193+
// Rounded up value is:
194+
// len_rounded_up = (len + align - 1) & !(align - 1);
195+
// and then we return the padding difference: `len_rounded_up - len`.
196+
//
197+
// We use modular arithmetic throughout:
198+
//
199+
// 1. align is guaranteed to be > 0, so align - 1 is always
200+
// valid.
201+
//
202+
// 2. `len + align - 1` can overflow by at most `align - 1`,
203+
// so the &-mask with `!(align - 1)` will ensure that in the
204+
// case of overflow, `len_rounded_up` will itself be 0.
205+
// Thus the returned padding, when added to `len`, yields 0,
206+
// which trivially satisfies the alignment `align`.
207+
//
208+
// (Of course, attempts to allocate blocks of memory whose
209+
// size and padding overflow in the above manner should cause
210+
// the allocator to yield an error anyway.)
211+
212+
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
213+
len_rounded_up.wrapping_sub(len)
214+
}
215+
216+
/// Creates a layout by rounding the size of this layout up to a multiple
217+
/// of the layout's alignment.
218+
///
219+
/// This is equivalent to adding the result of `padding_needed_for`
220+
/// to the layout's current size.
221+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
222+
#[inline]
223+
pub fn pad_to_align(&self) -> Layout {
224+
let pad = self.padding_needed_for(self.align());
225+
// This cannot overflow. Quoting from the invariant of Layout:
226+
// > `size`, when rounded up to the nearest multiple of `align`,
227+
// > must not overflow (i.e., the rounded value must be less than
228+
// > `usize::MAX`)
229+
let new_size = self.size() + pad;
230+
231+
Layout::from_size_align(new_size, self.align()).unwrap()
232+
}
233+
234+
/// Creates a layout describing the record for `n` instances of
235+
/// `self`, with a suitable amount of padding between each to
236+
/// ensure that each instance is given its requested size and
237+
/// alignment. On success, returns `(k, offs)` where `k` is the
238+
/// layout of the array and `offs` is the distance between the start
239+
/// of each element in the array.
240+
///
241+
/// On arithmetic overflow, returns `LayoutErr`.
242+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
243+
#[inline]
244+
pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> {
245+
// This cannot overflow. Quoting from the invariant of Layout:
246+
// > `size`, when rounded up to the nearest multiple of `align`,
247+
// > must not overflow (i.e., the rounded value must be less than
248+
// > `usize::MAX`)
249+
let padded_size = self.size() + self.padding_needed_for(self.align());
250+
let alloc_size = padded_size.checked_mul(n).ok_or(LayoutErr { private: () })?;
251+
252+
unsafe {
253+
// self.align is already known to be valid and alloc_size has been
254+
// padded already.
255+
Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size))
256+
}
257+
}
258+
259+
/// Creates a layout describing the record for `self` followed by
260+
/// `next`, including any necessary padding to ensure that `next`
261+
/// will be properly aligned. Note that the resulting layout will
262+
/// satisfy the alignment properties of both `self` and `next`.
263+
///
264+
/// The resulting layout will be the same as that of a C struct containing
265+
/// two fields with the layouts of `self` and `next`, in that order.
266+
///
267+
/// Returns `Some((k, offset))`, where `k` is layout of the concatenated
268+
/// record and `offset` is the relative location, in bytes, of the
269+
/// start of the `next` embedded within the concatenated record
270+
/// (assuming that the record itself starts at offset 0).
271+
///
272+
/// On arithmetic overflow, returns `LayoutErr`.
273+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
274+
#[inline]
275+
pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> {
276+
let new_align = cmp::max(self.align(), next.align());
277+
let pad = self.padding_needed_for(next.align());
278+
279+
let offset = self.size().checked_add(pad).ok_or(LayoutErr { private: () })?;
280+
let new_size = offset.checked_add(next.size()).ok_or(LayoutErr { private: () })?;
281+
282+
let layout = Layout::from_size_align(new_size, new_align)?;
283+
Ok((layout, offset))
284+
}
285+
286+
/// Creates a layout describing the record for `n` instances of
287+
/// `self`, with no padding between each instance.
288+
///
289+
/// Note that, unlike `repeat`, `repeat_packed` does not guarantee
290+
/// that the repeated instances of `self` will be properly
291+
/// aligned, even if a given instance of `self` is properly
292+
/// aligned. In other words, if the layout returned by
293+
/// `repeat_packed` is used to allocate an array, it is not
294+
/// guaranteed that all elements in the array will be properly
295+
/// aligned.
296+
///
297+
/// On arithmetic overflow, returns `LayoutErr`.
298+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
299+
#[inline]
300+
pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutErr> {
301+
let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?;
302+
Layout::from_size_align(size, self.align())
303+
}
304+
305+
/// Creates a layout describing the record for `self` followed by
306+
/// `next` with no additional padding between the two. Since no
307+
/// padding is inserted, the alignment of `next` is irrelevant,
308+
/// and is not incorporated *at all* into the resulting layout.
309+
///
310+
/// On arithmetic overflow, returns `LayoutErr`.
311+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
312+
#[inline]
313+
pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutErr> {
314+
let new_size = self.size().checked_add(next.size()).ok_or(LayoutErr { private: () })?;
315+
Layout::from_size_align(new_size, self.align())
316+
}
317+
318+
/// Creates a layout describing the record for a `[T; n]`.
319+
///
320+
/// On arithmetic overflow, returns `LayoutErr`.
321+
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
322+
#[inline]
323+
pub fn array<T>(n: usize) -> Result<Self, LayoutErr> {
324+
Layout::new::<T>().repeat(n).map(|(k, offs)| {
325+
debug_assert!(offs == mem::size_of::<T>());
326+
k
327+
})
328+
}
329+
}
330+
331+
/// The parameters given to `Layout::from_size_align`
332+
/// or some other `Layout` constructor
333+
/// do not satisfy its documented constraints.
334+
#[stable(feature = "alloc_layout", since = "1.28.0")]
335+
#[derive(Clone, PartialEq, Eq, Debug)]
336+
pub struct LayoutErr {
337+
private: (),
338+
}
339+
340+
// (we need this for downstream impl of trait Error)
341+
#[stable(feature = "alloc_layout", since = "1.28.0")]
342+
impl fmt::Display for LayoutErr {
343+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344+
f.write_str("invalid parameters to Layout::from_size_align")
345+
}
346+
}

‎src/libcore/alloc/mod.rs

+362
Large diffs are not rendered by default.

‎src/libstd/alloc.rs

+73-34
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
6262
#![stable(feature = "alloc_module", since = "1.28.0")]
6363

64+
use core::intrinsics;
6465
use core::ptr::NonNull;
6566
use core::sync::atomic::{AtomicPtr, Ordering};
6667
use core::{mem, ptr};
@@ -138,59 +139,99 @@ pub struct System;
138139
#[unstable(feature = "allocator_api", issue = "32838")]
139140
unsafe impl AllocRef for System {
140141
#[inline]
141-
fn alloc(&mut self, layout: Layout) -> Result<(NonNull<u8>, usize), AllocErr> {
142-
if layout.size() == 0 {
143-
Ok((layout.dangling(), 0))
144-
} else {
145-
unsafe {
146-
NonNull::new(GlobalAlloc::alloc(self, layout))
147-
.ok_or(AllocErr)
148-
.map(|p| (p, layout.size()))
142+
fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr> {
143+
unsafe {
144+
let size = layout.size();
145+
if size == 0 {
146+
Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
147+
} else {
148+
let raw_ptr = match init {
149+
AllocInit::Uninitialized => GlobalAlloc::alloc(self, layout),
150+
AllocInit::Zeroed => GlobalAlloc::alloc_zeroed(self, layout),
151+
};
152+
let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?;
153+
Ok(MemoryBlock { ptr, size })
149154
}
150155
}
151156
}
152157

153158
#[inline]
154-
fn alloc_zeroed(&mut self, layout: Layout) -> Result<(NonNull<u8>, usize), AllocErr> {
155-
if layout.size() == 0 {
156-
Ok((layout.dangling(), 0))
157-
} else {
158-
unsafe {
159-
NonNull::new(GlobalAlloc::alloc_zeroed(self, layout))
160-
.ok_or(AllocErr)
161-
.map(|p| (p, layout.size()))
162-
}
159+
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
160+
if layout.size() != 0 {
161+
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
163162
}
164163
}
165164

166165
#[inline]
167-
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
168-
if layout.size() != 0 {
169-
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
166+
unsafe fn grow(
167+
&mut self,
168+
ptr: NonNull<u8>,
169+
layout: Layout,
170+
new_size: usize,
171+
placement: ReallocPlacement,
172+
init: AllocInit,
173+
) -> Result<MemoryBlock, AllocErr> {
174+
let size = layout.size();
175+
debug_assert!(
176+
new_size >= size,
177+
"`new_size` must be greater than or equal to `memory.size()`"
178+
);
179+
180+
if size == new_size {
181+
return Ok(MemoryBlock { ptr, size });
182+
}
183+
184+
match placement {
185+
ReallocPlacement::InPlace => Err(AllocErr),
186+
ReallocPlacement::MayMove if layout.size() == 0 => {
187+
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
188+
self.alloc(new_layout, init)
189+
}
190+
ReallocPlacement::MayMove => {
191+
// `realloc` probably checks for `new_size > size` or something similar.
192+
intrinsics::assume(new_size > size);
193+
let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
194+
let memory =
195+
MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size };
196+
init.init_offset(memory, size);
197+
Ok(memory)
198+
}
170199
}
171200
}
172201

173202
#[inline]
174-
unsafe fn realloc(
203+
unsafe fn shrink(
175204
&mut self,
176205
ptr: NonNull<u8>,
177206
layout: Layout,
178207
new_size: usize,
179-
) -> Result<(NonNull<u8>, usize), AllocErr> {
180-
match (layout.size(), new_size) {
181-
(0, 0) => Ok((layout.dangling(), 0)),
182-
(0, _) => self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())),
183-
(_, 0) => {
208+
placement: ReallocPlacement,
209+
) -> Result<MemoryBlock, AllocErr> {
210+
let size = layout.size();
211+
debug_assert!(
212+
new_size <= size,
213+
"`new_size` must be smaller than or equal to `memory.size()`"
214+
);
215+
216+
if size == new_size {
217+
return Ok(MemoryBlock { ptr, size });
218+
}
219+
220+
match placement {
221+
ReallocPlacement::InPlace => Err(AllocErr),
222+
ReallocPlacement::MayMove if new_size == 0 => {
184223
self.dealloc(ptr, layout);
185-
Ok((layout.dangling(), 0))
224+
Ok(MemoryBlock { ptr: layout.dangling(), size: 0 })
225+
}
226+
ReallocPlacement::MayMove => {
227+
// `realloc` probably checks for `new_size < size` or something similar.
228+
intrinsics::assume(new_size < size);
229+
let ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size);
230+
Ok(MemoryBlock { ptr: NonNull::new(ptr).ok_or(AllocErr)?, size: new_size })
186231
}
187-
(_, _) => NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size))
188-
.ok_or(AllocErr)
189-
.map(|p| (p, new_size)),
190232
}
191233
}
192234
}
193-
194235
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
195236

196237
/// Registers a custom allocation error hook, replacing any that was previously registered.
@@ -238,9 +279,7 @@ pub fn rust_oom(layout: Layout) -> ! {
238279
let hook: fn(Layout) =
239280
if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } };
240281
hook(layout);
241-
unsafe {
242-
crate::sys::abort_internal();
243-
}
282+
unsafe { crate::sys::abort_internal() }
244283
}
245284

246285
#[cfg(not(test))]

‎src/libstd/error.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
use core::array;
1717

18-
use crate::alloc::{AllocErr, CannotReallocInPlace, LayoutErr};
18+
use crate::alloc::{AllocErr, LayoutErr};
1919
use crate::any::TypeId;
2020
use crate::backtrace::Backtrace;
2121
use crate::borrow::Cow;
@@ -409,13 +409,6 @@ impl Error for AllocErr {}
409409
)]
410410
impl Error for LayoutErr {}
411411

412-
#[unstable(
413-
feature = "allocator_api",
414-
reason = "the precise API and guarantees it provides may be tweaked.",
415-
issue = "32838"
416-
)]
417-
impl Error for CannotReallocInPlace {}
418-
419412
#[stable(feature = "rust1", since = "1.0.0")]
420413
impl Error for str::ParseBoolError {
421414
#[allow(deprecated)]

‎src/test/ui/allocator/custom.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
extern crate helper;
99

10-
use std::alloc::{self, Global, AllocRef, System, Layout};
10+
use std::alloc::{self, AllocInit, AllocRef, Global, Layout, System};
1111
use std::sync::atomic::{AtomicUsize, Ordering};
1212

1313
static HITS: AtomicUsize = AtomicUsize::new(0);
@@ -37,10 +37,10 @@ fn main() {
3737
unsafe {
3838
let layout = Layout::from_size_align(4, 2).unwrap();
3939

40-
let (ptr, _) = Global.alloc(layout.clone()).unwrap();
41-
helper::work_with(&ptr);
40+
let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
41+
helper::work_with(&memory.ptr);
4242
assert_eq!(HITS.load(Ordering::SeqCst), n + 1);
43-
Global.dealloc(ptr, layout.clone());
43+
Global.dealloc(memory.ptr, layout);
4444
assert_eq!(HITS.load(Ordering::SeqCst), n + 2);
4545

4646
let s = String::with_capacity(10);
@@ -49,10 +49,10 @@ fn main() {
4949
drop(s);
5050
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
5151

52-
let (ptr, _) = System.alloc(layout.clone()).unwrap();
52+
let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
5353
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
54-
helper::work_with(&ptr);
55-
System.dealloc(ptr, layout);
54+
helper::work_with(&memory.ptr);
55+
System.dealloc(memory.ptr, layout);
5656
assert_eq!(HITS.load(Ordering::SeqCst), n + 4);
5757
}
5858
}

‎src/test/ui/allocator/xcrate-use.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
extern crate custom;
1010
extern crate helper;
1111

12-
use std::alloc::{Global, AllocRef, System, Layout};
13-
use std::sync::atomic::{Ordering, AtomicUsize};
12+
use std::alloc::{AllocInit, AllocRef, Global, Layout, System};
13+
use std::sync::atomic::{AtomicUsize, Ordering};
1414

1515
#[global_allocator]
1616
static GLOBAL: custom::A = custom::A(AtomicUsize::new(0));
@@ -20,16 +20,16 @@ fn main() {
2020
let n = GLOBAL.0.load(Ordering::SeqCst);
2121
let layout = Layout::from_size_align(4, 2).unwrap();
2222

23-
let (ptr, _) = Global.alloc(layout.clone()).unwrap();
24-
helper::work_with(&ptr);
23+
let memory = Global.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
24+
helper::work_with(&memory.ptr);
2525
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1);
26-
Global.dealloc(ptr, layout.clone());
26+
Global.dealloc(memory.ptr, layout);
2727
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
2828

29-
let (ptr, _) = System.alloc(layout.clone()).unwrap();
29+
let memory = System.alloc(layout.clone(), AllocInit::Uninitialized).unwrap();
3030
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
31-
helper::work_with(&ptr);
32-
System.dealloc(ptr, layout);
31+
helper::work_with(&memory.ptr);
32+
System.dealloc(memory.ptr, layout);
3333
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
3434
}
3535
}

‎src/test/ui/realloc-16687.rs

+51-37
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#![feature(allocator_api)]
88

9-
use std::alloc::{Global, AllocRef, Layout, handle_alloc_error};
9+
use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout, ReallocPlacement};
1010
use std::ptr::{self, NonNull};
1111

1212
fn main() {
@@ -16,38 +16,40 @@ fn main() {
1616
}
1717

1818
unsafe fn test_triangle() -> bool {
19-
static COUNT : usize = 16;
19+
static COUNT: usize = 16;
2020
let mut ascend = vec![ptr::null_mut(); COUNT];
2121
let ascend = &mut *ascend;
22-
static ALIGN : usize = 1;
22+
static ALIGN: usize = 1;
2323

2424
// Checks that `ascend` forms triangle of ascending size formed
2525
// from pairs of rows (where each pair of rows is equally sized),
2626
// and the elements of the triangle match their row-pair index.
2727
unsafe fn sanity_check(ascend: &[*mut u8]) {
2828
for i in 0..COUNT / 2 {
29-
let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
29+
let (p0, p1, size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
3030
for j in 0..size {
3131
assert_eq!(*p0.add(j), i as u8);
3232
assert_eq!(*p1.add(j), i as u8);
3333
}
3434
}
3535
}
3636

37-
static PRINT : bool = false;
37+
static PRINT: bool = false;
3838

3939
unsafe fn allocate(layout: Layout) -> *mut u8 {
4040
if PRINT {
4141
println!("allocate({:?})", layout);
4242
}
4343

44-
let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
44+
let memory = Global
45+
.alloc(layout, AllocInit::Uninitialized)
46+
.unwrap_or_else(|_| handle_alloc_error(layout));
4547

4648
if PRINT {
47-
println!("allocate({:?}) = {:?}", layout, ptr);
49+
println!("allocate({:?}) = {:?}", layout, memory.ptr);
4850
}
4951

50-
ptr.cast().as_ptr()
52+
memory.ptr.cast().as_ptr()
5153
}
5254

5355
unsafe fn deallocate(ptr: *mut u8, layout: Layout) {
@@ -63,33 +65,45 @@ unsafe fn test_triangle() -> bool {
6365
println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new);
6466
}
6567

66-
let (ptr, _) = Global.realloc(NonNull::new_unchecked(ptr), old, new.size())
67-
.unwrap_or_else(|_| handle_alloc_error(
68-
Layout::from_size_align_unchecked(new.size(), old.align())
69-
));
68+
let memory = if new.size() > old.size() {
69+
Global.grow(
70+
NonNull::new_unchecked(ptr),
71+
old,
72+
new.size(),
73+
ReallocPlacement::MayMove,
74+
AllocInit::Uninitialized,
75+
)
76+
} else {
77+
Global.shrink(NonNull::new_unchecked(ptr), old, new.size(), ReallocPlacement::MayMove)
78+
};
79+
80+
let memory = memory.unwrap_or_else(|_| {
81+
handle_alloc_error(Layout::from_size_align_unchecked(new.size(), old.align()))
82+
});
7083

7184
if PRINT {
72-
println!("reallocate({:?}, old={:?}, new={:?}) = {:?}",
73-
ptr, old, new, ptr);
85+
println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, memory.ptr);
7486
}
75-
ptr.cast().as_ptr()
87+
memory.ptr.cast().as_ptr()
7688
}
7789

78-
fn idx_to_size(i: usize) -> usize { (i+1) * 10 }
90+
fn idx_to_size(i: usize) -> usize {
91+
(i + 1) * 10
92+
}
7993

8094
// Allocate pairs of rows that form a triangle shape. (Hope is
8195
// that at least two rows will be allocated near each other, so
8296
// that we trigger the bug (a buffer overrun) in an observable
8397
// way.)
8498
for i in 0..COUNT / 2 {
8599
let size = idx_to_size(i);
86-
ascend[2*i] = allocate(Layout::from_size_align(size, ALIGN).unwrap());
87-
ascend[2*i+1] = allocate(Layout::from_size_align(size, ALIGN).unwrap());
100+
ascend[2 * i] = allocate(Layout::from_size_align(size, ALIGN).unwrap());
101+
ascend[2 * i + 1] = allocate(Layout::from_size_align(size, ALIGN).unwrap());
88102
}
89103

90104
// Initialize each pair of rows to distinct value.
91105
for i in 0..COUNT / 2 {
92-
let (p0, p1, size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
106+
let (p0, p1, size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
93107
for j in 0..size {
94108
*p0.add(j) = i as u8;
95109
*p1.add(j) = i as u8;
@@ -104,8 +118,8 @@ unsafe fn test_triangle() -> bool {
104118

105119
for i in 0..COUNT / 2 {
106120
let size = idx_to_size(i);
107-
deallocate(ascend[2*i], Layout::from_size_align(size, ALIGN).unwrap());
108-
deallocate(ascend[2*i+1], Layout::from_size_align(size, ALIGN).unwrap());
121+
deallocate(ascend[2 * i], Layout::from_size_align(size, ALIGN).unwrap());
122+
deallocate(ascend[2 * i + 1], Layout::from_size_align(size, ALIGN).unwrap());
109123
}
110124

111125
return true;
@@ -115,68 +129,68 @@ unsafe fn test_triangle() -> bool {
115129
// realloc'ing each row from top to bottom, and checking all the
116130
// rows as we go.
117131
unsafe fn test_1(ascend: &mut [*mut u8]) {
118-
let new_size = idx_to_size(COUNT-1);
132+
let new_size = idx_to_size(COUNT - 1);
119133
let new = Layout::from_size_align(new_size, ALIGN).unwrap();
120134
for i in 0..COUNT / 2 {
121-
let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
135+
let (p0, p1, old_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
122136
assert!(old_size < new_size);
123137
let old = Layout::from_size_align(old_size, ALIGN).unwrap();
124138

125-
ascend[2*i] = reallocate(p0, old.clone(), new.clone());
139+
ascend[2 * i] = reallocate(p0, old.clone(), new.clone());
126140
sanity_check(&*ascend);
127141

128-
ascend[2*i+1] = reallocate(p1, old.clone(), new.clone());
142+
ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone());
129143
sanity_check(&*ascend);
130144
}
131145
}
132146

133147
// Test 2: turn the square back into a triangle, top to bottom.
134148
unsafe fn test_2(ascend: &mut [*mut u8]) {
135-
let old_size = idx_to_size(COUNT-1);
149+
let old_size = idx_to_size(COUNT - 1);
136150
let old = Layout::from_size_align(old_size, ALIGN).unwrap();
137151
for i in 0..COUNT / 2 {
138-
let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
152+
let (p0, p1, new_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
139153
assert!(new_size < old_size);
140154
let new = Layout::from_size_align(new_size, ALIGN).unwrap();
141155

142-
ascend[2*i] = reallocate(p0, old.clone(), new.clone());
156+
ascend[2 * i] = reallocate(p0, old.clone(), new.clone());
143157
sanity_check(&*ascend);
144158

145-
ascend[2*i+1] = reallocate(p1, old.clone(), new.clone());
159+
ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone());
146160
sanity_check(&*ascend);
147161
}
148162
}
149163

150164
// Test 3: turn triangle into a square, bottom to top.
151165
unsafe fn test_3(ascend: &mut [*mut u8]) {
152-
let new_size = idx_to_size(COUNT-1);
166+
let new_size = idx_to_size(COUNT - 1);
153167
let new = Layout::from_size_align(new_size, ALIGN).unwrap();
154168
for i in (0..COUNT / 2).rev() {
155-
let (p0, p1, old_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
169+
let (p0, p1, old_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
156170
assert!(old_size < new_size);
157171
let old = Layout::from_size_align(old_size, ALIGN).unwrap();
158172

159-
ascend[2*i+1] = reallocate(p1, old.clone(), new.clone());
173+
ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone());
160174
sanity_check(&*ascend);
161175

162-
ascend[2*i] = reallocate(p0, old.clone(), new.clone());
176+
ascend[2 * i] = reallocate(p0, old.clone(), new.clone());
163177
sanity_check(&*ascend);
164178
}
165179
}
166180

167181
// Test 4: turn the square back into a triangle, bottom to top.
168182
unsafe fn test_4(ascend: &mut [*mut u8]) {
169-
let old_size = idx_to_size(COUNT-1);
183+
let old_size = idx_to_size(COUNT - 1);
170184
let old = Layout::from_size_align(old_size, ALIGN).unwrap();
171185
for i in (0..COUNT / 2).rev() {
172-
let (p0, p1, new_size) = (ascend[2*i], ascend[2*i+1], idx_to_size(i));
186+
let (p0, p1, new_size) = (ascend[2 * i], ascend[2 * i + 1], idx_to_size(i));
173187
assert!(new_size < old_size);
174188
let new = Layout::from_size_align(new_size, ALIGN).unwrap();
175189

176-
ascend[2*i+1] = reallocate(p1, old.clone(), new.clone());
190+
ascend[2 * i + 1] = reallocate(p1, old.clone(), new.clone());
177191
sanity_check(&*ascend);
178192

179-
ascend[2*i] = reallocate(p0, old.clone(), new.clone());
193+
ascend[2 * i] = reallocate(p0, old.clone(), new.clone());
180194
sanity_check(&*ascend);
181195
}
182196
}

‎src/test/ui/regions/regions-mock-codegen.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
// run-pass
22
#![allow(dead_code)]
33
#![allow(non_camel_case_types)]
4-
54
// pretty-expanded FIXME #23616
6-
75
#![feature(allocator_api)]
86

9-
use std::alloc::{AllocRef, Global, Layout, handle_alloc_error};
7+
use std::alloc::{handle_alloc_error, AllocInit, AllocRef, Global, Layout};
108
use std::ptr::NonNull;
119

1210
struct arena(());
1311

1412
struct Bcx<'a> {
15-
fcx: &'a Fcx<'a>
13+
fcx: &'a Fcx<'a>,
1614
}
1715

1816
struct Fcx<'a> {
1917
arena: &'a arena,
20-
ccx: &'a Ccx
18+
ccx: &'a Ccx,
2119
}
2220

2321
struct Ccx {
24-
x: isize
22+
x: isize,
2523
}
2624

2725
fn alloc(_bcx: &arena) -> &Bcx<'_> {
2826
unsafe {
2927
let layout = Layout::new::<Bcx>();
30-
let (ptr, _) = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout));
31-
&*(ptr.as_ptr() as *const _)
28+
let memory = Global
29+
.alloc(layout, AllocInit::Uninitialized)
30+
.unwrap_or_else(|_| handle_alloc_error(layout));
31+
&*(memory.ptr.as_ptr() as *const _)
3232
}
3333
}
3434

0 commit comments

Comments
 (0)
Please sign in to comment.