pub trait ReplaceWith<'a>: Dummy<'a> {
// Provided method
fn replace_with(&mut self, replacer: impl FnOnce(Self) -> Self) { ... }
}Expand description
A trait to replace an existing AST node in place with a new node built from the old one.
Provided Methods§
Sourcefn replace_with(&mut self, replacer: impl FnOnce(Self) -> Self)
fn replace_with(&mut self, replacer: impl FnOnce(Self) -> Self)
Replace the node in place with the value returned by replacer.
replacer is called with an owned copy of the node, and whatever it returns is written back in its place.
This is for the common case where a node is taken purely to build a new node out of it
(typically wrapping the old node, or extracting just a part of it), which is then stored back in the same slot.
Prefer this over take_in + writing the result back. That writes a dummy node into arena,
which takes up space in arena forever, even though it’s pointless - it’s overwritten immediately.
This method avoids the need for the dummy node, so is faster, and takes less memory.
use oxc_allocator::ReplaceWith;
// If `expr` is a parenthesized expression `(inner)`, unwrap it to `inner` in place
fn unwrap_parens<'a>(expr: &mut Expression<'a>) {
if matches!(expr, Expression::ParenthesizedExpression(_)) {
expr.replace_with(|expr| {
let Expression::ParenthesizedExpression(paren) = expr else { unreachable!() };
paren.unbox().expression
});
}
}§Panic handling
If replacer panics, the original place is left holding a dummy (see Dummy), so it always
remains valid and usable. Building that dummy is the only situation in which this method allocates,
so the common (non-panicking) path allocates nothing.
When compiled with panic = "abort", the code for writing the dummy is optimized out entirely.
Handling panics in this way is only required to avoid UB in obscure cases where catch_unwind
is used and the original value is accessed after the panic.
Without the panic handling, these would both be Undefined Behavior:
§Double drop
use std::{
boxed::Box as StdBox,
mem::ManuallyDrop,
panic::{AssertUnwindSafe, catch_unwind},
};
use oxc_allocator::{Allocator, Dummy, ReplaceWith};
// `ManuallyDrop` wrapper is not required here, but it demonstrates that
// a const assertion in `replace_with` that `Self: !Drop` would not be
// sufficient to prevent UB - `Wrapper` is `!Drop`.
struct Wrapper(ManuallyDrop<StdBox<u32>>);
impl ReplaceWith<'_> for Wrapper {}
impl<'a> Dummy<'a> for Wrapper {
fn dummy(_allocator: &'a Allocator) -> Self {
Self(ManuallyDrop::new(StdBox::new(0)))
}
}
let mut wrapper = Wrapper(ManuallyDrop::new(StdBox::new(123)));
let _ = catch_unwind(AssertUnwindSafe(|| {
wrapper.replace_with(|old| {
let boxed: StdBox<u32> = ManuallyDrop::into_inner(old.0);
// `Box` is freed
drop(boxed);
panic!("Unwind before write-back");
});
}));
let boxed: StdBox<u32> = ManuallyDrop::into_inner(wrapper.0);
// Without the panic guard, `boxed` would be the same `Box`
// that was freed in closure above. Double drop!
drop(boxed);§Aliasing violation
use std::panic::{AssertUnwindSafe, catch_unwind};
use oxc_allocator::{Allocator, ArenaBox, Dummy, ReplaceWith};
struct Wrapper<'a>(ArenaBox<'a, u32>);
impl<'a> ReplaceWith<'a> for Wrapper<'a> {}
impl<'a> Dummy<'a> for Wrapper<'a> {
fn dummy(allocator: &'a Allocator) -> Self {
Self(ArenaBox::new_in(0, &allocator))
}
}
let allocator = Allocator::new();
let allocator = &allocator;
let mut wrapper = Wrapper(ArenaBox::new_in(1, &allocator));
let mut copy: Option<Wrapper> = None;
let _ = catch_unwind(AssertUnwindSafe(|| {
wrapper.replace_with(|old| {
// Move the copy out of the closure
copy = Some(old);
panic!("Unwind before write-back");
});
}));
let mut boxed: ArenaBox<u32> = wrapper.0;
let mut copy: ArenaBox<u32> = copy.unwrap().0;
let boxed_mut: &mut u32 = boxed.as_mut();
let copy_mut: &mut u32 = copy.as_mut();
// Without the panic guard, `boxed` and `copy` would both point to same `u32`.
// We'd now have 2 `&mut u32`s pointing to same place. Aliasing violation!
*boxed_mut = 2;
*copy_mut = 3;Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".