]> git.proxmox.com Git - rustc.git/blob - src/librustc/middle/intrinsicck.rs
Imported Upstream version 1.9.0+dfsg1
[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 dep_graph::DepNode;
12 use hir::def::Def;
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};
18
19 use syntax::abi::Abi::RustIntrinsic;
20 use syntax::ast;
21 use syntax::codemap::Span;
22 use hir::intravisit::{self, Visitor, FnKind};
23 use hir;
24
25 pub fn check_crate(tcx: &TyCtxt) {
26 let mut visitor = ItemVisitor {
27 tcx: tcx
28 };
29 tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
30 }
31
32 struct ItemVisitor<'a, 'tcx: 'a> {
33 tcx: &'a TyCtxt<'tcx>
34 }
35
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,
40 Some(param_env),
41 ProjectionMode::Any);
42 let mut visitor = ExprVisitor {
43 infcx: &infcx
44 };
45 visitor.visit_expr(expr);
46 }
47 }
48
49 struct ExprVisitor<'a, 'tcx: 'a> {
50 infcx: &'a InferCtxt<'a, 'tcx>
51 }
52
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,
57 _ => return false
58 };
59 intrinsic && self.infcx.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 let sk_from = SizeSkeleton::compute(from, self.infcx);
64 let sk_to = SizeSkeleton::compute(to, self.infcx);
65
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) {
69 return;
70 }
71
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 `{}`",
78 from, to);
79 self.infcx.tcx.sess.add_lint(
80 ::lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES, id, span, msg);
81 return;
82 }
83 _ => {}
84 }
85 }
86
87 // Try to display a sensible error with as much information as possible.
88 let skeleton_string = |ty: Ty<'tcx>, sk| {
89 match sk {
90 Ok(SizeSkeleton::Known(size)) => {
91 format!("{} bits", size.bits())
92 }
93 Ok(SizeSkeleton::Pointer { tail, .. }) => {
94 format!("pointer to {}", tail)
95 }
96 Err(LayoutError::Unknown(bad)) => {
97 if bad == ty {
98 format!("size can vary")
99 } else {
100 format!("size can vary because of {}", bad)
101 }
102 }
103 Err(err) => err.to_string()
104 }
105 };
106
107 span_err!(self.infcx.tcx.sess, span, E0512,
108 "transmute called with differently sized types: \
109 {} ({}) to {} ({})",
110 from, skeleton_string(from, sk_from),
111 to, skeleton_string(to, sk_to));
112 }
113 }
114
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 {
121 infcx: &infcx
122 };
123 visitor.visit_expr(expr);
124 }
125
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);
129 } else {
130 intravisit::walk_trait_item(self, item);
131 }
132 }
133
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);
137 } else {
138 intravisit::walk_impl_item(self, item);
139 }
140 }
141
142 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
143 b: &'v hir::Block, s: Span, id: ast::NodeId) {
144 match fk {
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,
148 Some(param_env),
149 ProjectionMode::Any);
150 let mut visitor = ExprVisitor {
151 infcx: &infcx
152 };
153 visitor.visit_fn(fk, fd, b, s, id);
154 }
155 FnKind::Closure(..) => {
156 span_bug!(s, "intrinsicck: closure outside of function")
157 }
158 }
159 }
160 }
161
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);
168 match typ.sty {
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);
173 }
174 }
175 _ => {
176 span_bug!(expr.span, "transmute wasn't a bare fn?!");
177 }
178 }
179 }
180 _ => {}
181 }
182 }
183
184 intravisit::walk_expr(self, expr);
185 }
186 }