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::dep_graph::DepNode;
28 use rustc::middle::ty::cast::{CastKind};
29 use rustc::middle::const_eval::{self, ConstEvalErr};
30 use rustc::middle::const_eval::ErrKind::IndexOpFeatureGated;
31 use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
32 use rustc::middle::def::Def;
33 use rustc::middle::def_id::DefId;
34 use rustc::middle::expr_use_visitor as euv;
35 use rustc::middle::infer;
36 use rustc::middle::mem_categorization as mc;
37 use rustc::middle::mem_categorization::Categorization;
38 use rustc::middle::traits;
39 use rustc::middle::ty::{self, Ty};
40 use rustc::util::nodemap::NodeMap;
41 use rustc::middle::const_qualif::ConstQualif;
42 use rustc::lint::builtin::CONST_ERR;
46 use syntax::codemap::Span;
47 use syntax::feature_gate::UnstableFeatures;
48 use rustc_front::intravisit::{self, FnKind, Visitor};
50 use std::collections::hash_map::Entry;
51 use std::cmp::Ordering;
53 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
60 // An expression that occurs outside of any constant context
61 // (i.e. `const`, `static`, array lengths, etc.). The value
62 // can be variable at runtime, but will be promotable to
63 // static memory if we can prove it is actually constant.
67 struct CheckCrateVisitor<'a, 'tcx: 'a> {
68 tcx: &'a ty::ctxt<'tcx>,
71 rvalue_borrows: NodeMap<hir::Mutability>
74 impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
75 fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
76 F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
78 let (old_mode, old_qualif) = (self.mode, self.qualif);
80 self.qualif = ConstQualif::empty();
83 self.qualif = old_qualif;
87 fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
88 F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R,
90 let param_env = match item_id {
91 Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
92 None => self.tcx.empty_parameter_environment()
95 let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env));
97 f(&mut euv::ExprUseVisitor::new(self, &infcx))
100 fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif {
101 assert!(mode != Mode::Var);
102 match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
103 Entry::Occupied(entry) => return *entry.get(),
104 Entry::Vacant(entry) => {
105 // Prevent infinite recursion on re-entry.
106 entry.insert(ConstQualif::empty());
109 self.with_mode(mode, |this| {
110 this.with_euv(None, |euv| euv.consume_expr(expr));
111 this.visit_expr(expr);
116 fn fn_like(&mut self,
123 match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
124 Entry::Occupied(entry) => return *entry.get(),
125 Entry::Vacant(entry) => {
126 // Prevent infinite recursion on re-entry.
127 entry.insert(ConstQualif::empty());
131 let mode = match fk {
132 FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => {
135 FnKind::Method(_, m, _) => {
136 if m.constness == hir::Constness::Const {
145 let qualif = self.with_mode(mode, |this| {
146 this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
147 intravisit::walk_fn(this, fk, fd, b, s);
151 // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
152 // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
153 let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
155 self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
159 fn add_qualif(&mut self, qualif: ConstQualif) {
160 self.qualif = self.qualif | qualif;
163 /// Returns true if the call is to a const fn or method.
164 fn handle_const_fn_call(&mut self,
169 if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
171 // we are in a static/const initializer
172 self.mode != Mode::Var &&
174 // feature-gate is not enabled
175 !self.tcx.sess.features.borrow().const_fn &&
177 // this doesn't come from a macro that has #[allow_internal_unstable]
178 !self.tcx.sess.codemap().span_allows_unstable(expr.span)
180 let mut err = self.tcx.sess.struct_span_err(
182 "const fns are an unstable feature");
186 "in Nightly builds, add `#![feature(const_fn)]` to the crate \
187 attributes to enable");
191 let qualif = self.fn_like(fn_like.kind(),
196 self.add_qualif(qualif);
198 if ret_ty.type_contents(self.tcx).interior_unsafe() {
199 self.add_qualif(ConstQualif::MUTABLE_MEM);
208 fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) {
209 match self.rvalue_borrows.entry(id) {
210 Entry::Occupied(mut entry) => {
211 // Merge the two borrows, taking the most demanding
212 // one, mutability-wise.
213 if mutbl == hir::MutMutable {
217 Entry::Vacant(entry) => {
223 fn msg(&self) -> &'static str {
225 Mode::Const => "constant",
226 Mode::ConstFn => "constant function",
227 Mode::StaticMut | Mode::Static => "static",
228 Mode::Var => unreachable!(),
232 fn check_static_mut_type(&self, e: &hir::Expr) {
233 let node_ty = self.tcx.node_id_to_type(e.id);
234 let tcontents = node_ty.type_contents(self.tcx);
236 let suffix = if tcontents.has_dtor() {
238 } else if tcontents.owns_owned() {
244 span_err!(self.tcx.sess, e.span, E0397,
245 "mutable statics are not allowed to have {}", suffix);
248 fn check_static_type(&self, e: &hir::Expr) {
249 let ty = self.tcx.node_id_to_type(e.id);
250 let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None);
251 let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
252 let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
253 fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
254 match fulfill_cx.select_all_or_error(&infcx) {
257 traits::report_fulfillment_errors(&infcx, errors);
263 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
264 fn visit_item(&mut self, i: &hir::Item) {
265 debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
266 assert_eq!(self.mode, Mode::Var);
268 hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
269 self.check_static_type(&**expr);
270 self.global_expr(Mode::Static, &**expr);
272 hir::ItemStatic(_, hir::MutMutable, ref expr) => {
273 self.check_static_mut_type(&**expr);
274 self.global_expr(Mode::StaticMut, &**expr);
276 hir::ItemConst(_, ref expr) => {
277 self.global_expr(Mode::Const, &**expr);
279 hir::ItemEnum(ref enum_definition, _) => {
280 for var in &enum_definition.variants {
281 if let Some(ref ex) = var.node.disr_expr {
282 self.global_expr(Mode::Const, &**ex);
287 intravisit::walk_item(self, i);
292 fn visit_trait_item(&mut self, t: &'v hir::TraitItem) {
294 hir::ConstTraitItem(_, ref default) => {
295 if let Some(ref expr) = *default {
296 self.global_expr(Mode::Const, &*expr);
298 intravisit::walk_trait_item(self, t);
301 _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)),
305 fn visit_impl_item(&mut self, i: &'v hir::ImplItem) {
307 hir::ImplItemKind::Const(_, ref expr) => {
308 self.global_expr(Mode::Const, &*expr);
310 _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)),
314 fn visit_fn(&mut self,
319 fn_id: ast::NodeId) {
320 self.fn_like(fk, fd, b, s, fn_id);
323 fn visit_pat(&mut self, p: &hir::Pat) {
325 hir::PatLit(ref lit) => {
326 self.global_expr(Mode::Const, &**lit);
328 hir::PatRange(ref start, ref end) => {
329 self.global_expr(Mode::Const, &**start);
330 self.global_expr(Mode::Const, &**end);
332 match const_eval::compare_lit_exprs(self.tcx, start, end) {
333 Some(Ordering::Less) |
334 Some(Ordering::Equal) => {}
335 Some(Ordering::Greater) => {
336 span_err!(self.tcx.sess, start.span, E0030,
337 "lower range bound must be less than or equal to upper");
340 self.tcx.sess.delay_span_bug(start.span,
341 "non-constant path in constant expr");
345 _ => intravisit::walk_pat(self, p)
349 fn visit_block(&mut self, block: &hir::Block) {
350 // Check all statements in the block
351 for stmt in &block.stmts {
353 hir::StmtDecl(ref decl, _) => {
355 hir::DeclLocal(_) => {},
356 // Item statements are allowed
357 hir::DeclItem(_) => continue
360 hir::StmtExpr(_, _) => {},
361 hir::StmtSemi(_, _) => {},
363 self.add_qualif(ConstQualif::NOT_CONST);
364 // anything else should have been caught by check_const_fn
365 assert_eq!(self.mode, Mode::Var);
367 intravisit::walk_block(self, block);
370 fn visit_expr(&mut self, ex: &hir::Expr) {
371 let mut outer = self.qualif;
372 self.qualif = ConstQualif::empty();
374 let node_ty = self.tcx.node_id_to_type(ex.id);
375 check_expr(self, ex, node_ty);
376 check_adjustments(self, ex);
378 // Special-case some expressions to avoid certain flags bubbling up.
380 hir::ExprCall(ref callee, ref args) => {
382 self.visit_expr(&**arg)
385 let inner = self.qualif;
386 self.visit_expr(&**callee);
387 // The callee's size doesn't count in the call.
388 let added = self.qualif - inner;
389 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
391 hir::ExprRepeat(ref element, _) => {
392 self.visit_expr(&**element);
393 // The count is checked elsewhere (typeck).
394 let count = match node_ty.sty {
395 ty::TyArray(_, n) => n,
398 // [element; 0] is always zero-sized.
400 self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
403 hir::ExprMatch(ref discr, ref arms, _) => {
404 // Compute the most demanding borrow from all the arms'
405 // patterns and set that on the discriminator.
406 let mut borrow = None;
407 for pat in arms.iter().flat_map(|arm| &arm.pats) {
408 let pat_borrow = self.rvalue_borrows.remove(&pat.id);
409 match (borrow, pat_borrow) {
410 (None, _) | (_, Some(hir::MutMutable)) => {
416 if let Some(mutbl) = borrow {
417 self.record_borrow(discr.id, mutbl);
419 intravisit::walk_expr(self, ex);
421 // Division by zero and overflow checking.
422 hir::ExprBinary(op, _, _) => {
423 intravisit::walk_expr(self, ex);
424 let div_or_rem = op.node == hir::BiDiv || op.node == hir::BiRem;
426 ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
427 if !self.qualif.intersects(ConstQualif::NOT_CONST) {
428 match const_eval::eval_const_expr_partial(
429 self.tcx, ex, ExprTypeChecked, None) {
431 Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
433 self.tcx.sess.add_lint(CONST_ERR, ex.id,
435 msg.description().into_owned())
443 _ => intravisit::walk_expr(self, ex)
446 // Handle borrows on (or inside the autorefs of) this expression.
447 match self.rvalue_borrows.remove(&ex.id) {
448 Some(hir::MutImmutable) => {
449 // Constants cannot be borrowed if they contain interior mutability as
450 // it means that our "silent insertion of statics" could change
451 // initializer values (very bad).
452 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
453 // propagated from another error, so erroring again would be just noise.
454 let tc = node_ty.type_contents(self.tcx);
455 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
456 outer = outer | ConstQualif::NOT_CONST;
457 if self.mode != Mode::Var {
458 span_err!(self.tcx.sess, ex.span, E0492,
459 "cannot borrow a constant which contains \
460 interior mutability, create a static instead");
463 // If the reference has to be 'static, avoid in-place initialization
464 // as that will end up pointing to the stack instead.
465 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
466 self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
467 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
470 Some(hir::MutMutable) => {
471 // `&mut expr` means expr could be mutated, unless it's zero-sized.
472 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
473 if self.mode == Mode::Var {
474 outer = outer | ConstQualif::NOT_CONST;
475 self.add_qualif(ConstQualif::MUTABLE_MEM);
477 span_err!(self.tcx.sess, ex.span, E0017,
478 "references in {}s may only refer \
479 to immutable values", self.msg())
482 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
483 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
488 self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
489 // Don't propagate certain flags.
490 self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
494 /// This function is used to enforce the constraints on
495 /// const/static items. It walks through the *value*
496 /// of the item walking down the expression and evaluating
497 /// every nested expression. If the expression is not part
498 /// of a const/static item, it is qualified for promotion
499 /// instead of producing errors.
500 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
501 e: &hir::Expr, node_ty: Ty<'tcx>) {
503 ty::TyStruct(def, _) |
504 ty::TyEnum(def, _) if def.has_dtor() => {
505 v.add_qualif(ConstQualif::NEEDS_DROP);
506 if v.mode != Mode::Var {
507 span_err!(v.tcx.sess, e.span, E0493,
508 "{}s are not allowed to have destructors",
515 let method_call = ty::MethodCall::expr(e.id);
518 hir::ExprBinary(..) |
519 hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
520 v.add_qualif(ConstQualif::NOT_CONST);
521 if v.mode != Mode::Var {
522 span_err!(v.tcx.sess, e.span, E0011,
523 "user-defined operators are not allowed in {}s", v.msg());
527 v.add_qualif(ConstQualif::NOT_CONST);
528 if v.mode != Mode::Var {
529 span_err!(v.tcx.sess, e.span, E0010,
530 "allocations are not allowed in {}s", v.msg());
533 hir::ExprUnary(op, ref inner) => {
534 match v.tcx.node_id_to_type(inner.id).sty {
536 assert!(op == hir::UnDeref);
538 v.add_qualif(ConstQualif::NOT_CONST);
539 if v.mode != Mode::Var {
540 span_err!(v.tcx.sess, e.span, E0396,
541 "raw pointers cannot be dereferenced in {}s", v.msg());
547 hir::ExprBinary(op, ref lhs, _) => {
548 match v.tcx.node_id_to_type(lhs.id).sty {
550 assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
551 op.node == hir::BiLe || op.node == hir::BiLt ||
552 op.node == hir::BiGe || op.node == hir::BiGt);
554 v.add_qualif(ConstQualif::NOT_CONST);
555 if v.mode != Mode::Var {
556 span_err!(v.tcx.sess, e.span, E0395,
557 "raw pointers cannot be compared in {}s", v.msg());
563 hir::ExprCast(ref from, _) => {
564 debug!("Checking const cast(id={})", from.id);
565 match v.tcx.cast_kinds.borrow().get(&from.id) {
566 None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
567 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
568 v.add_qualif(ConstQualif::NOT_CONST);
569 if v.mode != Mode::Var {
570 span_err!(v.tcx.sess, e.span, E0018,
571 "raw pointers cannot be cast to integers in {}s", v.msg());
577 hir::ExprPath(..) => {
578 let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
580 Some(Def::Variant(..)) => {
581 // Count the discriminator or function pointer.
582 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
584 Some(Def::Struct(..)) => {
585 if let ty::TyBareFn(..) = node_ty.sty {
586 // Count the function pointer.
587 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
590 Some(Def::Fn(..)) | Some(Def::Method(..)) => {
591 // Count the function pointer.
592 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
594 Some(Def::Static(..)) => {
596 Mode::Static | Mode::StaticMut => {}
597 Mode::Const | Mode::ConstFn => {
598 span_err!(v.tcx.sess, e.span, E0013,
599 "{}s cannot refer to other statics, insert \
600 an intermediate constant instead", v.msg());
602 Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
605 Some(Def::Const(did)) |
606 Some(Def::AssociatedConst(did)) => {
607 if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
610 let inner = v.global_expr(Mode::Const, expr);
614 Some(Def::Local(..)) if v.mode == Mode::ConstFn => {
615 // Sadly, we can't determine whether the types are zero-sized.
616 v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
619 v.add_qualif(ConstQualif::NOT_CONST);
620 if v.mode != Mode::Var {
621 debug!("(checking const) found bad def: {:?}", def);
622 span_err!(v.tcx.sess, e.span, E0014,
623 "paths in {}s may only refer to constants \
624 or functions", v.msg());
629 hir::ExprCall(ref callee, _) => {
630 let mut callee = &**callee;
632 callee = match callee.node {
633 hir::ExprBlock(ref block) => match block.expr {
634 Some(ref tail) => &**tail,
640 let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
641 let is_const = match def {
642 Some(Def::Struct(..)) => true,
643 Some(Def::Variant(..)) => {
644 // Count the discriminator.
645 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
648 Some(Def::Fn(did)) => {
649 v.handle_const_fn_call(e, did, node_ty)
651 Some(Def::Method(did)) => {
652 match v.tcx.impl_or_trait_item(did).container() {
653 ty::ImplContainer(_) => {
654 v.handle_const_fn_call(e, did, node_ty)
656 ty::TraitContainer(_) => false
662 v.add_qualif(ConstQualif::NOT_CONST);
663 if v.mode != Mode::Var {
664 // FIXME(#24111) Remove this check when const fn stabilizes
666 if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
667 (format!("function calls in {}s are limited to \
668 struct and enum constructors",
670 Some("a limited form of compile-time function \
671 evaluation is available on a nightly \
672 compiler via `const fn`"))
674 (format!("function calls in {}s are limited \
675 to constant functions, \
676 struct and enum constructors",
680 let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
681 if let Some(note) = note {
682 err.span_note(e.span, note);
688 hir::ExprMethodCall(..) => {
689 let method = v.tcx.tables.borrow().method_map[&method_call];
690 let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
691 ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
692 ty::TraitContainer(_) => false
695 v.add_qualif(ConstQualif::NOT_CONST);
696 if v.mode != Mode::Var {
697 span_err!(v.tcx.sess, e.span, E0378,
698 "method calls in {}s are limited to \
699 constant inherent methods", v.msg());
703 hir::ExprStruct(..) => {
704 let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
705 if did == v.tcx.lang_items.unsafe_cell_type() {
706 v.add_qualif(ConstQualif::MUTABLE_MEM);
711 hir::ExprAddrOf(..) => {
712 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
715 hir::ExprRepeat(..) => {
716 v.add_qualif(ConstQualif::PREFER_IN_PLACE);
719 hir::ExprClosure(..) => {
720 // Paths in constant contexts cannot refer to local variables,
721 // as there are none, and thus closures can't have upvars there.
722 if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
723 assert!(v.mode == Mode::Var,
724 "global closures can't capture anything");
725 v.add_qualif(ConstQualif::NOT_CONST);
732 hir::ExprTupField(..) |
735 hir::ExprTup(..) => {}
737 // Conditional control flow (possible to implement).
741 // Loops (not very meaningful in constants).
745 // More control flow (also not very meaningful).
750 // Miscellaneous expressions that could be implemented.
753 // Expressions with side-effects.
754 hir::ExprAssign(..) |
755 hir::ExprAssignOp(..) |
756 hir::ExprInlineAsm(_) => {
757 v.add_qualif(ConstQualif::NOT_CONST);
758 if v.mode != Mode::Var {
759 span_err!(v.tcx.sess, e.span, E0019,
760 "{} contains unimplemented expression type", v.msg());
766 /// Check the adjustments of an expression
767 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
768 match v.tcx.tables.borrow().adjustments.get(&e.id) {
770 Some(&ty::adjustment::AdjustReifyFnPointer) |
771 Some(&ty::adjustment::AdjustUnsafeFnPointer) => {}
773 Some(&ty::adjustment::AdjustDerefRef(
774 ty::adjustment::AutoDerefRef { autoderefs, .. }
776 if (0..autoderefs as u32).any(|autoderef| {
777 v.tcx.is_overloaded_autoderef(e.id, autoderef)
779 v.add_qualif(ConstQualif::NOT_CONST);
780 if v.mode != Mode::Var {
781 span_err!(v.tcx.sess, e.span, E0400,
782 "user-defined dereference operators are not allowed in {}s",
790 pub fn check_crate(tcx: &ty::ctxt) {
791 tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor {
794 qualif: ConstQualif::NOT_CONST,
795 rvalue_borrows: NodeMap()
797 tcx.sess.abort_if_errors();
800 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
801 fn consume(&mut self,
802 _consume_id: ast::NodeId,
805 _mode: euv::ConsumeMode) {
809 Categorization::StaticItem => {
810 if self.mode != Mode::Var {
811 // statics cannot be consumed by value at any time, that would imply
812 // that they're an initializer (what a const is for) or kept in sync
813 // over time (not feasible), so deny it outright.
814 span_err!(self.tcx.sess, consume_span, E0394,
815 "cannot refer to other statics by value, use the \
816 address-of operator or a constant instead");
820 Categorization::Deref(ref cmt, _, _) |
821 Categorization::Downcast(ref cmt, _) |
822 Categorization::Interior(ref cmt, _) => cur = cmt,
824 Categorization::Rvalue(..) |
825 Categorization::Upvar(..) |
826 Categorization::Local(..) => break
831 borrow_id: ast::NodeId,
834 _loan_region: ty::Region,
836 loan_cause: euv::LoanCause)
838 // Kind of hacky, but we allow Unsafe coercions in constants.
839 // These occur when we convert a &T or *T to a *U, as well as
840 // when making a thin pointer (e.g., `*T`) into a fat pointer
843 euv::LoanCause::AutoUnsafe => {
850 let mut is_interior = false;
853 Categorization::Rvalue(..) => {
854 if loan_cause == euv::MatchDiscriminant {
855 // Ignore the dummy immutable borrow created by EUV.
858 let mutbl = bk.to_mutbl_lossy();
859 if mutbl == hir::MutMutable && self.mode == Mode::StaticMut {
860 // Mutable slices are the only `&mut` allowed in
861 // globals, but only in `static mut`, nowhere else.
862 // FIXME: This exception is really weird... there isn't
863 // any fundamental reason to restrict this based on
864 // type of the expression. `&mut [1]` has exactly the
865 // same representation as &mut 1.
867 ty::TyArray(_, _) | ty::TySlice(_) => break,
871 self.record_borrow(borrow_id, mutbl);
874 Categorization::StaticItem => {
875 if is_interior && self.mode != Mode::Var {
876 // Borrowed statics can specifically *only* have their address taken,
877 // not any number of other borrows such as borrowing fields, reading
878 // elements of an array, etc.
879 span_err!(self.tcx.sess, borrow_span, E0494,
880 "cannot refer to the interior of another \
881 static, use a constant instead");
885 Categorization::Deref(ref cmt, _, _) |
886 Categorization::Downcast(ref cmt, _) |
887 Categorization::Interior(ref cmt, _) => {
892 Categorization::Upvar(..) |
893 Categorization::Local(..) => break
898 fn decl_without_init(&mut self,
902 _assignment_id: ast::NodeId,
903 _assignment_span: Span,
904 _assignee_cmt: mc::cmt,
905 _mode: euv::MutateMode) {}
907 fn matched_pat(&mut self,
910 _: euv::MatchMode) {}
912 fn consume_pat(&mut self,
913 _consume_pat: &hir::Pat,
915 _mode: euv::ConsumeMode) {}