about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/lint/builtin.rs9
-rw-r--r--src/librustc/lint/context.rs3
-rw-r--r--src/librustc_lint/lib.rs4
-rw-r--r--src/librustc_trans/trans/intrinsic.rs60
-rw-r--r--src/test/compile-fail/transmute-from-fn-item-types-lint.rs51
-rw-r--r--src/test/run-pass/transmute-from-fn-item-types.rs27
6 files changed, 140 insertions, 14 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 4bb69a2688a..97a550a4076 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -148,6 +148,12 @@ declare_lint! {
     "uses of #[derive] with raw pointers are rarely correct"
 }
 
+declare_lint! {
+    pub TRANSMUTE_FROM_FN_ITEM_TYPES,
+    Warn,
+    "transmute from function item type to pointer-sized type erroneously allowed"
+}
+
 /// Does nothing as a lint pass, but registers some `Lint`s
 /// which are used by other parts of the compiler.
 #[derive(Copy, Clone)]
@@ -177,7 +183,8 @@ impl LintPass for HardwiredLints {
             INVALID_TYPE_PARAM_DEFAULT,
             MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
             CONST_ERR,
-            RAW_POINTER_DERIVE
+            RAW_POINTER_DERIVE,
+            TRANSMUTE_FROM_FN_ITEM_TYPES
         )
     }
 }
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 023b9722504..c11e9dc822e 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -1287,6 +1287,9 @@ pub fn check_crate(tcx: &TyCtxt, access_levels: &AccessLevels) {
     }
 
     *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
+
+    // Put the lint store back in the session.
+    mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints);
 }
 
 pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 1cf0339c086..e47f67dad8f 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -171,6 +171,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
             reference: "RFC 218 <https://github.com/rust-lang/rfcs/blob/\
                         master/text/0218-empty-struct-with-braces.md>",
         },
+        FutureIncompatibleInfo {
+            id: LintId::of(TRANSMUTE_FROM_FN_ITEM_TYPES),
+            reference: "issue #19925 <https://github.com/rust-lang/rust/issues/19925>",
+        },
         ]);
 
     // We have one lint pass defined specially
diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs
index dff6652e541..96c1d6a4d69 100644
--- a/src/librustc_trans/trans/intrinsic.rs
+++ b/src/librustc_trans/trans/intrinsic.rs
@@ -22,7 +22,7 @@ use trans::adt;
 use trans::attributes;
 use trans::base::*;
 use trans::build::*;
-use trans::callee;
+use trans::callee::{self, Callee};
 use trans::cleanup;
 use trans::cleanup::CleanupMethods;
 use trans::common::*;
@@ -45,6 +45,7 @@ use syntax::ast;
 use syntax::ptr::P;
 use syntax::parse::token;
 
+use rustc::lint;
 use rustc::session::Session;
 use syntax::codemap::Span;
 
@@ -125,29 +126,41 @@ pub fn check_intrinsics(ccx: &CrateContext) {
                                                transmute_restriction.substituted_to);
         let from_type_size = machine::llbitsize_of_real(ccx, llfromtype);
         let to_type_size = machine::llbitsize_of_real(ccx, lltotype);
+
+        if let ty::TyFnDef(..) = transmute_restriction.substituted_from.sty {
+            if to_type_size == machine::llbitsize_of_real(ccx, ccx.int_type()) {
+                // FIXME #19925 Remove this warning after a release cycle.
+                lint::raw_emit_lint(&ccx.tcx().sess,
+                                    &ccx.tcx().sess.lint_store.borrow(),
+                                    lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES,
+                                    (lint::Warn, lint::LintSource::Default),
+                                    Some(transmute_restriction.span),
+                                    &format!("`{}` is now zero-sized and has to be cast \
+                                              to a pointer before transmuting to `{}`",
+                                             transmute_restriction.substituted_from,
+                                             transmute_restriction.substituted_to));
+                continue;
+            }
+        }
         if from_type_size != to_type_size {
             last_failing_id = Some(transmute_restriction.id);
 
             if transmute_restriction.original_from != transmute_restriction.substituted_from {
                 span_transmute_size_error(ccx.sess(), transmute_restriction.span,
                     &format!("transmute called with differently sized types: \
-                              {} (could be {} bit{}) to {} (could be {} bit{})",
+                              {} (could be {} bits) to {} (could be {} bits)",
                              transmute_restriction.original_from,
-                             from_type_size as usize,
-                             if from_type_size == 1 {""} else {"s"},
+                             from_type_size,
                              transmute_restriction.original_to,
-                             to_type_size as usize,
-                             if to_type_size == 1 {""} else {"s"}));
+                             to_type_size));
             } else {
                 span_transmute_size_error(ccx.sess(), transmute_restriction.span,
                     &format!("transmute called with differently sized types: \
-                              {} ({} bit{}) to {} ({} bit{})",
+                              {} ({} bits) to {} ({} bits)",
                              transmute_restriction.original_from,
-                             from_type_size as usize,
-                             if from_type_size == 1 {""} else {"s"},
+                             from_type_size,
                              transmute_restriction.original_to,
-                             to_type_size as usize,
-                             if to_type_size == 1 {""} else {"s"}));
+                             to_type_size));
             }
         }
     }
