]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/intrinsicck.rs
Merge pull request #20510 from tshepang/patch-6
[rust.git] / src / librustc / middle / intrinsicck.rs
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.
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 metadata::csearch;
12 use middle::def::DefFn;
13 use middle::subst::{Subst, Substs, EnumeratedItems};
14 use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn};
15 use middle::ty::{self, Ty};
16 use util::ppaux::Repr;
17
18 use syntax::abi::RustIntrinsic;
19 use syntax::ast::DefId;
20 use syntax::ast;
21 use syntax::ast_map::NodeForeignItem;
22 use syntax::codemap::Span;
23 use syntax::parse::token;
24 use syntax::visit::Visitor;
25 use syntax::visit;
26
27 pub fn check_crate(tcx: &ctxt) {
28     let mut visitor = IntrinsicCheckingVisitor {
29         tcx: tcx,
30         param_envs: Vec::new(),
31         dummy_sized_ty: tcx.types.int,
32         dummy_unsized_ty: ty::mk_vec(tcx, tcx.types.int, None),
33     };
34     visit::walk_crate(&mut visitor, tcx.map.krate());
35 }
36
37 struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
38     tcx: &'a ctxt<'tcx>,
39
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>>,
45
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>,
52 }
53
54 impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
55     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
56         let intrinsic = match ty::lookup_item_type(self.tcx, def_id).ty.sty {
57             ty::ty_bare_fn(_, ref bfty) => bfty.abi == RustIntrinsic,
58             _ => return false
59         };
60         if def_id.krate == ast::LOCAL_CRATE {
61             match self.tcx.map.get(def_id.node) {
62                 NodeForeignItem(ref item) if intrinsic => {
63                     token::get_ident(item.ident) ==
64                         token::intern_and_get_ident("transmute")
65                 }
66                 _ => false,
67             }
68         } else {
69             match csearch::get_item_path(self.tcx, def_id).last() {
70                 Some(ref last) if intrinsic => {
71                     token::get_name(last.name()) ==
72                         token::intern_and_get_ident("transmute")
73                 }
74                 _ => false,
75             }
76         }
77     }
78
79     fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
80         // Find the parameter environment for the most recent function that
81         // we entered.
82
83         let param_env = match self.param_envs.last() {
84             Some(p) => p,
85             None => {
86                 self.tcx.sess.span_bug(
87                     span,
88                     "transmute encountered outside of any fn");
89             }
90         };
91
92         // Simple case: no type parameters involved.
93         if
94             !ty::type_has_params(from) && !ty::type_has_self(from) &&
95             !ty::type_has_params(to) && !ty::type_has_self(to)
96         {
97             let restriction = TransmuteRestriction {
98                 span: span,
99                 original_from: from,
100                 original_to: to,
101                 substituted_from: from,
102                 substituted_to: to,
103                 id: id,
104             };
105             self.push_transmute_restriction(restriction);
106             return;
107         }
108
109         // The rules around type parameters are a bit subtle. We are
110         // checking these rules before monomorphization, so there may
111         // be unsubstituted type parameters present in the
112         // types. Obviously we cannot create LLVM types for those.
113         // However, if a type parameter appears only indirectly (i.e.,
114         // through a pointer), it does not necessarily affect the
115         // size, so that should be allowed. The only catch is that we
116         // DO want to be careful around unsized type parameters, since
117         // fat pointers have a different size than a thin pointer, and
118         // hence `&T` and `&U` have different sizes if `T : Sized` but
119         // `U : Sized` does not hold.
120         //
121         // However, it's not as simple as checking whether `T :
122         // Sized`, because even if `T : Sized` does not hold, that
123         // just means that `T` *may* not be sized.  After all, even a
124         // type parameter `Sized? T` could be bound to a sized
125         // type. (Issue #20116)
126         //
127         // To handle this, we first check for "interior" type
128         // parameters, which are always illegal. If there are none of
129         // those, then we know that the only way that all type
130         // parameters `T` are referenced indirectly, e.g. via a
131         // pointer type like `&T`. In that case, we only care whether
132         // `T` is sized or not, because that influences whether `&T`
133         // is a thin or fat pointer.
134         //
135         // One could imagine establishing a sophisticated constraint
136         // system to ensure that the transmute is legal, but instead
137         // we do something brutally dumb. We just substitute dummy
138         // sized or unsized types for every type parameter in scope,
139         // exhaustively checking all possible combinations. Here are some examples:
140         //
141         // ```
142         // fn foo<T,U>() {
143         //     // T=int, U=int
144         // }
145         //
146         // fn bar<Sized? T,U>() {
147         //     // T=int, U=int
148         //     // T=[int], U=int
149         // }
150         //
151         // fn baz<Sized? T, Sized?U>() {
152         //     // T=int, U=int
153         //     // T=[int], U=int
154         //     // T=int, U=[int]
155         //     // T=[int], U=[int]
156         // }
157         // ```
158         //
159         // In all cases, we keep the original unsubstituted types
160         // around for error reporting.
161
162         let from_tc = ty::type_contents(self.tcx, from);
163         let to_tc = ty::type_contents(self.tcx, to);
164         if from_tc.interior_param() || to_tc.interior_param() {
165             span_err!(self.tcx.sess, span, E0139,
166                       "cannot transmute to or from a type that contains \
167                        type parameters in its interior");
168             return;
169         }
170
171         let mut substs = param_env.free_substs.clone();
172         self.with_each_combination(
173             span,
174             param_env,
175             param_env.free_substs.types.iter_enumerated(),
176             &mut substs,
177             &mut |substs| {
178                 let restriction = TransmuteRestriction {
179                     span: span,
180                     original_from: from,
181                     original_to: to,
182                     substituted_from: from.subst(self.tcx, substs),
183                     substituted_to: to.subst(self.tcx, substs),
184                     id: id,
185                 };
186                 self.push_transmute_restriction(restriction);
187             });
188     }
189
190     fn with_each_combination(&self,
191                              span: Span,
192                              param_env: &ty::ParameterEnvironment<'a,'tcx>,
193                              mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
194                              substs: &mut Substs<'tcx>,
195                              callback: &mut FnMut(&Substs<'tcx>))
196     {
197         // This parameter invokes `callback` many times with different
198         // substitutions that replace all the parameters in scope with
199         // either `int` or `[int]`, depending on whether the type
200         // parameter is known to be sized. See big comment above for
201         // an explanation of why this is a reasonable thing to do.
202
203         match types_in_scope.next() {
204             None => {
205                 debug!("with_each_combination(substs={})",
206                        substs.repr(self.tcx));
207
208                 callback.call_mut((substs,));
209             }
210
211             Some((space, index, &param_ty)) => {
212                 debug!("with_each_combination: space={}, index={}, param_ty={}",
213                        space, index, param_ty.repr(self.tcx));
214
215                 if !ty::type_is_sized(param_env, span, param_ty) {
216                     debug!("with_each_combination: param_ty is not known to be sized");
217
218                     substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
219                     self.with_each_combination(span, param_env, types_in_scope.clone(),
220                                                substs, callback);
221                 }
222
223                 substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
224                 self.with_each_combination(span, param_env, types_in_scope,
225                                            substs, callback);
226             }
227         }
228     }
229
230     fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
231         debug!("Pushing transmute restriction: {}", restriction.repr(self.tcx));
232         self.tcx.transmute_restrictions.borrow_mut().push(restriction);
233     }
234 }
235
236 impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
237     fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl,
238                 b: &'v ast::Block, s: Span, id: ast::NodeId) {
239         match fk {
240             visit::FkItemFn(..) | visit::FkMethod(..) => {
241                 let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
242                 self.param_envs.push(param_env);
243                 visit::walk_fn(self, fk, fd, b, s);
244                 self.param_envs.pop();
245             }
246             visit::FkFnBlock(..) => {
247                 visit::walk_fn(self, fk, fd, b, s);
248             }
249         }
250
251     }
252
253     fn visit_expr(&mut self, expr: &ast::Expr) {
254         if let ast::ExprPath(..) = expr.node {
255             match ty::resolve_expr(self.tcx, expr) {
256                 DefFn(did, _) if self.def_id_is_transmute(did) => {
257                     let typ = ty::node_id_to_type(self.tcx, expr.id);
258                     match typ.sty {
259                         ty_bare_fn(_, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
260                             if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
261                                 let from = bare_fn_ty.sig.0.inputs[0];
262                                 self.check_transmute(expr.span, from, to, expr.id);
263                             }
264                         }
265                         _ => {
266                             self.tcx
267                                 .sess
268                                 .span_bug(expr.span, "transmute wasn't a bare fn?!");
269                         }
270                     }
271                 }
272                 _ => {}
273             }
274         }
275
276         visit::walk_expr(self, expr);
277     }
278 }
279
280 impl<'tcx> Repr<'tcx> for TransmuteRestriction<'tcx> {
281     fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
282         format!("TransmuteRestriction(id={}, original=({},{}), substituted=({},{}))",
283                 self.id,
284                 self.original_from.repr(tcx),
285                 self.original_to.repr(tcx),
286                 self.substituted_from.repr(tcx),
287                 self.substituted_to.repr(tcx))
288     }
289 }