diff options
| author | Huon Wilson <dbau.pp+github@gmail.com> | 2014-05-04 23:17:37 +1000 |
|---|---|---|
| committer | Huon Wilson <dbau.pp+github@gmail.com> | 2014-05-05 18:20:41 +1000 |
| commit | 781ac3e777a5f47bdfaba05ee17f8b79845670b1 (patch) | |
| tree | 4c79abc527b6dfdfadd39bf7fe8ac96ad05326a8 /src/libstd | |
| parent | abdacecdf86b4b5a4f432560445a24e1c5f4751b (diff) | |
std: deprecate cast::transmute_mut.
Turning a `&T` into an `&mut T` carries a large risk of undefined
behaviour, and needs to be done very very carefully. Providing a
convenience function for exactly this task is a bad idea, just tempting
people into doing the wrong thing.
The right thing is to use types like `Cell`, `RefCell` or `Unsafe`.
For memory safety, Rust has that guarantee that `&mut` pointers do not
alias with any other pointer, that is, if you have a `&mut T` then that
is the only usable pointer to that `T`. This allows Rust to assume that
writes through a `&mut T` do not affect the values of any other `&` or
`&mut` references. `&` pointers have no guarantees about aliasing or
not, so it's entirely possible for the same pointer to be passed into
both arguments of a function like
fn foo(x: &int, y: &int) { ... }
Converting either of `x` or `y` to a `&mut` pointer and modifying it
would affect the other value: invalid behaviour.
(Similarly, it's undefined behaviour to modify the value of an immutable
local, like `let x = 1;`.)
At a low-level, the *only* safe way to obtain an `&mut` out of a `&` is
using the `Unsafe` type (there are higher level wrappers around it, like
`Cell`, `RefCell`, `Mutex` etc.). The `Unsafe` type is registered with
the compiler so that it can reason a little about these `&` to `&mut`
casts, but it is still up to the user to ensure that the `&mut`s
obtained out of an `Unsafe` never alias.
(Note that *any* conversion from `&` to `&mut` can be invalid, including
a plain `transmute`, or casting `&T` -> `*T` -> `*mut T` -> `&mut T`.)
[breaking-change]
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/cast.rs | 1 | ||||
| -rw-r--r-- | src/libstd/comm/mod.rs | 19 | ||||
| -rw-r--r-- | src/libstd/local_data.rs | 4 | ||||
| -rw-r--r-- | src/libstd/slice.rs | 20 |
4 files changed, 27 insertions, 17 deletions
diff --git a/src/libstd/cast.rs b/src/libstd/cast.rs index bcbf804d7b3..7a8f517bbf9 100644 --- a/src/libstd/cast.rs +++ b/src/libstd/cast.rs @@ -60,6 +60,7 @@ pub unsafe fn transmute<L, G>(thing: L) -> G { /// Coerce an immutable reference to be mutable. #[inline] +#[deprecated="casting &T to &mut T is undefined behaviour: use Cell<T>, RefCell<T> or Unsafe<T>"] pub unsafe fn transmute_mut<'a,T>(ptr: &'a T) -> &'a mut T { transmute(ptr) } /// Coerce a reference to have an arbitrary associated lifetime. diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs index 92e3e82c1c5..bbe34d20f6a 100644 --- a/src/libstd/comm/mod.rs +++ b/src/libstd/comm/mod.rs @@ -318,6 +318,11 @@ mod stream; mod shared; mod sync; +// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes +unsafe fn transmute_mut<'a,T>(x: &'a T) -> &'a mut T { + cast::transmute::<&_, &mut _>(x) +} + // Use a power of 2 to allow LLVM to optimize to something that's not a // division, this is hit pretty regularly. static RESCHED_FREQ: int = 256; @@ -565,7 +570,7 @@ impl<T: Send> Sender<T> { unsafe { let mut tmp = Sender::new(Stream(new_inner)); - mem::swap(&mut cast::transmute_mut(self).inner, &mut tmp.inner); + mem::swap(&mut transmute_mut(self).inner, &mut tmp.inner); } return ret; } @@ -599,7 +604,7 @@ impl<T: Send> Clone for Sender<T> { (*packet.get()).inherit_blocker(sleeper); let mut tmp = Sender::new(Shared(packet.clone())); - mem::swap(&mut cast::transmute_mut(self).inner, &mut tmp.inner); + mem::swap(&mut transmute_mut(self).inner, &mut tmp.inner); } Sender::new(Shared(packet)) } @@ -790,7 +795,7 @@ impl<T: Send> Receiver<T> { } }; unsafe { - mem::swap(&mut cast::transmute_mut(self).inner, + mem::swap(&mut transmute_mut(self).inner, &mut new_port.inner); } } @@ -837,7 +842,7 @@ impl<T: Send> Receiver<T> { Sync(ref p) => return unsafe { (*p.get()).recv() } }; unsafe { - mem::swap(&mut cast::transmute_mut(self).inner, + mem::swap(&mut transmute_mut(self).inner, &mut new_port.inner); } } @@ -874,7 +879,7 @@ impl<T: Send> select::Packet for Receiver<T> { } }; unsafe { - mem::swap(&mut cast::transmute_mut(self).inner, + mem::swap(&mut transmute_mut(self).inner, &mut new_port.inner); } } @@ -906,7 +911,7 @@ impl<T: Send> select::Packet for Receiver<T> { }; task = t; unsafe { - mem::swap(&mut cast::transmute_mut(self).inner, + mem::swap(&mut transmute_mut(self).inner, &mut new_port.inner); } } @@ -930,7 +935,7 @@ impl<T: Send> select::Packet for Receiver<T> { let mut new_port = match result { Ok(b) => return b, Err(p) => p }; was_upgrade = true; unsafe { - mem::swap(&mut cast::transmute_mut(self).inner, + mem::swap(&mut transmute_mut(self).inner, &mut new_port.inner); } } diff --git a/src/libstd/local_data.rs b/src/libstd/local_data.rs index bc6e324a5f7..e5c7ba4aa54 100644 --- a/src/libstd/local_data.rs +++ b/src/libstd/local_data.rs @@ -196,11 +196,11 @@ pub fn get_mut<T: 'static, U>(key: Key<T>, f: |Option<&mut T>| -> U) -> U { match x { None => f(None), // We're violating a lot of compiler guarantees with this - // invocation of `transmute_mut`, but we're doing runtime checks to + // invocation of `transmute`, but we're doing runtime checks to // ensure that it's always valid (only one at a time). // // there is no need to be upset! - Some(x) => { f(Some(unsafe { cast::transmute_mut(x) })) } + Some(x) => { f(Some(unsafe { cast::transmute::<&_, &mut _>(x) })) } } }) } diff --git a/src/libstd/slice.rs b/src/libstd/slice.rs index cb8550d248e..d8c866ef44a 100644 --- a/src/libstd/slice.rs +++ b/src/libstd/slice.rs @@ -1739,7 +1739,9 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] { if self.len() == 0 { return None; } unsafe { let s: &mut Slice<T> = transmute(self); - Some(cast::transmute_mut(&*raw::shift_ptr(s))) + // FIXME #13933: this `&` -> `&mut` cast is a little + // dubious + Some(&mut *(raw::shift_ptr(s) as *mut _)) } } @@ -1747,7 +1749,9 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] { if self.len() == 0 { return None; } unsafe { let s: &mut Slice<T> = transmute(self); - Some(cast::transmute_mut(&*raw::pop_ptr(s))) + // FIXME #13933: this `&` -> `&mut` cast is a little + // dubious + Some(&mut *(raw::pop_ptr(s) as *mut _)) } } @@ -3108,23 +3112,23 @@ mod tests { #[should_fail] fn test_from_elem_fail() { use cast; + use cell::Cell; use rc::Rc; struct S { - f: int, + f: Cell<int>, boxes: (~int, Rc<int>) } impl Clone for S { fn clone(&self) -> S { - let s = unsafe { cast::transmute_mut(self) }; - s.f += 1; - if s.f == 10 { fail!() } - S { f: s.f, boxes: s.boxes.clone() } + self.f.set(self.f.get() + 1); + if self.f.get() == 10 { fail!() } + S { f: self.f, boxes: self.boxes.clone() } } } - let s = S { f: 0, boxes: (box 0, Rc::new(0)) }; + let s = S { f: Cell::new(0), boxes: (box 0, Rc::new(0)) }; let _ = Vec::from_elem(100, s); } |
