Skip to content

Commit 996ff2e

Browse files
committed
Auto merge of #87408 - kornelski:try_reserve_error, r=yaahc
Hide allocator details from TryReserveError I think there's [no need for TryReserveError to carry detailed information](#48043 (comment)), but I wouldn't want that issue to delay stabilization of the `try_reserve` feature. So I'm proposing to stabilize `try_reserve` with a `TryReserveError` as an opaque structure, and if needed, expose error details later. This PR moves the `enum` to an unstable inner `TryReserveErrorKind` that lives under a separate feature flag. `TryReserveErrorKind` could possibly be left as an implementation detail forever, and the `TryReserveError` get methods such as `allocation_size() -> Option<usize>` or `layout() -> Option<Layout>` instead, or the details could be dropped completely to make try-reserve errors just a unit struct, and thus smaller and cheaper.
2 parents db3cb43 + a294aa8 commit 996ff2e

File tree

13 files changed

+269
-109
lines changed

13 files changed

+269
-109
lines changed

compiler/rustc_middle/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#![feature(iter_zip)]
5252
#![feature(thread_local_const_init)]
5353
#![feature(try_reserve)]
54+
#![feature(try_reserve_kind)]
5455
#![feature(nonzero_ops)]
5556
#![recursion_limit = "512"]
5657

compiler/rustc_mir/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Rust MIR: a lowered representation of Rust.
3030
#![feature(once_cell)]
3131
#![feature(control_flow_enum)]
3232
#![feature(try_reserve)]
33+
#![feature(try_reserve_kind)]
3334
#![recursion_limit = "256"]
3435

3536
#[macro_use]

library/alloc/src/collections/mod.rs

+43-6
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,31 @@ use core::fmt::Display;
5858
/// The error type for `try_reserve` methods.
5959
#[derive(Clone, PartialEq, Eq, Debug)]
6060
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
61-
pub enum TryReserveError {
61+
pub struct TryReserveError {
62+
kind: TryReserveErrorKind,
63+
}
64+
65+
impl TryReserveError {
66+
/// Details about the allocation that caused the error
67+
#[inline]
68+
#[unstable(
69+
feature = "try_reserve_kind",
70+
reason = "Uncertain how much info should be exposed",
71+
issue = "48043"
72+
)]
73+
pub fn kind(&self) -> TryReserveErrorKind {
74+
self.kind.clone()
75+
}
76+
}
77+
78+
/// Details of the allocation that caused a `TryReserveError`
79+
#[derive(Clone, PartialEq, Eq, Debug)]
80+
#[unstable(
81+
feature = "try_reserve_kind",
82+
reason = "Uncertain how much info should be exposed",
83+
issue = "48043"
84+
)]
85+
pub enum TryReserveErrorKind {
6286
/// Error due to the computed capacity exceeding the collection's maximum
6387
/// (usually `isize::MAX` bytes).
6488
CapacityOverflow,
@@ -81,12 +105,23 @@ pub enum TryReserveError {
81105
},
82106
}
83107

108+
#[unstable(
109+
feature = "try_reserve_kind",
110+
reason = "Uncertain how much info should be exposed",
111+
issue = "48043"
112+
)]
113+
impl From<TryReserveErrorKind> for TryReserveError {
114+
fn from(kind: TryReserveErrorKind) -> Self {
115+
Self { kind }
116+
}
117+
}
118+
84119
#[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
85120
impl From<LayoutError> for TryReserveError {
86-
/// Always evaluates to [`TryReserveError::CapacityOverflow`].
121+
/// Always evaluates to [`TryReserveErrorKind::CapacityOverflow`].
87122
#[inline]
88123
fn from(_: LayoutError) -> Self {
89-
TryReserveError::CapacityOverflow
124+
TryReserveErrorKind::CapacityOverflow.into()
90125
}
91126
}
92127

