-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add APIs for uninitialized Box, Rc, and Arc. (Plus get_mut_unchecked) #62451
Conversation
r? @bluss (rust_highfive has picked a reviewer for you, use r? to override) |
CC @rust-lang/libs for new unstable APIs |
d7d3352
to
24fd456
Compare
We have a codegen test for allocating uninitialized boxes at https://github.com/rust-lang/rust/blob/master/src/test/codegen/box-maybe-uninit.rs. Seems like a good idea to update that to test |
If you insist I don’t really mind adding it, but I think it wouldn’t be useful. The existing test uses rust/src/librustc_mir/build/expr/as_rvalue.rs Lines 135 to 147 in 4b65a86
The At some later point (I’m not sure where exactly) an optimization eliminates this move, presumably because the source is recognized to be entirely uninitialized. But that optimization is not reliable, the test links to #58201. A codegen test is a good way to ensure that the optimization keeps removing that move operation. But the point of |
My thinking was that a codegen test would demonstrate that this is indeed the case. But if you think the chances of anyone every accidentally changing |
Here from the tracking issue for |
I don’t understand how this would help. Making a method safe by having it return a raw pointer only moves the As to cases where creating the |
I don't think the Returning let a = Arc::new(0);
let r1 = Arc::get_mut_unchecked();
let r2 = Arc::get_mut_unchecked();
// Two aliasing raw pointers, no problem.
*r1 = 5;
assert_eq!(*r2, 5); To make this work with Stacked Borrows, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly just doc nits, but I think one existing method should be renamed to account for the new way in which it is used.
@SimonSapin you know you can batch all suggested changes into one commit? Go to https://github.com/rust-lang/rust/pull/62451/files for that. |
Ah, is going to that view what I need to do to have that button not be disabled? Good to know for next time. I’ll squash these. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me with the last nit fixed.
@bors r=RalfJung |
📌 Commit 9bd7083 has been approved by |
Add APIs for uninitialized Box, Rc, and Arc. (Plus get_mut_unchecked) Assigning `MaybeUninit::<Foo>::uninit()` to a local variable is usually free, even when `size_of::<Foo>()` is large. However, passing it for example to `Arc::new` [causes at least one copy](https://youtu.be/F1AquroPfcI?t=4116) (from the stack to the newly allocated heap memory) even though there is no meaningful data. It is theoretically possible that a Sufficiently Advanced Compiler could optimize this copy away, but this is [reportedly unlikely to happen soon in LLVM](https://youtu.be/F1AquroPfcI?t=5431). This PR proposes two sets of features: * Constructors for containers (`Box`, `Rc`, `Arc`) of `MaybeUninit<T>` or `[MaybeUninit<T>]` that do not initialized the data, and unsafe conversions to the known-initialized types (without `MaybeUninit`). The constructors are guaranteed not to make unnecessary copies. * On `Rc` and `Arc`, an unsafe `get_mut_unchecked` method that provides `&mut T` access without checking the reference count. `Arc::get_mut` involves multiple atomic operations whose cost can be non-trivial. `Rc::get_mut` is less costly, but we add `Rc::get_mut_unchecked` anyway for symmetry with `Arc`. These can be useful independently, but they will presumably be typical when the new constructors of `Rc` and `Arc` are used. An alternative with a safe API would be to introduce `UniqueRc` and `UniqueArc` types that have the same memory layout as `Rc` and `Arc` (and so zero-cost conversion to them) but are guaranteed to have only one reference. But introducing entire new types feels “heavier” than new constructors on existing types, and initialization of `MaybeUninit<T>` typically requires unsafe code anyway. Summary of new APIs (all unstable in this PR): ```rust impl<T> Box<T> { pub fn new_uninit() -> Box<MaybeUninit<T>> {…} } impl<T> Box<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Box<T> {…} } impl<T> Box<[T]> { pub fn new_uninit_slice(len: usize) -> Box<[MaybeUninit<T>]> {…} } impl<T> Box<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Box<[T]> {…} } impl<T> Rc<T> { pub fn new_uninit() -> Rc<MaybeUninit<T>> {…} } impl<T> Rc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Rc<T> {…} } impl<T> Rc<[T]> { pub fn new_uninit_slice(len: usize) -> Rc<[MaybeUninit<T>]> {…} } impl<T> Rc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Rc<[T]> {…} } impl<T> Arc<T> { pub fn new_uninit() -> Arc<MaybeUninit<T>> {…} } impl<T> Arc<MaybeUninit<T>> { pub unsafe fn assume_init(self) -> Arc<T> {…} } impl<T> Arc<[T]> { pub fn new_uninit_slice(len: usize) -> Arc<[MaybeUninit<T>]> {…} } impl<T> Arc<[MaybeUninit<T>]> { pub unsafe fn assume_init(self) -> Arc<[T]> {…} } impl<T: ?Sized> Rc<T> { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {…} } impl<T: ?Sized> Arc<T> { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {…} } ```
Rollup of 5 pull requests Successful merges: - #62451 (Add APIs for uninitialized Box, Rc, and Arc. (Plus get_mut_unchecked)) - #63487 (Remove meaningless comments in src/test) - #63657 (Crank up invalid value lint) - #63667 (resolve: Properly integrate derives and `macro_rules` scopes) - #63669 (fix typos in mir/interpret) Failed merges: r? @ghost
Assigning
MaybeUninit::<Foo>::uninit()
to a local variable is usually free, even whensize_of::<Foo>()
is large. However, passing it for example toArc::new
causes at least one copy (from the stack to the newly allocated heap memory) even though there is no meaningful data. It is theoretically possible that a Sufficiently Advanced Compiler could optimize this copy away, but this is reportedly unlikely to happen soon in LLVM.This PR proposes two sets of features:
Constructors for containers (
Box
,Rc
,Arc
) ofMaybeUninit<T>
or[MaybeUninit<T>]
that do not initialized the data, and unsafe conversions to the known-initialized types (withoutMaybeUninit
). The constructors are guaranteed not to make unnecessary copies.On
Rc
andArc
, an unsafeget_mut_unchecked
method that provides&mut T
access without checking the reference count.Arc::get_mut
involves multiple atomic operations whose cost can be non-trivial.Rc::get_mut
is less costly, but we addRc::get_mut_unchecked
anyway for symmetry withArc
.These can be useful independently, but they will presumably be typical when the new constructors of
Rc
andArc
are used.An alternative with a safe API would be to introduce
UniqueRc
andUniqueArc
types that have the same memory layout asRc
andArc
(and so zero-cost conversion to them) but are guaranteed to have only one reference. But introducing entire new types feels “heavier” than new constructors on existing types, and initialization ofMaybeUninit<T>
typically requires unsafe code anyway.Summary of new APIs (all unstable in this PR):