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 middle::cast::{CastKind};
28 use middle::const_eval;
29 use middle::const_eval::EvalHint::ExprTypeChecked;
31 use middle::def_id::DefId;
32 use middle::expr_use_visitor as euv;
34 use middle::mem_categorization as mc;
36 use middle::ty::{self, Ty};
37 use util::nodemap::NodeMap;
40 use syntax::codemap::Span;
41 use syntax::visit::{self, Visitor};
43 use std::collections::hash_map::Entry;
44 use std::cmp::Ordering;
46 // Const qualification, from partial to completely promotable.
48 #[derive(RustcEncodable, RustcDecodable)]
49 flags ConstQualif: u8 {
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 = 1 << 0,
53 // Constant value with a type that implements Drop. Can be copied
54 // from static memory, similar to MUTABLE_MEM.
55 const NEEDS_DROP = 1 << 1,
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 = 1 << 2,
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 = 1 << 3,
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 = 1 << 4,
69 // Invalid const for miscellaneous reasons (e.g. not implemented).
70 const NOT_CONST = 1 << 5,
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 = ConstQualif::MUTABLE_MEM.bits |
76 ConstQualif::NEEDS_DROP.bits |
77 ConstQualif::NOT_CONST.bits
81 #[derive(Copy, Clone, Eq, PartialEq)]
88 // An expression that occurs outside of any constant context
89 // (i.e. `const`, `static`, array lengths, etc.). The value
90 // can be variable at runtime, but will be promotable to
91 // static memory if we can prove it is actually constant.
95 struct CheckCrateVisitor<'a, 'tcx: 'a> {
96 tcx: &'a ty::ctxt<'tcx>,
99 rvalue_borrows: NodeMap<ast::Mutability>
102 impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
103 fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
104 F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
106 let (old_mode, old_qualif) = (self.mode, self.qualif);
108 self.qualif = ConstQualif::empty();
110 self.mode = old_mode;
111 self.qualif = old_qualif;
115 fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
116 F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R,
118 let param_env = match item_id {
119 Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
120 None => self.tcx.empty_parameter_environment()
123 let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), false);
125 f(&mut euv::ExprUseVisitor::new(self, &infcx))
128 fn global_expr(&mut self, mode: Mode, expr: &ast::Expr) -> ConstQualif {
129 assert!(mode != Mode::Var);
130 match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
131 Entry::Occupied(entry) => return *entry.get(),
132 Entry::Vacant(entry) => {
133 // Prevent infinite recursion on re-entry.
134 entry.insert(ConstQualif::empty());
137 self.with_mode(mode, |this| {
138 this.with_euv(None, |euv| euv.consume_expr(expr));
139 this.visit_expr(expr);
144 fn fn_like(&mut self,
151 match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
152 Entry::Occupied(entry) => return *entry.get(),
153 Entry::Vacant(entry) => {
154 // Prevent infinite recursion on re-entry.
155 entry.insert(ConstQualif::empty());
159 let mode = match fk {
160 visit::FkItemFn(_, _, _, ast::Constness::Const, _, _) => {
163 visit::FkMethod(_, m, _) => {
164 if m.constness == ast::Constness::Const {
173 // Ensure the arguments are simple, not mutable/by-ref or patterns.
174 if mode == Mode::ConstFn {
175 for arg in &fd.inputs {
177 ast::PatIdent(ast::BindByValue(ast::MutImmutable), _, None) => {}
179 span_err!(self.tcx.sess, arg.pat.span, E0022,
180 "arguments of constant functions can only \
181 be immutable by-value bindings");
187 let qualif = self.with_mode(mode, |this| {
188 this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
189 visit::walk_fn(this, fk, fd, b, s);
193 // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
194 // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
195 let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
197 self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
201 fn add_qualif(&mut self, qualif: ConstQualif) {
202 self.qualif = self.qualif | qualif;
205 /// Returns true if the call is to a const fn or method.
206 fn handle_const_fn_call(&mut self,
211 if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
213 // we are in a static/const initializer
214 self.mode != Mode::Var &&
216 // feature-gate is not enabled
217 !self.tcx.sess.features.borrow().const_fn &&
219 // this doesn't come from a macro that has #[allow_internal_unstable]
220 !self.tcx.sess.codemap().span_allows_unstable(expr.span)
222 self.tcx.sess.span_err(
224 &format!("const fns are an unstable feature"));
228 "in Nightly builds, add `#![feature(const_fn)]` to the crate \
229 attributes to enable");
232 let qualif = self.fn_like(fn_like.kind(),
237 self.add_qualif(qualif);
239 if ret_ty.type_contents(self.tcx).interior_unsafe() {
240 self.add_qualif(ConstQualif::MUTABLE_MEM);
249 fn record_borrow(&mut self, id: ast::NodeId, mutbl: ast::Mutability) {
250 match self.rvalue_borrows.entry(id) {
251 Entry::Occupied(mut entry) => {
252 // Merge the two borrows, taking the most demanding
253 // one, mutability-wise.
254 if mutbl == ast::MutMutable {
258 Entry::Vacant(entry) => {
264 fn msg(&self) -> &'static str {
266 Mode::Const => "constant",
267 Mode::ConstFn => "constant function",
268 Mode::StaticMut | Mode::Static => "static",
269 Mode::Var => unreachable!(),
273 fn check_static_mut_type(&self, e: &ast::Expr) {
274 let node_ty = self.tcx.node_id_to_type(e.id);
275 let tcontents = node_ty.type_contents(self.tcx);
277 let suffix = if tcontents.has_dtor() {
279 } else if tcontents.owns_owned() {
285 span_err!(self.tcx.sess, e.span, E0397,
286 "mutable statics are not allowed to have {}", suffix);
289 fn check_static_type(&self, e: &ast::Expr) {
290 let ty = self.tcx.node_id_to_type(e.id);
291 let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false);
292 let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
293 let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
294 fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
295 match fulfill_cx.select_all_or_error(&infcx) {
298 traits::report_fulfillment_errors(&infcx, errors);
304 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
305 fn visit_item(&mut self, i: &ast::Item) {
306 debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
308 ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
309 self.check_static_type(&**expr);
310 self.global_expr(Mode::Static, &**expr);
312 ast::ItemStatic(_, ast::MutMutable, ref expr) => {
313 self.check_static_mut_type(&**expr);
314 self.global_expr(Mode::StaticMut, &**expr);
316 ast::ItemConst(_, ref expr) => {
317 self.global_expr(Mode::Const, &**expr);
319 ast::ItemEnum(ref enum_definition, _) => {
320 for var in &enum_definition.variants {
321 if let Some(ref ex) = var.node.disr_expr {
322 self.global_expr(Mode::Const, &**ex);
327 self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
332 fn visit_trait_item(&mut self, t: &'v ast::TraitItem) {
334 ast::ConstTraitItem(_, ref default) => {
335 if let Some(ref expr) = *default {
336 self.global_expr(Mode::Const, &*expr);
338 visit::walk_trait_item(self, t);
341 _ => self.with_mode(Mode::Var, |v| visit::walk_trait_item(v, t)),
345 fn visit_impl_item(&mut self, i: &'v ast::ImplItem) {
347 ast::ConstImplItem(_, ref expr) => {
348 self.global_expr(Mode::Const, &*expr);
350 _ => self.with_mode(Mode::Var, |v| visit::walk_impl_item(v, i)),
354 fn visit_fn(&mut self,
355 fk: visit::FnKind<'v>,
359 fn_id: ast::NodeId) {
360 self.fn_like(fk, fd, b, s, fn_id);
363 fn visit_pat(&mut self, p: &ast::Pat) {
365 ast::PatLit(ref lit) => {
366 self.global_expr(Mode::Const, &**lit);
368 ast::PatRange(ref start, ref end) => {
369 self.global_expr(Mode::Const, &**start);
370 self.global_expr(Mode::Const, &**end);
372 match const_eval::compare_lit_exprs(self.tcx, start, end) {
373 Some(Ordering::Less) |
374 Some(Ordering::Equal) => {}
375 Some(Ordering::Greater) => {
376 span_err!(self.tcx.sess, start.span, E0030,
377 "lower range bound must be less than or equal to upper");
380 self.tcx.sess.span_bug(
381 start.span, "literals of different types in range pat");
385 _ => visit::walk_pat(self, p)
389 fn visit_block(&mut self, block: &ast::Block) {
390 // Check all statements in the block
391 for stmt in &block.stmts {
392 let span = match stmt.node {
393 ast::StmtDecl(ref decl, _) => {
395 ast::DeclLocal(_) => decl.span,
397 // Item statements are allowed
398 ast::DeclItem(_) => continue
401 ast::StmtExpr(ref expr, _) => expr.span,
402 ast::StmtSemi(ref semi, _) => semi.span,
403 ast::StmtMac(..) => {
404 self.tcx.sess.span_bug(stmt.span, "unexpanded statement \
408 self.add_qualif(ConstQualif::NOT_CONST);
409 if self.mode != Mode::Var {
410 span_err!(self.tcx.sess, span, E0016,
411 "blocks in {}s are limited to items and \
412 tail expressions", self.msg());
415 visit::walk_block(self, block);
418 fn visit_expr(&mut self, ex: &ast::Expr) {
419 let mut outer = self.qualif;
420 self.qualif = ConstQualif::empty();
422 let node_ty = self.tcx.node_id_to_type(ex.id);
423 check_expr(self, ex, node_ty);
424 check_adjustments(self, ex);
426 // Special-case some expressions to avoid certain flags bubbling up.
428 ast::ExprCall(ref callee, ref args) => {
430 self.visit_expr(&**arg)
433 let inner = self.qualif;
434 self.visit_expr(&**callee);
435 // The callee's size doesn't count in the call.
436 let added = self.qualif - inner;
437 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
439 ast::ExprRepeat(ref element, _) => {
440 self.visit_expr(&**element);
441 // The count is checked elsewhere (typeck).
442 let count = match node_ty.sty {
443 ty::TyArray(_, n) => n,
446 // [element; 0] is always zero-sized.
448 self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
451 ast::ExprMatch(ref discr, ref arms, _) => {
452 // Compute the most demanding borrow from all the arms'
453 // patterns and set that on the discriminator.
454 let mut borrow = None;
455 for pat in arms.iter().flat_map(|arm| &arm.pats) {
456 let pat_borrow = self.rvalue_borrows.remove(&pat.id);
457 match (borrow, pat_borrow) {
458 (None, _) | (_, Some(ast::MutMutable)) => {
464 if let Some(mutbl) = borrow {
465 self.record_borrow(discr.id, mutbl);
467 visit::walk_expr(self, ex);
469 // Division by zero and overflow checking.
470 ast::ExprBinary(op, _, _) => {
471 visit::walk_expr(self, ex);
472 let div_or_rem = op.node == ast::BiDiv || op.node == ast::BiRem;
474 ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
475 if !self.qualif.intersects(ConstQualif::NOT_CONST) {
476 match const_eval::eval_const_expr_partial(
477 self.tcx, ex, ExprTypeChecked) {
480 span_err!(self.tcx.sess, msg.span, E0020,
481 "{} in a constant expression",
490 _ => visit::walk_expr(self, ex)
493 // Handle borrows on (or inside the autorefs of) this expression.
494 match self.rvalue_borrows.remove(&ex.id) {
495 Some(ast::MutImmutable) => {
496 // Constants cannot be borrowed if they contain interior mutability as
497 // it means that our "silent insertion of statics" could change
498 // initializer values (very bad).
499 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
500 // propagated from another error, so erroring again would be just noise.
501 let tc = node_ty.type_contents(self.tcx);
502 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
503 outer = outer | ConstQualif::NOT_CONST;
504 if self.mode != Mode::Var {
505 self.tcx.sess.span_err(ex.span,
506 "cannot borrow a constant which contains \
507 interior mutability, create a static instead");
510 // If the reference has to be 'static, avoid in-place initialization
511 // as that will end up pointing to the stack instead.
512 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
513 self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
514 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
517 Some(ast::MutMutable) => {
518 // `&mut expr` means expr could be mutated, unless it's zero-sized.
519 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
520 if self.mode == Mode::Var {
521 outer = outer | ConstQualif::NOT_CONST;
522 self.add_qualif(ConstQualif::MUTABLE_MEM);
524 span_err!(self.tcx.sess, ex.span, E0017,
525 "references in {}s may only refer \
526 to immutable values", self.msg())
529 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
530 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
535 self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
536 // Don't propagate certain flags.
537 self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
541 /// This function is used to enforce the constraints on
542 /// const/static items. It walks through the *value*
543 /// of the item walking down the expression and evaluating
544 /// every nested expression. If the expression is not part
545 /// of a const/static item, it is qualified for promotion
546 /// instead of producing errors.
547 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
548 e: &ast::Expr, node_ty: Ty<'tcx>) {
550 ty::TyStruct(def, _) |
551 ty::TyEnum(def, _) if def.has_dtor(v.tcx) => {
552 v.add_qualif(ConstQualif::NEEDS_DROP);
553 if v.mode != Mode::Var {
554 v.tcx.sess.span_err(e.span,
555 &format!("{}s are not allowed to have destructors",
562 let method_call = ty::MethodCall::expr(e.id);
565 ast::ExprBinary(..) |
566 ast::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
567 v.add_qualif(ConstQualif::NOT_CONST);
568 if v.mode != Mode::Var {
569 span_err!(v.tcx.sess, e.span, E0011,
570 "user-defined operators are not allowed in {}s", v.msg());
574 ast::ExprUnary(ast::UnUniq, _) => {
575 v.add_qualif(ConstQualif::NOT_CONST);
576 if v.mode != Mode::Var {
577 span_err!(v.tcx.sess, e.span, E0010,
578 "allocations are not allowed in {}s", v.msg());
581 ast::ExprUnary(op, ref inner) => {
582 match v.tcx.node_id_to_type(inner.id).sty {
584 assert!(op == ast::UnDeref);
586 v.add_qualif(ConstQualif::NOT_CONST);
587 if v.mode != Mode::Var {
588 span_err!(v.tcx.sess, e.span, E0396,
589 "raw pointers cannot be dereferenced in {}s", v.msg());
595 ast::ExprBinary(op, ref lhs, _) => {
596 match v.tcx.node_id_to_type(lhs.id).sty {
598 assert!(op.node == ast::BiEq || op.node == ast::BiNe ||
599 op.node == ast::BiLe || op.node == ast::BiLt ||
600 op.node == ast::BiGe || op.node == ast::BiGt);
602 v.add_qualif(ConstQualif::NOT_CONST);
603 if v.mode != Mode::Var {
604 span_err!(v.tcx.sess, e.span, E0395,
605 "raw pointers cannot be compared in {}s", v.msg());
611 ast::ExprCast(ref from, _) => {
612 debug!("Checking const cast(id={})", from.id);
613 match v.tcx.cast_kinds.borrow().get(&from.id) {
614 None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
615 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
616 v.add_qualif(ConstQualif::NOT_CONST);
617 if v.mode != Mode::Var {
618 span_err!(v.tcx.sess, e.span, E0018,
619 "raw pointers cannot be cast to integers in {}s", v.msg());
625 ast::ExprPath(..) => {
626 let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
628 Some(def::DefVariant(_, _, _)) => {
629 // Count the discriminator or function pointer.
630 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
632 Some(def::DefStruct(_)) => {
633 if let ty::TyBareFn(..) = node_ty.sty {
634 // Count the function pointer.
635 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
638 Some(def::DefFn(..)) | Some(def::DefMethod(..)) => {
639 // Count the function pointer.
640 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
642 Some(def::DefStatic(..)) => {
644 Mode::Static | Mode::StaticMut => {}
645 Mode::Const | Mode::ConstFn => {
646 span_err!(v.tcx.sess, e.span, E0013,
647 "{}s cannot refer to other statics, insert \
648 an intermediate constant instead", v.msg());
650 Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
653 Some(def::DefConst(did)) |
654 Some(def::DefAssociatedConst(did)) => {
655 if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
657 let inner = v.global_expr(Mode::Const, expr);
660 v.tcx.sess.span_bug(e.span,
661 "DefConst or DefAssociatedConst \
662 doesn't point to a constant");
665 Some(def::DefLocal(_)) if v.mode == Mode::ConstFn => {
666 // Sadly, we can't determine whether the types are zero-sized.
667 v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
670 v.add_qualif(ConstQualif::NOT_CONST);
671 if v.mode != Mode::Var {
672 debug!("(checking const) found bad def: {:?}", def);
673 span_err!(v.tcx.sess, e.span, E0014,
674 "paths in {}s may only refer to constants \
675 or functions", v.msg());
680 ast::ExprCall(ref callee, _) => {
681 let mut callee = &**callee;
683 callee = match callee.node {
684 ast::ExprParen(ref inner) => &**inner,
685 ast::ExprBlock(ref block) => match block.expr {
686 Some(ref tail) => &**tail,
692 let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
693 let is_const = match def {
694 Some(def::DefStruct(..)) => true,
695 Some(def::DefVariant(..)) => {
696 // Count the discriminator.
697 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
700 Some(def::DefFn(did, _)) => {
701 v.handle_const_fn_call(e, did, node_ty)
703 Some(def::DefMethod(did)) => {
704 match v.tcx.impl_or_trait_item(did).container() {
705 ty::ImplContainer(_) => {
706 v.handle_const_fn_call(e, did, node_ty)
708 ty::TraitContainer(_) => false
714 v.add_qualif(ConstQualif::NOT_CONST);
715 if v.mode != Mode::Var {
716 span_err!(v.tcx.sess, e.span, E0015,
717 "function calls in {}s are limited to \
718 constant functions, \
719 struct and enum constructors", v.msg());
723 ast::ExprMethodCall(..) => {
724 let method = v.tcx.tables.borrow().method_map[&method_call];
725 let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
726 ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
727 ty::TraitContainer(_) => false
730 v.add_qualif(ConstQualif::NOT_CONST);
731 if v.mode != Mode::Var {
732 span_err!(v.tcx.sess, e.span, E0378,
733 "method calls in {}s are limited to \
734 constant inherent methods", v.msg());
738 ast::ExprStruct(..) => {
739 let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
740 if did == v.tcx.lang_items.unsafe_cell_type() {
741 v.add_qualif(ConstQualif::MUTABLE_MEM);
746 ast::ExprAddrOf(..) => {
747 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
750 ast::ExprRepeat(..) => {
751 v.add_qualif(ConstQualif::PREFER_IN_PLACE);
754 ast::ExprClosure(..) => {
755 // Paths in constant contexts cannot refer to local variables,
756 // as there are none, and thus closures can't have upvars there.
757 if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
758 assert!(v.mode == Mode::Var,
759 "global closures can't capture anything");
760 v.add_qualif(ConstQualif::NOT_CONST);
767 ast::ExprTupField(..) |
770 ast::ExprTup(..) => {}
772 // Conditional control flow (possible to implement).
777 // Loops (not very meaningful in constants).
779 ast::ExprWhileLet(..) |
780 ast::ExprForLoop(..) |
783 // More control flow (also not very meaningful).
788 // Miscellaneous expressions that could be implemented.
791 // Expressions with side-effects.
792 ast::ExprAssign(..) |
793 ast::ExprAssignOp(..) |
794 ast::ExprInlineAsm(_) |
796 v.add_qualif(ConstQualif::NOT_CONST);
797 if v.mode != Mode::Var {
798 span_err!(v.tcx.sess, e.span, E0019,
799 "{} contains unimplemented expression type", v.msg());
805 /// Check the adjustments of an expression
806 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &ast::Expr) {
807 match v.tcx.tables.borrow().adjustments.get(&e.id) {
808 None | Some(&ty::AdjustReifyFnPointer) | Some(&ty::AdjustUnsafeFnPointer) => {}
809 Some(&ty::AdjustDerefRef(ty::AutoDerefRef { autoderefs, .. })) => {
810 if (0..autoderefs as u32).any(|autoderef| {
811 v.tcx.is_overloaded_autoderef(e.id, autoderef)
813 v.add_qualif(ConstQualif::NOT_CONST);
814 if v.mode != Mode::Var {
815 span_err!(v.tcx.sess, e.span, E0400,
816 "user-defined dereference operators are not allowed in {}s",
824 pub fn check_crate(tcx: &ty::ctxt) {
825 visit::walk_crate(&mut CheckCrateVisitor {
828 qualif: ConstQualif::NOT_CONST,
829 rvalue_borrows: NodeMap()
832 tcx.sess.abort_if_errors();
835 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
836 fn consume(&mut self,
837 _consume_id: ast::NodeId,
840 _mode: euv::ConsumeMode) {
844 mc::cat_static_item => {
845 if self.mode != Mode::Var {
846 // statics cannot be consumed by value at any time, that would imply
847 // that they're an initializer (what a const is for) or kept in sync
848 // over time (not feasible), so deny it outright.
849 span_err!(self.tcx.sess, consume_span, E0394,
850 "cannot refer to other statics by value, use the \
851 address-of operator or a constant instead");
855 mc::cat_deref(ref cmt, _, _) |
856 mc::cat_downcast(ref cmt, _) |
857 mc::cat_interior(ref cmt, _) => cur = cmt,
861 mc::cat_local(..) => break
866 borrow_id: ast::NodeId,
869 _loan_region: ty::Region,
871 loan_cause: euv::LoanCause)
873 // Kind of hacky, but we allow Unsafe coercions in constants.
874 // These occur when we convert a &T or *T to a *U, as well as
875 // when making a thin pointer (e.g., `*T`) into a fat pointer
878 euv::LoanCause::AutoUnsafe => {
885 let mut is_interior = false;
888 mc::cat_rvalue(..) => {
889 if loan_cause == euv::MatchDiscriminant {
890 // Ignore the dummy immutable borrow created by EUV.
893 let mutbl = bk.to_mutbl_lossy();
894 if mutbl == ast::MutMutable && self.mode == Mode::StaticMut {
895 // Mutable slices are the only `&mut` allowed in
896 // globals, but only in `static mut`, nowhere else.
897 // FIXME: This exception is really weird... there isn't
898 // any fundamental reason to restrict this based on
899 // type of the expression. `&mut [1]` has exactly the
900 // same representation as &mut 1.
902 ty::TyArray(_, _) | ty::TySlice(_) => break,
906 self.record_borrow(borrow_id, mutbl);
909 mc::cat_static_item => {
910 if is_interior && self.mode != Mode::Var {
911 // Borrowed statics can specifically *only* have their address taken,
912 // not any number of other borrows such as borrowing fields, reading
913 // elements of an array, etc.
914 self.tcx.sess.span_err(borrow_span,
915 "cannot refer to the interior of another \
916 static, use a constant instead");
920 mc::cat_deref(ref cmt, _, _) |
921 mc::cat_downcast(ref cmt, _) |
922 mc::cat_interior(ref cmt, _) => {
928 mc::cat_local(..) => break
933 fn decl_without_init(&mut self,
937 _assignment_id: ast::NodeId,
938 _assignment_span: Span,
939 _assignee_cmt: mc::cmt,
940 _mode: euv::MutateMode) {}
942 fn matched_pat(&mut self,
945 _: euv::MatchMode) {}
947 fn consume_pat(&mut self,
948 _consume_pat: &ast::Pat,
950 _mode: euv::ConsumeMode) {}