1 // Copyright 2012-2014 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.
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.
11 use ast_map::NodeForeignItem;
12 use metadata::csearch;
13 use middle::def::DefFn;
14 use middle::subst::{Subst, Substs, EnumeratedItems};
15 use middle::ty::{TransmuteRestriction, ctxt, TyBareFn};
16 use middle::ty::{self, Ty, HasTypeFlags};
20 use syntax::abi::RustIntrinsic;
21 use syntax::ast::DefId;
23 use syntax::codemap::Span;
24 use syntax::visit::Visitor;
27 pub fn check_crate(tcx: &ctxt) {
28 let mut visitor = IntrinsicCheckingVisitor {
30 param_envs: Vec::new(),
31 dummy_sized_ty: tcx.types.isize,
32 dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
34 visit::walk_crate(&mut visitor, tcx.map.krate());
37 struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
40 // As we traverse the AST, we keep a stack of the parameter
41 // environments for each function we encounter. When we find a
42 // call to `transmute`, we can check it in the context of the top
43 // of the stack (which ought not to be empty).
44 param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
46 // Dummy sized/unsized types that use to substitute for type
47 // parameters in order to estimate how big a type will be for any
48 // possible instantiation of the type parameters in scope. See
49 // `check_transmute` for more details.
50 dummy_sized_ty: Ty<'tcx>,
51 dummy_unsized_ty: Ty<'tcx>,
54 impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
55 fn def_id_is_transmute(&self, def_id: DefId) -> bool {
56 let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
57 ty::TyBareFn(_, ref bfty) => bfty.abi == RustIntrinsic,
60 if def_id.krate == ast::LOCAL_CRATE {
61 match self.tcx.map.get(def_id.node) {
62 NodeForeignItem(ref item) if intrinsic => {
63 item.ident.name == "transmute"
68 match csearch::get_item_path(self.tcx, def_id).last() {
69 Some(ref last) if intrinsic => {
70 last.name() == "transmute"
77 fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
78 // Find the parameter environment for the most recent function that
81 let param_env = match self.param_envs.last() {
84 self.tcx.sess.span_bug(
86 "transmute encountered outside of any fn");
90 // Simple case: no type parameters involved.
92 !from.has_param_types() && !from.has_self_ty() &&
93 !to.has_param_types() && !to.has_self_ty()
95 let restriction = TransmuteRestriction {
99 substituted_from: from,
103 self.push_transmute_restriction(restriction);
107 // The rules around type parameters are a bit subtle. We are
108 // checking these rules before monomorphization, so there may
109 // be unsubstituted type parameters present in the
110 // types. Obviously we cannot create LLVM types for those.
111 // However, if a type parameter appears only indirectly (i.e.,
112 // through a pointer), it does not necessarily affect the
113 // size, so that should be allowed. The only catch is that we
114 // DO want to be careful around unsized type parameters, since
115 // fat pointers have a different size than a thin pointer, and
116 // hence `&T` and `&U` have different sizes if `T : Sized` but
117 // `U : Sized` does not hold.
119 // However, it's not as simple as checking whether `T :
120 // Sized`, because even if `T : Sized` does not hold, that
121 // just means that `T` *may* not be sized. After all, even a
122 // type parameter `T: ?Sized` could be bound to a sized
123 // type. (Issue #20116)
125 // To handle this, we first check for "interior" type
126 // parameters, which are always illegal. If there are none of
127 // those, then we know that the only way that all type
128 // parameters `T` are referenced indirectly, e.g. via a
129 // pointer type like `&T`. In that case, we only care whether
130 // `T` is sized or not, because that influences whether `&T`
131 // is a thin or fat pointer.
133 // One could imagine establishing a sophisticated constraint
134 // system to ensure that the transmute is legal, but instead
135 // we do something brutally dumb. We just substitute dummy
136 // sized or unsized types for every type parameter in scope,
137 // exhaustively checking all possible combinations. Here are some examples:
144 // fn bar<T: ?Sized, U>() {
149 // fn baz<T: ?Sized, U: ?Sized>() {
153 // // T=[int], U=[int]
157 // In all cases, we keep the original unsubstituted types
158 // around for error reporting.
160 let from_tc = from.type_contents(self.tcx);
161 let to_tc = to.type_contents(self.tcx);
162 if from_tc.interior_param() || to_tc.interior_param() {
163 span_err!(self.tcx.sess, span, E0139,
164 "cannot transmute to or from a type that contains \
165 unsubstituted type parameters");
169 let mut substs = param_env.free_substs.clone();
170 self.with_each_combination(
173 param_env.free_substs.types.iter_enumerated(),
176 let restriction = TransmuteRestriction {
180 substituted_from: from.subst(self.tcx, substs),
181 substituted_to: to.subst(self.tcx, substs),
184 self.push_transmute_restriction(restriction);
188 fn with_each_combination(&self,
190 param_env: &ty::ParameterEnvironment<'a,'tcx>,
191 mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
192 substs: &mut Substs<'tcx>,
193 callback: &mut FnMut(&Substs<'tcx>))
195 // This parameter invokes `callback` many times with different
196 // substitutions that replace all the parameters in scope with
197 // either `int` or `[int]`, depending on whether the type
198 // parameter is known to be sized. See big comment above for
199 // an explanation of why this is a reasonable thing to do.
201 match types_in_scope.next() {
203 debug!("with_each_combination(substs={:?})",
209 Some((space, index, ¶m_ty)) => {
210 debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
211 space, index, param_ty);
213 if !param_ty.is_sized(param_env, span) {
214 debug!("with_each_combination: param_ty is not known to be sized");
216 substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
217 self.with_each_combination(span, param_env, types_in_scope.clone(),
221 substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
222 self.with_each_combination(span, param_env, types_in_scope,
228 fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
229 debug!("Pushing transmute restriction: {:?}", restriction);
230 self.tcx.transmute_restrictions.borrow_mut().push(restriction);
234 impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
235 fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
236 b: &'v ast::Block, s: Span, id: ast::NodeId) {
238 visit::FkItemFn(..) | visit::FkMethod(..) => {
239 let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
240 self.param_envs.push(param_env);
241 visit::walk_fn(self, fk, fd, b, s);
242 self.param_envs.pop();
244 visit::FkFnBlock(..) => {
245 visit::walk_fn(self, fk, fd, b, s);
251 fn visit_expr(&mut self, expr: &ast::Expr) {
252 if let ast::ExprPath(..) = expr.node {
253 match self.tcx.resolve_expr(expr) {
254 DefFn(did, _) if self.def_id_is_transmute(did) => {
255 let typ = self.tcx.node_id_to_type(expr.id);
257 TyBareFn(_, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
258 if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
259 let from = bare_fn_ty.sig.0.inputs[0];
260 self.check_transmute(expr.span, from, to, expr.id);
266 .span_bug(expr.span, "transmute wasn't a bare fn?!");
274 visit::walk_expr(self, expr);
278 impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
279 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280 write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
284 self.substituted_from,