]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/check_const.rs
Auto merge of #29148 - petrochenkov:noshow, r=alexcrichton
[rust.git] / src / librustc / middle / check_const.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 middle::ty::cast::{CastKind};
28 use middle::const_eval;
29 use middle::const_eval::EvalHint::ExprTypeChecked;
30 use middle::def;
31 use middle::def_id::DefId;
32 use middle::expr_use_visitor as euv;
33 use middle::infer;
34 use middle::mem_categorization as mc;
35 use middle::traits;
36 use middle::ty::{self, Ty};
37 use util::nodemap::NodeMap;
38
39 use rustc_front::hir;
40 use syntax::ast;
41 use syntax::codemap::Span;
42 use syntax::feature_gate::UnstableFeatures;
43 use rustc_front::visit::{self, FnKind, Visitor};
44
45 use std::collections::hash_map::Entry;
46 use std::cmp::Ordering;
47
48 // Const qualification, from partial to completely promotable.
49 bitflags! {
50     #[derive(RustcEncodable, RustcDecodable)]
51     flags ConstQualif: u8 {
52         // Inner mutability (can not be placed behind a reference) or behind
53         // &mut in a non-global expression. Can be copied from static memory.
54         const MUTABLE_MEM        = 1 << 0,
55         // Constant value with a type that implements Drop. Can be copied
56         // from static memory, similar to MUTABLE_MEM.
57         const NEEDS_DROP         = 1 << 1,
58         // Even if the value can be placed in static memory, copying it from
59         // there is more expensive than in-place instantiation, and/or it may
60         // be too large. This applies to [T; N] and everything containing it.
61         // N.B.: references need to clear this flag to not end up on the stack.
62         const PREFER_IN_PLACE    = 1 << 2,
63         // May use more than 0 bytes of memory, doesn't impact the constness
64         // directly, but is not allowed to be borrowed mutably in a constant.
65         const NON_ZERO_SIZED     = 1 << 3,
66         // Actually borrowed, has to always be in static memory. Does not
67         // propagate, and requires the expression to behave like a 'static
68         // lvalue. The set of expressions with this flag is the minimum
69         // that have to be promoted.
70         const HAS_STATIC_BORROWS = 1 << 4,
71         // Invalid const for miscellaneous reasons (e.g. not implemented).
72         const NOT_CONST          = 1 << 5,
73
74         // Borrowing the expression won't produce &'static T if any of these
75         // bits are set, though the value could be copied from static memory
76         // if `NOT_CONST` isn't set.
77         const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
78                                    ConstQualif::NEEDS_DROP.bits |
79                                    ConstQualif::NOT_CONST.bits
80     }
81 }
82
83 #[derive(Copy, Clone, Eq, PartialEq)]
84 enum Mode {
85     Const,
86     ConstFn,
87     Static,
88     StaticMut,
89
90     // An expression that occurs outside of any constant context
91     // (i.e. `const`, `static`, array lengths, etc.). The value
92     // can be variable at runtime, but will be promotable to
93     // static memory if we can prove it is actually constant.
94     Var,
95 }
96
97 struct CheckCrateVisitor<'a, 'tcx: 'a> {
98     tcx: &'a ty::ctxt<'tcx>,
99     mode: Mode,
100     qualif: ConstQualif,
101     rvalue_borrows: NodeMap<hir::Mutability>
102 }
103
104 impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
105     fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
106         F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
107     {
108         let (old_mode, old_qualif) = (self.mode, self.qualif);
109         self.mode = mode;
110         self.qualif = ConstQualif::empty();
111         let r = f(self);
112         self.mode = old_mode;
113         self.qualif = old_qualif;
114         r
115     }
116
117     fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
118         F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R,
119     {
120         let param_env = match item_id {
121             Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
122             None => self.tcx.empty_parameter_environment()
123         };
124
125         let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, Some(param_env), false);
126
127         f(&mut euv::ExprUseVisitor::new(self, &infcx))
128     }
129
130     fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif {
131         assert!(mode != Mode::Var);
132         match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
133             Entry::Occupied(entry) => return *entry.get(),
134             Entry::Vacant(entry) => {
135                 // Prevent infinite recursion on re-entry.
136                 entry.insert(ConstQualif::empty());
137             }
138         }
139         self.with_mode(mode, |this| {
140             this.with_euv(None, |euv| euv.consume_expr(expr));
141             this.visit_expr(expr);
142             this.qualif
143         })
144     }
145
146     fn fn_like(&mut self,
147                fk: FnKind,
148                fd: &hir::FnDecl,
149                b: &hir::Block,
150                s: Span,
151                fn_id: ast::NodeId)
152                -> ConstQualif {
153         match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
154             Entry::Occupied(entry) => return *entry.get(),
155             Entry::Vacant(entry) => {
156                 // Prevent infinite recursion on re-entry.
157                 entry.insert(ConstQualif::empty());
158             }
159         }
160
161         let mode = match fk {
162             FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => {
163                 Mode::ConstFn
164             }
165             FnKind::Method(_, m, _) => {
166                 if m.constness == hir::Constness::Const {
167                     Mode::ConstFn
168                 } else {
169                     Mode::Var
170                 }
171             }
172             _ => Mode::Var
173         };
174
175         // Ensure the arguments are simple, not mutable/by-ref or patterns.
176         if mode == Mode::ConstFn {
177             for arg in &fd.inputs {
178                 match arg.pat.node {
179                     hir::PatWild(_) => {}
180                     hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {}
181                     _ => {
182                         span_err!(self.tcx.sess, arg.pat.span, E0022,
183                                   "arguments of constant functions can only \
184                                    be immutable by-value bindings");
185                     }
186                 }
187             }
188         }
189
190         let qualif = self.with_mode(mode, |this| {
191             this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
192             visit::walk_fn(this, fk, fd, b, s);
193             this.qualif
194         });
195
196         // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
197         // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
198         let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
199
200         self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
201         qualif
202     }
203
204     fn add_qualif(&mut self, qualif: ConstQualif) {
205         self.qualif = self.qualif | qualif;
206     }
207
208     /// Returns true if the call is to a const fn or method.
209     fn handle_const_fn_call(&mut self,
210                             expr: &hir::Expr,
211                             def_id: DefId,
212                             ret_ty: Ty<'tcx>)
213                             -> bool {
214         if let Some(fn_like) = const_eval::lookup_const_fn_by_id(self.tcx, def_id) {
215             if
216                 // we are in a static/const initializer
217                 self.mode != Mode::Var &&
218
219                 // feature-gate is not enabled
220                 !self.tcx.sess.features.borrow().const_fn &&
221
222                 // this doesn't come from a macro that has #[allow_internal_unstable]
223                 !self.tcx.sess.codemap().span_allows_unstable(expr.span)
224             {
225                 self.tcx.sess.span_err(
226                     expr.span,
227                     &format!("const fns are an unstable feature"));
228                 fileline_help!(
229                     self.tcx.sess,
230                     expr.span,
231                     "in Nightly builds, add `#![feature(const_fn)]` to the crate \
232                      attributes to enable");
233             }
234
235             let qualif = self.fn_like(fn_like.kind(),
236                                       fn_like.decl(),
237                                       fn_like.body(),
238                                       fn_like.span(),
239                                       fn_like.id());
240             self.add_qualif(qualif);
241
242             if ret_ty.type_contents(self.tcx).interior_unsafe() {
243                 self.add_qualif(ConstQualif::MUTABLE_MEM);
244             }
245
246             true
247         } else {
248             false
249         }
250     }
251
252     fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) {
253         match self.rvalue_borrows.entry(id) {
254             Entry::Occupied(mut entry) => {
255                 // Merge the two borrows, taking the most demanding
256                 // one, mutability-wise.
257                 if mutbl == hir::MutMutable {
258                     entry.insert(mutbl);
259                 }
260             }
261             Entry::Vacant(entry) => {
262                 entry.insert(mutbl);
263             }
264         }
265     }
266
267     fn msg(&self) -> &'static str {
268         match self.mode {
269             Mode::Const => "constant",
270             Mode::ConstFn => "constant function",
271             Mode::StaticMut | Mode::Static => "static",
272             Mode::Var => unreachable!(),
273         }
274     }
275
276     fn check_static_mut_type(&self, e: &hir::Expr) {
277         let node_ty = self.tcx.node_id_to_type(e.id);
278         let tcontents = node_ty.type_contents(self.tcx);
279
280         let suffix = if tcontents.has_dtor() {
281             "destructors"
282         } else if tcontents.owns_owned() {
283             "boxes"
284         } else {
285             return
286         };
287
288         span_err!(self.tcx.sess, e.span, E0397,
289                  "mutable statics are not allowed to have {}", suffix);
290     }
291
292     fn check_static_type(&self, e: &hir::Expr) {
293         let ty = self.tcx.node_id_to_type(e.id);
294         let infcx = infer::new_infer_ctxt(self.tcx, &self.tcx.tables, None, false);
295         let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
296         let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
297         fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
298         match fulfill_cx.select_all_or_error(&infcx) {
299             Ok(()) => { },
300             Err(ref errors) => {
301                 traits::report_fulfillment_errors(&infcx, errors);
302             }
303         }
304     }
305 }
306
307 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
308     fn visit_item(&mut self, i: &hir::Item) {
309         debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
310         match i.node {
311             hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
312                 self.check_static_type(&**expr);
313                 self.global_expr(Mode::Static, &**expr);
314             }
315             hir::ItemStatic(_, hir::MutMutable, ref expr) => {
316                 self.check_static_mut_type(&**expr);
317                 self.global_expr(Mode::StaticMut, &**expr);
318             }
319             hir::ItemConst(_, ref expr) => {
320                 self.global_expr(Mode::Const, &**expr);
321             }
322             hir::ItemEnum(ref enum_definition, _) => {
323                 for var in &enum_definition.variants {
324                     if let Some(ref ex) = var.node.disr_expr {
325                         self.global_expr(Mode::Const, &**ex);
326                     }
327                 }
328             }
329             _ => {
330                 self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
331             }
332         }
333     }
334
335     fn visit_trait_item(&mut self, t: &'v hir::TraitItem) {
336         match t.node {
337             hir::ConstTraitItem(_, ref default) => {
338                 if let Some(ref expr) = *default {
339                     self.global_expr(Mode::Const, &*expr);
340                 } else {
341                     visit::walk_trait_item(self, t);
342                 }
343             }
344             _ => self.with_mode(Mode::Var, |v| visit::walk_trait_item(v, t)),
345         }
346     }
347
348     fn visit_impl_item(&mut self, i: &'v hir::ImplItem) {
349         match i.node {
350             hir::ConstImplItem(_, ref expr) => {
351                 self.global_expr(Mode::Const, &*expr);
352             }
353             _ => self.with_mode(Mode::Var, |v| visit::walk_impl_item(v, i)),
354         }
355     }
356
357     fn visit_fn(&mut self,
358                 fk: FnKind<'v>,
359                 fd: &'v hir::FnDecl,
360                 b: &'v hir::Block,
361                 s: Span,
362                 fn_id: ast::NodeId) {
363         self.fn_like(fk, fd, b, s, fn_id);
364     }
365
366     fn visit_pat(&mut self, p: &hir::Pat) {
367         match p.node {
368             hir::PatLit(ref lit) => {
369                 self.global_expr(Mode::Const, &**lit);
370             }
371             hir::PatRange(ref start, ref end) => {
372                 self.global_expr(Mode::Const, &**start);
373                 self.global_expr(Mode::Const, &**end);
374
375                 match const_eval::compare_lit_exprs(self.tcx, start, end) {
376                     Some(Ordering::Less) |
377                     Some(Ordering::Equal) => {}
378                     Some(Ordering::Greater) => {
379                         span_err!(self.tcx.sess, start.span, E0030,
380                             "lower range bound must be less than or equal to upper");
381                     }
382                     None => {
383                         self.tcx.sess.delay_span_bug(start.span,
384                                                      "non-constant path in constant expr");
385                     }
386                 }
387             }
388             _ => visit::walk_pat(self, p)
389         }
390     }
391
392     fn visit_block(&mut self, block: &hir::Block) {
393         // Check all statements in the block
394         for stmt in &block.stmts {
395             let span = match stmt.node {
396                 hir::StmtDecl(ref decl, _) => {
397                     match decl.node {
398                         hir::DeclLocal(_) => decl.span,
399
400                         // Item statements are allowed
401                         hir::DeclItem(_) => continue
402                     }
403                 }
404                 hir::StmtExpr(ref expr, _) => expr.span,
405                 hir::StmtSemi(ref semi, _) => semi.span,
406             };
407             self.add_qualif(ConstQualif::NOT_CONST);
408             if self.mode != Mode::Var {
409                 span_err!(self.tcx.sess, span, E0016,
410                           "blocks in {}s are limited to items and \
411                            tail expressions", self.msg());
412             }
413         }
414         visit::walk_block(self, block);
415     }
416
417     fn visit_expr(&mut self, ex: &hir::Expr) {
418         let mut outer = self.qualif;
419         self.qualif = ConstQualif::empty();
420
421         let node_ty = self.tcx.node_id_to_type(ex.id);
422         check_expr(self, ex, node_ty);
423         check_adjustments(self, ex);
424
425         // Special-case some expressions to avoid certain flags bubbling up.
426         match ex.node {
427             hir::ExprCall(ref callee, ref args) => {
428                 for arg in args {
429                     self.visit_expr(&**arg)
430                 }
431
432                 let inner = self.qualif;
433                 self.visit_expr(&**callee);
434                 // The callee's size doesn't count in the call.
435                 let added = self.qualif - inner;
436                 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
437             }
438             hir::ExprRepeat(ref element, _) => {
439                 self.visit_expr(&**element);
440                 // The count is checked elsewhere (typeck).
441                 let count = match node_ty.sty {
442                     ty::TyArray(_, n) => n,
443                     _ => unreachable!()
444                 };
445                 // [element; 0] is always zero-sized.
446                 if count == 0 {
447                     self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
448                 }
449             }
450             hir::ExprMatch(ref discr, ref arms, _) => {
451                 // Compute the most demanding borrow from all the arms'
452                 // patterns and set that on the discriminator.
453                 let mut borrow = None;
454                 for pat in arms.iter().flat_map(|arm| &arm.pats) {
455                     let pat_borrow = self.rvalue_borrows.remove(&pat.id);
456                     match (borrow, pat_borrow) {
457                         (None, _) | (_, Some(hir::MutMutable)) => {
458                             borrow = pat_borrow;
459                         }
460                         _ => {}
461                     }
462                 }
463                 if let Some(mutbl) = borrow {
464                     self.record_borrow(discr.id, mutbl);
465                 }
466                 visit::walk_expr(self, ex);
467             }
468             // Division by zero and overflow checking.
469             hir::ExprBinary(op, _, _) => {
470                 visit::walk_expr(self, ex);
471                 let div_or_rem = op.node == hir::BiDiv || op.node == hir::BiRem;
472                 match node_ty.sty {
473                     ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
474                         if !self.qualif.intersects(ConstQualif::NOT_CONST) {
475                             match const_eval::eval_const_expr_partial(
476                                     self.tcx, ex, ExprTypeChecked) {
477                                 Ok(_) => {}
478                                 Err(msg) => {
479                                     self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,
480                                                            msg.span,
481                                                            msg.description().into_owned())
482                                 }
483                             }
484                         }
485                     }
486                     _ => {}
487                 }
488             }
489             _ => visit::walk_expr(self, ex)
490         }
491
492         // Handle borrows on (or inside the autorefs of) this expression.
493         match self.rvalue_borrows.remove(&ex.id) {
494             Some(hir::MutImmutable) => {
495                 // Constants cannot be borrowed if they contain interior mutability as
496                 // it means that our "silent insertion of statics" could change
497                 // initializer values (very bad).
498                 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
499                 // propagated from another error, so erroring again would be just noise.
500                 let tc = node_ty.type_contents(self.tcx);
501                 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
502                     outer = outer | ConstQualif::NOT_CONST;
503                     if self.mode != Mode::Var {
504                         span_err!(self.tcx.sess, ex.span, E0492,
505                                   "cannot borrow a constant which contains \
506                                    interior mutability, create a static instead");
507                     }
508                 }
509                 // If the reference has to be 'static, avoid in-place initialization
510                 // as that will end up pointing to the stack instead.
511                 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
512                     self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
513                     self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
514                 }
515             }
516             Some(hir::MutMutable) => {
517                 // `&mut expr` means expr could be mutated, unless it's zero-sized.
518                 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
519                     if self.mode == Mode::Var {
520                         outer = outer | ConstQualif::NOT_CONST;
521                         self.add_qualif(ConstQualif::MUTABLE_MEM);
522                     } else {
523                         span_err!(self.tcx.sess, ex.span, E0017,
524                             "references in {}s may only refer \
525                              to immutable values", self.msg())
526                     }
527                 }
528                 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
529                     self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
530                 }
531             }
532             None => {}
533         }
534         self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
535         // Don't propagate certain flags.
536         self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
537     }
538 }
539
540 /// This function is used to enforce the constraints on
541 /// const/static items. It walks through the *value*
542 /// of the item walking down the expression and evaluating
543 /// every nested expression. If the expression is not part
544 /// of a const/static item, it is qualified for promotion
545 /// instead of producing errors.
546 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
547                         e: &hir::Expr, node_ty: Ty<'tcx>) {
548     match node_ty.sty {
549         ty::TyStruct(def, _) |
550         ty::TyEnum(def, _) if def.has_dtor() => {
551             v.add_qualif(ConstQualif::NEEDS_DROP);
552             if v.mode != Mode::Var {
553                 span_err!(v.tcx.sess, e.span, E0493,
554                           "{}s are not allowed to have destructors",
555                           v.msg());
556             }
557         }
558         _ => {}
559     }
560
561     let method_call = ty::MethodCall::expr(e.id);
562     match e.node {
563         hir::ExprUnary(..) |
564         hir::ExprBinary(..) |
565         hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
566             v.add_qualif(ConstQualif::NOT_CONST);
567             if v.mode != Mode::Var {
568                 span_err!(v.tcx.sess, e.span, E0011,
569                             "user-defined operators are not allowed in {}s", v.msg());
570             }
571         }
572         hir::ExprBox(_) => {
573             v.add_qualif(ConstQualif::NOT_CONST);
574             if v.mode != Mode::Var {
575                 span_err!(v.tcx.sess, e.span, E0010,
576                           "allocations are not allowed in {}s", v.msg());
577             }
578         }
579         hir::ExprUnary(op, ref inner) => {
580             match v.tcx.node_id_to_type(inner.id).sty {
581                 ty::TyRawPtr(_) => {
582                     assert!(op == hir::UnDeref);
583
584                     v.add_qualif(ConstQualif::NOT_CONST);
585                     if v.mode != Mode::Var {
586                         span_err!(v.tcx.sess, e.span, E0396,
587                                   "raw pointers cannot be dereferenced in {}s", v.msg());
588                     }
589                 }
590                 _ => {}
591             }
592         }
593         hir::ExprBinary(op, ref lhs, _) => {
594             match v.tcx.node_id_to_type(lhs.id).sty {
595                 ty::TyRawPtr(_) => {
596                     assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
597                             op.node == hir::BiLe || op.node == hir::BiLt ||
598                             op.node == hir::BiGe || op.node == hir::BiGt);
599
600                     v.add_qualif(ConstQualif::NOT_CONST);
601                     if v.mode != Mode::Var {
602                         span_err!(v.tcx.sess, e.span, E0395,
603                                   "raw pointers cannot be compared in {}s", v.msg());
604                     }
605                 }
606                 _ => {}
607             }
608         }
609         hir::ExprCast(ref from, _) => {
610             debug!("Checking const cast(id={})", from.id);
611             match v.tcx.cast_kinds.borrow().get(&from.id) {
612                 None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
613                 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
614                     v.add_qualif(ConstQualif::NOT_CONST);
615                     if v.mode != Mode::Var {
616                         span_err!(v.tcx.sess, e.span, E0018,
617                                   "raw pointers cannot be cast to integers in {}s", v.msg());
618                     }
619                 }
620                 _ => {}
621             }
622         }
623         hir::ExprPath(..) => {
624             let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
625             match def {
626                 Some(def::DefVariant(_, _, _)) => {
627                     // Count the discriminator or function pointer.
628                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
629                 }
630                 Some(def::DefStruct(_)) => {
631                     if let ty::TyBareFn(..) = node_ty.sty {
632                         // Count the function pointer.
633                         v.add_qualif(ConstQualif::NON_ZERO_SIZED);
634                     }
635                 }
636                 Some(def::DefFn(..)) | Some(def::DefMethod(..)) => {
637                     // Count the function pointer.
638                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
639                 }
640                 Some(def::DefStatic(..)) => {
641                     match v.mode {
642                         Mode::Static | Mode::StaticMut => {}
643                         Mode::Const | Mode::ConstFn => {
644                             span_err!(v.tcx.sess, e.span, E0013,
645                                 "{}s cannot refer to other statics, insert \
646                                  an intermediate constant instead", v.msg());
647                         }
648                         Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
649                     }
650                 }
651                 Some(def::DefConst(did)) |
652                 Some(def::DefAssociatedConst(did)) => {
653                     if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
654                                                                        Some(e.id)) {
655                         let inner = v.global_expr(Mode::Const, expr);
656                         v.add_qualif(inner);
657                     } else {
658                         v.tcx.sess.span_bug(e.span,
659                                             "DefConst or DefAssociatedConst \
660                                              doesn't point to a constant");
661                     }
662                 }
663                 Some(def::DefLocal(..)) if v.mode == Mode::ConstFn => {
664                     // Sadly, we can't determine whether the types are zero-sized.
665                     v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
666                 }
667                 def => {
668                     v.add_qualif(ConstQualif::NOT_CONST);
669                     if v.mode != Mode::Var {
670                         debug!("(checking const) found bad def: {:?}", def);
671                         span_err!(v.tcx.sess, e.span, E0014,
672                                   "paths in {}s may only refer to constants \
673                                    or functions", v.msg());
674                     }
675                 }
676             }
677         }
678         hir::ExprCall(ref callee, _) => {
679             let mut callee = &**callee;
680             loop {
681                 callee = match callee.node {
682                     hir::ExprBlock(ref block) => match block.expr {
683                         Some(ref tail) => &**tail,
684                         None => break
685                     },
686                     _ => break
687                 };
688             }
689             let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
690             let is_const = match def {
691                 Some(def::DefStruct(..)) => true,
692                 Some(def::DefVariant(..)) => {
693                     // Count the discriminator.
694                     v.add_qualif(ConstQualif::NON_ZERO_SIZED);
695                     true
696                 }
697                 Some(def::DefFn(did, _)) => {
698                     v.handle_const_fn_call(e, did, node_ty)
699                 }
700                 Some(def::DefMethod(did)) => {
701                     match v.tcx.impl_or_trait_item(did).container() {
702                         ty::ImplContainer(_) => {
703                             v.handle_const_fn_call(e, did, node_ty)
704                         }
705                         ty::TraitContainer(_) => false
706                     }
707                 }
708                 _ => false
709             };
710             if !is_const {
711                 v.add_qualif(ConstQualif::NOT_CONST);
712                 if v.mode != Mode::Var {
713                     fn span_limited_call_error(tcx: &ty::ctxt, span: Span, s: &str) {
714                         span_err!(tcx.sess, span, E0015, "{}", s);
715                     }
716
717                     // FIXME(#24111) Remove this check when const fn stabilizes
718                     if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
719                         span_limited_call_error(&v.tcx, e.span,
720                                                 &format!("function calls in {}s are limited to \
721                                                           struct and enum constructors",
722                                                          v.msg()));
723                         v.tcx.sess.span_note(e.span,
724                                              "a limited form of compile-time function \
725                                               evaluation is available on a nightly \
726                                               compiler via `const fn`");
727                     } else {
728                         span_limited_call_error(&v.tcx, e.span,
729                                                 &format!("function calls in {}s are limited \
730                                                           to constant functions, \
731                                                           struct and enum constructors",
732                                                          v.msg()));
733                     }
734                 }
735             }
736         }
737         hir::ExprMethodCall(..) => {
738             let method = v.tcx.tables.borrow().method_map[&method_call];
739             let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
740                 ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
741                 ty::TraitContainer(_) => false
742             };
743             if !is_const {
744                 v.add_qualif(ConstQualif::NOT_CONST);
745                 if v.mode != Mode::Var {
746                     span_err!(v.tcx.sess, e.span, E0378,
747                               "method calls in {}s are limited to \
748                                constant inherent methods", v.msg());
749                 }
750             }
751         }
752         hir::ExprStruct(..) => {
753             let did = v.tcx.def_map.borrow().get(&e.id).map(|def| def.def_id());
754             if did == v.tcx.lang_items.unsafe_cell_type() {
755                 v.add_qualif(ConstQualif::MUTABLE_MEM);
756             }
757         }
758
759         hir::ExprLit(_) |
760         hir::ExprAddrOf(..) => {
761             v.add_qualif(ConstQualif::NON_ZERO_SIZED);
762         }
763
764         hir::ExprRepeat(..) => {
765             v.add_qualif(ConstQualif::PREFER_IN_PLACE);
766         }
767
768         hir::ExprClosure(..) => {
769             // Paths in constant contexts cannot refer to local variables,
770             // as there are none, and thus closures can't have upvars there.
771             if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
772                 assert!(v.mode == Mode::Var,
773                         "global closures can't capture anything");
774                 v.add_qualif(ConstQualif::NOT_CONST);
775             }
776         }
777
778         hir::ExprBlock(_) |
779         hir::ExprIndex(..) |
780         hir::ExprField(..) |
781         hir::ExprTupField(..) |
782         hir::ExprVec(_) |
783         hir::ExprTup(..) => {}
784
785         // Conditional control flow (possible to implement).
786         hir::ExprMatch(..) |
787         hir::ExprIf(..) |
788
789         // Loops (not very meaningful in constants).
790         hir::ExprWhile(..) |
791         hir::ExprLoop(..) |
792
793         // More control flow (also not very meaningful).
794         hir::ExprBreak(_) |
795         hir::ExprAgain(_) |
796         hir::ExprRet(_) |
797
798         // Miscellaneous expressions that could be implemented.
799         hir::ExprRange(..) |
800
801         // Expressions with side-effects.
802         hir::ExprAssign(..) |
803         hir::ExprAssignOp(..) |
804         hir::ExprInlineAsm(_) => {
805             v.add_qualif(ConstQualif::NOT_CONST);
806             if v.mode != Mode::Var {
807                 span_err!(v.tcx.sess, e.span, E0019,
808                           "{} contains unimplemented expression type", v.msg());
809             }
810         }
811     }
812 }
813
814 /// Check the adjustments of an expression
815 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
816     match v.tcx.tables.borrow().adjustments.get(&e.id) {
817         None |
818         Some(&ty::adjustment::AdjustReifyFnPointer) |
819         Some(&ty::adjustment::AdjustUnsafeFnPointer) => {}
820
821         Some(&ty::adjustment::AdjustDerefRef(
822             ty::adjustment::AutoDerefRef { autoderefs, .. }
823         )) => {
824             if (0..autoderefs as u32).any(|autoderef| {
825                     v.tcx.is_overloaded_autoderef(e.id, autoderef)
826             }) {
827                 v.add_qualif(ConstQualif::NOT_CONST);
828                 if v.mode != Mode::Var {
829                     span_err!(v.tcx.sess, e.span, E0400,
830                               "user-defined dereference operators are not allowed in {}s",
831                               v.msg());
832                 }
833             }
834         }
835     }
836 }
837
838 pub fn check_crate(tcx: &ty::ctxt) {
839     visit::walk_crate(&mut CheckCrateVisitor {
840         tcx: tcx,
841         mode: Mode::Var,
842         qualif: ConstQualif::NOT_CONST,
843         rvalue_borrows: NodeMap()
844     }, tcx.map.krate());
845
846     tcx.sess.abort_if_errors();
847 }
848
849 impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
850     fn consume(&mut self,
851                _consume_id: ast::NodeId,
852                consume_span: Span,
853                cmt: mc::cmt,
854                _mode: euv::ConsumeMode) {
855         let mut cur = &cmt;
856         loop {
857             match cur.cat {
858                 mc::cat_static_item => {
859                     if self.mode != Mode::Var {
860                         // statics cannot be consumed by value at any time, that would imply
861                         // that they're an initializer (what a const is for) or kept in sync
862                         // over time (not feasible), so deny it outright.
863                         span_err!(self.tcx.sess, consume_span, E0394,
864                                   "cannot refer to other statics by value, use the \
865                                    address-of operator or a constant instead");
866                     }
867                     break;
868                 }
869                 mc::cat_deref(ref cmt, _, _) |
870                 mc::cat_downcast(ref cmt, _) |
871                 mc::cat_interior(ref cmt, _) => cur = cmt,
872
873                 mc::cat_rvalue(..) |
874                 mc::cat_upvar(..) |
875                 mc::cat_local(..) => break
876             }
877         }
878     }
879     fn borrow(&mut self,
880               borrow_id: ast::NodeId,
881               borrow_span: Span,
882               cmt: mc::cmt<'tcx>,
883               _loan_region: ty::Region,
884               bk: ty::BorrowKind,
885               loan_cause: euv::LoanCause)
886     {
887         // Kind of hacky, but we allow Unsafe coercions in constants.
888         // These occur when we convert a &T or *T to a *U, as well as
889         // when making a thin pointer (e.g., `*T`) into a fat pointer
890         // (e.g., `*Trait`).
891         match loan_cause {
892             euv::LoanCause::AutoUnsafe => {
893                 return;
894             }
895             _ => { }
896         }
897
898         let mut cur = &cmt;
899         let mut is_interior = false;
900         loop {
901             match cur.cat {
902                 mc::cat_rvalue(..) => {
903                     if loan_cause == euv::MatchDiscriminant {
904                         // Ignore the dummy immutable borrow created by EUV.
905                         break;
906                     }
907                     let mutbl = bk.to_mutbl_lossy();
908                     if mutbl == hir::MutMutable && self.mode == Mode::StaticMut {
909                         // Mutable slices are the only `&mut` allowed in
910                         // globals, but only in `static mut`, nowhere else.
911                         // FIXME: This exception is really weird... there isn't
912                         // any fundamental reason to restrict this based on
913                         // type of the expression.  `&mut [1]` has exactly the
914                         // same representation as &mut 1.
915                         match cmt.ty.sty {
916                             ty::TyArray(_, _) | ty::TySlice(_) => break,
917                             _ => {}
918                         }
919                     }
920                     self.record_borrow(borrow_id, mutbl);
921                     break;
922                 }
923                 mc::cat_static_item => {
924                     if is_interior && self.mode != Mode::Var {
925                         // Borrowed statics can specifically *only* have their address taken,
926                         // not any number of other borrows such as borrowing fields, reading
927                         // elements of an array, etc.
928                         span_err!(self.tcx.sess, borrow_span, E0494,
929                                   "cannot refer to the interior of another \
930                                    static, use a constant instead");
931                     }
932                     break;
933                 }
934                 mc::cat_deref(ref cmt, _, _) |
935                 mc::cat_downcast(ref cmt, _) |
936                 mc::cat_interior(ref cmt, _) => {
937                     is_interior = true;
938                     cur = cmt;
939                 }
940
941                 mc::cat_upvar(..) |
942                 mc::cat_local(..) => break
943             }
944         }
945     }
946
947     fn decl_without_init(&mut self,
948                          _id: ast::NodeId,
949                          _span: Span) {}
950     fn mutate(&mut self,
951               _assignment_id: ast::NodeId,
952               _assignment_span: Span,
953               _assignee_cmt: mc::cmt,
954               _mode: euv::MutateMode) {}
955
956     fn matched_pat(&mut self,
957                    _: &hir::Pat,
958                    _: mc::cmt,
959                    _: euv::MatchMode) {}
960
961     fn consume_pat(&mut self,
962                    _consume_pat: &hir::Pat,
963                    _cmt: mc::cmt,
964                    _mode: euv::ConsumeMode) {}
965 }