@@ -179,6 +192,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
     let foreign_item = tcx.map.expect_foreign_item(node);
     let name = foreign_item.name.as_str();
 
+    let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
+
     // For `transmute` we can just trans the input expr directly into dest
     if name == "transmute" {
         let llret_ty = type_of::type_of(ccx, ret_ty.unwrap());
@@ -194,6 +209,27 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
                 let in_type_size = machine::llbitsize_of_real(ccx, llintype);
                 let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
 
+                if let ty::TyFnDef(def_id, substs, _) = in_type.sty {
+                    if out_type_size != 0 {
+                        // FIXME #19925 Remove this hack after a release cycle.
+                        let _ = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
+                        let llfn = Callee::def(ccx, def_id, substs, in_type).reify(ccx).val;
+                        let llfnty = val_ty(llfn);
+                        let llresult = match dest {
+                            expr::SaveIn(d) => d,
+                            expr::Ignore => alloc_ty(bcx, out_type, "ret")
+                        };
+                        Store(bcx, llfn, PointerCast(bcx, llresult, llfnty.ptr_to()));
+                        if dest == expr::Ignore {
+                            bcx = glue::drop_ty(bcx, llresult, out_type,
+                                                call_debug_location);
+                        }
+                        fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
+                        fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
+                        return Result::new(bcx, llresult);
+                    }
+                }
+
                 // This should be caught by the intrinsicck pass
                 assert_eq!(in_type_size, out_type_size);
 
@@ -311,8 +347,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
         }
     }
 
-    let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
-
     // For `try` we need some custom control flow
     if &name[..] == "try" {
         if let callee::ArgExprs(ref exprs) = args {
diff --git a/src/test/compile-fail/transmute-from-fn-item-types-lint.rs b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
new file mode 100644
index 00000000000..3eae76f9492
--- /dev/null
+++ b/src/test/compile-fail/transmute-from-fn-item-types-lint.rs
@@ -0,0 +1,51 @@
+// Copyright 2016 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.
+
+use std::mem;
+
+unsafe fn foo() -> (isize, *const (), Option<fn()>) {
+    let i = mem::transmute(bar);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    let p = mem::transmute(foo);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    let of = mem::transmute(main);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    (i, p, of)
+}
+
+unsafe fn bar() {
+    mem::transmute::<_, *mut ()>(foo);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    mem::transmute::<_, fn()>(bar);
+    //~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
+    //~^^ WARN was previously accepted
+
+    // No error if a coercion would otherwise occur.
+    mem::transmute::<fn(), usize>(main);
+
+    // Error, still, if the resulting type is not pointer-sized.
+    mem::transmute::<_, u8>(main);
+    //~^ ERROR transmute called with differently sized types
+}
+
+fn main() {
+    unsafe {
+        foo();
+        bar();
+    }
+}
diff --git a/src/test/run-pass/transmute-from-fn-item-types.rs b/src/test/run-pass/transmute-from-fn-item-types.rs
new file mode 100644
index 00000000000..574a90e2ad6
--- /dev/null
+++ b/src/test/run-pass/transmute-from-fn-item-types.rs
@@ -0,0 +1,27 @@
+// Copyright 2016 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.
+
+#![allow(transmute_from_fn_item_types)]
+
+use std::mem;
+
+fn main() {
+    unsafe {
+        let u = mem::transmute(main);
+        let p = mem::transmute(main);
+        let f = mem::transmute(main);
+        let tuple: (usize, *mut (), fn()) = (u, p, f);
+        assert_eq!(mem::transmute::<_, [usize; 3]>(tuple), [main as usize; 3]);
+
+        mem::transmute::<_, usize>(main);
+        mem::transmute::<_, *mut ()>(main);
+        mem::transmute::<_, fn()>(main);
+    }
+}