Skip to content

Commit c7d564d

Browse files
committed
Check transmutes between types without statically known sizes.
1 parent 24ca1ec commit c7d564d

14 files changed

+341
-351
lines changed

src/librustc/dep_graph/dep_node.rs

-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ pub enum DepNode<D: Clone + Debug> {
7171
DeadCheck,
7272
StabilityCheck,
7373
LateLintCheck,
74-
IntrinsicUseCheck,
7574
TransCrate,
7675
TransCrateItem(D),
7776
TransInlinedItem(D),
@@ -169,7 +168,6 @@ impl<D: Clone + Debug> DepNode<D> {
169168
DeadCheck => Some(DeadCheck),
170169
StabilityCheck => Some(StabilityCheck),
171170
LateLintCheck => Some(LateLintCheck),
172-
IntrinsicUseCheck => Some(IntrinsicUseCheck),
173171
TransCrate => Some(TransCrate),
174172
TransWriteMetadata => Some(TransWriteMetadata),
175173
Hir(ref d) => op(d).map(Hir),

src/librustc/diagnostics.rs

+26
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,32 @@ It is not possible to use stability attributes outside of the standard library.
14101410
Also, for now, it is not possible to write deprecation messages either.
14111411
"##,
14121412

1413+
E0512: r##"
1414+
Transmute with two differently sized types was attempted. Erroneous code
1415+
example:
1416+
1417+
```compile_fail
1418+
fn takes_u8(_: u8) {}
1419+
1420+
fn main() {
1421+
unsafe { takes_u8(::std::mem::transmute(0u16)); }
1422+
// error: transmute called with differently sized types
1423+
}
1424+
```
1425+
1426+
Please use types with same size or use the expected type directly. Example:
1427+
1428+
```
1429+
fn takes_u8(_: u8) {}
1430+
1431+
fn main() {
1432+
unsafe { takes_u8(::std::mem::transmute(0i8)); } // ok!
1433+
// or:
1434+
unsafe { takes_u8(0u8); } // ok!
1435+
}
1436+
```
1437+
"##,
1438+
14131439
E0517: r##"
14141440
This error indicates that a `#[repr(..)]` attribute was placed on an
14151441
unsupported item.

src/librustc/middle/intrinsicck.rs

+103-186
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@
1111
use dep_graph::DepNode;
1212
use hir::def::Def;
1313
use hir::def_id::DefId;
14-
use ty::subst::{Subst, Substs, EnumeratedItems};
15-
use ty::{TransmuteRestriction, TyCtxt};
16-
use ty::{self, Ty, TypeFoldable};
17-
18-
use std::fmt;
14+
use infer::{InferCtxt, new_infer_ctxt};
15+
use traits::ProjectionMode;
16+
use ty::{self, Ty, TyCtxt};
17+
use ty::layout::{LayoutError, Pointer, SizeSkeleton};
1918

2019
use syntax::abi::Abi::RustIntrinsic;
2120
use syntax::ast;
@@ -24,219 +23,148 @@ use hir::intravisit::{self, Visitor, FnKind};
2423
use hir;
2524

2625
pub fn check_crate(tcx: &TyCtxt) {
27-
let mut visitor = IntrinsicCheckingVisitor {
28-
tcx: tcx,
29-
param_envs: Vec::new(),
30-
dummy_sized_ty: tcx.types.isize,
31-
dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
26+
let mut visitor = ItemVisitor {
27+
tcx: tcx
3228
};
3329
tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
3430
}
3531

36-
struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
37-
tcx: &'a TyCtxt<'tcx>,
32+
struct ItemVisitor<'a, 'tcx: 'a> {
33+
tcx: &'a TyCtxt<'tcx>
34+
}
3835

39-
// As we traverse the AST, we keep a stack of the parameter
40-
// environments for each function we encounter. When we find a
41-
// call to `transmute`, we can check it in the context of the top
42-
// of the stack (which ought not to be empty).
43-
param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
36+
impl<'a, 'tcx> ItemVisitor<'a, 'tcx> {
37+
fn visit_const(&mut self, item_id: ast::NodeId, expr: &hir::Expr) {
38+
let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id);
39+
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
40+
Some(param_env),
41+
ProjectionMode::Any);
42+
let mut visitor = ExprVisitor {
43+
infcx: &infcx
44+
};
45+
visitor.visit_expr(expr);
46+
}
47+
}
4448

45-
// Dummy sized/unsized types that use to substitute for type
46-
// parameters in order to estimate how big a type will be for any
47-
// possible instantiation of the type parameters in scope. See
48-
// `check_transmute` for more details.
49-
dummy_sized_ty: Ty<'tcx>,
50-
dummy_unsized_ty: Ty<'tcx>,
49+
struct ExprVisitor<'a, 'tcx: 'a> {
50+
infcx: &'a InferCtxt<'a, 'tcx>
5151
}
5252

