trans: Keep transmutes from fn item types working, but lint them.

This commit is contained in:
Eduard Burtescu 2016-03-09 18:35:27 +02:00
parent eb926dd4b7
commit 3855fa99ca
6 changed files with 140 additions and 14 deletions

View File

@ -148,6 +148,12 @@
"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 @@ fn get_lints(&self) -> LintArray {
INVALID_TYPE_PARAM_DEFAULT,
MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
CONST_ERR,
RAW_POINTER_DERIVE
RAW_POINTER_DERIVE,
TRANSMUTE_FROM_FN_ITEM_TYPES
)
}
}

View File

@ -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) {

View File

@ -171,6 +171,10 @@ macro_rules! add_lint_group {
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

View File

@ -22,7 +22,7 @@
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::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 {

View File

@ -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();
}
}

View File

@ -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);
}
}