Skip to content
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

Vec: avoid creating slices to the elements #61114

Merged
merged 2 commits into from
May 26, 2019
Merged

Conversation

RalfJung
Copy link
Member

Instead of self.deref_mut().as_mut_ptr() to get a raw pointer to the buffer, use self.buf.ptr_mut(). This (a) avoids creating a unique reference to all existing elements without any need, and (b) creates a pointer that can actually be used for the entire buffer, and not just for the part of it covered by self.deref_mut().

I also got worried about RawVec::ptr returning a *mut T from an &self, so I added both a mutable and an immutable version.

Cc @gankro in particular for the assume changes -- I don't know why that is not in Unique, but I moved it up from Vec::deref to RawVec::ptr to avoid having to repeat it everywhere.

Fixes #60847

@rust-highfive
Copy link
Collaborator

r? @sfackler

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label May 24, 2019
@rust-highfive

This comment has been minimized.

@rust-highfive

This comment has been minimized.

@RalfJung
Copy link
Member Author

There's just too many things using RawVec, I am going to give up on that "better mutability tracking". Not sure if y'all would want that anyway.

@@ -194,7 +195,9 @@ impl<T, A: Alloc> RawVec<T, A> {
/// Unique::empty() if `cap = 0` or T is zero-sized. In the former case, you must
/// be careful.
pub fn ptr(&self) -> *mut T {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just return a NonNull<T>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe? There might be a reason for why this looks the way it does. Possibly because NonNull is not very ergonomic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, NonNull is only useful as a storage type, it's useless for doing actual work.

@hanna-kruppe
Copy link
Contributor

hanna-kruppe commented May 24, 2019

(b) creates a pointer that can actually be used for the entire buffer, and not just for the part of it covered by self.deref_mut().

This seems like it would also be a problem for anyone outside liballoc who uses as_mut_ptr to e.g.

  • initialize parts of a vector before calling set_len
  • disassemble a vector and later reconstruct it with Vec::from_raw_parts
  • keep a cursor into the Vec as its length changes (similar to the test you added, but moving the pointer around, into portions of the buffer that are added later)

Is that right? And the workaround used here (accessing the internal RawVec) isn't available outside liballoc, right?

So perhaps we should add Vec::as_mut_ptr that returns a pointer covering the vector's entire buffer, not just the 0..len portion? (Plus Vec::as_ptr for symmetry, I guess.)

@RalfJung
Copy link
Member Author

Yes that is correct. Vec::as_mut_ptr (via DerefMut) may only be used to access the elements 0..len with len taken at the time of the invocation.

That's what Stacked Borrows says, anyway. ;) This might also be an indication that the model is too strict? The desired interaction with unsized types is not really clear. One custom DST are a thing, I doubt we want Stacked Borrows to run the arbitrary user code to compute the size...

@hanna-kruppe
Copy link
Contributor

Assuming that this restriction on the use of <[T]>::{as_ptr, as_mut_ptr} prevails in some form, what do you think about (rephrasing the last part of my previous comment for clarity) adding inherent methods as_ptr, as_mut_ptr to Vec that shadow the slice methods and differ only in that they return a pointer that may be used to access elements 0..capacity?

@RalfJung
Copy link
Member Author

That sounds very reasonable!

I didn't do that here because I was not sure how that shadowing would work out.

@Gankra
Copy link
Contributor

Gankra commented May 24, 2019

re: assume: I believe historically we found that llvm gets really overexcited when it finds assumes and wastes waaay too much time trying to profitably use them. So we moved to only inserting them in strategic hotspots, instead of everywhere we could.

@Gankra
Copy link
Contributor

Gankra commented May 24, 2019

Also I agree with @rkruppe. If as_ptr is a semantic footgun for the implementation, it's a footgun for our users too. We should add the shadowing implementation (which works fine, Vec shadows slice methods in a few places already).

@RalfJung
Copy link
Member Author

I will have to make them insta-stable though to not regress people doing vec.as_mut_ptr(), right?

@RalfJung
Copy link
Member Author

All right, I rewrote this PR to add shadowing methods instead. I also moved the assume there.

@@ -1754,7 +1819,6 @@ impl<T> IntoIterator for Vec<T> {
fn into_iter(mut self) -> IntoIter<T> {
unsafe {
let begin = self.as_mut_ptr();
assume(!begin.is_null());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as_mut_ptr now contains an assume so I think I can remove this? Unfortunately whoever added this did not leave a comment, because it seemed redundant before already.

@sfackler
Copy link
Member

r? @gankro

@rust-highfive rust-highfive assigned Gankra and unassigned sfackler May 25, 2019
@Gankra
Copy link
Contributor

Gankra commented May 25, 2019

@bors r+

@bors
Copy link
Contributor

bors commented May 25, 2019

@gankro: 🔑 Insufficient privileges: Not in reviewers

@RalfJung
Copy link
Member Author

@bors r=Gankro

@bors
Copy link
Contributor

bors commented May 25, 2019

📌 Commit 428ab7e has been approved by Gankro

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 25, 2019
Centril added a commit to Centril/rust that referenced this pull request May 25, 2019
Vec: avoid creating slices to the elements

Instead of `self.deref_mut().as_mut_ptr()` to get a raw pointer to the buffer, use `self.buf.ptr_mut()`. This (a) avoids creating a unique reference to all existing elements without any need, and (b) creates a pointer that can actually be used for the *entire* buffer, and not just for the part of it covered by `self.deref_mut()`.

I also got worried about `RawVec::ptr` returning a `*mut T` from an `&self`, so I added both a mutable and an immutable version.

Cc @gankro in particular for the `assume` changes -- I don't know why that is not in `Unique`, but I moved it up from `Vec::deref` to `RawVec::ptr` to avoid having to repeat it everywhere.

Fixes rust-lang#60847
Centril added a commit to Centril/rust that referenced this pull request May 26, 2019
Vec: avoid creating slices to the elements

Instead of `self.deref_mut().as_mut_ptr()` to get a raw pointer to the buffer, use `self.buf.ptr_mut()`. This (a) avoids creating a unique reference to all existing elements without any need, and (b) creates a pointer that can actually be used for the *entire* buffer, and not just for the part of it covered by `self.deref_mut()`.

I also got worried about `RawVec::ptr` returning a `*mut T` from an `&self`, so I added both a mutable and an immutable version.

Cc @gankro in particular for the `assume` changes -- I don't know why that is not in `Unique`, but I moved it up from `Vec::deref` to `RawVec::ptr` to avoid having to repeat it everywhere.

Fixes rust-lang#60847
bors added a commit that referenced this pull request May 26, 2019
Rollup of 9 pull requests

Successful merges:

 - #61087 (Tweak `self` arg not as first argument of a method diagnostic)
 - #61114 (Vec: avoid creating slices to the elements)
 - #61144 (Suggest borrowing for loop head on move error)
 - #61149 (Fix spelling in release notes)
 - #61161 (MaybeUninit doctest: remove unnecessary type ascription)
 - #61173 (Auto-derive Encode and Decode implementations of DefPathTable)
 - #61184 (Add additional trace statements to the const propagator)
 - #61189 (Turn turbo 🐟 🍨 into an error)
 - #61193 (Add comment to explain why we change the layout for Projection)

Failed merges:

r? @ghost
@bors bors merged commit 428ab7e into rust-lang:master May 26, 2019
@RalfJung RalfJung deleted the vec branch May 29, 2019 08:01
Noratrieb added a commit to Noratrieb/rust that referenced this pull request May 28, 2022
Before, they went through `&str` and `&mut str`, which created
intermediary references, shrinking provenance to only the initialized
parts. `Vec<T>` already has such inherent methods added in rust-lang#61114.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Vec::push invalidates interior references even when it does not reallocate
6 participants