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}
;
20 use syntax
::abi
::RustIntrinsic
;
21 use syntax
::ast
::DefId
;
23 use syntax
::codemap
::Span
;
24 use syntax
::parse
::token
;
25 use syntax
::visit
::Visitor
;
28 pub fn check_crate(tcx
: &ctxt
) {
29 let mut visitor
= IntrinsicCheckingVisitor
{
31 param_envs
: Vec
::new(),
32 dummy_sized_ty
: tcx
.types
.isize,
33 dummy_unsized_ty
: ty
::mk_vec(tcx
, tcx
.types
.isize, None
),
35 visit
::walk_crate(&mut visitor
, tcx
.map
.krate());
38 struct IntrinsicCheckingVisitor
<'a
, 'tcx
: 'a
> {
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
>>,
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
>,
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
,
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")
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")
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
84 let param_env
= match self.param_envs
.last() {
87 self.tcx
.sess
.span_bug(
89 "transmute encountered outside of any fn");
93 // Simple case: no type parameters involved.
95 !ty
::type_has_params(from
) && !ty
::type_has_self(from
) &&
96 !ty
::type_has_params(to
) && !ty
::type_has_self(to
)
98 let restriction
= TransmuteRestriction
{
102 substituted_from
: from
,
106 self.push_transmute_restriction(restriction
);
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.
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)
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.
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:
147 // fn bar<T: ?Sized, U>() {
152 // fn baz<T: ?Sized, U: ?Sized>() {
156 // // T=[int], U=[int]
160 // In all cases, we keep the original unsubstituted types
161 // around for error reporting.
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");
172 let mut substs
= param_env
.free_substs
.clone();
173 self.with_each_combination(
176 param_env
.free_substs
.types
.iter_enumerated(),
179 let restriction
= TransmuteRestriction
{
183 substituted_from
: from
.subst(self.tcx
, substs
),
184 substituted_to
: to
.subst(self.tcx
, substs
),
187 self.push_transmute_restriction(restriction
);
191 fn with_each_combination(&self,
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
>))
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.
204 match types_in_scope
.next() {
206 debug
!("with_each_combination(substs={:?})",
212 Some((space
, index
, ¶m_ty
)) => {
213 debug
!("with_each_combination: space={:?}, index={}, param_ty={:?}",
214 space
, index
, param_ty
);
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");
219 substs
.types
.get_mut_slice(space
)[index
] = self.dummy_unsized_ty
;
220 self.with_each_combination(span
, param_env
, types_in_scope
.clone(),
224 substs
.types
.get_mut_slice(space
)[index
] = self.dummy_sized_ty
;
225 self.with_each_combination(span
, param_env
, types_in_scope
,
231 fn push_transmute_restriction(&self, restriction
: TransmuteRestriction
<'tcx
>) {
232 debug
!("Pushing transmute restriction: {:?}", restriction
);
233 self.tcx
.transmute_restrictions
.borrow_mut().push(restriction
);
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
) {
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();
247 visit
::FkFnBlock(..) => {
248 visit
::walk_fn(self, fk
, fd
, b
, s
);
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
);
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
);
269 .span_bug(expr
.span
, "transmute wasn't a bare fn?!");
277 visit
::walk_expr(self, expr
);
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=({:?},{:?}))",
287 self.substituted_from
,