diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 4bb69a2688a41..97a550a4076fa 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 023b972250491..c11e9dc822eb3 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 1cf0339c086e5..e47f67dad8fcf 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 ", }, + FutureIncompatibleInfo { + id: LintId::of(TRANSMUTE_FROM_FN_ITEM_TYPES), + reference: "issue #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 dff6652e5419b..96c1d6a4d691a 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 0000000000000..3eae76f9492a2 --- /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 or the MIT license +// , 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) { + 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::(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 0000000000000..574a90e2ad697 --- /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 or the MIT license +// , 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); + } +}