Skip to content
Open
2 changes: 0 additions & 2 deletions src/bundler/Chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,6 @@ impl Chunk {
// Look up the CSS chunk via the JS chunk's css_chunks indices.
// This correctly handles deduplicated CSS chunks that are shared
// across multiple HTML entry points (see issue #23668).
// Note: reshaped for borrowck — we scan immutably for the JS chunk, copy the
// css-chunk index into a local, drop the borrow, then re-borrow mutably.
let entry_point_id = self.entry_point.entry_point_id();
let css_idx: Option<usize> = 'find: {
for other in chunks.iter() {
Expand Down
5 changes: 0 additions & 5 deletions src/bundler/LinkerContext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,14 +556,9 @@
);
}

if self.options.output_format == Format::Cjs || self.options.output_format == Format::Iife {
// Note: reshaped for borrowck — `Slice<T>` is a value-type
// snapshot of column pointers (does not borrow `self.graph.ast`),
// so `split_mut()` on the local can coexist with the
// `self.graph.meta` borrow below. The slab does not reallocate for
// the duration of this loop.
let mut ast_slice = self.graph.ast.slice();
let ast_cols = ast_slice.split_mut();

Check warning on line 561 in src/bundler/LinkerContext.rs

View check run for this annotation

Claude / Claude Code Review

A1 deletion drops 'slab does not reallocate' invariant at Slice<T> snapshot sites

Two more A1 deletions drop the caller-side "the slab does not reallocate/resize for the duration of this loop" invariant at `Slice<T>` snapshot sites — `LinkerContext.rs:560` and `bundle_v2.rs:1900` (`find_reachable_files`). This is the same borrowck-cannot-enforce class already restored at ParseTask/IOReader/IOWriter in 6bcc8c2 (`slice()` takes `&self`, so a mid-loop `self.graph.ast.append(…)` would compile and dangle the raw column pointers), and it also leaves `bundle_v2.rs:5114` ("Mirrors th
Comment thread
robobun marked this conversation as resolved.
let exports_kind: &mut [ExportsKind] = ast_cols.exports_kind;
let ast_flags_list: &mut [AstFlags] = ast_cols.flags;
let meta_flags_list = self.graph.meta.items_flags_mut();
Expand Down
11 changes: 4 additions & 7 deletions src/bundler/ParseTask.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2728,13 +2728,10 @@ pub mod parse_worker {
}
}

// reshaped for borrowck — `this` and `this.stage.needs_parse`
// both borrowed mutably. The entry must live
// in-place so its `Contents::Owned` buffer survives in
// `task.stage` for the bundle's lifetime (Success.source.contents
// borrows it via the arena-erased `StoreStr` path). Take it out, parse, then *write it
// back* on every path before `break 'value` so dropping the local
// can't free the buffer underneath the borrowed source.
// `entry` must be written back into `this.stage` on every path before `break 'value`:
// `Success.source.contents` borrows its `Contents::Owned` buffer via the
// arena-erased `StoreStr` path (see `run_with_source_code`), so dropping the local
// would free the buffer underneath the borrowed source.
let mut entry =
match core::mem::replace(&mut this.stage, ParseTaskStage::NeedsSourceCode) {
ParseTaskStage::NeedsParse(e) => e,
Comment thread
robobun marked this conversation as resolved.
Expand Down
6 changes: 2 additions & 4 deletions src/bundler/ThreadPool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,10 +691,8 @@ impl Worker {

/// Build a per-worker `Transpiler` from `from`.
///
/// reshaped for borrowck — associated fn (no `&mut self`) so
/// callers can borrow `self.data.log` disjointly. The returned value is a
/// fully-owned `Transpiler` whose `Drop` is sound; `wire_after_move` must
/// be called once it is at its final address.
/// The returned value is a fully-owned `Transpiler` whose `Drop` is sound;
/// `wire_after_move` must be called once it is at its final address.
fn initialize_transpiler(
log: *mut bun_ast::Log,
from: &Transpiler<'_>,
Expand Down
36 changes: 9 additions & 27 deletions src/bundler/bundle_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1129,8 +1129,9 @@ pub mod bv2_impl {
}
pub fn run_on_js_thread(&mut self) {
let kind = self.import_record.kind;
// reshaped for borrowck — capture the erased self
// pointer before borrowing fields immutably for the FFI call.
// SAFETY: self_ptr is the *mut c_void context handed to
// JSBundlerPlugin__matchOnResolve; the JS completion callback
// mutates/consumes self, so *mut Self provenance is required.
let self_ptr = std::ptr::from_mut::<Self>(self).cast::<core::ffi::c_void>();
// SAFETY: `bv2` is a valid backref set by `init`; the plugin
// storage is disjoint from `self`, so the `&mut JSBundlerPlugin`
Expand Down Expand Up @@ -1262,8 +1263,9 @@ pub mod bv2_impl {
pub fn run_on_js_thread(&mut self) {
let is_server_side = self.bake_graph() != crate::bake_types::Graph::Client;
let default_loader = self.default_loader;
// reshaped for borrowck — capture the erased self
// pointer before borrowing fields immutably for the FFI call.
// SAFETY: self_ptr is the *mut c_void context handed to
// JSBundlerPlugin__matchOnLoad; the JS completion callback
// mutates/consumes self, so *mut Self provenance is required.
let self_ptr = std::ptr::from_mut::<Self>(self).cast::<core::ffi::c_void>();
// SAFETY: `bv2` is a valid backref set by `init`; the plugin
// storage is disjoint from `self`, so the `&mut JSBundlerPlugin`
Expand Down Expand Up @@ -1889,19 +1891,12 @@ pub mod bv2_impl {

self.dynamic_import_entry_points = ArrayHashMap::new();

// reshaped for borrowck — hoist the values that would
// otherwise re-borrow `self`/`self.graph` while the visitor holds
// disjoint column refs.
// Always materialize a valid slice; when the boundary list is empty
// this is a cheap `{ list: empty, map: &map }`. Avoids constructing a
// null `&Map` via `mem::zeroed()` (UB even though it was never read
// when `scb_bitset` is `None`).
let scb_list = self.graph.server_component_boundaries.slice();

// reshaped for borrowck — `Slice<T>` is a value-type
// snapshot of column pointers (does not borrow `self.graph.ast`), so
// `split_mut()` on the local can coexist with the shared borrows
// below. The slab does not resize for the duration of this function.
let mut ast_slice = self.graph.ast.slice();
let all_import_records: &mut [import_record::List<'_>] =
ast_slice.split_mut().import_records;
Expand Down Expand Up @@ -1960,13 +1955,8 @@ pub mod bv2_impl {
}
}

// reshaped for borrowck — release the visitor's `&mut`
// borrows on the two bitsets and `input_files` columns before the
// cleanup loop reads them.
let ReachableFileVisitor { reachable, .. } = visitor;

// reshaped for borrowck — three disjoint mutable SoA
// columns via `split_mut()` on a value-type `Slice` snapshot.
let mut input_files_slice = self.graph.input_files.slice();
let input_files_cols = input_files_slice.split_mut();
let additional_files: &mut [bun_alloc::AstVec<crate::AdditionalFile>] =
Expand Down Expand Up @@ -2066,12 +2056,6 @@ pub mod bv2_impl {
// version and exports a non-object in CommonJS (often a function). If we
// pick the "module" field and the package is imported with "require" then
// code expecting a function will crash.
//
// reshaped for borrowck — the mutable `import_records` column is
// needed alongside shared columns. `split_mut()` on a
// value-type `Slice` snapshot yields the one mutable column without
// borrowing `self.graph.ast`; read the per-target map through the
// disjoint `build_graphs` field instead of the `&mut self` accessor.
let mut ast_slice = self.graph.ast.slice();
let ast_import_records: &mut [import_record::List<'_>] =
ast_slice.split_mut().import_records;
Expand Down Expand Up @@ -6454,11 +6438,9 @@ pub mod bv2_impl {
importer_source_index: IndexInt,
) -> i32 {
let mut diff: i32 = 0;
// reshaped for borrowck — `graph` and the
// path map are both needed across the loop body. We (a) capture a raw self ptr for
// ParseTask.ctx, (b) hoist dev_server check, and (c) scope the map
// borrow to the get_or_put so later `self.graph.*` writes don't overlap.
// SAFETY: write provenance from `ptr::from_mut`; outlives every ParseTask.
// SAFETY: stored into ParseTask.ctx and scheduled on the thread pool; the task
// calls back into BundleV2, so `ptr::from_mut` write provenance is required.
// Outlives every ParseTask.
let self_ptr: Option<bun_ptr::ParentRef<BundleV2<'static>>> = Some(unsafe {
bun_ptr::ParentRef::from_raw_mut(
std::ptr::from_mut::<Self>(self).cast::<BundleV2<'static>>(),
Expand Down
5 changes: 0 additions & 5 deletions src/bundler/entry_points.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ impl MacroEntryPoint {
let hash = hasher.final_();
let fmt = bun_fmt::hex_int_lower::<16>(hash);

// reshaped for borrowck — capture cursor position, drop &mut
// borrow, then re-borrow `buf` immutably.
let n = {
let mut cursor = std::io::Cursor::new(&mut buf[..]);
write!(
Expand Down Expand Up @@ -192,9 +190,6 @@ impl MacroEntryPoint {
} else {
import_path.dir_with_trailing_slash()
};
// reshaped for borrowck — capture the label length, write the
// body via a scoped &mut borrow, then re-borrow `code_buffer` immutably
// for the (label, code) slices passed to `init_path_string`.
let label_len = macro_label_.len();
entry.code_buffer[..label_len].copy_from_slice(macro_label_);

Expand Down
1 change: 0 additions & 1 deletion src/bundler/linker_context/computeChunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,6 @@ pub fn compute_chunks(this: &mut LinkerContext, unique_key: u64) -> crate::Resul
}
}
}
// reshaped for borrowck — re-borrow file_entry_bits after the loop above mutated it
let file_entry_bits: &mut [AutoBitSet] = this.graph.files.items_entry_bits_mut();

let css_reprs = this.graph.ast.items_css();
Expand Down
2 changes: 0 additions & 2 deletions src/bundler/linker_context/generateCodeForLazyExport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,6 @@ pub fn generate_code_for_lazy_export(
}
}

// The Visitor is constructed inside the loop with a fresh `parts`
// borrow each time (reshaped for borrowck).
let all_symbols = this.graph.ast.items_symbols();
// SAFETY: `LinkerContext::arena()` returns a stable `&Arena` valid for the
// link pass; detach via raw-pointer round-trip so it doesn't hold a `&self`
Expand Down
1 change: 0 additions & 1 deletion src/bundler/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,6 @@ impl<'a> BundleOptions<'a> {

Some(Cow::Borrowed(b"\"development\"".as_slice()))
};
// reshaped for borrowck — node_env computed before passing self.log
self.define = defines_from_transform_options(
// No other `&mut Log` is live across this call (see `log_mut`
// caller contract).
Expand Down
8 changes: 0 additions & 8 deletions src/bundler/transpiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,11 +463,6 @@ impl<'a> Transpiler<'a> {
let mut cache_bust_buf = bun_paths::PathBuffer::uninit();

// Bust directory cache and try again
// reshaped for borrowck — a single labelled block would
// return a slice that aliases either `entry_point` (via
// `dirname`) or `cache_bust_buf`. Rust can't unify the two
// disjoint mutable borrows of `cache_bust_buf` across `break`,
// so compute `busted` directly instead.
let busted: bool = 'name: {
if bun_paths::is_absolute(entry_point) {
let dir = bun_paths::resolve_path::dirname::<bun_paths::platform::Auto>(
Expand Down Expand Up @@ -1928,9 +1923,6 @@ fn parse_data_loader<'a>(
bun_collections::StringHashMap::default();
// duplicate_key_checker drops at end of scope (defer .deinit())
let mut count: usize = 0;
// reshaped for borrowck — cannot zip 4
// slices with one mutable borrow into `decls` and
// also random-access `decls[prev]`.
for i in 0..n {
let prop = &mut properties[i];
// SAFETY: data-format parsers always emit
Expand Down
7 changes: 0 additions & 7 deletions src/collections/linear_fifo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ impl<T, B: LinearFifoBuffer<T>> LinearFifo<T, B> {
#[cfg(debug_assertions)]
{
// set old range to undefined. Note: may be wrapped around
// reshaped for borrowck — capture len, then re-borrow.
let slice_len = self.readable_slice_mut(0).len();
if slice_len >= count {
poison(self.readable_slice_mut(0), count);
Expand Down Expand Up @@ -448,8 +447,6 @@ impl<T, B: LinearFifoBuffer<T>> LinearFifo<T, B> {
self.ensure_unused_capacity(size)?;

// try to avoid realigning buffer
// reshaped for borrowck — check len, drop borrow, maybe
// realign, then take the final borrow.
if self.writable_slice(0).len() < size {
self.realign();
}
Expand All @@ -474,8 +471,6 @@ impl<T, B: LinearFifoBuffer<T>> LinearFifo<T, B> {

let mut src_left = src;
while !src_left.is_empty() {
// reshaped for borrowck — scoped block drops the
// `writable` borrow before `self.update`.
let n = {
let writable = self.writable_slice(0);
debug_assert!(!writable.is_empty());
Expand Down Expand Up @@ -544,8 +539,6 @@ impl<T, B: LinearFifoBuffer<T>> LinearFifo<T, B> {

self.rewind(src.len());

// reshaped for borrowck — copy into first chunk in a scoped
// block, drop borrow, then re-borrow for the wrapped chunk.
let slice_len = {
let s = self.readable_slice_mut(0);
let n = s.len().min(src.len());
Expand Down
1 change: 0 additions & 1 deletion src/css/css_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,6 @@ fn parse_nested_block<T>(
BlockType::SquareBracket => Delimiters::CLOSE_SQUARE_BRACKET,
BlockType::Parenthesis => Delimiters::CLOSE_PARENTHESIS,
};
// Note: reshaped for borrowck — same aliasing as parse_until_before.
// Swap stop_before/at_start_of in place rather than constructing a second
// Parser over the invariant `&'a mut ParserInput<'a>`.
let saved_stop_before = parser.stop_before;
Expand Down
3 changes: 0 additions & 3 deletions src/css/properties/animation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,6 @@ impl AnimationName {
// SAFETY: arena-owned slice valid for 'bump.
let name: &[u8] = unsafe { crate::arena_str(s.v) };
if css_module_animation_enabled {
// reshaped for borrowck — capture arena/source_index
// before borrowing dest.css_module mutably.
let arena = dest.arena;
let source_index = dest.loc.source_index;
if let Some(css_module) = &mut dest.css_module {
Expand All @@ -353,7 +351,6 @@ impl AnimationName {
// SAFETY: arena-owned slice valid for 'bump.
let name: &[u8] = unsafe { crate::arena_str(*s) };
if css_module_animation_enabled {
// reshaped for borrowck
let arena = dest.arena;
let source_index = dest.loc.source_index;
if let Some(css_module) = &mut dest.css_module {
Expand Down
3 changes: 0 additions & 3 deletions src/dotenv/env_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1106,9 +1106,6 @@ impl<'a> Parser<'a> {
if end >= self.src.len() {
return Ok(&self.src[self.src.len()..]);
}
// reshaped for borrowck — `parse_quoted` returns a borrow of
// `self.value_buffer`; capture only its length, then re-borrow the buffer
// after the match so the unquoted fallthrough can re-borrow `self`.
let quoted_len: Option<usize> = match self.src[end] {
b'`' => self.parse_quoted::<b'`'>()?.map(|v| v.len()),
b'"' => self.parse_quoted::<b'"'>()?.map(|v| v.len()),
Expand Down
4 changes: 0 additions & 4 deletions src/exe_format/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ impl ElfFile {
return;
}

// reshaped for borrowck — compute replacement under an
// immutable borrow, then take a mutable borrow for the writes.
let replacement: &'static [u8] = {
let interp_region = &self.data[interp_offset..][..interp_filesz];
let current = slice_to_nul(interp_region);
Expand Down Expand Up @@ -167,8 +165,6 @@ impl ElfFile {
if strtab_end > self.data.len() as u64 {
return;
}
// reshaped for borrowck — copy strtab bounds out so we can
// re-borrow self.data mutably below.
let strtab_off = usize::try_from(strtab_shdr.sh_offset).expect("int cast");
let strtab_len = usize::try_from(strtab_shdr.sh_size).expect("int cast");

Expand Down
1 change: 0 additions & 1 deletion src/exe_format/macho.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,6 @@ impl MachoSigner {
self.data.extend_from_slice(id);

// Hash and write pages
// reshaped for borrowck — index instead of slicing self.data while pushing.
let mut off: usize = 0;
let end = self.sig_off;
while end - off >= PAGE_SIZE {
Expand Down
4 changes: 0 additions & 4 deletions src/glob/GlobWalker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,6 @@ impl<'a, A: Accessor, const SENTINEL: bool> Iterator<'a, A, SENTINEL> {

pub fn next(&mut self) -> Result<Maybe<Option<MatchedPath>>, Error> {
'outer: loop {
// Note: reshaped for borrowck — take/replace iter_state where needed.
match &mut self.iter_state {
IterState::Matched(_) => {
let IterState::Matched(path) =
Expand Down Expand Up @@ -842,8 +841,6 @@ impl<'a, A: Accessor, const SENTINEL: bool> Iterator<'a, A, SENTINEL> {
// `path_buf` and `pattern_components` (disjoint fields) for the
// write+normalize, then drop the &mut and read via `self.walker`.
let mut symlink_full_path_len = work_item_path.len();
// Note: reshaped for borrowck — entry_name is a sub-slice
// of symlink_full_path; capture range and re-slice later.
let entry_start = work_item.entry_start as usize;

let mut has_dot_dot = false;
Expand Down Expand Up @@ -1004,7 +1001,6 @@ impl<'a, A: Accessor, const SENTINEL: bool> Iterator<'a, A, SENTINEL> {
let dir_fd = dir.fd;
let at_cwd = dir.at_cwd;
let dir_path = dir.dir_path();
// Note: reshaped for borrowck
let err = self.walker.handle_sys_err_with_path(&err, dir_path);
if !at_cwd {
self.close_disallowing_cwd(dir_fd);
Expand Down
2 changes: 0 additions & 2 deletions src/http/HeaderBuilder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ impl HeaderBuilder {

let _ = self.content.append(name);

// Note: reshaped for borrowck — `fmt` returns a borrow into the
// builder buffer; capture its length, then re-read `content.len`.
let value_len = self.content.fmt(args).len();

let value_ptr = api::StringPointer {
Expand Down
1 change: 0 additions & 1 deletion src/http/h2_client/PendingConnect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ impl PendingConnect {
/// Box until scope exit.
pub fn unregister_from(this: *const Self, ctx: &mut NewHTTPContext<true>) -> Option<Box<Self>> {
let list = &mut ctx.pending_h2_connects;
// reshaped for borrowck (was `for + swapRemove + return`)
list.iter()
.position(|p| core::ptr::eq(&raw const **p, this))
.map(|i| list.swap_remove(i))
Expand Down
1 change: 0 additions & 1 deletion src/http/h3_client/AltSvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ fn key<'a>(buf: &'a mut [u8], hostname: &[u8], port: u16) -> &'a [u8] {
// hostname verbatim, then format only the port.
cursor.write_all(hostname).expect("unreachable");
write!(cursor, ":{}", port).expect("unreachable");
// reshaped for borrowck — capture remaining len before reborrowing buf.
let remaining = cursor.len();
let written = buf.len() - remaining;
&buf[..written]
Expand Down
Loading
Loading