Skip to content

Commit 3855fa9

Browse files
committed
trans: Keep transmutes from fn item types working, but lint them.
1 parent eb926dd commit 3855fa9

File tree

6 files changed

+140
-14
lines changed

6 files changed

+140
-14
lines changed

src/librustc/lint/builtin.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ declare_lint! {
148148
"uses of #[derive] with raw pointers are rarely correct"
149149
}
150150

151+
declare_lint! {
152+
pub TRANSMUTE_FROM_FN_ITEM_TYPES,
153+
Warn,
154+
"transmute from function item type to pointer-sized type erroneously allowed"
155+
}
156+
151157
/// Does nothing as a lint pass, but registers some `Lint`s
152158
/// which are used by other parts of the compiler.
153159
#[derive(Copy, Clone)]
@@ -177,7 +183,8 @@ impl LintPass for HardwiredLints {
177183
INVALID_TYPE_PARAM_DEFAULT,
178184
MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
179185
CONST_ERR,
180-
RAW_POINTER_DERIVE
186+
RAW_POINTER_DERIVE,
187+
TRANSMUTE_FROM_FN_ITEM_TYPES
181188
)
182189
}
183190
}

src/librustc/lint/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,9 @@ pub fn check_crate(tcx: &TyCtxt, access_levels: &AccessLevels) {
12871287
}
12881288

12891289
*tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner();
1290+
1291+
// Put the lint store back in the session.
1292+
mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints);
12901293
}
12911294

12921295
pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) {

src/librustc_lint/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
171171
reference: "RFC 218 <https://github.com/rust-lang/rfcs/blob/\
172172
master/text/0218-empty-struct-with-braces.md>",
173173
},
174+
FutureIncompatibleInfo {
175+
id: LintId::of(TRANSMUTE_FROM_FN_ITEM_TYPES),
176+
reference: "issue #19925 <https://github.com/rust-lang/rust/issues/19925>",
177+
},
174178
]);
175179

176180
// We have one lint pass defined specially

src/librustc_trans/trans/intrinsic.rs

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use trans::adt;
2222
use trans::attributes;
2323
use trans::base::*;
2424
use trans::build::*;
25-
use trans::callee;
25+
use trans::callee::{self, Callee};
2626
use trans::cleanup;
2727
use trans::cleanup::CleanupMethods;
2828
use trans::common::*;
@@ -45,6 +45,7 @@ use syntax::ast;
4545
use syntax::ptr::P;
4646
use syntax::parse::token;
4747

48+
use rustc::lint;
4849
use rustc::session::Session;
4950
use syntax::codemap::Span;
5051

@@ -125,29 +126,41 @@ pub fn check_intrinsics(ccx: &CrateContext) {
125126
transmute_restriction.substituted_to);
126127
let from_type_size = machine::llbitsize_of_real(ccx, llfromtype);
127128
let to_type_size = machine::llbitsize_of_real(ccx, lltotype);
129+
130+
if let ty::TyFnDef(..) = transmute_restriction.substituted_from.sty {
131+
if to_type_size == machine::llbitsize_of_real(ccx, ccx.int_type()) {
132+
// FIXME #19925 Remove this warning after a release cycle.
133+
lint::raw_emit_lint(&ccx.tcx().sess,
134+
&ccx.tcx().sess.lint_store.borrow(),
135+
lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES,
136+
(lint::Warn, lint::LintSource::Default),
137+
Some(transmute_restriction.span),
138+
&format!("`{}` is now zero-sized and has to be cast \
139+
to a pointer before transmuting to `{}`",
140+
transmute_restriction.substituted_from,
141+
transmute_restriction.substituted_to));
142+
continue;
143+
}
144+
}
128145
if from_type_size != to_type_size {
129146
last_failing_id = Some(transmute_restriction.id);
130147

131148
if transmute_restriction.original_from != transmute_restriction.substituted_from {
132149
span_transmute_size_error(ccx.sess(), transmute_restriction.span,
133150
&format!("transmute called with differently sized types: \
134-
{} (could be {} bit{}) to {} (could be {} bit{})",
151+
{} (could be {} bits) to {} (could be {} bits)",
135152
transmute_restriction.original_from,
136-
from_type_size as usize,
137-
if from_type_size == 1 {""} else {"s"},
153+
from_type_size,
138154
transmute_restriction.original_to,
139-
to_type_size as usize,
140-
if to_type_size == 1 {""} else {"s"}));
155+
to_type_size));
141156
} else {
142157
span_transmute_size_error(ccx.sess(), transmute_restriction.span,
143158
&format!("transmute called with differently sized types: \
144-
{} ({} bit{}) to {} ({} bit{})",
159+
{} ({} bits) to {} ({} bits)",
145160
transmute_restriction.original_from,
146-
from_type_size as usize,
147-
if from_type_size == 1 {""} else {"s"},
161+
from_type_size,
148162
transmute_restriction.original_to,
149-
to_type_size as usize,
150-
if to_type_size == 1 {""} else {"s"}));
163+
to_type_size));
151164
}
152165
}
153166
}
@@ -179,6 +192,8 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
179192
let foreign_item = tcx.map.expect_foreign_item(node);
180193
let name = foreign_item.name.as_str();
181194

