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