]> git.lizzy.rs Git - rust.git/blob - src/librustc_passes/consts.rs
Rollup merge of #34175 - rwz:patch-2, r=alexcrichton
[rust.git] / src / librustc_passes / consts.rs
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.
4 //
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.
10
11 // Verifies that the types and values of const and static items
12 // are safe. The rules enforced by this module are:
13 //
14 // - For each *mutable* static item, it checks that its **type**:
15 //     - doesn't have a destructor
16 //     - doesn't own a box
17 //
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
22 //
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
26
27 use rustc::dep_graph::DepNode;
28 use rustc::ty::cast::{CastKind};
29 use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs};
30 use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id};
31 use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math};
32 use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
33 use rustc_const_eval::ErrKind::UnresolvedPath;
34 use rustc_const_eval::EvalHint::ExprTypeChecked;
35 use rustc_const_math::{ConstMathErr, Op};
36 use rustc::hir::def::Def;
37 use rustc::hir::def_id::DefId;
38 use rustc::middle::expr_use_visitor as euv;
39 use rustc::middle::mem_categorization as mc;
40 use rustc::middle::mem_categorization::Categorization;
41 use rustc::ty::{self, Ty, TyCtxt};
42 use rustc::traits::ProjectionMode;
43 use rustc::util::nodemap::NodeMap;
44 use rustc::middle::const_qualif::ConstQualif;
45 use rustc::lint::builtin::CONST_ERR;
46
47 use rustc::hir::{self, PatKind};
48 use syntax::ast;
49 use syntax::codemap::Span;
50 use rustc::hir::intravisit::{self, FnKind, Visitor};
51
52 use std::collections::hash_map::Entry;
53 use std::cmp::Ordering;
54
55 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
56 enum Mode {
57     Const,
58     ConstFn,
59     Static,
60     StaticMut,
61
62     // An expression that occurs outside of any constant context
63     // (i.e. `const`, `static`, array lengths, etc.). The value
64     // can be variable at runtime, but will be promotable to
65     // static memory if we can prove it is actually constant.
66     Var,
67 }
68
69 struct CheckCrateVisitor<'a, 'tcx: 'a> {
70     tcx: TyCtxt<'a, 'tcx, 'tcx>,
71     mode: Mode,
72     qualif: ConstQualif,
73     rvalue_borrows: NodeMap<hir::Mutability>
74 }
75
76 impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
77     fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
78         F: FnOnce(&mut CheckCrateVisitor<'a, 'gcx>) -> R,
79     {
80         let (old_mode, old_qualif) = (self.mode, self.qualif);
81         self.mode = mode;
82         self.qualif = ConstQualif::empty();
83         let r = f(self);
84         self.mode = old_mode;
85         self.qualif = old_qualif;
86         r
87     }
88
89     fn with_euv<F, R>(&mut self, item_id: Option<ast::NodeId>, f: F) -> R where
90         F: for<'b, 'tcx> FnOnce(&mut euv::ExprUseVisitor<'b, 'gcx, 'tcx>) -> R,
91     {
92         let param_env = match item_id {
93             Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
94             None => self.tcx.empty_parameter_environment()
95         };
96
97         self.tcx.infer_ctxt(None, Some(param_env), ProjectionMode::AnyFinal).enter(|infcx| {
98             f(&mut euv::ExprUseVisitor::new(self, &infcx))
99         })
100     }
101
102     fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif {
103         assert!(mode != Mode::Var);
104         match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
105             Entry::Occupied(entry) => return *entry.get(),
106             Entry::Vacant(entry) => {
107                 // Prevent infinite recursion on re-entry.
108                 entry.insert(ConstQualif::empty());
109             }
110         }
111         if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) {
112             match err.kind {
113                 UnimplementedConstVal(_) => {},
114                 IndexOpFeatureGated => {},
115                 ErroneousReferencedConstant(_) => {},
116                 _ => self.tcx.sess.add_lint(CONST_ERR, expr.id, expr.span,
117                                          format!("constant evaluation error: {}. This will \
118                                                  become a HARD ERROR in the future",
119                                                  err.description())),
120             }
121         }
122         self.with_mode(mode, |this| {
123             this.with_euv(None, |euv| euv.consume_expr(expr));
124             this.visit_expr(expr);
125             this.qualif
126         })
127     }
128
129     fn fn_like(&mut self,
130                fk: FnKind,
131                fd: &hir::FnDecl,
132                b: &hir::Block,
133                s: Span,
134                fn_id: ast::NodeId)
135                -> ConstQualif {
136         match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
137             Entry::Occupied(entry) => return *entry.get(),
138             Entry::Vacant(entry) => {
139                 // Prevent infinite recursion on re-entry.
140                 entry.insert(ConstQualif::empty());
141             }
142         }
143
144         let mode = match fk {
145             FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => {
146                 Mode::ConstFn
147             }
148             FnKind::Method(_, m, _, _) => {
149                 if m.constness == hir::Constness::Const {
150                     Mode::ConstFn
151                 } else {
152                     Mode::Var
153                 }
154             }
155             _ => Mode::Var
156         };
157
158         let qualif = self.with_mode(mode, |this| {
159             this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
160             intravisit::walk_fn(this, fk, fd, b, s);
161             this.qualif
162         });
163
164         // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
165         // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
166         let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
167
168         self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
169         qualif
170     }
171
172     fn add_qualif(&mut self, qualif: ConstQualif) {
173         self.qualif = self.qualif | qualif;
174     }
175
176     /// Returns true if the call is to a const fn or method.
177     fn handle_const_fn_call(&mut self,
178                             _expr: &hir::Expr,
179                             def_id: DefId,
180                             ret_ty: Ty<'gcx>)
181                             -> bool {
182         if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
183             let qualif = self.fn_like(fn_like.kind(),
184                                       fn_like.decl(),
185                                       fn_like.body(),
186                                       fn_like.span(),
187                                       fn_like.id());
188             self.add_qualif(qualif);
189
190             if ret_ty.type_contents(self.tcx).interior_unsafe() {
191                 self.add_qualif(ConstQualif::MUTABLE_MEM);
192             }
193
194             true
195         } else {
196             false
197         }
198     }
199
200     fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) {
201         match self.rvalue_borrows.entry(id) {
202             Entry::Occupied(mut entry) => {
203                 // Merge the two borrows, taking the most demanding
204                 // one, mutability-wise.
205                 if mutbl == hir::MutMutable {
206                     entry.insert(mutbl);
207                 }
208             }
209             Entry::Vacant(entry) => {
210                 entry.insert(mutbl);
211             }
212         }
213     }
214
215     fn msg(&self) -> &'static str {
216         match self.mode {
217             Mode::Const => "constant",
218             Mode::ConstFn => "constant function",
219             Mode::StaticMut | Mode::Static => "static",
220             Mode::Var => bug!(),
221         }
222     }
223 }
224
225 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
226     fn visit_item(&mut self, i: &hir::Item) {
227         debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
228         assert_eq!(self.mode, Mode::Var);
229         match i.node {
230             hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
231                 self.global_expr(Mode::Static, &expr);
232             }
233             hir::ItemStatic(_, hir::MutMutable, ref expr) => {
234                 self.global_expr(Mode::StaticMut, &expr);
235             }
236             hir::ItemConst(_, ref expr) => {
237                 self.global_expr(Mode::Const, &expr);
238             }
239             hir::ItemEnum(ref enum_definition, _) => {
240                 for var in &enum_definition.variants {
241                     if let Some(ref ex) = var.node.disr_expr {
242                         self.global_expr(Mode::Const, &ex);
243                     }
244                 }
245             }
246             _ => {
247                 intravisit::walk_item(self, i);
248             }
249         }
250     }
251
252     fn visit_trait_item(&mut self, t: &'v hir::TraitItem) {
253         match t.node {
254             hir::ConstTraitItem(_, ref default) => {
255                 if let Some(ref expr) = *default {
256                     self.global_expr(Mode::Const, &expr);
257                 } else {
258                     intravisit::walk_trait_item(self, t);
259                 }
260             }
261             _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)),
262         }
263     }
264
265     fn visit_impl_item(&mut self, i: &'v hir::ImplItem) {
266         match i.node {
267             hir::ImplItemKind::Const(_, ref expr) => {
268                 self.global_expr(Mode::Const, &expr);
269             }
270             _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)),
271         }
272     }
273
274     fn visit_fn(&mut self,
275                 fk: FnKind<'v>,
276                 fd: &'v hir::FnDecl,
277                 b: &'v hir::Block,
278                 s: Span,
279                 fn_id: ast::NodeId) {
280         self.fn_like(fk, fd, b, s, fn_id);
281     }
282
283     fn visit_pat(&mut self, p: &hir::Pat) {
284         match p.node {
285             PatKind::Lit(ref lit) => {
286                 self.global_expr(Mode::Const, &lit);
287             }
288             PatKind::Range(ref start, ref end) => {
289                 self.global_expr(Mode::Const, &start);
290                 self.global_expr(Mode::Const, &end);
291
292                 match compare_lit_exprs(self.tcx, start, end) {
293                     Some(Ordering::Less) |
294                     Some(Ordering::Equal) => {}
295                     Some(Ordering::Greater) => {
296                         span_err!(self.tcx.sess, start.span, E0030,
297                             "lower range bound must be less than or equal to upper");
298                     }
299                     None => {
300                         span_err!(self.tcx.sess, p.span, E0014,
301                                   "paths in {}s may only refer to constants",
302                                   self.msg());
303                     }
304                 }
305             }
306             _ => intravisit::walk_pat(self, p)
307         }
308     }
309
310     fn visit_block(&mut self, block: &hir::Block) {
311         // Check all statements in the block
312         for stmt in &block.stmts {
313             match stmt.node {
314                 hir::StmtDecl(ref decl, _) => {
315                     match decl.node {
316                         hir::DeclLocal(_) => {},
317                         // Item statements are allowed
318                         hir::DeclItem(_) => continue
319                     }
320                 }
321                 hir::StmtExpr(_, _) => {},
322                 hir::StmtSemi(_, _) => {},
323             }
324             self.add_qualif(ConstQualif::NOT_CONST);
325         }
326         intravisit::walk_block(self, block);
327     }
328
329     fn visit_expr(&mut self, ex: &hir::Expr) {
330         let mut outer = self.qualif;
331         self.qualif = ConstQualif::empty();
332
333         let node_ty = self.tcx.node_id_to_type(ex.id);
334         check_expr(self, ex, node_ty);
335         check_adjustments(self, ex);
336
337         // Special-case some expressions to avoid certain flags bubbling up.
338         match ex.node {
339             hir::ExprCall(ref callee, ref args) => {
340                 for arg in args {
341                     self.visit_expr(&arg)
342                 }
343
344                 let inner = self.qualif;
345                 self.visit_expr(&callee);
346                 // The callee's size doesn't count in the call.
347                 let added = self.qualif - inner;
348                 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
349             }
350             hir::ExprRepeat(ref element, _) => {
351                 self.visit_expr(&element);
352                 // The count is checked elsewhere (typeck).
353                 let count = match node_ty.sty {
354                     ty::TyArray(_, n) => n,
355                     _ => bug!()
356                 };
357                 // [element; 0] is always zero-sized.
358                 if count == 0 {
359                     self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
360                 }
361             }
362             hir::ExprMatch(ref discr, ref arms, _) => {
363                 // Compute the most demanding borrow from all the arms'
364                 // patterns and set that on the discriminator.
365                 let mut borrow = None;
366                 for pat in arms.iter().flat_map(|arm| &arm.pats) {
367                     let pat_borrow = self.rvalue_borrows.remove(&pat.id);
368                     match (borrow, pat_borrow) {
369                         (None, _) | (_, Some(hir::MutMutable)) => {
370                             borrow = pat_borrow;
371                         }
372                         _ => {}
373                     }
374                 }
375                 if let Some(mutbl) = borrow {
376                     self.record_borrow(discr.id, mutbl);
377                 }
378                 intravisit::walk_expr(self, ex);
379             }
380             _ => intravisit::walk_expr(self, ex)
381         }
382
383         // Handle borrows on (or inside the autorefs of) this expression.
384         match self.rvalue_borrows.remove(&ex.id) {
385             Some(hir::MutImmutable) => {
386                 // Constants cannot be borrowed if they contain interior mutability as
387                 // it means that our "silent insertion of statics" could change
388                 // initializer values (very bad).
389                 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
390                 // propagated from another error, so erroring again would be just noise.
391                 let tc = node_ty.type_contents(self.tcx);
392                 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
393                     outer = outer | ConstQualif::NOT_CONST;
394                 }
395                 // If the reference has to be 'static, avoid in-place initialization
396                 // as that will end up pointing to the stack instead.
397                 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
398                     self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
399                     self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
400                 }
401             }
402             Some(hir::MutMutable) => {
403                 // `&mut expr` means expr could be mutated, unless it's zero-sized.
404                 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
405                     if self.mode == Mode::Var {
406                         outer = outer | ConstQualif::NOT_CONST;
407                         self.add_qualif(ConstQualif::MUTABLE_MEM);
408                     }
409                 }
410                 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
411                     self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
412                 }
413             }
414             None => {}
415         }
416
417         if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) {
418             match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) {
419                 Ok(_) => {}
420                 Err(ConstEvalErr { kind: UnimplementedConstVal(_), ..}) |
421                 Err(ConstEvalErr { kind: MiscCatchAll, ..}) |
422                 Err(ConstEvalErr { kind: MiscBinaryOp, ..}) |
423                 Err(ConstEvalErr { kind: NonConstPath, ..}) |
424                 Err(ConstEvalErr { kind: UnresolvedPath, ..}) |
425                 Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), ..}) |
426                 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), ..}) |
427                 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), ..}) |
428                 Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
429                 Err(msg) => {
430                     self.tcx.sess.add_lint(CONST_ERR, ex.id,
431                                            msg.span,
432                                            msg.description().into_owned())
433                 }
434             }
435         }
436
437         self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
438         // Don't propagate certain flags.
439         self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
440     }
441 }
442
443 /// This function is used to enforce the constraints on
444 /// const/static items. It walks through the *value*
445 /// of the item walking down the expression and evaluating
446 /// every nested expression. If the expression is not part
447 /// of a const/static item, it is qualified for promotion
448 /// instead of producing errors.
449 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
450                         e: &hir::Expr, node_ty: Ty<'tcx>) {
451     match node_ty.sty {
452         ty::TyStruct(def, _) |
453         ty::TyEnum(def, _) if def.has_dtor() => {
454             v.add_qualif(ConstQualif::NEEDS_DROP);
455         }
456         _ => {}
457     }
458
459     let method_call = ty::MethodCall::expr(e.id);
460     match e.node {
461         hir::ExprUnary(..) |
462         hir::ExprBinary(..) |
463         hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
464             v.add_qualif(ConstQualif::NOT_CONST);
465         }
466         hir::ExprBox(_) => {
467             v.add_qualif(ConstQualif::NOT_CONST);
468         }
469         hir::ExprUnary(op, ref inner) => {
470             match v.tcx.node_id_to_type(inner.id).sty {
471                 ty::TyRawPtr(_) => {
472                     assert!(op == hir::UnDeref);
473
474                     v.add_qualif(ConstQualif::NOT_CONST);
475                 }
476                 _ => {}
477             }
478         }
479         hir::ExprBinary(op, ref lhs, _) => {
480             match v.tcx.node_id_to_type(lhs.id).sty {
481                 ty::TyRawPtr(_) => {
482                     assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
483                             op.node == hir::BiLe || op.node == hir::BiLt ||
484                             op.node == hir::BiGe || op.node == hir::BiGt);
485
486                     v.add_qualif(ConstQualif::NOT_CONST);
487                 }
488                 _ => {}
489             }
490         }
491         hir::ExprCast(ref from, _) => {
492             debug!("Checking const cast(id={})", from.id);
493             match v.tcx.cast_kinds.borrow().get(&from.id) {
494                 None => span_bug!(e.span, "no kind for cast"),
495                 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
496                     v.add_qualif(ConstQualif::NOT_CONST);
497                 }
498                 _ => {}
499             }
500         }
501         hir::ExprPath(..) => {
502             match v.tcx.expect_def(e.id) {
503                 Def::Variant(..) => {
504                     // Count the discriminator or function pointer.
505                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
506                 }
507                 Def::Struct(..) => {
508                     if let ty::TyFnDef(..) = node_ty.sty {
509                         // Count the function pointer.
510                         v.add_qualif(ConstQualif::NON_ZERO_SIZED);
511                     }
512                 }
513                 Def::Fn(..) | Def::Method(..) => {
514                     // Count the function pointer.
515                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
516                 }
517                 Def::Static(..) => {
518                     match v.mode {
519                         Mode::Static | Mode::StaticMut => {}
520                         Mode::Const | Mode::ConstFn => {}
521                         Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
522                     }
523                 }
524                 Def::Const(did) | Def::AssociatedConst(did) => {
525                     let substs = Some(v.tcx.node_id_item_substs(e.id).substs);
526                     if let Some((expr, _)) = lookup_const_by_id(v.tcx, did, substs) {
527                         let inner = v.global_expr(Mode::Const, expr);
528                         v.add_qualif(inner);
529                     }
530                 }
531                 Def::Local(..) if v.mode == Mode::ConstFn => {
532                     // Sadly, we can't determine whether the types are zero-sized.
533                     v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
534                 }
535                 _ => {
536                     v.add_qualif(ConstQualif::NOT_CONST);
537                 }
538             }
539         }
540         hir::ExprCall(ref callee, _) => {
541             let mut callee = &**callee;
542             loop {
543                 callee = match callee.node {
544                     hir::ExprBlock(ref block) => match block.expr {
545                         Some(ref tail) => &tail,
546                         None => break
547                     },
548                     _ => break
549                 };
550             }
551             // The callee is an arbitrary expression, it doesn't necessarily have a definition.
552             let is_const = match v.tcx.expect_def_or_none(callee.id) {
553                 Some(Def::Struct(..)) => true,
554                 Some(Def::Variant(..)) => {
555                     // Count the discriminator.
556                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
557                     true
558                 }
559                 Some(Def::Fn(did)) => {
560                     v.handle_const_fn_call(e, did, node_ty)
561                 }
562                 Some(Def::Method(did)) => {
563                     match v.tcx.impl_or_trait_item(did).container() {
564                         ty::ImplContainer(_) => {
565                             v.handle_const_fn_call(e, did, node_ty)
566                         }
567                         ty::TraitContainer(_) => false
568                     }
569                 }
570                 _ => false
571             };
572             if !is_const {
573                 v.add_qualif(ConstQualif::NOT_CONST);
574             }
575         }
576         hir::ExprMethodCall(..) => {
577             let method = v.tcx.tables.borrow().method_map[&method_call];
578             let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
579                 ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
580                 ty::TraitContainer(_) => false
581             };
582             if !is_const {
583                 v.add_qualif(ConstQualif::NOT_CONST);
584             }
585         }
586         hir::ExprStruct(..) => {
587             // unsafe_cell_type doesn't necessarily exist with no_core
588             if Some(v.tcx.expect_def(e.id).def_id()) == v.tcx.lang_items.unsafe_cell_type() {
589                 v.add_qualif(ConstQualif::MUTABLE_MEM);
590             }
591         }
592
593         hir::ExprLit(_) |
594         hir::ExprAddrOf(..) => {
595             v.add_qualif(ConstQualif::NON_ZERO_SIZED);
596         }
597
598         hir::ExprRepeat(..) => {
599             v.add_qualif(ConstQualif::PREFER_IN_PLACE);
600         }
601
602         hir::ExprClosure(..) => {
603             // Paths in constant contexts cannot refer to local variables,
604             // as there are none, and thus closures can't have upvars there.
605             if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
606                 assert!(v.mode == Mode::Var,
607                         "global closures can't capture anything");
608                 v.add_qualif(ConstQualif::NOT_CONST);
609             }
610         }
611
612         hir::ExprBlock(_) |
613         hir::ExprIndex(..) |
614         hir::ExprField(..) |
615         hir::ExprTupField(..) |
616         hir::ExprVec(_) |
617         hir::ExprType(..) |
618         hir::ExprTup(..) => {}
619
620         // Conditional control flow (possible to implement).
621         hir::ExprMatch(..) |
622         hir::ExprIf(..) |
623
624         // Loops (not very meaningful in constants).
625         hir::ExprWhile(..) |
626         hir::ExprLoop(..) |
627
628         // More control flow (also not very meaningful).
629         hir::ExprBreak(_) |
630         hir::ExprAgain(_) |
631         hir::ExprRet(_) |
632
633         // Expressions with side-effects.
634         hir::ExprAssign(..) |
635         hir::ExprAssignOp(..) |
636         hir::ExprInlineAsm(..) => {
637             v.add_qualif(ConstQualif::NOT_CONST);
638         }
639     }
640 }
641
642 /// Check the adjustments of an expression
643 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
644     match v.tcx.tables.borrow().adjustments.get(&e.id) {
645         None |
646         Some(&ty::adjustment::AdjustReifyFnPointer) |
647         Some(&ty::adjustment::AdjustUnsafeFnPointer) |
648         Some(&ty::adjustment::AdjustMutToConstPointer) => {}
649
650         Some(&ty::adjustment::AdjustDerefRef(
651             ty::adjustment::AutoDerefRef { autoderefs, .. }
652         )) => {
653             if (0..autoderefs as u32).any(|autoderef| {
654                     v.tcx.is_overloaded_autoderef(e.id, autoderef)
655             }) {
656                 v.add_qualif(ConstQualif::NOT_CONST);
657             }
658         }
659     }
660 }
661
662 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
663     tcx.visit_all_items_in_krate(DepNode::CheckConst, &mut CheckCrateVisitor {
664         tcx: tcx,
665         mode: Mode::Var,
666         qualif: ConstQualif::NOT_CONST,
667         rvalue_borrows: NodeMap()
668     });
669     tcx.sess.abort_if_errors();
670 }
671
672 impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
673     fn consume(&mut self,
674                _consume_id: ast::NodeId,
675                _consume_span: Span,
676                cmt: mc::cmt,
677                _mode: euv::ConsumeMode) {
678         let mut cur = &cmt;
679         loop {
680             match cur.cat {
681                 Categorization::StaticItem => {
682                     break;
683                 }
684                 Categorization::Deref(ref cmt, _, _) |
685                 Categorization::Downcast(ref cmt, _) |
686                 Categorization::Interior(ref cmt, _) => cur = cmt,
687
688                 Categorization::Rvalue(..) |
689                 Categorization::Upvar(..) |
690                 Categorization::Local(..) => break
691             }
692         }
693     }
694     fn borrow(&mut self,
695               borrow_id: ast::NodeId,
696               _borrow_span: Span,
697               cmt: mc::cmt<'tcx>,
698               _loan_region: ty::Region,
699               bk: ty::BorrowKind,
700               loan_cause: euv::LoanCause)
701     {
702         // Kind of hacky, but we allow Unsafe coercions in constants.
703         // These occur when we convert a &T or *T to a *U, as well as
704         // when making a thin pointer (e.g., `*T`) into a fat pointer
705         // (e.g., `*Trait`).
706         match loan_cause {
707             euv::LoanCause::AutoUnsafe => {
708                 return;
709             }
710             _ => { }
711         }
712
713         let mut cur = &cmt;
714         loop {
715             match cur.cat {
716                 Categorization::Rvalue(..) => {
717                     if loan_cause == euv::MatchDiscriminant {
718                         // Ignore the dummy immutable borrow created by EUV.
719                         break;
720                     }
721                     let mutbl = bk.to_mutbl_lossy();
722                     if mutbl == hir::MutMutable && self.mode == Mode::StaticMut {
723                         // Mutable slices are the only `&mut` allowed in
724                         // globals, but only in `static mut`, nowhere else.
725                         // FIXME: This exception is really weird... there isn't
726                         // any fundamental reason to restrict this based on
727                         // type of the expression.  `&mut [1]` has exactly the
728                         // same representation as &mut 1.
729                         match cmt.ty.sty {
730                             ty::TyArray(_, _) | ty::TySlice(_) => break,
731                             _ => {}
732                         }
733                     }
734                     self.record_borrow(borrow_id, mutbl);
735                     break;
736                 }
737                 Categorization::StaticItem => {
738                     break;
739                 }
740                 Categorization::Deref(ref cmt, _, _) |
741                 Categorization::Downcast(ref cmt, _) |
742                 Categorization::Interior(ref cmt, _) => {
743                     cur = cmt;
744                 }
745
746                 Categorization::Upvar(..) |
747                 Categorization::Local(..) => break
748             }
749         }
750     }
751
752     fn decl_without_init(&mut self,
753                          _id: ast::NodeId,
754                          _span: Span) {}
755     fn mutate(&mut self,
756               _assignment_id: ast::NodeId,
757               _assignment_span: Span,
758               _assignee_cmt: mc::cmt,
759               _mode: euv::MutateMode) {}
760
761     fn matched_pat(&mut self,
762                    _: &hir::Pat,
763                    _: mc::cmt,
764                    _: euv::MatchMode) {}
765
766     fn consume_pat(&mut self,
767                    _consume_pat: &hir::Pat,
768                    _cmt: mc::cmt,
769                    _mode: euv::ConsumeMode) {}
770 }