Merge branch 'master' into handle-drop-zst

pull/425/head
pwbh 1 year ago committed by GitHub
commit a322ae476a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -172,7 +172,7 @@ impl<'a, T> Hole<'a, T> {
fn removed(&self) -> &T { self.elt.as_ref().unwrap() } fn removed(&self) -> &T { self.elt.as_ref().unwrap() }
unsafe fn get(&self, index: usize) -> &T { &self.data[index] } fn get(&self, index: usize) -> &T { &self.data[index] }
unsafe fn move_to(&mut self, index: usize) { unsafe fn move_to(&mut self, index: usize) {
let index_ptr: *const _ = &self.data[index]; let index_ptr: *const _ = &self.data[index];

@ -106,7 +106,14 @@ that that `Vec<T>` _owns_ values of type `T` (more precisely: may use values of
in its `Drop` implementation), and Rust will thus not allow them to _dangle_ should a in its `Drop` implementation), and Rust will thus not allow them to _dangle_ should a
`Vec<T>` be dropped. `Vec<T>` be dropped.
**Adding an extra `_owns_T: PhantomData<T>` field is thus _superfluous_ and accomplishes nothing**. When a type already has a `Drop impl`, **adding an extra `_owns_T: PhantomData<T>` field
is thus _superfluous_ and accomplishes nothing**, dropck-wise (it still affects variance
and auto-traits).
- (advanced edge case: if the type containing the `PhantomData` has no `Drop` impl at all,
but still has drop glue (by having _another_ field with drop glue), then the
dropck/`#[may_dangle]` considerations mentioned herein do apply as well: a `PhantomData<T>`
field will then require `T` to be droppable whenever the containing type goes out of scope).
___ ___
@ -234,14 +241,18 @@ standard library made a utility for itself called `Unique<T>` which:
Heres a table of all the wonderful ways `PhantomData` could be used: Heres a table of all the wonderful ways `PhantomData` could be used:
| Phantom type | `'a` | `T` | `Send` | `Sync` | | Phantom type | variance of `'a` | variance of `T` | `Send`/`Sync`<br/>(or lack thereof) | dangling `'a` or `T` in drop glue<br/>(_e.g._, `#[may_dangle] Drop`) |
|-----------------------------|-----------|-----------------------------|-----------|-----------| |-----------------------------|:----------------:|:-----------------:|:-----------------------------------------:|:------------------------------------------------:|
| `PhantomData<T>` | - | covariant (with drop check) | `T: Send` | `T: Sync` | | `PhantomData<T>` | - | **cov**ariant | inherited | disallowed ("owns `T`") |
| `PhantomData<&'a T>` | covariant | covariant | `T: Sync` | `T: Sync` | | `PhantomData<&'a T>` | **cov**ariant | **cov**ariant | `Send + Sync`<br/>requires<br/>`T : Sync` | allowed |
| `PhantomData<&'a mut T>` | covariant | invariant | `T: Send` | `T: Sync` | | `PhantomData<&'a mut T>` | **cov**ariant | **inv**ariant | inherited | allowed |
| `PhantomData<*const T>` | - | covariant | - | - | | `PhantomData<*const T>` | - | **cov**ariant | `!Send + !Sync` | allowed |
| `PhantomData<*mut T>` | - | invariant | - | - | | `PhantomData<*mut T>` | - | **inv**ariant | `!Send + !Sync` | allowed |
| `PhantomData<fn(T)>` | - | contravariant | `Send` | `Sync` | | `PhantomData<fn(T)>` | - | **contra**variant | `Send + Sync` | allowed |
| `PhantomData<fn() -> T>` | - | covariant | `Send` | `Sync` | | `PhantomData<fn() -> T>` | - | **cov**ariant | `Send + Sync` | allowed |
| `PhantomData<fn(T) -> T>` | - | invariant | `Send` | `Sync` | | `PhantomData<fn(T) -> T>` | - | **inv**ariant | `Send + Sync` | allowed |
| `PhantomData<Cell<&'a ()>>` | invariant | - | `Send` | - | | `PhantomData<Cell<&'a ()>>` | **inv**ariant | - | `Send + !Sync` | allowed |
- Note: opting out of the `Unpin` auto-trait requires the dedicated [`PhantomPinned`] type instead.
[`PhantomPinned`]: ../core/marker/struct.PhantomPinned.html

@ -6,26 +6,28 @@ Safe Rust guarantees an absence of data races, which are defined as:
* one or more of them is a write * one or more of them is a write
* one or more of them is unsynchronized * one or more of them is unsynchronized
A data race has Undefined Behavior, and is therefore impossible to perform A data race has Undefined Behavior, and is therefore impossible to perform in
in Safe Rust. Data races are *mostly* prevented through Rust's ownership system: Safe Rust. Data races are *mostly* prevented through Rust's ownership system:
it's impossible to alias a mutable reference, so it's impossible to perform a it's impossible to alias a mutable reference, so it's impossible to perform a
data race. Interior mutability makes this more complicated, which is largely why data race. Interior mutability makes this more complicated, which is largely why
we have the Send and Sync traits (see below). we have the Send and Sync traits (see the next section for more on this).
**However Rust does not prevent general race conditions.** **However Rust does not prevent general race conditions.**
This is pretty fundamentally impossible, and probably honestly undesirable. Your This is mathematically impossible in situations where you do not control the
hardware is racy, your OS is racy, the other programs on your computer are racy, scheduler, which is true for the normal OS environment. If you do control
and the world this all runs in is racy. Any system that could genuinely claim to preemption, it _can be_ possible to prevent general races - this technique is
prevent *all* race conditions would be pretty awful to use, if not just used by frameworks such as [RTIC](https://github.com/rtic-rs/rtic). However,
incorrect. actually having control over scheduling is a very uncommon case.
So it's perfectly "fine" for a Safe Rust program to get deadlocked or do For this reason, it is considered "safe" for Rust to get deadlocked or do
something nonsensical with incorrect synchronization. Obviously such a program something nonsensical with incorrect synchronization: this is known as a general
isn't very good, but Rust can only hold your hand so far. Still, a race race condition or resource race. Obviously such a program isn't very good, but
condition can't violate memory safety in a Rust program on its own. Only in Rust of course cannot prevent all logic errors.
conjunction with some other unsafe code can a race condition actually violate
memory safety. For instance: In any case, a race condition cannot violate memory safety in a Rust program on
its own. Only in conjunction with some other unsafe code can a race condition
actually violate memory safety. For instance, a correct program looks like this:
```rust,no_run ```rust,no_run
use std::thread; use std::thread;
@ -58,6 +60,9 @@ thread::spawn(move || {
println!("{}", data[idx.load(Ordering::SeqCst)]); println!("{}", data[idx.load(Ordering::SeqCst)]);
``` ```
We can cause a data race if we instead do the bound check in advance, and then
unsafely access the data with an unchecked value:
```rust,no_run ```rust,no_run
use std::thread; use std::thread;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};

@ -310,9 +310,7 @@ thread_local! {
/// saves the input given into a thread local `Vec<&'static str>` /// saves the input given into a thread local `Vec<&'static str>`
fn store(input: &'static str) { fn store(input: &'static str) {
StaticVecs.with(|v| { StaticVecs.with_borrow_mut(|v| v.push(input));
v.borrow_mut().push(input);
})
} }
/// Calls the function with it's input (must have the same lifetime!) /// Calls the function with it's input (must have the same lifetime!)
@ -332,9 +330,8 @@ fn main() {
demo(&smuggle, store); demo(&smuggle, store);
} }
StaticVecs.with(|v| { // use after free 😿
println!("{:?}", v.borrow()); // use after free 😿 StaticVecs.with_borrow(|v| println!("{v:?}"));
});
} }
``` ```

@ -93,7 +93,7 @@ impl<T> IntoIterator for Vec<T> {
mem::forget(self); mem::forget(self);
IntoIter { IntoIter {
iter: iter, iter,
_buf: buf, _buf: buf,
} }
} }
@ -135,18 +135,16 @@ impl<'a, T> Drop for Drain<'a, T> {
impl<T> Vec<T> { impl<T> Vec<T> {
pub fn drain(&mut self) -> Drain<T> { pub fn drain(&mut self) -> Drain<T> {
unsafe { let iter = unsafe { RawValIter::new(&self) };
let iter = RawValIter::new(&self);
// this is a mem::forget safety thing. If Drain is forgotten, we just // this is a mem::forget safety thing. If Drain is forgotten, we just
// leak the whole Vec's contents. Also we need to do this *eventually* // leak the whole Vec's contents. Also we need to do this *eventually*
// anyway, so why not do it now? // anyway, so why not do it now?
self.len = 0; self.len = 0;
Drain { Drain {
iter: iter, iter,
vec: PhantomData, vec: PhantomData,
}
} }
} }
} }

@ -127,7 +127,7 @@ impl<T> Vec<T> {
pub fn insert(&mut self, index: usize, elem: T) { pub fn insert(&mut self, index: usize, elem: T) {
assert!(index <= self.len, "index out of bounds"); assert!(index <= self.len, "index out of bounds");
if self.cap() == self.len { if self.len == self.cap() {
self.buf.grow(); self.buf.grow();
} }
@ -138,14 +138,17 @@ impl<T> Vec<T> {
self.len - index, self.len - index,
); );
ptr::write(self.ptr().add(index), elem); ptr::write(self.ptr().add(index), elem);
self.len += 1;
} }
self.len += 1;
} }
pub fn remove(&mut self, index: usize) -> T { pub fn remove(&mut self, index: usize) -> T {
assert!(index < self.len, "index out of bounds"); assert!(index < self.len, "index out of bounds");
self.len -= 1;
unsafe { unsafe {
self.len -= 1;
let result = ptr::read(self.ptr().add(index)); let result = ptr::read(self.ptr().add(index));
ptr::copy( ptr::copy(
self.ptr().add(index + 1), self.ptr().add(index + 1),
@ -157,18 +160,16 @@ impl<T> Vec<T> {
} }
pub fn drain(&mut self) -> Drain<T> { pub fn drain(&mut self) -> Drain<T> {
unsafe { let iter = unsafe { RawValIter::new(&self) };
let iter = RawValIter::new(&self);
// this is a mem::forget safety thing. If Drain is forgotten, we just // this is a mem::forget safety thing. If Drain is forgotten, we just
// leak the whole Vec's contents. Also we need to do this *eventually* // leak the whole Vec's contents. Also we need to do this *eventually*
// anyway, so why not do it now? // anyway, so why not do it now?
self.len = 0; self.len = 0;
Drain { Drain {
iter: iter, iter,
vec: PhantomData, vec: PhantomData,
}
} }
} }
} }
@ -197,15 +198,15 @@ impl<T> IntoIterator for Vec<T> {
type Item = T; type Item = T;
type IntoIter = IntoIter<T>; type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> { fn into_iter(self) -> IntoIter<T> {
unsafe { let (iter, buf) = unsafe {
let iter = RawValIter::new(&self); (RawValIter::new(&self), ptr::read(&self.buf))
let buf = ptr::read(&self.buf); };
mem::forget(self);
IntoIter { mem::forget(self);
iter: iter,
_buf: buf, IntoIter {
} iter,
_buf: buf,
} }
} }
} }

@ -18,7 +18,7 @@ pub fn insert(&mut self, index: usize, elem: T) {
// Note: `<=` because it's valid to insert after everything // Note: `<=` because it's valid to insert after everything
// which would be equivalent to push. // which would be equivalent to push.
assert!(index <= self.len, "index out of bounds"); assert!(index <= self.len, "index out of bounds");
if self.cap == self.len { self.grow(); } if self.len == self.cap { self.grow(); }
unsafe { unsafe {
// ptr::copy(src, dest, len): "copy from src to dest len elems" // ptr::copy(src, dest, len): "copy from src to dest len elems"
@ -28,8 +28,9 @@ pub fn insert(&mut self, index: usize, elem: T) {
self.len - index, self.len - index,
); );
ptr::write(self.ptr.as_ptr().add(index), elem); ptr::write(self.ptr.as_ptr().add(index), elem);
self.len += 1;
} }
self.len += 1;
} }
``` ```

