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]
This commit is contained in:
parent
abdacecdf8
commit
781ac3e777
@ -26,7 +26,7 @@
|
||||
|
||||
extern crate collections;
|
||||
|
||||
use std::cast::{transmute, transmute_mut, transmute_mut_lifetime};
|
||||
use std::cast::{transmute, transmute_mut_lifetime};
|
||||
use std::cast;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::mem;
|
||||
@ -281,8 +281,8 @@ impl Arena {
|
||||
#[inline]
|
||||
pub fn alloc<'a, T>(&'a self, op: || -> T) -> &'a T {
|
||||
unsafe {
|
||||
// FIXME: Borrow check
|
||||
let this = transmute_mut(self);
|
||||
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
|
||||
let this: &mut Arena = transmute::<&_, &mut _>(self);
|
||||
if intrinsics::needs_drop::<T>() {
|
||||
this.alloc_noncopy(op)
|
||||
} else {
|
||||
@ -438,7 +438,8 @@ impl<T> TypedArena<T> {
|
||||
#[inline]
|
||||
pub fn alloc<'a>(&'a self, object: T) -> &'a T {
|
||||
unsafe {
|
||||
let this = cast::transmute_mut(self);
|
||||
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
|
||||
let this: &mut TypedArena<T> = cast::transmute::<&_, &mut _>(self);
|
||||
if this.ptr == this.end {
|
||||
this.grow()
|
||||
}
|
||||
|
@ -174,7 +174,8 @@ impl rtio::RtioFileStream for FileDesc {
|
||||
fn tell(&self) -> Result<u64, IoError> {
|
||||
// This transmute is fine because our seek implementation doesn't
|
||||
// actually use the mutable self at all.
|
||||
unsafe { cast::transmute_mut(self).seek(0, io::SeekCur) }
|
||||
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
|
||||
unsafe { cast::transmute::<&_, &mut FileDesc>(self).seek(0, io::SeekCur) }
|
||||
}
|
||||
|
||||
fn fsync(&mut self) -> Result<(), IoError> {
|
||||
|
@ -445,7 +445,8 @@ impl rtio::RtioFileStream for FileWatcher {
|
||||
fn tell(&self) -> Result<u64, IoError> {
|
||||
use libc::SEEK_CUR;
|
||||
// this is temporary
|
||||
let self_ = unsafe { cast::transmute_mut(self) };
|
||||
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
|
||||
let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
|
||||
self_.seek_common(0, SEEK_CUR)
|
||||
}
|
||||
fn fsync(&mut self) -> Result<(), IoError> {
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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) })) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ impl<T: Send + Share + Clone> Arc<T> {
|
||||
// reference count is guaranteed to be 1 at this point, and we required
|
||||
// the Arc itself to be `mut`, so we're returning the only possible
|
||||
// reference to the inner data.
|
||||
unsafe { cast::transmute_mut(self.deref()) }
|
||||
unsafe { cast::transmute::<&_, &mut _>(self.deref()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user