Skip to content

Commit

Permalink
lint: deny transmuting from immutable to mutable, since it's undefine…
Browse files Browse the repository at this point in the history
…d behavior

[breaking-change] Technically breaking, since code that had been using
these transmutes before will no longer compile. However, it was
undefined behavior, so really, it's a good thing. Fixing your code would
require some re-working to use an UnsafeCell instead.

Closes #13146
  • Loading branch information
seanmonstar committed May 6, 2015
1 parent 6cd7486 commit d131f33
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
66 changes: 66 additions & 0 deletions src/librustc_lint/builtin.rs
Expand Up @@ -2121,6 +2121,72 @@ impl LintPass for InvalidNoMangleItems {
}
}

#[derive(Clone, Copy)]
pub struct MutableTransmutes;

declare_lint! {
MUTABLE_TRANSMUTES,
Deny,
"mutating transmuted &mut T from &T may cause undefined behavior"
}

impl LintPass for MutableTransmutes {
fn get_lints(&self) -> LintArray {
lint_array!(MUTABLE_TRANSMUTES)
}

fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) {
use syntax::ast::DefId;
use syntax::abi::RustIntrinsic;
let msg = "mutating transmuted &mut T from &T may cause undefined behavior,\
consider instead using an UnsafeCell";
match get_transmute_from_to(cx, expr) {
Some((&ty::ty_rptr(_, from_mt), &ty::ty_rptr(_, to_mt))) => {
if to_mt.mutbl == ast::Mutability::MutMutable
&& from_mt.mutbl == ast::Mutability::MutImmutable {
cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg);
}
}
_ => ()
}

fn get_transmute_from_to<'a, 'tcx>(cx: &Context<'a, 'tcx>, expr: &ast::Expr)
-> Option<(&'tcx ty::sty<'tcx>, &'tcx ty::sty<'tcx>)> {
match expr.node {
ast::ExprPath(..) => (),
_ => return None
}
if let DefFn(did, _) = ty::resolve_expr(cx.tcx, expr) {
if !def_id_is_transmute(cx, did) {
return None;
}
let typ = ty::node_id_to_type(cx.tcx, expr.id);
match typ.sty {
ty::ty_bare_fn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => {
if let ty::FnConverging(to) = bare_fn.sig.0.output {
let from = bare_fn.sig.0.inputs[0];
return Some((&from.sty, &to.sty));
}
},
_ => ()
}
}
None
}

fn def_id_is_transmute(cx: &Context, def_id: DefId) -> bool {
match ty::lookup_item_type(cx.tcx, def_id).ty.sty {
ty::ty_bare_fn(_, ref bfty) if bfty.abi == RustIntrinsic => (),
_ => return false
}
ty::with_path(cx.tcx, def_id, |path| match path.last() {
Some(ref last) => last.name().as_str() == "transmute",
_ => false
})
}
}
}

/// Forbids using the `#[feature(...)]` attribute
#[derive(Copy, Clone)]
pub struct UnstableFeatures;
Expand Down
1 change: 1 addition & 0 deletions src/librustc_lint/lib.rs
Expand Up @@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
InvalidNoMangleItems,
PluginAsLibrary,
DropWithReprExtern,
MutableTransmutes,
);

add_builtin_with_new!(sess,
Expand Down
20 changes: 20 additions & 0 deletions src/test/compile-fail/transmute-imut-to-mut.rs
@@ -0,0 +1,20 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Tests that transmuting from &T to &mut T is Undefined Behavior.

use std::mem::transmute;

fn main() {
let _a: &mut u8 = unsafe { transmute(&1u8) };
//~^ ERROR mutating transmuted &mut T from &T may cause undefined behavior
}


0 comments on commit d131f33

Please sign in to comment.