Skip to content

Commit 4a35895

Browse files
committed
fix(allocator): remove Vec::bump method
1 parent 72247e6 commit 4a35895

File tree

2 files changed

+56
-24
lines changed

2 files changed

+56
-24
lines changed

crates/oxc_allocator/src/vec2/mod.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -720,26 +720,6 @@ impl<'a, T: 'a, A: Alloc> Vec<'a, T, A> {
720720
self.buf.set_len(new_len);
721721
}
722722

723-
/// Returns a shared reference to the allocator backing this `Vec`.
724-
///
725-
/// # Examples
726-
///
727-
/// ```
728-
/// use bumpalo::{Bump, collections::Vec};
729-
///
730-
/// // uses the same allocator as the provided `Vec`
731-
/// fn add_strings<'a>(vec: &mut Vec<'a, &'a str>) {
732-
/// for string in ["foo", "bar", "baz"] {
733-
/// vec.push(vec.bump().alloc_str(string));
734-
/// }
735-
/// }
736-
/// ```
737-
#[inline]
738-
#[must_use]
739-
pub fn bump(&self) -> &'a A {
740-
self.buf.bump()
741-
}
742-
743723
/// Reserves capacity for at least `additional` more elements to be inserted
744724
/// in the given `Vec<'a, T>`. The collection may reserve more space to avoid
745725
/// frequent reallocations. After calling `reserve`, capacity will be
@@ -1735,7 +1715,10 @@ impl<'a, T: 'a, A: Alloc> Vec<'a, T, A> {
17351715
assert!(at <= self.len_usize(), "`at` out of bounds");
17361716

17371717
let other_len = self.len_usize() - at;
1738-
let mut other = Vec::with_capacity_in(other_len, self.buf.bump());
1718+
// SAFETY: This method takes a `&mut self`. It lives for the duration of this method
1719+
// - longer than we use `bump` for.
1720+
let bump = unsafe { self.buf.bump() };
1721+
let mut other = Vec::with_capacity_in(other_len, bump);
17391722

17401723
// Unsafely `set_len` and copy items to `other`.
17411724
unsafe {
@@ -2683,7 +2666,15 @@ impl<I: Iterator, A: Alloc> Drop for Splice<'_, '_, I, A> {
26832666

26842667
// Collect any remaining elements.
26852668
// This is a zero-length vector which does not allocate if `lower_bound` was exact.
2686-
let mut collected = Vec::new_in(self.drain.vec.as_ref().buf.bump());
2669+
2670+
// SAFETY: `Splice` iterator is created in `Vec::splice`, which takes a `&mut self`.
2671+
// `Splice` inherits the lifetime of `&mut self` from that method, so the mut borrow
2672+
// of the `Vec` is held for the life of the `Splice`.
2673+
// Therefore we have exclusive access to the `Vec` until end of this method.
2674+
// That is longer than we use `bump` for.
2675+
let bump = self.drain.vec.as_ref().buf.bump();
2676+
2677+
let mut collected = Vec::new_in(bump);
26872678
collected.extend(self.replace_with.by_ref());
26882679
let mut collected = collected.into_iter();
26892680
// Now we have an exact count.

crates/oxc_allocator/src/vec2/raw_vec.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ pub struct RawVec<'a, T, A: Alloc> {
7272
ptr: NonNull<T>,
7373
len: u32,
7474
cap: u32,
75+
// SAFETY: Methods must not mutate the allocator (e.g. allocate into it), unless they can guarantee
76+
// they have exclusive access to it, by taking `&mut self`
7577
alloc: &'a A,
7678
}
7779

@@ -192,8 +194,47 @@ impl<'a, T, A: Alloc> RawVec<'a, T, A> {
192194
if mem::size_of::<T>() == 0 { !0 } else { self.cap as usize }
193195
}
194196

195-
/// Returns a shared reference to the allocator backing this RawVec.
196-
pub fn bump(&self) -> &'a A {
197+
/// Get a shared reference to the allocator backing this `RawVec`.
198+
///
199+
/// This method is hazardous.
200+
///
201+
/// `Vec` is `Sync`, but `Bump` is not, because it utilizes interior mutability.
202+
/// It is possible to make allocations into the arena while holding only a `&Bump`.
203+
/// Because `Vec` is `Sync`, it's possible for multiple `&Vec` references to the same `Vec`,
204+
/// or references to multiple `Vec`s attached to the same `Bump`, to exist simultaneously
205+
/// on different threads.
206+
///
207+
/// So this method could be used to obtain 2 `&Bump` references simultaneously on different threads.
208+
/// Utilizing those references to allocate into the arena simultaneously from different threads
209+
/// would be UB.
210+
///
211+
/// We cannot rely on the type system or borrow checker to ensure correct synchronization.
212+
///
213+
/// Therefore callers must ensure by other means that they have exclusive access, by:
214+
///
215+
/// 1. Taking a `&mut self`.
216+
/// No methods of `Vec` or `RawVec` which do not hold a `&mut Vec` / `&mut RawVec` can use this method.
217+
///
218+
/// 2. That `&mut self` must be held for at least as long as the `&'a A` reference returned by
219+
/// this method is held.
220+
///
221+
/// Note: It's tempting to think we could make this a safe method by making it take `&mut self`,
222+
/// but that's insufficient. That would enforce that the caller holds a `&mut self`, but the `&'a A`
223+
/// returned by this method outlives the lifetime of `&self` that the method takes, so it would
224+
/// NOT guarantee anything about *how long* they hold it for.
225+
/// Taking a `&'a mut self` *would* be safe, but it'd be impractical.
226+
///
227+
/// For further information, see comments on the `impl Sync` implementation of `Vec`.
228+
///
229+
/// # IMPORTANT
230+
/// The ability to obtain a reference to the allocator MUST NOT be exposed to user,
231+
/// outside of `Vec`'s internals.
232+
///
233+
/// # SAFETY
234+
/// Caller must ensure they have exclusive access, but holding a `&mut Vec` or `&mut RawVec`
235+
/// for the duration that the reference returned by this method is held.
236+
/// See text above for further detail.
237+
pub unsafe fn bump(&self) -> &'a A {
197238
self.alloc
198239
}
199240

0 commit comments

Comments
 (0)