Merge branch 'master' into handle-drop-zst

pull/425/head
pwbh 10 months 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() }
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) {
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
`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:
| Phantom type | `'a` | `T` | `Send` | `Sync` |
|-----------------------------|-----------|-----------------------------|-----------|-----------|
| `PhantomData<T>` | - | covariant (with drop check) | `T: Send` | `T: Sync` |
| `PhantomData<&'a T>` | covariant | covariant | `T: Sync` | `T: Sync` |
| `PhantomData<&'a mut T>` | covariant | invariant | `T: Send` | `T: Sync` |
| `PhantomData<*const T>` | - | covariant | - | - |
| `PhantomData<*mut T>` | - | invariant | - | - |
| `PhantomData<fn(T)>` | - | contravariant | `Send` | `Sync` |
| `PhantomData<fn() -> T>` | - | covariant | `Send` | `Sync` |
| `PhantomData<fn(T) -> T>` | - | invariant | `Send` | `Sync` |
| `PhantomData<Cell<&'a ()>>` | invariant | - | `Send` | - |
| 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>` | - | **cov**ariant | inherited | disallowed ("owns `T`") |
| `PhantomData<&'a T>` | **cov**ariant | **cov**ariant | `Send + Sync`<br/>requires<br/>`T : Sync` | allowed |
| `PhantomData<&'a mut T>` | **cov**ariant | **inv**ariant | inherited | allowed |
| `PhantomData<*const T>` | - | **cov**ariant | `!Send + !Sync` | allowed |
| `PhantomData<*mut T>` | - | **inv**ariant | `!Send + !Sync` | allowed |
| `PhantomData<fn(T)>` | - | **contra**variant | `Send + Sync` | allowed |
| `PhantomData<fn() -> T>` | - | **cov**ariant | `Send + Sync` | allowed |
| `PhantomData<fn(T) -> T>` | - | **inv**ariant | `Send + Sync` | allowed |
| `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 unsynchronized
A data race has Undefined Behavior, and is therefore impossible to perform
in Safe Rust. Data races are *mostly* prevented through Rust's ownership system:
A data race has Undefined Behavior, and is therefore impossible to perform in
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
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.**
This is pretty fundamentally impossible, and probably honestly undesirable. Your
hardware is racy, your OS is racy, the other programs on your computer are racy,
and the world this all runs in is racy. Any system that could genuinely claim to
prevent *all* race conditions would be pretty awful to use, if not just
incorrect.
This is mathematically impossible in situations where you do not control the
scheduler, which is true for the normal OS environment. If you do control
preemption, it _can be_ possible to prevent general races - this technique is
used by frameworks such as [RTIC](https://github.com/rtic-rs/rtic). However,
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
something nonsensical with incorrect synchronization. Obviously such a program
isn't very good, but Rust can only hold your hand so far. Still, a race
condition can't 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:
For this reason, it is considered "safe" for Rust to get deadlocked or do
something nonsensical with incorrect synchronization: this is known as a general
race condition or resource race. Obviously such a program isn't very good, but
Rust of course cannot prevent all logic errors.
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
use std::thread;
@ -58,6 +60,9 @@ thread::spawn(move || {
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
use std::thread;
use std::sync::atomic::{AtomicUsize, Ordering};

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

@ -93,7 +93,7 @@ impl<T> IntoIterator for Vec<T> {
mem::forget(self);
IntoIter {
iter: iter,
iter,
_buf: buf,
}
}
@ -135,18 +135,16 @@ impl<'a, T> Drop for Drain<'a, T> {
impl<T> Vec<T> {
pub fn drain(&mut self) -> Drain<T> {
unsafe {
let iter = RawValIter::new(&self);
let iter = unsafe { RawValIter::new(&self) };
// 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*
// anyway, so why not do it now?
self.len = 0;
// 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*
// anyway, so why not do it now?
self.len = 0;
Drain {
iter: iter,
vec: PhantomData,
}
Drain {
iter,
vec: PhantomData,
}
}
}

@ -127,7 +127,7 @@ impl<T> Vec<T> {
pub fn insert(&mut self, index: usize, elem: T) {
assert!(index <= self.len, "index out of bounds");
if self.cap() == self.len {
if self.len == self.cap() {
self.buf.grow();
}
@ -138,14 +138,17 @@ impl<T> Vec<T> {
self.len - index,
);
ptr::write(self.ptr().add(index), elem);
self.len += 1;
}
self.len += 1;
}
pub fn remove(&mut self, index: usize) -> T {
assert!(index < self.len, "index out of bounds");
self.len -= 1;
unsafe {
self.len -= 1;
let result = ptr::read(self.ptr().add(index));
ptr::copy(
self.ptr().add(index + 1),
@ -157,18 +160,16 @@ impl<T> Vec<T> {
}
pub fn drain(&mut self) -> Drain<T> {
unsafe {
let iter = RawValIter::new(&self);
let iter = unsafe { RawValIter::new(&self) };
// 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*
// anyway, so why not do it now?
self.len = 0;
// 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*
// anyway, so why not do it now?
self.len = 0;
Drain {
iter: iter,
vec: PhantomData,
}
Drain {
iter,
vec: PhantomData,
}
}
}
@ -197,15 +198,15 @@ impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> {
unsafe {
let iter = RawValIter::new(&self);
let buf = ptr::read(&self.buf);
mem::forget(self);
let (iter, buf) = unsafe {
(RawValIter::new(&self), ptr::read(&self.buf))
};
IntoIter {
iter: iter,
_buf: buf,
}
mem::forget(self);
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
// which would be equivalent to push.
assert!(index <= self.len, "index out of bounds");
if self.cap == self.len { self.grow(); }
if self.len == self.cap { self.grow(); }
unsafe {
// 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,
);
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 len = vec.len;
unsafe {
IntoIter {
buf: ptr,
cap: cap,
start: ptr.as_ptr(),
end: if cap == 0 {
// can't offset off this pointer, it's not allocated!
ptr.as_ptr()
} else {
ptr.as_ptr().add(len)
},
}
IntoIter {
buf: ptr,
cap,
start: ptr.as_ptr(),
end: if cap == 0 {
// can't offset off this pointer, it's not allocated!
ptr.as_ptr()
} else {
unsafe { ptr.as_ptr().add(len) }
},
}
}
}

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

@ -131,23 +131,21 @@ impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> {
unsafe {
// 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).
let buf = ptr::read(&self.buf);
let len = self.len;
mem::forget(self);
IntoIter {
start: buf.ptr.as_ptr(),
end: if buf.cap == 0 {
// can't offset off of a pointer unless it's part of an allocation
buf.ptr.as_ptr()
} else {
buf.ptr.as_ptr().add(len)
},
_buf: buf,
}
// 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).
let buf = unsafe { ptr::read(&self.buf) };
let len = self.len;
mem::forget(self);
IntoIter {
start: buf.ptr.as_ptr(),
end: if buf.cap == 0 {
// can't offset off of a pointer unless it's part of an allocation
buf.ptr.as_ptr()
} else {
unsafe { buf.ptr.as_ptr().add(len) }
},
_buf: buf,
}
}
}

Loading…
Cancel
Save