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 dep_graph
::DepNode
;
13 use hir
::def_id
::DefId
;
14 use infer
::{InferCtxt, new_infer_ctxt}
;
15 use traits
::ProjectionMode
;
16 use ty
::{self, Ty, TyCtxt}
;
17 use ty
::layout
::{LayoutError, Pointer, SizeSkeleton}
;
19 use syntax
::abi
::Abi
::RustIntrinsic
;
21 use syntax
::codemap
::Span
;
22 use hir
::intravisit
::{self, Visitor, FnKind}
;
25 pub fn check_crate(tcx
: &TyCtxt
) {
26 let mut visitor
= ItemVisitor
{
29 tcx
.visit_all_items_in_krate(DepNode
::IntrinsicCheck
, &mut visitor
);
32 struct ItemVisitor
<'a
, 'tcx
: 'a
> {
36 impl<'a
, 'tcx
> ItemVisitor
<'a
, 'tcx
> {
37 fn visit_const(&mut self, item_id
: ast
::NodeId
, expr
: &hir
::Expr
) {
38 let param_env
= ty
::ParameterEnvironment
::for_item(self.tcx
, item_id
);
39 let infcx
= new_infer_ctxt(self.tcx
, &self.tcx
.tables
,
42 let mut visitor
= ExprVisitor
{
45 visitor
.visit_expr(expr
);
49 struct ExprVisitor
<'a
, 'tcx
: 'a
> {
50 infcx
: &'a InferCtxt
<'a
, 'tcx
>
53 impl<'a
, 'tcx
> ExprVisitor
<'a
, 'tcx
> {
54 fn def_id_is_transmute(&self, def_id
: DefId
) -> bool
{
55 let intrinsic
= match self.infcx
.tcx
.lookup_item_type(def_id
).ty
.sty
{
56 ty
::TyFnDef(_
, _
, ref bfty
) => bfty
.abi
== RustIntrinsic
,
59 intrinsic
&& self.infcx
.tcx
.item_name(def_id
).as_str() == "transmute"
62 fn check_transmute(&self, span
: Span
, from
: Ty
<'tcx
>, to
: Ty
<'tcx
>, id
: ast
::NodeId
) {
63 let sk_from
= SizeSkeleton
::compute(from
, self.infcx
);
64 let sk_to
= SizeSkeleton
::compute(to
, self.infcx
);
66 // Check for same size using the skeletons.
67 if let (Ok(sk_from
), Ok(sk_to
)) = (sk_from
, sk_to
) {
68 if sk_from
.same_size(sk_to
) {
72 match (&from
.sty
, sk_to
) {
73 (&ty
::TyFnDef(..), SizeSkeleton
::Known(size_to
))
74 if size_to
== Pointer
.size(&self.infcx
.tcx
.data_layout
) => {
75 // FIXME #19925 Remove this warning after a release cycle.
76 let msg
= format
!("`{}` is now zero-sized and has to be cast \
77 to a pointer before transmuting to `{}`",
79 self.infcx
.tcx
.sess
.add_lint(
80 ::lint
::builtin
::TRANSMUTE_FROM_FN_ITEM_TYPES
, id
, span
, msg
);
87 // Try to display a sensible error with as much information as possible.
88 let skeleton_string
= |ty
: Ty
<'tcx
>, sk
| {
90 Ok(SizeSkeleton
::Known(size
)) => {
91 format
!("{} bits", size
.bits())
93 Ok(SizeSkeleton
::Pointer { tail, .. }
) => {
94 format
!("pointer to {}", tail
)
96 Err(LayoutError
::Unknown(bad
)) => {
98 format
!("size can vary")
100 format
!("size can vary because of {}", bad
)
103 Err(err
) => err
.to_string()
107 span_err
!(self.infcx
.tcx
.sess
, span
, E0512
,
108 "transmute called with differently sized types: \
110 from
, skeleton_string(from
, sk_from
),
111 to
, skeleton_string(to
, sk_to
));
115 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for ItemVisitor
<'a
, 'tcx
> {
116 // const, static and N in [T; N].
117 fn visit_expr(&mut self, expr
: &hir
::Expr
) {
118 let infcx
= new_infer_ctxt(self.tcx
, &self.tcx
.tables
,
119 None
, ProjectionMode
::Any
);
120 let mut visitor
= ExprVisitor
{
123 visitor
.visit_expr(expr
);
126 fn visit_trait_item(&mut self, item
: &hir
::TraitItem
) {
127 if let hir
::ConstTraitItem(_
, Some(ref expr
)) = item
.node
{
128 self.visit_const(item
.id
, expr
);
130 intravisit
::walk_trait_item(self, item
);
134 fn visit_impl_item(&mut self, item
: &hir
::ImplItem
) {
135 if let hir
::ImplItemKind
::Const(_
, ref expr
) = item
.node
{
136 self.visit_const(item
.id
, expr
);
138 intravisit
::walk_impl_item(self, item
);
142 fn visit_fn(&mut self, fk
: FnKind
<'v
>, fd
: &'v hir
::FnDecl
,
143 b
: &'v hir
::Block
, s
: Span
, id
: ast
::NodeId
) {
145 FnKind
::ItemFn(..) | FnKind
::Method(..) => {
146 let param_env
= ty
::ParameterEnvironment
::for_item(self.tcx
, id
);
147 let infcx
= new_infer_ctxt(self.tcx
, &self.tcx
.tables
,
149 ProjectionMode
::Any
);
150 let mut visitor
= ExprVisitor
{
153 visitor
.visit_fn(fk
, fd
, b
, s
, id
);
155 FnKind
::Closure(..) => {
156 span_bug
!(s
, "intrinsicck: closure outside of function")
162 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for ExprVisitor
<'a
, 'tcx
> {
163 fn visit_expr(&mut self, expr
: &hir
::Expr
) {
164 if let hir
::ExprPath(..) = expr
.node
{
165 match self.infcx
.tcx
.resolve_expr(expr
) {
166 Def
::Fn(did
) if self.def_id_is_transmute(did
) => {
167 let typ
= self.infcx
.tcx
.node_id_to_type(expr
.id
);
169 ty
::TyFnDef(_
, _
, ref bare_fn_ty
) if bare_fn_ty
.abi
== RustIntrinsic
=> {
170 if let ty
::FnConverging(to
) = bare_fn_ty
.sig
.0.output
{
171 let from
= bare_fn_ty
.sig
.0.inputs
[0];
172 self.check_transmute(expr
.span
, from
, to
, expr
.id
);
176 span_bug
!(expr
.span
, "transmute wasn't a bare fn?!");
184 intravisit
::walk_expr(self, expr
);