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