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