Skip to content

Commit 781ac3e

Browse files
committed
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]
1 parent abdacec commit 781ac3e

File tree

8 files changed

+37
-24
lines changed

8 files changed

+37
-24
lines changed

src/libarena/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
extern crate collections;
2828

29-
use std::cast::{transmute, transmute_mut, transmute_mut_lifetime};
29+
use std::cast::{transmute, transmute_mut_lifetime};
3030
use std::cast;
3131
use std::cell::{Cell, RefCell};
3232
use std::mem;
@@ -281,8 +281,8 @@ impl Arena {
281281
#[inline]
282282
pub fn alloc<'a, T>(&'a self, op: || -> T) -> &'a T {
283283
unsafe {
284-
// FIXME: Borrow check
285-
let this = transmute_mut(self);
284+
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
285+
let this: &mut Arena = transmute::<&_, &mut _>(self);
286286
if intrinsics::needs_drop::<T>() {
287287
this.alloc_noncopy(op)
288288
} else {
@@ -438,7 +438,8 @@ impl<T> TypedArena<T> {
438438
#[inline]
439439
pub fn alloc<'a>(&'a self, object: T) -> &'a T {
440440
unsafe {
441-
let this = cast::transmute_mut(self);
441+
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
442+
let this: &mut TypedArena<T> = cast::transmute::<&_, &mut _>(self);
442443
if this.ptr == this.end {
443444
this.grow()
444445
}

src/libnative/io/file_win32.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ impl rtio::RtioFileStream for FileDesc {
174174
fn tell(&self) -> Result<u64, IoError> {
175175
// This transmute is fine because our seek implementation doesn't
176176
// actually use the mutable self at all.
177-
unsafe { cast::transmute_mut(self).seek(0, io::SeekCur) }
177+
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
178+
unsafe { cast::transmute::<&_, &mut FileDesc>(self).seek(0, io::SeekCur) }
178179
}
179180

180181
fn fsync(&mut self) -> Result<(), IoError> {

src/librustuv/file.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,8 @@ impl rtio::RtioFileStream for FileWatcher {
445445
fn tell(&self) -> Result<u64, IoError> {
446446
use libc::SEEK_CUR;
447447
// this is temporary
448-
let self_ = unsafe { cast::transmute_mut(self) };
448+
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
449+
let self_ = unsafe { cast::transmute::<&_, &mut FileWatcher>(self) };
449450
self_.seek_common(0, SEEK_CUR)
450451
}
451452
fn fsync(&mut self) -> Result<(), IoError> {

src/libstd/cast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub unsafe fn transmute<L, G>(thing: L) -> G {
6060

6161
/// Coerce an immutable reference to be mutable.
6262
#[inline]
63+
#[deprecated="casting &T to &mut T is undefined behaviour: use Cell<T>, RefCell<T> or Unsafe<T>"]
6364
pub unsafe fn transmute_mut<'a,T>(ptr: &'a T) -> &'a mut T { transmute(ptr) }
6465

6566
/// Coerce a reference to have an arbitrary associated lifetime.

src/libstd/comm/mod.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ mod stream;
318318
mod shared;
319319
mod sync;
320320

321+
// FIXME #13933: Remove/justify all `&T` to `&mut T` transmutes
322+
unsafe fn transmute_mut<'a,T>(x: &'a T) -> &'a mut T {
323+
cast::transmute::<&_, &mut _>(x)
324+
}
325+
321326
// Use a power of 2 to allow LLVM to optimize to something that's not a
322327
// division, this is hit pretty regularly.
323328
static RESCHED_FREQ: int = 256;
@@ -565,7 +570,7 @@ impl<T: Send> Sender<T> {
565570

566571
unsafe {
567572
let mut tmp = Sender::new(Stream(new_inner));
568-
mem::swap(&mut cast::transmute_mut(self).inner, &mut tmp.inner);
573+
mem::swap(&mut transmute_mut(self).inner, &mut tmp.inner);
569574
}
570575
return ret;
571576
}
@@ -599,7 +604,7 @@ impl<T: Send> Clone for Sender<T> {
599604
(*packet.get()).inherit_blocker(sleeper);
600605

601606
let mut tmp = Sender::new(Shared(packet.clone()));
602-
mem::swap(&mut cast::transmute_mut(self).inner, &mut tmp.inner);
607+
mem::swap(&mut transmute_mut(self).inner, &mut tmp.inner);
603608
}
604609
Sender::new(Shared(packet))
605610
}
@@ -790,7 +795,7 @@ impl<T: Send> Receiver<T> {
790795
}
791796
};
792797
unsafe {
793-
mem::swap(&mut cast::transmute_mut(self).inner,
798+
mem::swap(&mut transmute_mut(self).inner,
794799
&mut new_port.inner);
795800
}
796801
}
@@ -837,7 +842,7 @@ impl<T: Send> Receiver<T> {
837842
Sync(ref p) => return unsafe { (*p.get()).recv() }
838843
};
839844
unsafe {
840-
mem::swap(&mut cast::transmute_mut(self).inner,
845+
mem::swap(&mut transmute_mut(self).inner,
841846
&mut new_port.inner);
842847
}
843848
}
@@ -874,7 +879,7 @@ impl<T: Send> select::Packet for Receiver<T> {
874879
}
875880
};
876881
unsafe {
877-
mem::swap(&mut cast::transmute_mut(self).inner,
882+
mem::swap(&mut transmute_mut(self).inner,
878883
&mut new_port.inner);
879884
}
880885
}
@@ -906,7 +911,7 @@ impl<T: Send> select::Packet for Receiver<T> {
906911
};
907912
task = t;
908913
unsafe {
909-
mem::swap(&mut cast::transmute_mut(self).inner,
914+
mem::swap(&mut transmute_mut(self).inner,
910915
&mut new_port.inner);
911916
}
912917
}
@@ -930,7 +935,7 @@ impl<T: Send> select::Packet for Receiver<T> {
930935
let mut new_port = match result { Ok(b) => return b, Err(p) => p };
931936
was_upgrade = true;
932937
unsafe {
933-
mem::swap(&mut cast::transmute_mut(self).inner,
938+
mem::swap(&mut transmute_mut(self).inner,
934939
&mut new_port.inner);
935940
}
936941
}

src/libstd/local_data.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ pub fn get_mut<T: 'static, U>(key: Key<T>, f: |Option<&mut T>| -> U) -> U {
196196
match x {
197197
None => f(None),
198198
// We're violating a lot of compiler guarantees with this
199-
// invocation of `transmute_mut`, but we're doing runtime checks to
199+
// invocation of `transmute`, but we're doing runtime checks to
200200
// ensure that it's always valid (only one at a time).
201201
//
202202
// there is no need to be upset!
203-
Some(x) => { f(Some(unsafe { cast::transmute_mut(x) })) }
203+
Some(x) => { f(Some(unsafe { cast::transmute::<&_, &mut _>(x) })) }
204204
}
205205
})
206206
}

