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