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
::middle
::expr_use_visitor
as euv
;
31 use rustc
::middle
::mem_categorization
as mc
;
32 use rustc
::middle
::mem_categorization
::Categorization
;
33 use rustc
::ty
::{self, Ty, TyCtxt}
;
34 use rustc
::ty
::query
::Providers
;
35 use rustc
::ty
::subst
::Substs
;
36 use rustc
::util
::nodemap
::{ItemLocalSet, NodeSet}
;
38 use rustc_data_structures
::sync
::Lrc
;
40 use syntax_pos
::{Span, DUMMY_SP}
;
41 use self::Promotability
::*;
42 use std
::ops
::{BitAnd, BitAndAssign, BitOr}
;
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
),
87 mut_rvalue_borrows
: NodeSet(),
88 param_env
: ty
::ParamEnv
::empty(),
89 identity_substs
: Substs
::empty(),
90 result
: ItemLocalSet(),
93 // `def_id` should be a `Body` owner
94 let node_id
= tcx
.hir
.as_local_node_id(def_id
)
95 .expect("rvalue_promotable_map invoked with non-local def-id");
96 let body_id
= tcx
.hir
.body_owned_by(node_id
);
97 let _
= visitor
.check_nested_body(body_id
);
99 Lrc
::new(visitor
.result
)
102 struct CheckCrateVisitor
<'a
, 'tcx
: 'a
> {
103 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
106 mut_rvalue_borrows
: NodeSet
,
107 param_env
: ty
::ParamEnv
<'tcx
>,
108 identity_substs
: &'tcx Substs
<'tcx
>,
109 tables
: &'a ty
::TypeckTables
<'tcx
>,
110 result
: ItemLocalSet
,
114 #[derive(Debug, Clone, Copy, PartialEq)]
120 impl BitAnd
for Promotability
{
123 fn bitand(self, rhs
: Self) -> Self {
125 (Promotable
, Promotable
) => Promotable
,
131 impl BitAndAssign
for Promotability
{
132 fn bitand_assign(&mut self, rhs
: Self) {
137 impl BitOr
for Promotability
{
140 fn bitor(self, rhs
: Self) -> Self {
142 (NotPromotable
, NotPromotable
) => NotPromotable
,
148 impl<'a
, 'gcx
> CheckCrateVisitor
<'a
, 'gcx
> {
149 // Returns true iff all the values of the type are promotable.
150 fn type_promotability(&mut self, ty
: Ty
<'gcx
>) -> Promotability
{
151 debug
!("type_promotability({})", ty
);
153 if ty
.is_freeze(self.tcx
, self.param_env
, DUMMY_SP
) &&
154 !ty
.needs_drop(self.tcx
, self.param_env
) {
161 fn handle_const_fn_call(
165 if self.tcx
.is_promotable_const_fn(def_id
) {
172 /// While the `ExprUseVisitor` walks, we will identify which
173 /// expressions are borrowed, and insert their ids into this
174 /// table. Actually, we insert the "borrow-id", which is normally
175 /// the id of the expession being borrowed: but in the case of
176 /// `ref mut` borrows, the `id` of the pattern is
177 /// inserted. Therefore later we remove that entry from the table
178 /// and transfer it over to the value being matched. This will
179 /// then prevent said value from being promoted.
180 fn remove_mut_rvalue_borrow(&mut self, pat
: &hir
::Pat
) -> bool
{
181 let mut any_removed
= false;
183 any_removed
|= self.mut_rvalue_borrows
.remove(&p
.id
);
190 impl<'a
, 'tcx
> CheckCrateVisitor
<'a
, 'tcx
> {
191 fn check_nested_body(&mut self, body_id
: hir
::BodyId
) -> Promotability
{
192 let item_id
= self.tcx
.hir
.body_owner(body_id
);
193 let item_def_id
= self.tcx
.hir
.local_def_id(item_id
);
195 let outer_in_fn
= self.in_fn
;
196 let outer_tables
= self.tables
;
197 let outer_param_env
= self.param_env
;
198 let outer_identity_substs
= self.identity_substs
;
201 self.in_static
= false;
203 match self.tcx
.hir
.body_owner_kind(item_id
) {
204 hir
::BodyOwnerKind
::Fn
=> self.in_fn
= true,
205 hir
::BodyOwnerKind
::Static(_
) => self.in_static
= true,
210 self.tables
= self.tcx
.typeck_tables_of(item_def_id
);
211 self.param_env
= self.tcx
.param_env(item_def_id
);
212 self.identity_substs
= Substs
::identity_for_item(self.tcx
, item_def_id
);
214 let body
= self.tcx
.hir
.body(body_id
);
217 let param_env
= self.param_env
;
218 let region_scope_tree
= self.tcx
.region_scope_tree(item_def_id
);
219 let tables
= self.tables
;
220 euv
::ExprUseVisitor
::new(self, tcx
, param_env
, ®ion_scope_tree
, tables
, None
)
223 let body_promotable
= self.check_expr(&body
.value
);
224 self.in_fn
= outer_in_fn
;
225 self.tables
= outer_tables
;
226 self.param_env
= outer_param_env
;
227 self.identity_substs
= outer_identity_substs
;
231 fn check_stmt(&mut self, stmt
: &'tcx hir
::Stmt
) -> Promotability
{
233 hir
::StmtKind
::Decl(ref decl
, _node_id
) => {
235 hir
::DeclKind
::Local(local
) => {
236 if self.remove_mut_rvalue_borrow(&local
.pat
) {
237 if let Some(init
) = &local
.init
{
238 self.mut_rvalue_borrows
.insert(init
.id
);
242 if let Some(ref expr
) = local
.init
{
243 let _
= self.check_expr(&expr
);
247 // Item statements are allowed
248 hir
::DeclKind
::Item(_
) => Promotable
251 hir
::StmtKind
::Expr(ref box_expr
, _node_id
) |
252 hir
::StmtKind
::Semi(ref box_expr
, _node_id
) => {
253 let _
= self.check_expr(box_expr
);
259 fn check_expr(&mut self, ex
: &'tcx hir
::Expr
) -> Promotability
{
260 let node_ty
= self.tables
.node_id_to_type(ex
.hir_id
);
261 let mut outer
= check_expr_kind(self, ex
, node_ty
);
262 outer
&= check_adjustments(self, ex
);
264 // Handle borrows on (or inside the autorefs of) this expression.
265 if self.mut_rvalue_borrows
.remove(&ex
.id
) {
266 outer
= NotPromotable
269 if outer
== Promotable
{
270 self.result
.insert(ex
.hir_id
.local_id
);
275 fn check_block(&mut self, block
: &'tcx hir
::Block
) -> Promotability
{
276 let mut iter_result
= Promotable
;
277 for index
in block
.stmts
.iter() {
278 iter_result
&= self.check_stmt(index
);
281 Some(ref box_expr
) => iter_result
& self.check_expr(&*box_expr
),
287 /// This function is used to enforce the constraints on
288 /// const/static items. It walks through the *value*
289 /// of the item walking down the expression and evaluating
290 /// every nested expression. If the expression is not part
291 /// of a const/static item, it is qualified for promotion
292 /// instead of producing errors.
293 fn check_expr_kind
<'a
, 'tcx
>(
294 v
: &mut CheckCrateVisitor
<'a
, 'tcx
>,
295 e
: &'tcx hir
::Expr
, node_ty
: Ty
<'tcx
>) -> Promotability
{
297 let ty_result
= match node_ty
.sty
{
298 ty
::Adt(def
, _
) if def
.has_dtor(v
.tcx
) => {
304 let node_result
= match e
.node
{
305 hir
::ExprKind
::Box(ref expr
) => {
306 let _
= v
.check_expr(&expr
);
309 hir
::ExprKind
::Unary(op
, ref expr
) => {
310 let expr_promotability
= v
.check_expr(expr
);
311 if v
.tables
.is_method_call(e
) || op
== hir
::UnDeref
{
312 return NotPromotable
;
316 hir
::ExprKind
::Binary(op
, ref lhs
, ref rhs
) => {
317 let lefty
= v
.check_expr(lhs
);
318 let righty
= v
.check_expr(rhs
);
319 if v
.tables
.is_method_call(e
) {
320 return NotPromotable
;
322 match v
.tables
.node_id_to_type(lhs
.hir_id
).sty
{
323 ty
::RawPtr(_
) | ty
::FnPtr(..) => {
324 assert
!(op
.node
== hir
::BinOpKind
::Eq
|| op
.node
== hir
::BinOpKind
::Ne
||
325 op
.node
== hir
::BinOpKind
::Le
|| op
.node
== hir
::BinOpKind
::Lt
||
326 op
.node
== hir
::BinOpKind
::Ge
|| op
.node
== hir
::BinOpKind
::Gt
);
333 hir
::ExprKind
::Cast(ref from
, _
) => {
334 let expr_promotability
= v
.check_expr(from
);
335 debug
!("Checking const cast(id={})", from
.id
);
336 match v
.tables
.cast_kinds().get(from
.hir_id
) {
338 v
.tcx
.sess
.delay_span_bug(e
.span
, "no kind for cast");
341 Some(&CastKind
::PtrAddrCast
) | Some(&CastKind
::FnPtrAddrCast
) => {
344 _
=> expr_promotability
347 hir
::ExprKind
::Path(ref qpath
) => {
348 let def
= v
.tables
.qpath_def(qpath
, e
.hir_id
);
350 Def
::VariantCtor(..) | Def
::StructCtor(..) |
351 Def
::Fn(..) | Def
::Method(..) | Def
::SelfCtor(..) => Promotable
,
353 // References to a static that are themselves within a static
354 // are inherently promotable with the exception
355 // of "#[thread_local]" statics, which may not
356 // outlive the current function
357 Def
::Static(did
, _
) => {
360 for attr
in &v
.tcx
.get_attrs(did
)[..] {
361 if attr
.check_name("thread_local") {
362 debug
!("Reference to Static(id={:?}) is unpromotable \
363 due to a #[thread_local] attribute", did
);
364 return NotPromotable
;
369 debug
!("Reference to Static(id={:?}) is unpromotable as it is not \
370 referenced from a static", did
);
376 Def
::AssociatedConst(did
) => {
377 let promotable
= if v
.tcx
.trait_of_item(did
).is_some() {
378 // Don't peek inside trait associated constants.
380 } else if v
.tcx
.at(e
.span
).const_is_rvalue_promotable_to_static(did
) {
385 // Just in case the type is more specific than the definition,
386 // e.g. impl associated const with type parameters, check it.
387 // Also, trait associated consts are relaxed by this.
388 promotable
| v
.type_promotability(node_ty
)
393 hir
::ExprKind
::Call(ref callee
, ref hirvec
) => {
394 let mut call_result
= v
.check_expr(callee
);
395 for index
in hirvec
.iter() {
396 call_result
&= v
.check_expr(index
);
398 let mut callee
= &**callee
;
400 callee
= match callee
.node
{
401 hir
::ExprKind
::Block(ref block
, _
) => match block
.expr
{
402 Some(ref tail
) => &tail
,
408 // The callee is an arbitrary expression, it doesn't necessarily have a definition.
409 let def
= if let hir
::ExprKind
::Path(ref qpath
) = callee
.node
{
410 v
.tables
.qpath_def(qpath
, callee
.hir_id
)
414 let def_result
= match def
{
415 Def
::StructCtor(_
, CtorKind
::Fn
) |
416 Def
::VariantCtor(_
, CtorKind
::Fn
) |
417 Def
::SelfCtor(..) => Promotable
,
418 Def
::Fn(did
) => v
.handle_const_fn_call(did
),
419 Def
::Method(did
) => {
420 match v
.tcx
.associated_item(did
).container
{
421 ty
::ImplContainer(_
) => v
.handle_const_fn_call(did
),
422 ty
::TraitContainer(_
) => NotPromotable
,
427 def_result
& call_result
429 hir
::ExprKind
::MethodCall(ref _pathsegment
, ref _span
, ref hirvec
) => {
430 let mut method_call_result
= Promotable
;
431 for index
in hirvec
.iter() {
432 method_call_result
&= v
.check_expr(index
);
434 if let Some(def
) = v
.tables
.type_dependent_defs().get(e
.hir_id
) {
435 let def_id
= def
.def_id();
436 match v
.tcx
.associated_item(def_id
).container
{
437 ty
::ImplContainer(_
) => method_call_result
& v
.handle_const_fn_call(def_id
),
438 ty
::TraitContainer(_
) => NotPromotable
,
441 v
.tcx
.sess
.delay_span_bug(e
.span
, "no type-dependent def for method call");
445 hir
::ExprKind
::Struct(ref _qpath
, ref hirvec
, ref option_expr
) => {
446 let mut struct_result
= Promotable
;
447 for index
in hirvec
.iter() {
448 struct_result
&= v
.check_expr(&index
.expr
);
450 if let Some(ref expr
) = *option_expr
{
451 struct_result
&= v
.check_expr(&expr
);
453 if let ty
::Adt(adt
, ..) = v
.tables
.expr_ty(e
).sty
{
454 // unsafe_cell_type doesn't necessarily exist with no_core
455 if Some(adt
.did
) == v
.tcx
.lang_items().unsafe_cell_type() {
456 return NotPromotable
;
462 hir
::ExprKind
::Lit(_
) => Promotable
,
464 hir
::ExprKind
::AddrOf(_
, ref expr
) |
465 hir
::ExprKind
::Repeat(ref expr
, _
) => {
469 hir
::ExprKind
::Closure(_capture_clause
, ref _box_fn_decl
,
470 body_id
, _span
, _option_generator_movability
) => {
471 let nested_body_promotable
= v
.check_nested_body(body_id
);
472 // Paths in constant contexts cannot refer to local variables,
473 // as there are none, and thus closures can't have upvars there.
474 if v
.tcx
.with_freevars(e
.id
, |fv
| !fv
.is_empty()) {
477 nested_body_promotable
481 hir
::ExprKind
::Field(ref expr
, _ident
) => {
482 let expr_promotability
= v
.check_expr(&expr
);
483 if let Some(def
) = v
.tables
.expr_ty(expr
).ty_adt_def() {
485 return NotPromotable
;
491 hir
::ExprKind
::Block(ref box_block
, ref _option_label
) => {
492 v
.check_block(box_block
)
495 hir
::ExprKind
::Index(ref lhs
, ref rhs
) => {
496 let lefty
= v
.check_expr(lhs
);
497 let righty
= v
.check_expr(rhs
);
498 if v
.tables
.is_method_call(e
) {
499 return NotPromotable
;
504 hir
::ExprKind
::Array(ref hirvec
) => {
505 let mut array_result
= Promotable
;
506 for index
in hirvec
.iter() {
507 array_result
&= v
.check_expr(index
);
512 hir
::ExprKind
::Type(ref expr
, ref _ty
) => {
516 hir
::ExprKind
::Tup(ref hirvec
) => {
517 let mut tup_result
= Promotable
;
518 for index
in hirvec
.iter() {
519 tup_result
&= v
.check_expr(index
);
525 // Conditional control flow (possible to implement).
526 hir
::ExprKind
::Match(ref expr
, ref hirvec_arm
, ref _match_source
) => {
527 // Compute the most demanding borrow from all the arms'
528 // patterns and set that on the discriminator.
529 let mut mut_borrow
= false;
530 for pat
in hirvec_arm
.iter().flat_map(|arm
| &arm
.pats
) {
531 mut_borrow
= v
.remove_mut_rvalue_borrow(pat
);
534 v
.mut_rvalue_borrows
.insert(expr
.id
);
537 let _
= v
.check_expr(expr
);
538 for index
in hirvec_arm
.iter() {
539 let _
= v
.check_expr(&*index
.body
);
540 if let Some(hir
::Guard
::If(ref expr
)) = index
.guard
{
541 let _
= v
.check_expr(&expr
);
547 hir
::ExprKind
::If(ref lhs
, ref rhs
, ref option_expr
) => {
548 let _
= v
.check_expr(lhs
);
549 let _
= v
.check_expr(rhs
);
550 if let Some(ref expr
) = option_expr
{
551 let _
= v
.check_expr(&expr
);
556 // Loops (not very meaningful in constants).
557 hir
::ExprKind
::While(ref expr
, ref box_block
, ref _option_label
) => {
558 let _
= v
.check_expr(expr
);
559 let _
= v
.check_block(box_block
);
563 hir
::ExprKind
::Loop(ref box_block
, ref _option_label
, ref _loop_source
) => {
564 let _
= v
.check_block(box_block
);
568 // More control flow (also not very meaningful).
569 hir
::ExprKind
::Break(_
, ref option_expr
) | hir
::ExprKind
::Ret(ref option_expr
) => {
570 if let Some(ref expr
) = *option_expr
{
571 let _
= v
.check_expr(&expr
);
576 hir
::ExprKind
::Continue(_
) => {
580 // Generator expressions
581 hir
::ExprKind
::Yield(ref expr
) => {
582 let _
= v
.check_expr(&expr
);
586 // Expressions with side-effects.
587 hir
::ExprKind
::AssignOp(_
, ref lhs
, ref rhs
) | hir
::ExprKind
::Assign(ref lhs
, ref rhs
) => {
588 let _
= v
.check_expr(lhs
);
589 let _
= v
.check_expr(rhs
);
593 hir
::ExprKind
::InlineAsm(ref _inline_asm
, ref hirvec_lhs
, ref hirvec_rhs
) => {
594 for index
in hirvec_lhs
.iter().chain(hirvec_rhs
.iter()) {
595 let _
= v
.check_expr(index
);
600 ty_result
& node_result
603 /// Check the adjustments of an expression
604 fn check_adjustments
<'a
, 'tcx
>(
605 v
: &mut CheckCrateVisitor
<'a
, 'tcx
>,
606 e
: &hir
::Expr
) -> Promotability
{
607 use rustc
::ty
::adjustment
::*;
609 let mut adjustments
= v
.tables
.expr_adjustments(e
).iter().peekable();
610 while let Some(adjustment
) = adjustments
.next() {
611 match adjustment
.kind
{
613 Adjust
::ReifyFnPointer
|
614 Adjust
::UnsafeFnPointer
|
615 Adjust
::ClosureFnPointer
|
616 Adjust
::MutToConstPointer
|
620 Adjust
::Deref(_
) => {
621 if let Some(next_adjustment
) = adjustments
.peek() {
622 if let Adjust
::Borrow(_
) = next_adjustment
.kind
{
626 return NotPromotable
;
633 impl<'a
, 'gcx
, 'tcx
> euv
::Delegate
<'tcx
> for CheckCrateVisitor
<'a
, 'gcx
> {
634 fn consume(&mut self,
635 _consume_id
: ast
::NodeId
,
638 _mode
: euv
::ConsumeMode
) {}
641 borrow_id
: ast
::NodeId
,
643 cmt
: &mc
::cmt_
<'tcx
>,
644 _loan_region
: ty
::Region
<'tcx
>,
646 loan_cause
: euv
::LoanCause
) {
648 "borrow(borrow_id={:?}, cmt={:?}, bk={:?}, loan_cause={:?})",
655 // Kind of hacky, but we allow Unsafe coercions in constants.
656 // These occur when we convert a &T or *T to a *U, as well as
657 // when making a thin pointer (e.g., `*T`) into a fat pointer
659 if let euv
::LoanCause
::AutoUnsafe
= loan_cause
{
666 Categorization
::Rvalue(..) => {
667 if loan_cause
== euv
::MatchDiscriminant
{
668 // Ignore the dummy immutable borrow created by EUV.
671 if bk
.to_mutbl_lossy() == hir
::MutMutable
{
672 self.mut_rvalue_borrows
.insert(borrow_id
);
676 Categorization
::StaticItem
=> {
679 Categorization
::Deref(ref cmt
, _
) |
680 Categorization
::Downcast(ref cmt
, _
) |
681 Categorization
::Interior(ref cmt
, _
) => {
685 Categorization
::Upvar(..) |
686 Categorization
::Local(..) => break,
691 fn decl_without_init(&mut self, _id
: ast
::NodeId
, _span
: Span
) {}
693 _assignment_id
: ast
::NodeId
,
694 _assignment_span
: Span
,
695 _assignee_cmt
: &mc
::cmt_
,
696 _mode
: euv
::MutateMode
) {
699 fn matched_pat(&mut self, _
: &hir
::Pat
, _
: &mc
::cmt_
, _
: euv
::MatchMode
) {}
701 fn consume_pat(&mut self, _consume_pat
: &hir
::Pat
, _cmt
: &mc
::cmt_
, _mode
: euv
::ConsumeMode
) {}