@@ -97,11 +132,13 @@ impl Display for TryReserveError {
97132
fmt: &mut core::fmt::Formatter<'_>,
98133
) -> core::result::Result<(), core::fmt::Error> {
99134
fmt.write_str("memory allocation failed")?;
100-
let reason = match &self {
101-
TryReserveError::CapacityOverflow => {
135+
let reason = match self.kind {
136+
TryReserveErrorKind::CapacityOverflow => {
102137
" because the computed capacity exceeded the collection's maximum"
103138
}
104-
TryReserveError::AllocError { .. } => " because the memory allocator returned a error",
139+
TryReserveErrorKind::AllocError { .. } => {
140+
" because the memory allocator returned a error"
141+
}
105142
};
106143
fmt.write_str(reason)
107144
}

library/alloc/src/collections/vec_deque/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use core::slice;
1919

2020
use crate::alloc::{Allocator, Global};
2121
use crate::collections::TryReserveError;
22+
use crate::collections::TryReserveErrorKind;
2223
use crate::raw_vec::RawVec;
2324
use crate::vec::Vec;
2425

@@ -773,7 +774,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
773774
let new_cap = used_cap
774775
.checked_add(additional)
775776
.and_then(|needed_cap| needed_cap.checked_next_power_of_two())
776-
.ok_or(TryReserveError::CapacityOverflow)?;
777+
.ok_or(TryReserveErrorKind::CapacityOverflow)?;
777778

778779
if new_cap > old_cap {
779780
self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?;

library/alloc/src/raw_vec.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ use core::slice;
1313
use crate::alloc::handle_alloc_error;
1414
use crate::alloc::{Allocator, Global, Layout};
1515
use crate::boxed::Box;
16-
use crate::collections::TryReserveError::{self, *};
16+
use crate::collections::TryReserveError;
17+
use crate::collections::TryReserveErrorKind::*;
1718

1819
#[cfg(test)]
1920
mod tests;
@@ -425,7 +426,7 @@ impl<T, A: Allocator> RawVec<T, A> {
425426
if mem::size_of::<T>() == 0 {
426427
// Since we return a capacity of `usize::MAX` when `elem_size` is
427428
// 0, getting to here necessarily means the `RawVec` is overfull.
428-
return Err(CapacityOverflow);
429+
return Err(CapacityOverflow.into());
429430
}
430431

431432
// Nothing we can really do about these checks, sadly.
@@ -451,7 +452,7 @@ impl<T, A: Allocator> RawVec<T, A> {
451452
if mem::size_of::<T>() == 0 {
452453
// Since we return a capacity of `usize::MAX` when the type size is
453454
// 0, getting to here necessarily means the `RawVec` is overfull.
454-
return Err(CapacityOverflow);
455+
return Err(CapacityOverflow.into());
455456
}
456457

457458
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
@@ -471,10 +472,9 @@ impl<T, A: Allocator> RawVec<T, A> {
471472

472473
let ptr = unsafe {
473474
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
474-
self.alloc.shrink(ptr, layout, new_layout).map_err(|_| TryReserveError::AllocError {
475-
layout: new_layout,
476-
non_exhaustive: (),
477-
})?
475+
self.alloc
476+
.shrink(ptr, layout, new_layout)
477+
.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?
478478
};
479479
self.set_ptr(ptr);
480480
Ok(())
@@ -510,7 +510,7 @@ where
510510
alloc.allocate(new_layout)
511511
};
512512

513-
memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })
513+
memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into())
514514
}
515515

516516
unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
@@ -526,7 +526,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
526526
#[cfg(not(no_global_oom_handling))]
527527
#[inline]
528528
fn handle_reserve(result: Result<(), TryReserveError>) {
529-
match result {
529+
match result.map_err(|e| e.kind()) {
530530
Err(CapacityOverflow) => capacity_overflow(),
531531
Err(AllocError { layout, .. }) => handle_alloc_error(layout),
532532
Ok(()) => { /* yay */ }
@@ -545,7 +545,7 @@ fn handle_reserve(result: Result<(), TryReserveError>) {
545545
#[inline]
546546
fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> {
547547
if usize::BITS < 64 && alloc_size > isize::MAX as usize {
548-
Err(CapacityOverflow)
548+
Err(CapacityOverflow.into())
549549
} else {
550550
Ok(())
551551
}

library/alloc/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(pattern)]
99
#![feature(trusted_len)]
1010
#![feature(try_reserve)]
11+
#![feature(try_reserve_kind)]
1112
#![feature(unboxed_closures)]
1213
#![feature(associated_type_bounds)]
1314
#![feature(binary_heap_into_iter_sorted)]

library/alloc/tests/string.rs

+51-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use std::cell::Cell;
3-
use std::collections::TryReserveError::*;
3+
use std::collections::TryReserveErrorKind::*;
44
use std::ops::Bound;
55
use std::ops::Bound::*;
66
use std::ops::RangeBounds;
@@ -703,35 +703,42 @@ fn test_try_reserve() {
703703
let mut empty_string: String = String::new();
704704

705705
// Check isize::MAX doesn't count as an overflow
706-
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) {
706+
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP).map_err(|e| e.kind()) {
707707
panic!("isize::MAX shouldn't trigger an overflow!");
708708
}
709709
// Play it again, frank! (just to be sure)
710-
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) {
710+
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP).map_err(|e| e.kind()) {
711711
panic!("isize::MAX shouldn't trigger an overflow!");
712712
}
713713

714714
if guards_against_isize {
715715
// Check isize::MAX + 1 does count as overflow
716-
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP + 1) {
716+
if let Err(CapacityOverflow) =
717+
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind())
718+
{
717719
} else {
718720
panic!("isize::MAX + 1 should trigger an overflow!")
719721
}
720722

721723
// Check usize::MAX does count as overflow
722-
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE) {
724+
if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind())
725+
{
723726
} else {
724727
panic!("usize::MAX should trigger an overflow!")
725728
}
726729
} else {
727730
// Check isize::MAX + 1 is an OOM
728-
if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_CAP + 1) {
731+
if let Err(AllocError { .. }) =
732+
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind())
733+
{
729734
} else {
730735
panic!("isize::MAX + 1 should trigger an OOM!")
731736
}
732737

733738
// Check usize::MAX is an OOM
734-
if let Err(AllocError { .. }) = empty_string.try_reserve(MAX_USIZE) {
739+
if let Err(AllocError { .. }) =
740+
empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind())
741+
{
735742
} else {
736743
panic!("usize::MAX should trigger an OOM!")
737744
}
@@ -742,25 +749,27 @@ fn test_try_reserve() {
742749
// Same basic idea, but with non-zero len
743750
let mut ten_bytes: String = String::from("0123456789");
744751

745-
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
752+
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
746753
panic!("isize::MAX shouldn't trigger an overflow!");
747754
}
748-
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) {
755+
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
749756
panic!("isize::MAX shouldn't trigger an overflow!");
750757
}
751758
if guards_against_isize {
752-
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) {
759+
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind())
760+
{
753761
} else {
754762
panic!("isize::MAX + 1 should trigger an overflow!");
755763
}
756764
} else {
757-
if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9) {
765+
if let Err(AllocError { .. }) = ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind())
766+
{
758767
} else {
759768
panic!("isize::MAX + 1 should trigger an OOM!")
760769
}
761770
}
762771
// Should always overflow in the add-to-len
763-
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) {
772+
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()) {
764773
} else {
765774
panic!("usize::MAX should trigger an overflow!")
766775
}
@@ -782,30 +791,40 @@ fn test_try_reserve_exact() {
782791
{
783792
let mut empty_string: String = String::new();
784793

785-
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) {
794+
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP).map_err(|e| e.kind())
795+
{
786796
panic!("isize::MAX shouldn't trigger an overflow!");
787797
}
788-
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) {
798+
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP).map_err(|e| e.kind())
799+
{
789800
panic!("isize::MAX shouldn't trigger an overflow!");
790801
}
791802

