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 an owned pointer
18 // - For each *immutable* static item, it checks that its **value**:
19 // - doesn't own owned, managed pointers
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 middle::const_eval;
29 use middle::expr_use_visitor as euv;
31 use middle::mem_categorization as mc;
33 use middle::ty::{self, Ty};
34 use util::nodemap::NodeMap;
38 use syntax::codemap::Span;
39 use syntax::print::pprust;
40 use syntax::visit::{self, Visitor};
42 use std::collections::hash_map::Entry;
44 // Const qualification, from partial to completely promotable.
46 #[derive(RustcEncodable, RustcDecodable)]
47 flags ConstQualif: u8 {
48 // Const rvalue which can be placed behind a reference.
49 const PURE_CONST = 0b000000,
50 // Inner mutability (can not be placed behind a reference) or behind
51 // &mut in a non-global expression. Can be copied from static memory.
52 const MUTABLE_MEM = 0b000001,
53 // Constant value with a type that implements Drop. Can be copied
54 // from static memory, similar to MUTABLE_MEM.
55 const NEEDS_DROP = 0b000010,
56 // Even if the value can be placed in static memory, copying it from
57 // there is more expensive than in-place instantiation, and/or it may
58 // be too large. This applies to [T; N] and everything containing it.
59 // N.B.: references need to clear this flag to not end up on the stack.
60 const PREFER_IN_PLACE = 0b000100,
61 // May use more than 0 bytes of memory, doesn't impact the constness
62 // directly, but is not allowed to be borrowed mutably in a constant.
63 const NON_ZERO_SIZED = 0b001000,
64 // Actually borrowed, has to always be in static memory. Does not
65 // propagate, and requires the expression to behave like a 'static
66 // lvalue. The set of expressions with this flag is the minimum
67 // that have to be promoted.
68 const HAS_STATIC_BORROWS = 0b010000,
69 // Invalid const for miscellaneous reasons (e.g. not implemented).
70 const NOT_CONST = 0b100000,
72 // Borrowing the expression won't produce &'static T if any of these
73 // bits are set, though the value could be copied from static memory
74 // if `NOT_CONST` isn't set.
75 const NON_STATIC_BORROWS = MUTABLE_MEM.bits | NEEDS_DROP.bits | NOT_CONST.bits
79 #[derive(Copy, Clone, Eq, PartialEq)]
85 // An expression that occurs outside of any constant context
86 // (i.e. `const`, `static`, array lengths, etc.). The value
87 // can be variable at runtime, but will be promotable to
88 // static memory if we can prove it is actually constant.
92 struct CheckCrateVisitor<'a, 'tcx: 'a> {
93 tcx: &'a ty::ctxt<'tcx>,
96 rvalue_borrows: NodeMap<ast::Mutability>
99 impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
100 fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
101 F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
103 let (old_mode, old_qualif) = (self.mode, self.qualif);
105 self.qualif = PURE_CONST;
107 self.mode = old_mode;
108 self.qualif = old_qualif;
112 fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
113 F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'tcx,
114 ty::ParameterEnvironment<'a, 'tcx>>) -> R,
116 let param_env = match item_id {
117 Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
118 None => ty::empty_parameter_environment(self.tcx)
120 f(&mut euv::ExprUseVisitor::new(self, ¶m_env))
123 fn global_expr(&mut self, mode: Mode, expr: &ast::Expr) -> ConstQualif {
124 assert!(mode != Mode::Var);
125 match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
126 Entry::Occupied(entry) => return *entry.get(),
127 Entry::Vacant(entry) => {
128 // Prevent infinite recursion on re-entry.
129 entry.insert(PURE_CONST);
132 self.with_mode(mode, |this| {
133 this.with_euv(None, |euv| euv.consume_expr(expr));
134 this.visit_expr(expr);
139 fn add_qualif(&mut self, qualif: ConstQualif) {
140 self.qualif = self.qualif | qualif;
143 fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) {
144 match self.rvalue_borrows.entry(id) {
145 Entry::Occupied(mut entry) => {
146 // Merge the two borrows, taking the most demanding
147 // one, mutability-wise.
148 if mutbl == ast::MutMutable {
152 Entry::Vacant(entry) => {
158 fn msg(&self) -> &'static str {
160 Mode::Const => "constant",
161 Mode::StaticMut | Mode::Static => "static",
162 Mode::Var => unreachable!(),
166 fn check_static_mut_type(&self, e: &ast::Expr) {
167 let node_ty = ty::node_id_to_type(self.tcx, e.id);
168 let tcontents = ty::type_contents(self.tcx, node_ty);
170 let suffix = if tcontents.has_dtor() {
172 } else if tcontents.owns_owned() {
178 self.tcx.sess.span_err(e.span, &format!("mutable statics are not allowed \
179 to have {}", suffix));
182 fn check_static_type(&self, e: &ast::Expr) {
183 let ty = ty::node_id_to_type(self.tcx, e.id);
184 let infcx = infer::new_infer_ctxt(self.tcx);
185 let mut fulfill_cx = traits::FulfillmentContext::new();
186 let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
187 fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
188 let env = ty::empty_parameter_environment(self.tcx);
189 match fulfill_cx.select_all_or_error(&infcx, &env) {
192 traits::report_fulfillment_errors(&infcx, errors);
198 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
199 fn visit_item(&mut self, i: &ast::Item) {
200 debug!("visit_item(item={})", pprust::item_to_string(i));
202 ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
203 self.check_static_type(&**expr);
204 self.global_expr(Mode::Static, &**expr);
206 ast::ItemStatic(_, ast::MutMutable, ref expr) => {
207 self.check_static_mut_type(&**expr);
208 self.global_expr(Mode::StaticMut, &**expr);
210 ast::ItemConst(_, ref expr) => {
211 self.global_expr(Mode::Const, &**expr);
213 ast::ItemEnum(ref enum_definition, _) => {
214 for var in &enum_definition.variants {
215 if let Some(ref ex) = var.node.disr_expr {
216 self.global_expr(Mode::Const, &**ex);
221 self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
226 fn visit_fn(&mut self,
227 fk: visit::FnKind<'v>,
231 fn_id: ast::NodeId) {
232 assert!(self.mode == Mode::Var);
233 self.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
234 visit::walk_fn(self, fk, fd, b, s);
237 fn visit_pat(&mut self, p: &ast::Pat) {
239 ast::PatLit(ref lit) => {
240 self.global_expr(Mode::Const, &**lit);
242 ast::PatRange(ref start, ref end) => {
243 self.global_expr(Mode::Const, &**start);
244 self.global_expr(Mode::Const, &**end);
246 _ => visit::walk_pat(self, p)
250 fn visit_expr(&mut self, ex: &ast::Expr) {
251 let mut outer = self.qualif;
252 self.qualif = PURE_CONST;
254 let node_ty = ty::node_id_to_type(self.tcx, ex.id);
255 check_expr(self, ex, node_ty);
257 // Special-case some expressions to avoid certain flags bubbling up.
259 ast::ExprCall(ref callee, ref args) => {
260 for arg in args.iter() {
261 self.visit_expr(&**arg)
264 let inner = self.qualif;
265 self.visit_expr(&**callee);
266 // The callee's size doesn't count in the call.
267 let added = self.qualif - inner;
268 self.qualif = inner | (added - NON_ZERO_SIZED);
270 ast::ExprRepeat(ref element, _) => {
271 self.visit_expr(&**element);
272 // The count is checked elsewhere (typeck).
273 let count = match node_ty.sty {
274 ty::ty_vec(_, Some(n)) => n,
277 // [element; 0] is always zero-sized.
279 self.qualif = self.qualif - (NON_ZERO_SIZED | PREFER_IN_PLACE);
282 ast::ExprMatch(ref discr, ref arms, _) => {
283 // Compute the most demanding borrow from all the arms'
284 // patterns and set that on the discriminator.
285 let mut borrow = None;
286 for pat in arms.iter().flat_map(|arm| arm.pats.iter()) {
287 let pat_borrow = self.rvalue_borrows.remove(&pat.id);
288 match (borrow, pat_borrow) {
289 (None, _) | (_, Some(ast::MutMutable)) => {
295 if let Some(mutbl) = borrow {
296 self.record_borrow(discr.id, mutbl);
298 visit::walk_expr(self, ex);
300 // Division by zero and overflow checking.
301 ast::ExprBinary(op, _, _) => {
302 visit::walk_expr(self, ex);
303 let div_or_rem = op.node == ast::BiDiv || op.node == ast::BiRem;
305 ty::ty_uint(_) | ty::ty_int(_) if div_or_rem => {
306 if !self.qualif.intersects(NOT_CONST) {
307 match const_eval::eval_const_expr_partial(self.tcx, ex, None) {
310 span_err!(self.tcx.sess, msg.span, E0020,
311 "{} in a constant expression",
320 _ => visit::walk_expr(self, ex)
323 // Handle borrows on (or inside the autorefs of) this expression.
324 match self.rvalue_borrows.remove(&ex.id) {
325 Some(ast::MutImmutable) => {
326 // Constants cannot be borrowed if they contain interior mutability as
327 // it means that our "silent insertion of statics" could change
328 // initializer values (very bad).
329 // If the type doesn't have interior mutability, then `MUTABLE_MEM` has
330 // propagated from another error, so erroring again would be just noise.
331 let tc = ty::type_contents(self.tcx, node_ty);
332 if self.qualif.intersects(MUTABLE_MEM) && tc.interior_unsafe() {
333 outer = outer | NOT_CONST;
334 if self.mode != Mode::Var {
335 self.tcx.sess.span_err(ex.span,
336 "cannot borrow a constant which contains \
337 interior mutability, create a static instead");
340 // If the reference has to be 'static, avoid in-place initialization
341 // as that will end up pointing to the stack instead.
342 if !self.qualif.intersects(NON_STATIC_BORROWS) {
343 self.qualif = self.qualif - PREFER_IN_PLACE;
344 self.add_qualif(HAS_STATIC_BORROWS);
347 Some(ast::MutMutable) => {
348 // `&mut expr` means expr could be mutated, unless it's zero-sized.
349 if self.qualif.intersects(NON_ZERO_SIZED) {
350 if self.mode == Mode::Var {
351 outer = outer | NOT_CONST;
352 self.add_qualif(MUTABLE_MEM);
354 span_err!(self.tcx.sess, ex.span, E0017,
355 "references in {}s may only refer \
356 to immutable values", self.msg())
359 if !self.qualif.intersects(NON_STATIC_BORROWS) {
360 self.add_qualif(HAS_STATIC_BORROWS);
365 self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
366 // Don't propagate certain flags.
367 self.qualif = outer | (self.qualif - HAS_STATIC_BORROWS);
371 /// This function is used to enforce the constraints on
372 /// const/static items. It walks through the *value*
373 /// of the item walking down the expression and evaluating
374 /// every nested expression. If the expression is not part
375 /// of a const/static item, it is qualified for promotion
376 /// instead of producing errors.
377 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
378 e: &ast::Expr, node_ty: Ty<'tcx>) {
380 ty::ty_struct(did, _) |
381 ty::ty_enum(did, _) if ty::has_dtor(v.tcx, did) => {
382 v.add_qualif(NEEDS_DROP);
383 if v.mode != Mode::Var {
384 v.tcx.sess.span_err(e.span,
385 &format!("{}s are not allowed to have destructors",
392 let method_call = ty::MethodCall::expr(e.id);
395 ast::ExprBinary(..) |
396 ast::ExprIndex(..) if v.tcx.method_map.borrow().contains_key(&method_call) => {
397 v.add_qualif(NOT_CONST);
398 if v.mode != Mode::Var {
399 span_err!(v.tcx.sess, e.span, E0011,
400 "user-defined operators are not allowed in {}s", v.msg());
404 ast::ExprUnary(ast::UnUniq, _) => {
405 v.add_qualif(NOT_CONST);
406 if v.mode != Mode::Var {
407 span_err!(v.tcx.sess, e.span, E0010,
408 "allocations are not allowed in {}s", v.msg());
411 ast::ExprUnary(ast::UnDeref, ref ptr) => {
412 match ty::node_id_to_type(v.tcx, ptr.id).sty {
414 // This shouldn't be allowed in constants at all.
415 v.add_qualif(NOT_CONST);
420 ast::ExprCast(ref from, _) => {
421 let toty = ty::expr_ty(v.tcx, e);
422 let fromty = ty::expr_ty(v.tcx, &**from);
424 ty::type_is_numeric(toty) ||
425 ty::type_is_unsafe_ptr(toty) ||
426 (ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
428 v.add_qualif(NOT_CONST);
429 if v.mode != Mode::Var {
430 span_err!(v.tcx.sess, e.span, E0012,
431 "can not cast to `{}` in {}s",
432 ppaux::ty_to_string(v.tcx, toty), v.msg());
435 if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
436 v.add_qualif(NOT_CONST);
437 if v.mode != Mode::Var {
438 span_err!(v.tcx.sess, e.span, E0018,
439 "can not cast a pointer to an integer in {}s", v.msg());
443 ast::ExprPath(..) => {
444 let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
446 Some(def::DefVariant(_, _, _)) => {
447 // Count the discriminator or function pointer.
448 v.add_qualif(NON_ZERO_SIZED);
450 Some(def::DefStruct(_)) => {
451 if let ty::ty_bare_fn(..) = node_ty.sty {
452 // Count the function pointer.
453 v.add_qualif(NON_ZERO_SIZED);
456 Some(def::DefFn(..)) | Some(def::DefMethod(..)) => {
457 // Count the function pointer.
458 v.add_qualif(NON_ZERO_SIZED);
460 Some(def::DefStatic(..)) => {
462 Mode::Static | Mode::StaticMut => {}
464 span_err!(v.tcx.sess, e.span, E0013,
465 "constants cannot refer to other statics, \
466 insert an intermediate constant instead");
468 Mode::Var => v.add_qualif(NOT_CONST)
471 Some(def::DefConst(did)) => {
472 if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did) {
473 let inner = v.global_expr(Mode::Const, expr);
476 v.tcx.sess.span_bug(e.span, "DefConst doesn't point \
481 v.add_qualif(NOT_CONST);
482 if v.mode != Mode::Var {
483 debug!("(checking const) found bad def: {:?}", def);
484 span_err!(v.tcx.sess, e.span, E0014,
485 "paths in {}s may only refer to constants \
486 or functions", v.msg());
491 ast::ExprCall(ref callee, _) => {
492 let mut callee = &**callee;
494 callee = match callee.node {
495 ast::ExprParen(ref inner) => &**inner,
496 ast::ExprBlock(ref block) => match block.expr {
497 Some(ref tail) => &**tail,
503 let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
505 Some(def::DefStruct(..)) => {}
506 Some(def::DefVariant(..)) => {
507 // Count the discriminator.
508 v.add_qualif(NON_ZERO_SIZED);
511 v.add_qualif(NOT_CONST);
512 if v.mode != Mode::Var {
513 span_err!(v.tcx.sess, e.span, E0015,
514 "function calls in {}s are limited to \
515 struct and enum constructors", v.msg());
520 ast::ExprBlock(ref block) => {
521 // Check all statements in the block
522 let mut block_span_err = |span| {
523 v.add_qualif(NOT_CONST);
524 if v.mode != Mode::Var {
525 span_err!(v.tcx.sess, span, E0016,
526 "blocks in {}s are limited to items and \
527 tail expressions", v.msg());
530 for stmt in &block.stmts {
532 ast::StmtDecl(ref decl, _) => {
534 ast::DeclLocal(_) => block_span_err(decl.span),
536 // Item statements are allowed
537 ast::DeclItem(_) => {}
540 ast::StmtExpr(ref expr, _) => block_span_err(expr.span),
541 ast::StmtSemi(ref semi, _) => block_span_err(semi.span),
542 ast::StmtMac(..) => {
543 v.tcx.sess.span_bug(e.span, "unexpanded statement \
549 ast::ExprStruct(..) => {
550 let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
551 if did == v.tcx.lang_items.unsafe_cell_type() {
552 v.add_qualif(MUTABLE_MEM);
557 ast::ExprAddrOf(..) => {
558 v.add_qualif(NON_ZERO_SIZED);
561 ast::ExprRepeat(..) => {
562 v.add_qualif(PREFER_IN_PLACE);
565 ast::ExprClosure(..) => {
566 // Paths in constant constexts cannot refer to local variables,
567 // as there are none, and thus closures can't have upvars there.
568 if ty::with_freevars(v.tcx, e.id, |fv| !fv.is_empty()) {
569 assert!(v.mode == Mode::Var,
570 "global closures can't capture anything");
571 v.add_qualif(NOT_CONST);
576 ast::ExprBinary(..) |
579 ast::ExprTupField(..) |
582 ast::ExprTup(..) => {}
584 // Conditional control flow (possible to implement).
589 // Loops (not very meaningful in constants).
591 ast::ExprWhileLet(..) |
592 ast::ExprForLoop(..) |
595 // More control flow (also not very meaningful).
600 // Miscellaneous expressions that could be implemented.
603 // Various other expressions.
604 ast::ExprMethodCall(..) |
605 ast::ExprAssign(..) |
606 ast::ExprAssignOp(..) |
607 ast::ExprInlineAsm(_) |
609 v.add_qualif(NOT_CONST);
610 if v.mode != Mode::Var {
611 span_err!(v.tcx.sess, e.span, E0019,
612 "{} contains unimplemented expression type", v.msg());
618 pub fn check_crate(tcx: &ty::ctxt) {
619 visit::walk_crate(&mut CheckCrateVisitor {
623 rvalue_borrows: NodeMap()
626 tcx.sess.abort_if_errors();
629 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
630 fn consume(&mut self,
631 _consume_id: ast::NodeId,
634 _mode: euv::ConsumeMode) {
638 mc::cat_static_item => {
639 if self.mode != Mode::Var {
640 // statics cannot be consumed by value at any time, that would imply
641 // that they're an initializer (what a const is for) or kept in sync
642 // over time (not feasible), so deny it outright.
643 self.tcx.sess.span_err(consume_span,
644 "cannot refer to other statics by value, use the \
645 address-of operator or a constant instead");
649 mc::cat_deref(ref cmt, _, _) |
650 mc::cat_downcast(ref cmt, _) |
651 mc::cat_interior(ref cmt, _) => cur = cmt,
655 mc::cat_local(..) => break
660 borrow_id: ast::NodeId,
663 _loan_region: ty::Region,
665 loan_cause: euv::LoanCause)
667 // Kind of hacky, but we allow Unsafe coercions in constants.
668 // These occur when we convert a &T or *T to a *U, as well as
669 // when making a thin pointer (e.g., `*T`) into a fat pointer
672 euv::LoanCause::AutoUnsafe => {
679 let mut is_interior = false;
682 mc::cat_rvalue(..) => {
683 if loan_cause == euv::MatchDiscriminant {
684 // Ignore the dummy immutable borrow created by EUV.
687 let mutbl = bk.to_mutbl_lossy();
688 if mutbl == ast::MutMutable && self.mode == Mode::StaticMut {
689 // Mutable slices are the only `&mut` allowed in globals,
690 // but only in `static mut`, nowhere else.
692 ty::ty_vec(_, _) => break,
696 self.record_borrow(borrow_id, mutbl);
699 mc::cat_static_item => {
700 if is_interior && self.mode != Mode::Var {
701 // Borrowed statics can specifically *only* have their address taken,
702 // not any number of other borrows such as borrowing fields, reading
703 // elements of an array, etc.
704 self.tcx.sess.span_err(borrow_span,
705 "cannot refer to the interior of another \
706 static, use a constant instead");
710 mc::cat_deref(ref cmt, _, _) |
711 mc::cat_downcast(ref cmt, _) |
712 mc::cat_interior(ref cmt, _) => {
718 mc::cat_local(..) => break
723 fn decl_without_init(&mut self,
727 _assignment_id: ast::NodeId,
728 _assignment_span: Span,
729 _assignee_cmt: mc::cmt,
730 _mode: euv::MutateMode) {}
732 fn matched_pat(&mut self,
735 _: euv::MatchMode) {}
737 fn consume_pat(&mut self,
738 _consume_pat: &ast::Pat,
740 _mode: euv::ConsumeMode) {}