]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/intrinsicck.rs
Auto merge of #31077 - nagisa:mir-temp-promotion, r=dotdash
[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 dep_graph::DepNode;
12 use middle::def::Def;
13 use middle::def_id::DefId;
14 use middle::subst::{Subst, Substs, EnumeratedItems};
15 use middle::ty::{TransmuteRestriction, ctxt, TyBareFn};
16 use middle::ty::{self, Ty, TypeFoldable};
17
18 use std::fmt;
19
20 use syntax::abi::RustIntrinsic;
21 use syntax::ast;
22 use syntax::codemap::Span;
23 use rustc_front::intravisit::{self, Visitor, FnKind};
24 use rustc_front::hir;
25
26 pub fn check_crate(tcx: &ctxt) {
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),
32     };
33     tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
34 }
35
36 struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
37     tcx: &'a ctxt<'tcx>,
38
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>>,
44
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>,
51 }
52
53 impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
54     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
55         let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
56             ty::TyBareFn(_, ref bfty) => bfty.abi == RustIntrinsic,
57             _ => return false
58         };
59         intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
60     }
61
62     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.
65
66         let param_env = match self.param_envs.last() {
67             Some(p) => p,
68             None => {
69                 self.tcx.sess.span_bug(
70                     span,
71                     "transmute encountered outside of any fn");
72             }
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         }
91
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;
152         }
153
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);
192             }
193
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");
200
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                 }
205
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             }
210         }
211     }
212
213     fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
214         debug!("Pushing transmute restriction: {:?}", restriction);
215         self.tcx.transmute_restrictions.borrow_mut().push(restriction);
216     }
217 }
218
219 impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
220     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
221                 b: &'v hir::Block, s: Span, id: ast::NodeId) {
222         match fk {
223             FnKind::ItemFn(..) | FnKind::Method(..) => {
224                 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();
228             }
229             FnKind::Closure => {
230                 intravisit::walk_fn(self, fk, fd, b, s);
231             }
232         }
233     }
234
235     fn visit_expr(&mut self, expr: &hir::Expr) {
236         if let hir::ExprPath(..) = expr.node {
237             match self.tcx.resolve_expr(expr) {
238                 Def::Fn(did) if self.def_id_is_transmute(did) => {
239                     let typ = self.tcx.node_id_to_type(expr.id);
240                     match typ.sty {
241                         TyBareFn(_, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
242                             if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
243                                 let from = bare_fn_ty.sig.0.inputs[0];
244                                 self.check_transmute(expr.span, from, to, expr.id);
245                             }
246                         }
247                         _ => {
248                             self.tcx
249                                 .sess
250                                 .span_bug(expr.span, "transmute wasn't a bare fn?!");
251                         }
252                     }
253                 }
254                 _ => {}
255             }
256         }
257
258         intravisit::walk_expr(self, expr);
259     }
260 }
261
262 impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
263     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264         write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
265                self.id,
266                self.original_from,
267                self.original_to,
268                self.substituted_from,
269                self.substituted_to)
270     }
271 }