792803
if guards_against_isize {
793-
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP + 1) {
804+
if let Err(CapacityOverflow) =
805+
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind())
806+
{
794807
} else {
795808
panic!("isize::MAX + 1 should trigger an overflow!")
796809
}
797810

798-
if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) {
811+
if let Err(CapacityOverflow) =
812+
empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind())
813+
{
799814
} else {
800815
panic!("usize::MAX should trigger an overflow!")
801816
}
802817
} else {
803-
if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_CAP + 1) {
818+
if let Err(AllocError { .. }) =
819+
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind())
820+
{
804821
} else {
805822
panic!("isize::MAX + 1 should trigger an OOM!")
806823
}
807824

808-
if let Err(AllocError { .. }) = empty_string.try_reserve_exact(MAX_USIZE) {
825+
if let Err(AllocError { .. }) =
826+
empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind())
827+
{
809828
} else {
810829
panic!("usize::MAX should trigger an OOM!")
811830
}
@@ -815,24 +834,33 @@ fn test_try_reserve_exact() {
815834
{
816835
let mut ten_bytes: String = String::from("0123456789");
817836

818-
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
837+
if let Err(CapacityOverflow) =
838+
ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind())
839+
{
819840
panic!("isize::MAX shouldn't trigger an overflow!");
820841
}
821-
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) {
842+
if let Err(CapacityOverflow) =
843+
ten_bytes.try_reserve_exact(MAX_CAP - 10).map_err(|e| e.kind())
844+
{
822845
panic!("isize::MAX shouldn't trigger an overflow!");
823846
}
824847
if guards_against_isize {
825-
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
848+
if let Err(CapacityOverflow) =
849+
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind())
850+
{
826851
} else {
827852
panic!("isize::MAX + 1 should trigger an overflow!");
828853
}
829854
} else {
830-
if let Err(AllocError { .. }) = ten_bytes.try_reserve_exact(MAX_CAP - 9) {
855+
if let Err(AllocError { .. }) =
856+
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind())
857+
{
831858
} else {
832859
panic!("isize::MAX + 1 should trigger an OOM!")
833860
}
834861
}
835-
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) {
862+
if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind())
863+
{
836864
} else {
837865
panic!("usize::MAX should trigger an overflow!")
838866
}

0 commit comments

Comments
 (0)