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 // Verifies that the types and values of const and static items
12 // are safe. The rules enforced by this module are:
14 // - For each *mutable* static item, it checks that its **type**:
15 // - doesn't have a destructor
16 // - doesn't own a box
18 // - For each *immutable* static item, it checks that its **value**:
19 // - doesn't own a box
20 // - doesn't contain a struct literal or a call to an enum variant / struct constructor where
21 // - the type of the struct/enum has a dtor
23 // Rules Enforced Elsewhere:
24 // - It's not possible to take the address of a static item with unsafe interior. This is enforced
25 // by borrowck::gather_loans
27 use rustc::ty::cast::CastKind;
28 use rustc::hir::def::{Def, CtorKind};
29 use rustc::hir::def_id::DefId;
30 use rustc::hir::map::blocks::FnLikeNode;
31 use rustc::middle::expr_use_visitor as euv;
32 use rustc::middle::mem_categorization as mc;
33 use rustc::middle::mem_categorization::Categorization;
34 use rustc::ty::{self, Ty, TyCtxt};
35 use rustc::ty::maps::Providers;
36 use rustc::ty::subst::Substs;
37 use rustc::util::nodemap::{ItemLocalSet, NodeSet};
39 use rustc_data_structures::sync::Lrc;
41 use syntax_pos::{Span, DUMMY_SP};
42 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
44 pub fn provide(providers: &mut Providers) {
45 *providers = Providers {
46 rvalue_promotable_map,
47 const_is_rvalue_promotable_to_static,
52 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
53 for &body_id in &tcx.hir.krate().body_ids {
54 let def_id = tcx.hir.body_owner_def_id(body_id);
55 tcx.const_is_rvalue_promotable_to_static(def_id);
57 tcx.sess.abort_if_errors();
60 fn const_is_rvalue_promotable_to_static<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
64 assert!(def_id.is_local());
66 let node_id = tcx.hir.as_local_node_id(def_id)
67 .expect("rvalue_promotable_map invoked with non-local def-id");
68 let body_id = tcx.hir.body_owned_by(node_id);
69 let body_hir_id = tcx.hir.node_to_hir_id(body_id.node_id);
70 tcx.rvalue_promotable_map(def_id).contains(&body_hir_id.local_id)
73 fn rvalue_promotable_map<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
77 let outer_def_id = tcx.closure_base_def_id(def_id);
78 if outer_def_id != def_id {
79 return tcx.rvalue_promotable_map(outer_def_id);
82 let mut visitor = CheckCrateVisitor {
84 tables: &ty::TypeckTables::empty(None),
88 mut_rvalue_borrows: NodeSet(),
89 param_env: ty::ParamEnv::empty(),
90 identity_substs: Substs::empty(),
91 result: ItemLocalSet(),
94 // `def_id` should be a `Body` owner
95 let node_id = tcx.hir.as_local_node_id(def_id)
96 .expect("rvalue_promotable_map invoked with non-local def-id");
97 let body_id = tcx.hir.body_owned_by(node_id);
98 visitor.visit_nested_body(body_id);
100 Lrc::new(visitor.result)
103 struct CheckCrateVisitor<'a, 'tcx: 'a> {
104 tcx: TyCtxt<'a, 'tcx, 'tcx>,
108 mut_rvalue_borrows: NodeSet,
109 param_env: ty::ParamEnv<'tcx>,
110 identity_substs: &'tcx Substs<'tcx>,
111 tables: &'a ty::TypeckTables<'tcx>,
112 result: ItemLocalSet,
115 impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
116 // Returns true iff all the values of the type are promotable.
117 fn type_has_only_promotable_values(&mut self, ty: Ty<'gcx>) -> bool {
118 ty.is_freeze(self.tcx, self.param_env, DUMMY_SP) &&
119 !ty.needs_drop(self.tcx, self.param_env)
122 fn handle_const_fn_call(&mut self, def_id: DefId, ret_ty: Ty<'gcx>) {
123 self.promotable &= self.type_has_only_promotable_values(ret_ty);
125 self.promotable &= if let Some(fn_id) = self.tcx.hir.as_local_node_id(def_id) {
126 FnLikeNode::from_node(self.tcx.hir.get(fn_id)).map_or(false, |fn_like| {
127 fn_like.constness() == hir::Constness::Const
130 self.tcx.is_const_fn(def_id)
135 impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
136 fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
137 // note that we *do* visit nested bodies, because we override `visit_nested_body` below
138 NestedVisitorMap::None
141 fn visit_nested_body(&mut self, body_id: hir::BodyId) {
142 let item_id = self.tcx.hir.body_owner(body_id);
143 let item_def_id = self.tcx.hir.local_def_id(item_id);
145 let outer_in_fn = self.in_fn;
146 let outer_tables = self.tables;
147 let outer_param_env = self.param_env;
148 let outer_identity_substs = self.identity_substs;
151 self.in_static = false;
153 match self.tcx.hir.body_owner_kind(item_id) {
154 hir::BodyOwnerKind::Fn => self.in_fn = true,
155 hir::BodyOwnerKind::Static(_) => self.in_static = true,
160 self.tables = self.tcx.typeck_tables_of(item_def_id);
161 self.param_env = self.tcx.param_env(item_def_id);
162 self.identity_substs = Substs::identity_for_item(self.tcx, item_def_id);
164 let body = self.tcx.hir.body(body_id);
167 let param_env = self.param_env;
168 let region_scope_tree = self.tcx.region_scope_tree(item_def_id);
169 euv::ExprUseVisitor::new(self, tcx, param_env, ®ion_scope_tree, self.tables, None)
172 self.visit_body(body);
174 self.in_fn = outer_in_fn;
175 self.tables = outer_tables;
176 self.param_env = outer_param_env;
177 self.identity_substs = outer_identity_substs;
180 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
182 hir::StmtDecl(ref decl, _) => {
184 hir::DeclLocal(_) => {
185 self.promotable = false;
187 // Item statements are allowed
188 hir::DeclItem(_) => {}
192 hir::StmtSemi(..) => {
193 self.promotable = false;
196 intravisit::walk_stmt(self, stmt);
199 fn visit_expr(&mut self, ex: &'tcx hir::Expr) {
200 let outer = self.promotable;
201 self.promotable = true;
203 let node_ty = self.tables.node_id_to_type(ex.hir_id);
204 check_expr(self, ex, node_ty);
205 check_adjustments(self, ex);
207 if let hir::ExprMatch(ref discr, ref arms, _) = ex.node {
208 // Compute the most demanding borrow from all the arms'
209 // patterns and set that on the discriminator.
210 let mut mut_borrow = false;
211 for pat in arms.iter().flat_map(|arm| &arm.pats) {
212 if self.mut_rvalue_borrows.remove(&pat.id) {
217 self.mut_rvalue_borrows.insert(discr.id);
221 intravisit::walk_expr(self, ex);
223 // Handle borrows on (or inside the autorefs of) this expression.
224 if self.mut_rvalue_borrows.remove(&ex.id) {
225 self.promotable = false;
229 self.result.insert(ex.hir_id.local_id);
231 self.promotable &= outer;
235 /// This function is used to enforce the constraints on
236 /// const/static items. It walks through the *value*
237 /// of the item walking down the expression and evaluating
238 /// every nested expression. If the expression is not part
239 /// of a const/static item, it is qualified for promotion
240 /// instead of producing errors.
241 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) {
243 ty::TyAdt(def, _) if def.has_dtor(v.tcx) => {
244 v.promotable = false;
251 hir::ExprBinary(..) |
252 hir::ExprIndex(..) if v.tables.is_method_call(e) => {
253 v.promotable = false;
256 v.promotable = false;
258 hir::ExprUnary(op, _) => {
259 if op == hir::UnDeref {
260 v.promotable = false;
263 hir::ExprBinary(op, ref lhs, _) => {
264 match v.tables.node_id_to_type(lhs.hir_id).sty {
266 assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
267 op.node == hir::BiLe || op.node == hir::BiLt ||
268 op.node == hir::BiGe || op.node == hir::BiGt);
270 v.promotable = false;
275 hir::ExprCast(ref from, _) => {
276 debug!("Checking const cast(id={})", from.id);
277 match v.tables.cast_kinds().get(from.hir_id) {
278 None => span_bug!(e.span, "no kind for cast"),
279 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
280 v.promotable = false;
285 hir::ExprPath(ref qpath) => {
286 let def = v.tables.qpath_def(qpath, e.hir_id);
288 Def::VariantCtor(..) | Def::StructCtor(..) |
289 Def::Fn(..) | Def::Method(..) => {}
291 // References to a static that are themselves within a static
292 // are inherently promotable with the exception
293 // of "#[thread_local]" statics, which may not
294 // outlive the current function
295 Def::Static(did, _) => {
298 let mut thread_local = false;
300 for attr in &v.tcx.get_attrs(did)[..] {
301 if attr.check_name("thread_local") {
302 debug!("Reference to Static(id={:?}) is unpromotable \
303 due to a #[thread_local] attribute", did);
304 v.promotable = false;
311 debug!("Allowing promotion of reference to Static(id={:?})", did);
314 debug!("Reference to Static(id={:?}) is unpromotable as it is not \
315 referenced from a static", did);
316 v.promotable = false;
322 Def::AssociatedConst(did) => {
323 let promotable = if v.tcx.trait_of_item(did).is_some() {
324 // Don't peek inside trait associated constants.
327 v.tcx.at(e.span).const_is_rvalue_promotable_to_static(did)
330 // Just in case the type is more specific than the definition,
331 // e.g. impl associated const with type parameters, check it.
332 // Also, trait associated consts are relaxed by this.
333 v.promotable &= promotable || v.type_has_only_promotable_values(node_ty);
337 v.promotable = false;
341 hir::ExprCall(ref callee, _) => {
342 let mut callee = &**callee;
344 callee = match callee.node {
345 hir::ExprBlock(ref block) => match block.expr {
346 Some(ref tail) => &tail,
352 // The callee is an arbitrary expression, it doesn't necessarily have a definition.
353 let def = if let hir::ExprPath(ref qpath) = callee.node {
354 v.tables.qpath_def(qpath, callee.hir_id)
359 Def::StructCtor(_, CtorKind::Fn) |
360 Def::VariantCtor(_, CtorKind::Fn) => {}
362 v.handle_const_fn_call(did, node_ty)
364 Def::Method(did) => {
365 match v.tcx.associated_item(did).container {
366 ty::ImplContainer(_) => {
367 v.handle_const_fn_call(did, node_ty)
369 ty::TraitContainer(_) => v.promotable = false
372 _ => v.promotable = false
375 hir::ExprMethodCall(..) => {
376 let def_id = v.tables.type_dependent_defs()[e.hir_id].def_id();
377 match v.tcx.associated_item(def_id).container {
378 ty::ImplContainer(_) => v.handle_const_fn_call(def_id, node_ty),
379 ty::TraitContainer(_) => v.promotable = false
382 hir::ExprStruct(..) => {
383 if let ty::TyAdt(adt, ..) = v.tables.expr_ty(e).sty {
384 // unsafe_cell_type doesn't necessarily exist with no_core
385 if Some(adt.did) == v.tcx.lang_items().unsafe_cell_type() {
386 v.promotable = false;
392 hir::ExprAddrOf(..) |
393 hir::ExprRepeat(..) => {}
395 hir::ExprClosure(..) => {
396 // Paths in constant contexts cannot refer to local variables,
397 // as there are none, and thus closures can't have upvars there.
398 if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
399 v.promotable = false;
406 hir::ExprTupField(..) |
409 hir::ExprTup(..) => {}
411 // Conditional control flow (possible to implement).
415 // Loops (not very meaningful in constants).
419 // More control flow (also not very meaningful).
424 // Generator expressions
427 // Expressions with side-effects.
428 hir::ExprAssign(..) |
429 hir::ExprAssignOp(..) |
430 hir::ExprInlineAsm(..) => {
431 v.promotable = false;
436 /// Check the adjustments of an expression
437 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
438 use rustc::ty::adjustment::*;
440 let mut adjustments = v.tables.expr_adjustments(e).iter().peekable();
441 while let Some(adjustment) = adjustments.next() {
442 match adjustment.kind {
444 Adjust::ReifyFnPointer |
445 Adjust::UnsafeFnPointer |
446 Adjust::ClosureFnPointer |
447 Adjust::MutToConstPointer |
451 Adjust::Deref(_) => {
452 if let Some(next_adjustment) = adjustments.peek() {
453 if let Adjust::Borrow(_) = next_adjustment.kind {
457 v.promotable = false;
464 impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
465 fn consume(&mut self,
466 _consume_id: ast::NodeId,
469 _mode: euv::ConsumeMode) {}
472 borrow_id: ast::NodeId,
475 _loan_region: ty::Region<'tcx>,
477 loan_cause: euv::LoanCause) {
478 // Kind of hacky, but we allow Unsafe coercions in constants.
479 // These occur when we convert a &T or *T to a *U, as well as
480 // when making a thin pointer (e.g., `*T`) into a fat pointer
483 euv::LoanCause::AutoUnsafe => {
492 Categorization::Rvalue(..) => {
493 if loan_cause == euv::MatchDiscriminant {
494 // Ignore the dummy immutable borrow created by EUV.
497 if bk.to_mutbl_lossy() == hir::MutMutable {
498 self.mut_rvalue_borrows.insert(borrow_id);
502 Categorization::StaticItem => {
505 Categorization::Deref(ref cmt, _) |
506 Categorization::Downcast(ref cmt, _) |
507 Categorization::Interior(ref cmt, _) => {
511 Categorization::Upvar(..) |
512 Categorization::Local(..) => break,
517 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {}
519 _assignment_id: ast::NodeId,
520 _assignment_span: Span,
521 _assignee_cmt: mc::cmt,
522 _mode: euv::MutateMode) {
525 fn matched_pat(&mut self, _: &hir::Pat, _: mc::cmt, _: euv::MatchMode) {}
527 fn consume_pat(&mut self, _consume_pat: &hir::Pat, _cmt: mc::cmt, _mode: euv::ConsumeMode) {}