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