]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/intrinsicck.rs
remove `get_ident` and `get_name`, make `as_str` sound
[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 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};
17
18 use std::fmt;
19
20 use syntax::abi::RustIntrinsic;
21 use syntax::ast::DefId;
22 use syntax::ast;
23 use syntax::codemap::Span;
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.isize,
32         dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
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 self.tcx.lookup_item_type(def_id).ty.sty {
57             ty::TyBareFn(_, 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                     item.ident.name == "transmute"
64                 }
65                 _ => false,
66             }
67         } else {
68             match csearch::get_item_path(self.tcx, def_id).last() {
69                 Some(ref last) if intrinsic => {
70                     last.name() == "transmute"
71                 }
72                 _ => false,
73             }
74         }
75     }
76
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
79         // we entered.
80
81         let param_env = match self.param_envs.last() {
82             Some(p) => p,
83             None => {
84                 self.tcx.sess.span_bug(
85                     span,
86                     "transmute encountered outside of any fn");
87             }
88         };
89
90         // Simple case: no type parameters involved.
91         if
92             !from.has_param_types() && !from.has_self_ty() &&
93             !to.has_param_types() && !to.has_self_ty()
94         {
95             let restriction = TransmuteRestriction {
96                 span: span,
97                 original_from: from,
98                 original_to: to,
99                 substituted_from: from,
100                 substituted_to: to,
101                 id: id,
102             };
103             self.push_transmute_restriction(restriction);
104             return;
105         }
106
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.
118         //
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)
124         //
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.
132         //
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:
138         //
139         // ```
140         // fn foo<T, U>() {
141         //     // T=int, U=int
142         // }
143         //
144         // fn bar<T: ?Sized, U>() {
145         //     // T=int, U=int
146         //     // T=[int], U=int
147         // }
148         //
149         // fn baz<T: ?Sized, U: ?Sized>() {
150         //     // T=int, U=int
151         //     // T=[int], U=int
152         //     // T=int, U=[int]
153         //     // T=[int], U=[int]
154         // }
155         // ```
156         //
157         // In all cases, we keep the original unsubstituted types
158         // around for error reporting.
159
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");
166             return;
167         }
168
169         let mut substs = param_env.free_substs.clone();
170         self.with_each_combination(
171             span,
172             param_env,
173             param_env.free_substs.types.iter_enumerated(),
174             &mut substs,
175             &mut |substs| {
176                 let restriction = TransmuteRestriction {
177                     span: span,
178                     original_from: from,
179                     original_to: to,
180                     substituted_from: from.subst(self.tcx, substs),
181                     substituted_to: to.subst(self.tcx, substs),
182                     id: id,
183                 };
184                 self.push_transmute_restriction(restriction);
185             });
186     }
187
188     fn with_each_combination(&self,
189                              span: Span,
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>))
194     {
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.
200
201         match types_in_scope.next() {
202             None => {
203                 debug!("with_each_combination(substs={:?})",
204                        substs);
205
206                 callback(substs);
207             }
208
209             Some((space, index, &param_ty)) => {
210                 debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
211                        space, index, param_ty);
212
213                 if !param_ty.is_sized(param_env, span) {
214                     debug!("with_each_combination: param_ty is not known to be sized");
215
216                     substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
217                     self.with_each_combination(span, param_env, types_in_scope.clone(),
218                                                substs, callback);
219                 }
220
221                 substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
222                 self.with_each_combination(span, param_env, types_in_scope,
223                                            substs, callback);
224             }
225         }
226     }
227
228     fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
229         debug!("Pushing transmute restriction: {:?}", restriction);
230         self.tcx.transmute_restrictions.borrow_mut().push(restriction);
231     }
232 }
233
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) {
237         match fk {
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();
243             }
244             visit::FkFnBlock(..) => {
245                 visit::walk_fn(self, fk, fd, b, s);
246             }
247         }
248
249     }
250
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);
256                     match typ.sty {
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);
261                             }
262                         }
263                         _ => {
264                             self.tcx
265                                 .sess
266                                 .span_bug(expr.span, "transmute wasn't a bare fn?!");
267                         }
268                     }
269                 }
270                 _ => {}
271             }
272         }
273
274         visit::walk_expr(self, expr);
275     }
276 }
277
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=({:?},{:?}))",
281                self.id,
282                self.original_from,
283                self.original_to,
284                self.substituted_from,
285                self.substituted_to)
286     }
287 }