53-
impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
53+
impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
5454
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
55-
let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
55+
let intrinsic = match self.infcx.tcx.lookup_item_type(def_id).ty.sty {
5656
ty::TyFnDef(_, _, ref bfty) => bfty.abi == RustIntrinsic,
5757
_ => return false
5858
};
59-
intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
59+
intrinsic && self.infcx.tcx.item_name(def_id).as_str() == "transmute"
6060
}
6161

6262
fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
63-
// Find the parameter environment for the most recent function that
64-
// we entered.
63+
let sk_from = SizeSkeleton::compute(from, self.infcx);
64+
let sk_to = SizeSkeleton::compute(to, self.infcx);
6565

66-
let param_env = match self.param_envs.last() {
67-
Some(p) => p,
68-
None => {
69-
span_bug!(
70-
span,
71-
"transmute encountered outside of any fn");
66+
// Check for same size using the skeletons.
67+
if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
68+
if sk_from.same_size(sk_to) {
69+
return;
7270
}
73-
};
74-
75-
// Simple case: no type parameters involved.
76-
if
77-
!from.has_param_types() && !from.has_self_ty() &&
78-
!to.has_param_types() && !to.has_self_ty()
79-
{
80-
let restriction = TransmuteRestriction {
81-
span: span,
82-
original_from: from,
83-
original_to: to,
84-
substituted_from: from,
85-
substituted_to: to,
86-
id: id,
87-
};
88-
self.push_transmute_restriction(restriction);
89-
return;
90-
}
9171

92-
// The rules around type parameters are a bit subtle. We are
93-
// checking these rules before monomorphization, so there may
94-
// be unsubstituted type parameters present in the
95-
// types. Obviously we cannot create LLVM types for those.
96-
// However, if a type parameter appears only indirectly (i.e.,
97-
// through a pointer), it does not necessarily affect the
98-
// size, so that should be allowed. The only catch is that we
99-
// DO want to be careful around unsized type parameters, since
100-
// fat pointers have a different size than a thin pointer, and
101-
// hence `&T` and `&U` have different sizes if `T : Sized` but
102-
// `U : Sized` does not hold.
103-
//
104-
// However, it's not as simple as checking whether `T :
105-
// Sized`, because even if `T : Sized` does not hold, that
106-
// just means that `T` *may* not be sized. After all, even a
107-
// type parameter `T: ?Sized` could be bound to a sized
108-
// type. (Issue #20116)
109-
//
110-
// To handle this, we first check for "interior" type
111-
// parameters, which are always illegal. If there are none of
112-
// those, then we know that the only way that all type
113-
// parameters `T` are referenced indirectly, e.g. via a
114-
// pointer type like `&T`. In that case, we only care whether
115-
// `T` is sized or not, because that influences whether `&T`
116-
// is a thin or fat pointer.
117-
//
118-
// One could imagine establishing a sophisticated constraint
119-
// system to ensure that the transmute is legal, but instead
120-
// we do something brutally dumb. We just substitute dummy
121-
// sized or unsized types for every type parameter in scope,
122-
// exhaustively checking all possible combinations. Here are some examples:
123-
//
124-
// ```
125-
// fn foo<T, U>() {
126-
// // T=int, U=int
127-
// }
128-
//
129-
// fn bar<T: ?Sized, U>() {
130-
// // T=int, U=int
131-
// // T=[int], U=int
132-
// }
133-
//
134-
// fn baz<T: ?Sized, U: ?Sized>() {
135-
// // T=int, U=int
136-
// // T=[int], U=int
137-
// // T=int, U=[int]
138-
// // T=[int], U=[int]
139-
// }
140-
// ```
141-
//
142-
// In all cases, we keep the original unsubstituted types
143-
// around for error reporting.
144-
145-
let from_tc = from.type_contents(self.tcx);
146-
let to_tc = to.type_contents(self.tcx);
147-
if from_tc.interior_param() || to_tc.interior_param() {
148-
span_err!(self.tcx.sess, span, E0139,
149-
"cannot transmute to or from a type that contains \
150-
unsubstituted type parameters");
151-
return;
72+
match (&from.sty, sk_to) {
73+
(&ty::TyFnDef(..), SizeSkeleton::Known(size_to))
74+
if size_to == Pointer.size(&self.infcx.tcx.data_layout) => {
75+
// FIXME #19925 Remove this warning after a release cycle.
76+
let msg = format!("`{}` is now zero-sized and has to be cast \
77+
to a pointer before transmuting to `{}`",
78+
from, to);
79+
self.infcx.tcx.sess.add_lint(
80+
::lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES, id, span, msg);
81+
return;
82+
}
83+
_ => {}
84+
}
15285
}
15386

154-
let mut substs = param_env.free_substs.clone();
155-
self.with_each_combination(
156-
span,
157-
param_env,
158-
param_env.free_substs.types.iter_enumerated(),
159-
&mut substs,
160-
&mut |substs| {
161-
let restriction = TransmuteRestriction {
162-
span: span,
163-
original_from: from,
164-
original_to: to,
165-
substituted_from: from.subst(self.tcx, substs),
166-
substituted_to: to.subst(self.tcx, substs),
167-
id: id,
168-
};
169-
self.push_transmute_restriction(restriction);
170-
});
171-
}
172-
173-
fn with_each_combination(&self,
174-
span: Span,
175-
param_env: &ty::ParameterEnvironment<'a,'tcx>,
176-
mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
177-
substs: &mut Substs<'tcx>,
178-
callback: &mut FnMut(&Substs<'tcx>))
179-
{
180-
// This parameter invokes `callback` many times with different
181-
// substitutions that replace all the parameters in scope with
182-
// either `int` or `[int]`, depending on whether the type
183-
// parameter is known to be sized. See big comment above for
184-
// an explanation of why this is a reasonable thing to do.
185-
186-
match types_in_scope.next() {
187-
None => {
188-
debug!("with_each_combination(substs={:?})",
189-
substs);
190-
191-
callback(substs);
87+
// Try to display a sensible error with as much information as possible.
88+
let skeleton_string = |ty: Ty<'tcx>, sk| {
89+
match sk {
90+
Ok(SizeSkeleton::Known(size)) => {
91+
format!("{} bits", size.bits())
92+
}
93+
Ok(SizeSkeleton::Pointer { tail, .. }) => {
94+
format!("pointer to {}", tail)
95+
}
96+
Err(LayoutError::Unknown(bad)) => {
97+
if bad == ty {
98+
format!("size can vary")
99+
} else {
100+
format!("size can vary because of {}", bad)
101+
}
102+
}
103+
Err(err) => err.to_string()
192104
}
105+
};
193106

194-
Some((space, index, &param_ty)) => {
195-
debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
196-
space, index, param_ty);
197-
198-
if !param_ty.is_sized(param_env, span) {
199-
debug!("with_each_combination: param_ty is not known to be sized");
107+
span_err!(self.infcx.tcx.sess, span, E0512,
108+
"transmute called with differently sized types: \
109+
{} ({}) to {} ({})",
110+
from, skeleton_string(from, sk_from),
111+
to, skeleton_string(to, sk_to));
112+
}
113+
}
200114

201-
substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
202-
self.with_each_combination(span, param_env, types_in_scope.clone(),
203-
substs, callback);
204-
}
115+
impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
116+
// const, static and N in [T; N].
117+
fn visit_expr(&mut self, expr: &hir::Expr) {
118+
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
119+
None, ProjectionMode::Any);
120+
let mut visitor = ExprVisitor {
121+
infcx: &infcx
122+
};
123+
visitor.visit_expr(expr);
124+
}
205125

206-
substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
207-
self.with_each_combination(span, param_env, types_in_scope,
208-
substs, callback);
209-
}
126+
fn visit_trait_item(&mut self, item: &hir::TraitItem) {
127+
if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
128+
self.visit_const(item.id, expr);
129+
} else {
130+
intravisit::walk_trait_item(self, item);
210131
}
211132
}
212133

