]> git.lizzy.rs Git - rust.git/commitdiff
lint: deny transmuting from immutable to mutable, since it's undefined behavior
authorSean McArthur <sean.monstar@gmail.com>
Mon, 13 Apr 2015 21:49:10 +0000 (14:49 -0700)
committerSean McArthur <sean.monstar@gmail.com>
Wed, 6 May 2015 05:26:23 +0000 (22:26 -0700)
[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

src/librustc_lint/builtin.rs
src/librustc_lint/lib.rs
src/test/compile-fail/transmute-imut-to-mut.rs [new file with mode: 0644]

index 19e71d6e40d5aecc755863a9042837d4d5fd5549..8d555240e707b31751facd1d4c5f8fbca1e010eb 100644 (file)
@@ -2121,6 +2121,72 @@ fn check_item(&mut self, cx: &Context, it: &ast::Item) {
     }
 }
 
+#[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;
index 970f9c634a2caad2c4cbe4abb402786486e03c5f..df834c36e5b225262d13ccd18d3b941cb7f336a0 100644 (file)
@@ -109,6 +109,7 @@ macro_rules! add_lint_group {
                  InvalidNoMangleItems,
                  PluginAsLibrary,
                  DropWithReprExtern,
+                 MutableTransmutes,
                  );
 
     add_builtin_with_new!(sess,
diff --git a/src/test/compile-fail/transmute-imut-to-mut.rs b/src/test/compile-fail/transmute-imut-to-mut.rs
new file mode 100644 (file)
index 0000000..2e07633
--- /dev/null
@@ -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
+}
+
+