195+
let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
196+
182197
// For `transmute` we can just trans the input expr directly into dest
183198
if name == "transmute" {
184199
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>,
194209
let in_type_size = machine::llbitsize_of_real(ccx, llintype);
195210
let out_type_size = machine::llbitsize_of_real(ccx, llouttype);
196211

212+
if let ty::TyFnDef(def_id, substs, _) = in_type.sty {
213+
if out_type_size != 0 {
214+
// FIXME #19925 Remove this hack after a release cycle.
215+
let _ = unpack_datum!(bcx, expr::trans(bcx, &arg_exprs[0]));
216+
let llfn = Callee::def(ccx, def_id, substs, in_type).reify(ccx).val;
217+
let llfnty = val_ty(llfn);
218+
let llresult = match dest {
219+
expr::SaveIn(d) => d,
220+
expr::Ignore => alloc_ty(bcx, out_type, "ret")
221+
};
222+
Store(bcx, llfn, PointerCast(bcx, llresult, llfnty.ptr_to()));
223+
if dest == expr::Ignore {
224+
bcx = glue::drop_ty(bcx, llresult, out_type,
225+
call_debug_location);
226+
}
227+
fcx.scopes.borrow_mut().last_mut().unwrap().drop_non_lifetime_clean();
228+
fcx.pop_and_trans_custom_cleanup_scope(bcx, cleanup_scope);
229+
return Result::new(bcx, llresult);
230+
}
231+
}
232+
197233
// This should be caught by the intrinsicck pass
198234
assert_eq!(in_type_size, out_type_size);
199235

@@ -311,8 +347,6 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
311347
}
312348
}
313349

314-
let call_debug_location = DebugLoc::At(call_info.id, call_info.span);
315-
316350
// For `try` we need some custom control flow
317351
if &name[..] == "try" {
318352
if let callee::ArgExprs(ref exprs) = args {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::mem;
12+
13+
unsafe fn foo() -> (isize, *const (), Option<fn()>) {
14+
let i = mem::transmute(bar);
15+
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
16+
//~^^ WARN was previously accepted
17+
18+
let p = mem::transmute(foo);
19+
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
20+
//~^^ WARN was previously accepted
21+
22+
let of = mem::transmute(main);
23+
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
24+
//~^^ WARN was previously accepted
25+
26+
(i, p, of)
27+
}
28+
29+
unsafe fn bar() {
30+
mem::transmute::<_, *mut ()>(foo);
31+
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
32+
//~^^ WARN was previously accepted
33+
34+
mem::transmute::<_, fn()>(bar);
35+
//~^ WARN is now zero-sized and has to be cast to a pointer before transmuting
36+
//~^^ WARN was previously accepted
37+
38+
// No error if a coercion would otherwise occur.
39+
mem::transmute::<fn(), usize>(main);
40+
41+
// Error, still, if the resulting type is not pointer-sized.
42+
mem::transmute::<_, u8>(main);
43+
//~^ ERROR transmute called with differently sized types
44+
}
45+
46+
fn main() {
47+
unsafe {
48+
foo();
49+
bar();
50+
}
51+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(transmute_from_fn_item_types)]
12+
13+
use std::mem;
14+
15+
fn main() {
16+
unsafe {
17+
let u = mem::transmute(main);
18+
let p = mem::transmute(main);
19+
let f = mem::transmute(main);
20+
let tuple: (usize, *mut (), fn()) = (u, p, f);
21+
assert_eq!(mem::transmute::<_, [usize; 3]>(tuple), [main as usize; 3]);
22+
23+
mem::transmute::<_, usize>(main);
24+
mem::transmute::<_, *mut ()>(main);
25+
mem::transmute::<_, fn()>(main);
26+
}
27+
}

0 commit comments

Comments
 (0)