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