213-
fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
214-
debug!("Pushing transmute restriction: {:?}", restriction);
215-
self.tcx.transmute_restrictions.borrow_mut().push(restriction);
134+
fn visit_impl_item(&mut self, item: &hir::ImplItem) {
135+
if let hir::ImplItemKind::Const(_, ref expr) = item.node {
136+
self.visit_const(item.id, expr);
137+
} else {
138+
intravisit::walk_impl_item(self, item);
139+
}
216140
}
217-
}
218141

219-
impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
220142
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
221143
b: &'v hir::Block, s: Span, id: ast::NodeId) {
222144
match fk {
223145
FnKind::ItemFn(..) | FnKind::Method(..) => {
224146
let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
225-
self.param_envs.push(param_env);
226-
intravisit::walk_fn(self, fk, fd, b, s);
227-
self.param_envs.pop();
147+
let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
148+
Some(param_env),
149+
ProjectionMode::Any);
150+
let mut visitor = ExprVisitor {
151+
infcx: &infcx
152+
};
153+
visitor.visit_fn(fk, fd, b, s, id);
228154
}
229155
FnKind::Closure(..) => {
230-
intravisit::walk_fn(self, fk, fd, b, s);
156+
span_bug!(s, "intrinsicck: closure outside of function")
231157
}
232158
}
233159
}
160+
}
234161

162+
impl<'a, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'tcx> {
235163
fn visit_expr(&mut self, expr: &hir::Expr) {
236164
if let hir::ExprPath(..) = expr.node {
237-
match self.tcx.resolve_expr(expr) {
165+
match self.infcx.tcx.resolve_expr(expr) {
238166
Def::Fn(did) if self.def_id_is_transmute(did) => {
239-
let typ = self.tcx.node_id_to_type(expr.id);
167+
let typ = self.infcx.tcx.node_id_to_type(expr.id);
240168
match typ.sty {
241169
ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
242170
if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
@@ -256,14 +184,3 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
256184
intravisit::walk_expr(self, expr);
257185
}
258186
}
259-
260-
impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
261-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262-
write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
263-
self.id,
264-
self.original_from,
265-
self.original_to,
266-
self.substituted_from,
267-
self.substituted_to)
268-
}
269-
}

0 commit comments

Comments
 (0)