src/libstd/slice.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,15 +1739,19 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] {
17391739
if self.len() == 0 { return None; }
17401740
unsafe {
17411741
let s: &mut Slice<T> = transmute(self);
1742-
Some(cast::transmute_mut(&*raw::shift_ptr(s)))
1742+
// FIXME #13933: this `&` -> `&mut` cast is a little
1743+
// dubious
1744+
Some(&mut *(raw::shift_ptr(s) as *mut _))
17431745
}
17441746
}
17451747

17461748
fn mut_pop_ref(&mut self) -> Option<&'a mut T> {
17471749
if self.len() == 0 { return None; }
17481750
unsafe {
17491751
let s: &mut Slice<T> = transmute(self);
1750-
Some(cast::transmute_mut(&*raw::pop_ptr(s)))
1752+
// FIXME #13933: this `&` -> `&mut` cast is a little
1753+
// dubious
1754+
Some(&mut *(raw::pop_ptr(s) as *mut _))
17511755
}
17521756
}
17531757

@@ -3108,23 +3112,23 @@ mod tests {
31083112
#[should_fail]
31093113
fn test_from_elem_fail() {
31103114
use cast;
3115+
use cell::Cell;
31113116
use rc::Rc;
31123117

31133118
struct S {
3114-
f: int,
3119+
f: Cell<int>,
31153120
boxes: (~int, Rc<int>)
31163121
}
31173122

31183123
impl Clone for S {
31193124
fn clone(&self) -> S {
3120-
let s = unsafe { cast::transmute_mut(self) };
3121-
s.f += 1;
3122-
if s.f == 10 { fail!() }
3123-
S { f: s.f, boxes: s.boxes.clone() }
3125+
self.f.set(self.f.get() + 1);
3126+
if self.f.get() == 10 { fail!() }
3127+
S { f: self.f, boxes: self.boxes.clone() }
31243128
}
31253129
}
31263130

3127-
let s = S { f: 0, boxes: (box 0, Rc::new(0)) };
3131+
let s = S { f: Cell::new(0), boxes: (box 0, Rc::new(0)) };
31283132
let _ = Vec::from_elem(100, s);
31293133
}
31303134

src/libsync/arc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<T: Send + Share + Clone> Arc<T> {
148148
// reference count is guaranteed to be 1 at this point, and we required
149149
// the Arc itself to be `mut`, so we're returning the only possible
150150
// reference to the inner data.
151-
unsafe { cast::transmute_mut(self.deref()) }
151+
unsafe { cast::transmute::<&_, &mut _>(self.deref()) }
152152
}
153153
}
154154

0 commit comments

Comments
 (0)