Skip to content

Commit d620ae1

Browse files
committed
Auto merge of rust-lang#84266 - QuiltOS:statically-disallow-global-oom-handling, r=Mark-Simulacrum
alloc: Add unstable Cfg feature `no-global_oom_handling For certain sorts of systems, programming, it's deemed essential that all allocation failures be explicitly handled where they occur. For example, see Linus Torvald's opinion in [1]. Merely not calling global panic handlers, or always `try_reserving` first (for vectors), is not deemed good enough, because the mere presence of the global OOM handlers is burdens static analysis. One option for these projects to use rust would just be to skip `alloc`, rolling their own allocation abstractions. But this would, in my opinion be a real shame. `alloc` has a few `try_*` methods already, and we could easily have more. Features like custom allocator support also demonstrate and existing to support diverse use-cases with the same abstractions. A natural way to add such a feature flag would a Cargo feature, but there are currently uncertainties around how std library crate's Cargo features may or not be stable, so to avoid any risk of stabilizing by mistake we are going with a more low-level "raw cfg" token, which cannot be interacted with via Cargo alone. Note also that since there is no notion of "default cfg tokens" outside of Cargo features, we have to invert the condition from `global_oom_handling` to to `not(no_global_oom_handling)`. This breaks the monotonicity that would be important for a Cargo feature (i.e. turning on more features should never break compatibility), but it doesn't matter for raw cfg tokens which are not intended to be "constraint solved" by Cargo or anything else. To support this use-case we create a new feature, "global-oom-handling", on by default, and put the global OOM handler infra and everything else it that depends on it behind it. By default, nothing is changed, but users concerned about global handling can make sure it is disabled, and be confident that all OOM handling is local and explicit. For this first iteration, non-flat collections are outright disabled. `Vec` and `String` don't yet have `try_*` allocation methods, but are kept anyways since they can be oom-safely created "from parts", and we hope to add those `try_` methods in the future. [1]: https://lore.kernel.org/lkml/CAHk-=wh_sNLoz84AUUzuqXEsYH35u=8HV3vK-jbRbJ_B-JjGrg@mail.gmail.com/
2 parents bacf770 + 19be438 commit d620ae1

File tree

18 files changed

+326
-57
lines changed

18 files changed

+326
-57
lines changed

library/alloc/src/alloc.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ unsafe impl Allocator for Global {
308308

309309
/// The allocator for unique pointers.
310310
// This function must not unwind. If it does, MIR codegen will fail.
311-
#[cfg(not(test))]
311+
#[cfg(all(not(no_global_oom_handling), not(test)))]
312312
#[lang = "exchange_malloc"]
313313
#[inline]
314314
unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
@@ -337,6 +337,7 @@ pub(crate) unsafe fn box_free<T: ?Sized, A: Allocator>(ptr: Unique<T>, alloc: A)
337337

338338
// # Allocation error handler
339339

340+
#[cfg(not(no_global_oom_handling))]
340341
extern "Rust" {
341342
// This is the magic symbol to call the global alloc error handler. rustc generates
342343
// it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
@@ -358,7 +359,7 @@ extern "Rust" {
358359
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
359360
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
360361
#[stable(feature = "global_alloc", since = "1.28.0")]
361-
#[cfg(not(test))]
362+
#[cfg(all(not(no_global_oom_handling), not(test)))]
362363
#[rustc_allocator_nounwind]
363364
#[cold]
364365
pub fn handle_alloc_error(layout: Layout) -> ! {
@@ -368,10 +369,10 @@ pub fn handle_alloc_error(layout: Layout) -> ! {
368369
}
369370

370371
// For alloc test `std::alloc::handle_alloc_error` can be used directly.
371-
#[cfg(test)]
372+
#[cfg(all(not(no_global_oom_handling), test))]
372373
pub use std::alloc::handle_alloc_error;
373374

374-
#[cfg(not(any(target_os = "hermit", test)))]
375+
#[cfg(all(not(no_global_oom_handling), not(any(target_os = "hermit", test))))]
375376
#[doc(hidden)]
376377
#[allow(unused_attributes)]
377378
#[unstable(feature = "alloc_internals", issue = "none")]

library/alloc/src/borrow.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44

55
use core::cmp::Ordering;
66
use core::hash::{Hash, Hasher};
7-
use core::ops::{Add, AddAssign, Deref};
7+
use core::ops::Deref;
8+
#[cfg(not(no_global_oom_handling))]
9+
use core::ops::{Add, AddAssign};
810

911
#[stable(feature = "rust1", since = "1.0.0")]
1012
pub use core::borrow::{Borrow, BorrowMut};
1113

1214
use crate::fmt;
15+
#[cfg(not(no_global_oom_handling))]
1316
use crate::string::String;
1417

1518
use Cow::*;
@@ -429,6 +432,7 @@ impl<T: ?Sized + ToOwned> AsRef<T> for Cow<'_, T> {
429432
}
430433
}
431434

435+
#[cfg(not(no_global_oom_handling))]
432436
#[stable(feature = "cow_add", since = "1.14.0")]
433437
impl<'a> Add<&'a str> for Cow<'a, str> {
434438
type Output = Cow<'a, str>;
@@ -440,6 +444,7 @@ impl<'a> Add<&'a str> for Cow<'a, str> {
440444
}
441445
}
442446

447+
#[cfg(not(no_global_oom_handling))]
443448
#[stable(feature = "cow_add", since = "1.14.0")]
444449
impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
445450
type Output = Cow<'a, str>;
@@ -451,6 +456,7 @@ impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
451456
}
452457
}
453458

459+
#[cfg(not(no_global_oom_handling))]
454460
#[stable(feature = "cow_add", since = "1.14.0")]
455461
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
456462
fn add_assign(&mut self, rhs: &'a str) {
@@ -467,6 +473,7 @@ impl<'a> AddAssign<&'a str> for Cow<'a, str> {
467473
}
468474
}
469475

476+
#[cfg(not(no_global_oom_handling))]
470477
#[stable(feature = "cow_add", since = "1.14.0")]
471478
impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> {
472479
fn add_assign(&mut self, rhs: Cow<'a, str>) {

library/alloc/src/boxed.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,9 @@ use core::convert::{From, TryFrom};
139139
use core::fmt;
140140
use core::future::Future;
141141
use core::hash::{Hash, Hasher};
142-
use core::iter::{FromIterator, FusedIterator, Iterator};
142+
#[cfg(not(no_global_oom_handling))]
143+
use core::iter::FromIterator;
144+
use core::iter::{FusedIterator, Iterator};
143145
use core::marker::{Unpin, Unsize};
144146
use core::mem;
145147
use core::ops::{
@@ -150,10 +152,16 @@ use core::ptr::{self, Unique};
150152
use core::stream::Stream;
151153
use core::task::{Context, Poll};
152154

153-
use crate::alloc::{handle_alloc_error, AllocError, Allocator, Global, Layout, WriteCloneIntoRaw};
155+
#[cfg(not(no_global_oom_handling))]
156+
use crate::alloc::{handle_alloc_error, WriteCloneIntoRaw};
157+
use crate::alloc::{AllocError, Allocator, Global, Layout};
158+
#[cfg(not(no_global_oom_handling))]
154159
use crate::borrow::Cow;
160+
#[cfg(not(no_global_oom_handling))]
155161
use crate::raw_vec::RawVec;
162+
#[cfg(not(no_global_oom_handling))]
156163
use crate::str::from_boxed_utf8_unchecked;
164+
#[cfg(not(no_global_oom_handling))]
157165
use crate::vec::Vec;
158166

159167
/// A pointer type for heap allocation.
@@ -177,6 +185,7 @@ impl<T> Box<T> {
177185
/// ```
178186
/// let five = Box::new(5);
179187
/// ```
188+
#[cfg(not(no_global_oom_handling))]
180189
#[inline(always)]
181190
#[doc(alias = "alloc")]
182191
#[doc(alias = "malloc")]
@@ -203,6 +212,7 @@ impl<T> Box<T> {
203212
///
204213
/// assert_eq!(*five, 5)
205214
/// ```
215+
#[cfg(not(no_global_oom_handling))]
206216
#[unstable(feature = "new_uninit", issue = "63291")]
207217
#[inline]
208218
pub fn new_uninit() -> Box<mem::MaybeUninit<T>> {
@@ -227,6 +237,7 @@ impl<T> Box<T> {
227237
/// ```
228238
///
229239
/// [zeroed]: mem::MaybeUninit::zeroed
240+
#[cfg(not(no_global_oom_handling))]
230241
#[inline]
231242
#[doc(alias = "calloc")]
232243
#[unstable(feature = "new_uninit", issue = "63291")]
@@ -236,6 +247,7 @@ impl<T> Box<T> {
236247

237248
/// Constructs a new `Pin<Box<T>>`. If `T` does not implement `Unpin`, then
238249
/// `x` will be pinned in memory and unable to be moved.
250+
#[cfg(not(no_global_oom_handling))]
239251
#[stable(feature = "pin", since = "1.33.0")]
240252
#[inline(always)]
241253
pub fn pin(x: T) -> Pin<Box<T>> {
@@ -329,6 +341,7 @@ impl<T, A: Allocator> Box<T, A> {
329341
///
330342
/// let five = Box::new_in(5, System);
331343
/// ```
344+
#[cfg(not(no_global_oom_handling))]
332345
#[unstable(feature = "allocator_api", issue = "32838")]
333346
#[inline]
334347
pub fn new_in(x: T, alloc: A) -> Self {
@@ -385,6 +398,7 @@ impl<T, A: Allocator> Box<T, A> {
385398
/// assert_eq!(*five, 5)
386399
/// ```
387400
#[unstable(feature = "allocator_api", issue = "32838")]
401+
#[cfg(not(no_global_oom_handling))]
388402
// #[unstable(feature = "new_uninit", issue = "63291")]
389403
pub fn new_uninit_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
390404
let layout = Layout::new::<mem::MaybeUninit<T>>();
@@ -447,6 +461,7 @@ impl<T, A: Allocator> Box<T, A> {
447461
///
448462
/// [zeroed]: mem::MaybeUninit::zeroed
449463
#[unstable(feature = "allocator_api", issue = "32838")]
464+
#[cfg(not(no_global_oom_handling))]
450465
// #[unstable(feature = "new_uninit", issue = "63291")]
451466
pub fn new_zeroed_in(alloc: A) -> Box<mem::MaybeUninit<T>, A> {
452467
let layout = Layout::new::<mem::MaybeUninit<T>>();
@@ -490,6 +505,7 @@ impl<T, A: Allocator> Box<T, A> {
490505

491506
/// Constructs a new `Pin<Box<T, A>>`. If `T` does not implement `Unpin`, then
492507
/// `x` will be pinned in memory and unable to be moved.
508+
#[cfg(not(no_global_oom_handling))]
493509
#[unstable(feature = "allocator_api", issue = "32838")]
494510
#[inline(always)]
495511
pub fn pin_in(x: T, alloc: A) -> Pin<Self>
@@ -547,6 +563,7 @@ impl<T> Box<[T]> {
547563
///
548564
/// assert_eq!(*values, [1, 2, 3])
549565
/// ```
566+
#[cfg(not(no_global_oom_handling))]
550567
#[unstable(feature = "new_uninit", issue = "63291")]
551568
pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {
552569
unsafe { RawVec::with_capacity(len).into_box(len) }
@@ -570,6 +587,7 @@ impl<T> Box<[T]> {
570587
/// ```
571588
///
572589
/// [zeroed]: mem::MaybeUninit::zeroed
590+
#[cfg(not(no_global_oom_handling))]
573591
#[unstable(feature = "new_uninit", issue = "63291")]
574592
pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit<T>]> {
575593
unsafe { RawVec::with_capacity_zeroed(len).into_box(len) }
@@ -599,6 +617,7 @@ impl<T, A: Allocator> Box<[T], A> {
599617
///
600618
/// assert_eq!(*values, [1, 2, 3])
601619
/// ```
620+
#[cfg(not(no_global_oom_handling))]
602621
#[unstable(feature = "allocator_api", issue = "32838")]
603622
// #[unstable(feature = "new_uninit", issue = "63291")]
604623
pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit<T>], A> {
@@ -625,6 +644,7 @@ impl<T, A: Allocator> Box<[T], A> {
625644
/// ```
626645
///
627646
/// [zeroed]: mem::MaybeUninit::zeroed
647+
#[cfg(not(no_global_oom_handling))]
628648
#[unstable(feature = "allocator_api", issue = "32838")]
629649
// #[unstable(feature = "new_uninit", issue = "63291")]
630650
pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit<T>], A> {
@@ -1013,20 +1033,23 @@ impl<T: Default> Default for Box<T> {
10131033
}
10141034
}
10151035

1036+
#[cfg(not(no_global_oom_handling))]
10161037
#[stable(feature = "rust1", since = "1.0.0")]
10171038
impl<T> Default for Box<[T]> {
10181039
fn default() -> Self {
10191040
Box::<[T; 0]>::new([])
10201041
}
10211042
}
10221043

1044+
#[cfg(not(no_global_oom_handling))]
10231045
#[stable(feature = "default_box_extra", since = "1.17.0")]
10241046
impl Default for Box<str> {
10251047
fn default() -> Self {
10261048
unsafe { from_boxed_utf8_unchecked(Default::default()) }
10271049
}
10281050
}
10291051

1052+
#[cfg(not(no_global_oom_handling))]
10301053
#[stable(feature = "rust1", since = "1.0.0")]
10311054
impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> {
10321055
/// Returns a new box with a `clone()` of this box's contents.
@@ -1076,6 +1099,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for Box<T, A> {
10761099
}
10771100
}
10781101

1102+
#[cfg(not(no_global_oom_handling))]
10791103
#[stable(feature = "box_slice_clone", since = "1.3.0")]
10801104
impl Clone for Box<str> {
10811105
fn clone(&self) -> Self {
@@ -1182,6 +1206,7 @@ impl<T: ?Sized + Hasher, A: Allocator> Hasher for Box<T, A> {
11821206
}
11831207
}
11841208

1209+
#[cfg(not(no_global_oom_handling))]
11851210
#[stable(feature = "from_for_ptrs", since = "1.6.0")]
11861211
impl<T> From<T> for Box<T> {
11871212
/// Converts a generic type `T` into a `Box<T>`
@@ -1214,6 +1239,7 @@ where
12141239
}
12151240
}
12161241

1242+
#[cfg(not(no_global_oom_handling))]
12171243
#[stable(feature = "box_from_slice", since = "1.17.0")]
12181244
impl<T: Copy> From<&[T]> for Box<[T]> {
12191245
/// Converts a `&[T]` into a `Box<[T]>`
@@ -1239,6 +1265,7 @@ impl<T: Copy> From<&[T]> for Box<[T]> {
12391265
}
12401266
}
12411267

1268+
#[cfg(not(no_global_oom_handling))]
12421269
#[stable(feature = "box_from_cow", since = "1.45.0")]
12431270
impl<T: Copy> From<Cow<'_, [T]>> for Box<[T]> {
12441271
#[inline]
@@ -1250,6 +1277,7 @@ impl<T: Copy> From<Cow<'_, [T]>> for Box<[T]> {
12501277
}
12511278
}
12521279

1280+
#[cfg(not(no_global_oom_handling))]
12531281
#[stable(feature = "box_from_slice", since = "1.17.0")]
12541282
impl From<&str> for Box<str> {
12551283
/// Converts a `&str` into a `Box<str>`
@@ -1268,6 +1296,7 @@ impl From<&str> for Box<str> {
12681296
}
12691297
}
12701298

1299+
#[cfg(not(no_global_oom_handling))]
12711300
#[stable(feature = "box_from_cow", since = "1.45.0")]
12721301
impl From<Cow<'_, str>> for Box<str> {
12731302
#[inline]
@@ -1567,13 +1596,15 @@ impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> fo
15671596
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
15681597
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T, Global> {}
15691598

1599+
#[cfg(not(no_global_oom_handling))]
15701600
#[stable(feature = "boxed_slice_from_iter", since = "1.32.0")]
15711601
impl<I> FromIterator<I> for Box<[I]> {
15721602
fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
15731603
iter.into_iter().collect::<Vec<_>>().into_boxed_slice()
15741604
}
15751605
}
15761606

1607+
#[cfg(not(no_global_oom_handling))]
15771608
#[stable(feature = "box_slice_clone", since = "1.3.0")]
15781609
impl<T: Clone, A: Allocator + Clone> Clone for Box<[T], A> {
15791610
fn clone(&self) -> Self {

library/alloc/src/collections/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,52 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

5+
#[cfg(not(no_global_oom_handling))]
56
pub mod binary_heap;
7+
#[cfg(not(no_global_oom_handling))]
68
mod btree;
9+
#[cfg(not(no_global_oom_handling))]
710
pub mod linked_list;
11+
#[cfg(not(no_global_oom_handling))]
812
pub mod vec_deque;
913

14+
#[cfg(not(no_global_oom_handling))]
1015
#[stable(feature = "rust1", since = "1.0.0")]
1116
pub mod btree_map {
1217
//! A map based on a B-Tree.
1318
#[stable(feature = "rust1", since = "1.0.0")]
1419
pub use super::btree::map::*;
1520
}
1621

22+
#[cfg(not(no_global_oom_handling))]
1723
#[stable(feature = "rust1", since = "1.0.0")]
1824
pub mod btree_set {
1925
//! A set based on a B-Tree.
2026
#[stable(feature = "rust1", since = "1.0.0")]
2127
pub use super::btree::set::*;
2228
}
2329

30+
#[cfg(not(no_global_oom_handling))]
2431
#[stable(feature = "rust1", since = "1.0.0")]
2532
#[doc(no_inline)]
2633
pub use binary_heap::BinaryHeap;
2734

35+
#[cfg(not(no_global_oom_handling))]
2836
#[stable(feature = "rust1", since = "1.0.0")]
2937
#[doc(no_inline)]
3038
pub use btree_map::BTreeMap;
3139

40+
#[cfg(not(no_global_oom_handling))]
3241
#[stable(feature = "rust1", since = "1.0.0")]
3342
#[doc(no_inline)]
3443
pub use btree_set::BTreeSet;
3544

45+
#[cfg(not(no_global_oom_handling))]
3646
#[stable(feature = "rust1", since = "1.0.0")]
3747
#[doc(no_inline)]
3848
pub use linked_list::LinkedList;
3949

50+
#[cfg(not(no_global_oom_handling))]
4051
#[stable(feature = "rust1", since = "1.0.0")]
4152
#[doc(no_inline)]
4253
pub use vec_deque::VecDeque;

library/alloc/src/fmt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ pub use core::fmt::{LowerExp, UpperExp};
546546
#[stable(feature = "rust1", since = "1.0.0")]
547547
pub use core::fmt::{LowerHex, Pointer, UpperHex};
548548

549+
#[cfg(not(no_global_oom_handling))]
549550
use crate::string;
550551

551552
/// The `format` function takes an [`Arguments`] struct and returns the resulting
@@ -574,6 +575,7 @@ use crate::string;
574575
///
575576
/// [`format_args!`]: core::format_args
576577
/// [`format!`]: crate::format
578+
#[cfg(not(no_global_oom_handling))]
577579
#[stable(feature = "rust1", since = "1.0.0")]
578580
pub fn format(args: Arguments<'_>) -> string::String {
579581
let capacity = args.estimated_capacity();

library/alloc/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
#![feature(cfg_sanitize)]
8888
#![feature(cfg_target_has_atomic)]
8989
#![feature(coerce_unsized)]
90-
#![feature(const_btree_new)]
90+
#![cfg_attr(not(no_global_oom_handling), feature(const_btree_new))]
9191
#![cfg_attr(bootstrap, feature(const_fn))]
9292
#![cfg_attr(not(bootstrap), feature(const_fn_trait_bound))]
9393
#![feature(cow_is_borrowed)]
@@ -183,7 +183,7 @@ pub mod str;
183183
pub mod string;
184184
#[cfg(target_has_atomic = "ptr")]
185185
pub mod sync;
186-
#[cfg(target_has_atomic = "ptr")]
186+
#[cfg(all(not(no_global_oom_handling), target_has_atomic = "ptr"))]
187187
pub mod task;
188188
#[cfg(test)]
189189
mod tests;

0 commit comments

Comments
 (0)