@ -68,18 +68,16 @@ impl<T> IntoIterator for Vec<T> {
let cap = vec.cap; let cap = vec.cap;
let len = vec.len; let len = vec.len;
unsafe { IntoIter {
IntoIter { buf: ptr,
buf: ptr, cap,
cap: cap, start: ptr.as_ptr(),
start: ptr.as_ptr(), end: if cap == 0 {
end: if cap == 0 { // can't offset off this pointer, it's not allocated!
// can't offset off this pointer, it's not allocated! ptr.as_ptr()
ptr.as_ptr() } else {
} else { unsafe { ptr.as_ptr().add(len) }
ptr.as_ptr().add(len) },
},
}
} }
} }
} }

@ -40,7 +40,6 @@ we get the same results as using `Unique<T>`:
```rust ```rust
use std::ptr::NonNull; use std::ptr::NonNull;
use std::marker::PhantomData;
pub struct Vec<T> { pub struct Vec<T> {
ptr: NonNull<T>, ptr: NonNull<T>,

@ -131,23 +131,21 @@ impl<T> IntoIterator for Vec<T> {
type Item = T; type Item = T;
type IntoIter = IntoIter<T>; type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> { fn into_iter(self) -> IntoIter<T> {
unsafe { // need to use ptr::read to unsafely move the buf out since it's
// need to use ptr::read to unsafely move the buf out since it's // not Copy, and Vec implements Drop (so we can't destructure it).
// not Copy, and Vec implements Drop (so we can't destructure it). let buf = unsafe { ptr::read(&self.buf) };
let buf = ptr::read(&self.buf); let len = self.len;
let len = self.len; mem::forget(self);
mem::forget(self);
IntoIter {
IntoIter { start: buf.ptr.as_ptr(),
start: buf.ptr.as_ptr(), end: if buf.cap == 0 {
end: if buf.cap == 0 { // can't offset off of a pointer unless it's part of an allocation
// can't offset off of a pointer unless it's part of an allocation buf.ptr.as_ptr()
buf.ptr.as_ptr() } else {
} else { unsafe { buf.ptr.as_ptr().add(len) }
buf.ptr.as_ptr().add(len) },
}, _buf: buf,
_buf: buf,
}
} }
} }
} }

Loading…
Cancel
Save