]> git.proxmox.com Git - rustc.git/blame - src/librustc/middle/check_const.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / librustc / middle / check_const.rs
CommitLineData
85aaf69f 1// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
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
85aaf69f
SL
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
62682a34 16// - doesn't own a box
85aaf69f
SL
17//
18// - For each *immutable* static item, it checks that its **value**:
62682a34 19// - doesn't own a box
85aaf69f
SL
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
223e47cc 26
62682a34 27use middle::cast::{CastKind};
85aaf69f 28use middle::const_eval;
c1a9b12d 29use middle::const_eval::EvalHint::ExprTypeChecked;
85aaf69f
SL
30use middle::def;
31use middle::expr_use_visitor as euv;
32use middle::infer;
33use middle::mem_categorization as mc;
34use middle::traits;
35use middle::ty::{self, Ty};
36use util::nodemap::NodeMap;
223e47cc 37
1a4d82fc 38use syntax::ast;
85aaf69f 39use syntax::codemap::Span;
1a4d82fc 40use syntax::visit::{self, Visitor};
223e47cc 41
85aaf69f 42use std::collections::hash_map::Entry;
c1a9b12d 43use std::cmp::Ordering;
85aaf69f
SL
44
45// Const qualification, from partial to completely promotable.
46bitflags! {
47 #[derive(RustcEncodable, RustcDecodable)]
48 flags ConstQualif: u8 {
85aaf69f
SL
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.
d9579d0f 51 const MUTABLE_MEM = 1 << 0,
85aaf69f
SL
52 // Constant value with a type that implements Drop. Can be copied
53 // from static memory, similar to MUTABLE_MEM.
d9579d0f 54 const NEEDS_DROP = 1 << 1,
85aaf69f
SL
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.
d9579d0f 59 const PREFER_IN_PLACE = 1 << 2,
85aaf69f
SL
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.
d9579d0f 62 const NON_ZERO_SIZED = 1 << 3,
85aaf69f
SL
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.
d9579d0f 67 const HAS_STATIC_BORROWS = 1 << 4,
85aaf69f 68 // Invalid const for miscellaneous reasons (e.g. not implemented).
d9579d0f 69 const NOT_CONST = 1 << 5,
85aaf69f
SL
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.
d9579d0f
AL
74 const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
75 ConstQualif::NEEDS_DROP.bits |
76 ConstQualif::NOT_CONST.bits
85aaf69f
SL
77 }
78}
79
c34b1796 80#[derive(Copy, Clone, Eq, PartialEq)]
85aaf69f
SL
81enum Mode {
82 Const,
62682a34 83 ConstFn,
85aaf69f
SL
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
1a4d82fc
JJ
94struct CheckCrateVisitor<'a, 'tcx: 'a> {
95 tcx: &'a ty::ctxt<'tcx>,
85aaf69f
SL
96 mode: Mode,
97 qualif: ConstQualif,
98 rvalue_borrows: NodeMap<ast::Mutability>
223e47cc
LB
99}
100
1a4d82fc 101impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
85aaf69f
SL
102 fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R where
103 F: FnOnce(&mut CheckCrateVisitor<'a, 'tcx>) -> R,
1a4d82fc 104 {
85aaf69f
SL
105 let (old_mode, old_qualif) = (self.mode, self.qualif);
106 self.mode = mode;
d9579d0f 107 self.qualif = ConstQualif::empty();
85aaf69f
SL
108 let r = f(self);
109 self.mode = old_mode;
110 self.qualif = old_qualif;
111 r
1a4d82fc 112 }
85aaf69f
SL
113
114 fn with_euv<'b, F, R>(&'b mut self, item_id: Option<ast::NodeId>, f: F) -> R where
c1a9b12d 115 F: for<'t> FnOnce(&mut euv::ExprUseVisitor<'b, 't, 'b, 'tcx>) -> R,
1a4d82fc 116 {
85aaf69f
SL
117 let param_env = match item_id {
118 Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
c1a9b12d 119 None => self.tcx.empty_parameter_environment()
85aaf69f 120 };
c1a9b12d
SL
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))
85aaf69f
SL
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.
d9579d0f 133 entry.insert(ConstQualif::empty());
85aaf69f
SL
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
62682a34
SL
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
85aaf69f
SL
200 fn add_qualif(&mut self, qualif: ConstQualif) {
201 self.qualif = self.qualif | qualif;
202 }
203
62682a34
SL
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
c1a9b12d 238 if ret_ty.type_contents(self.tcx).interior_unsafe() {
62682a34
SL
239 self.add_qualif(ConstQualif::MUTABLE_MEM);
240 }
241
242 true
243 } else {
244 false
245 }
246 }
247
85aaf69f
SL
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",
62682a34 266 Mode::ConstFn => "constant function",
85aaf69f
SL
267 Mode::StaticMut | Mode::Static => "static",
268 Mode::Var => unreachable!(),
269 }
270 }
271
272 fn check_static_mut_type(&self, e: &ast::Expr) {
c1a9b12d
SL
273 let node_ty = self.tcx.node_id_to_type(e.id);
274 let tcontents = node_ty.type_contents(self.tcx);
85aaf69f
SL
275
276 let suffix = if tcontents.has_dtor() {
277 "destructors"
278 } else if tcontents.owns_owned() {
62682a34 279 "boxes"
85aaf69f
SL
280 } else {
281 return
282 };
283
62682a34
SL
284 span_err!(self.tcx.sess, e.span, E0397,
285 "mutable statics are not allowed to have {}", suffix);
85aaf69f
SL
286 }
287
288 fn check_static_type(&self, e: &ast::Expr) {
c1a9b12d
SL
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);
85aaf69f 291 let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
c1a9b12d 292 let mut fulfill_cx = infcx.fulfillment_cx.borrow_mut();
85aaf69f 293 fulfill_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
c1a9b12d 294 match fulfill_cx.select_all_or_error(&infcx) {
85aaf69f
SL
295 Ok(()) => { },
296 Err(ref errors) => {
297 traits::report_fulfillment_errors(&infcx, errors);
298 }
299 }
223e47cc
LB
300 }
301}
302
1a4d82fc
JJ
303impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
304 fn visit_item(&mut self, i: &ast::Item) {
62682a34 305 debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
1a4d82fc 306 match i.node {
85aaf69f
SL
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);
1a4d82fc
JJ
317 }
318 ast::ItemEnum(ref enum_definition, _) => {
85aaf69f
SL
319 for var in &enum_definition.variants {
320 if let Some(ref ex) = var.node.disr_expr {
321 self.global_expr(Mode::Const, &**ex);
1a4d82fc 322 }
85aaf69f
SL
323 }
324 }
325 _ => {
326 self.with_mode(Mode::Var, |v| visit::walk_item(v, i));
1a4d82fc 327 }
223e47cc
LB
328 }
329 }
85aaf69f 330
d9579d0f
AL
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
85aaf69f
SL
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) {
62682a34 359 self.fn_like(fk, fd, b, s, fn_id);
85aaf69f
SL
360 }
361
1a4d82fc 362 fn visit_pat(&mut self, p: &ast::Pat) {
85aaf69f
SL
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);
c1a9b12d
SL
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 }
85aaf69f
SL
383 }
384 _ => visit::walk_pat(self, p)
385 }
223e47cc 386 }
85aaf69f 387
62682a34
SL
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
1a4d82fc 417 fn visit_expr(&mut self, ex: &ast::Expr) {
85aaf69f 418 let mut outer = self.qualif;
d9579d0f 419 self.qualif = ConstQualif::empty();
85aaf69f 420
c1a9b12d 421 let node_ty = self.tcx.node_id_to_type(ex.id);
85aaf69f 422 check_expr(self, ex, node_ty);
c1a9b12d 423 check_adjustments(self, ex);
85aaf69f
SL
424
425 // Special-case some expressions to avoid certain flags bubbling up.
426 match ex.node {
427 ast::ExprCall(ref callee, ref args) => {
62682a34 428 for arg in args {
85aaf69f
SL
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;
d9579d0f 436 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
85aaf69f
SL
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 {
62682a34 442 ty::TyArray(_, n) => n,
85aaf69f
SL
443 _ => unreachable!()
444 };
445 // [element; 0] is always zero-sized.
446 if count == 0 {
d9579d0f 447 self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
85aaf69f
SL
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;
62682a34 454 for pat in arms.iter().flat_map(|arm| &arm.pats) {
85aaf69f
SL
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 {
62682a34 473 ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
d9579d0f 474 if !self.qualif.intersects(ConstQualif::NOT_CONST) {
c1a9b12d
SL
475 match const_eval::eval_const_expr_partial(
476 self.tcx, ex, ExprTypeChecked) {
85aaf69f
SL
477 Ok(_) => {}
478 Err(msg) => {
c34b1796
AL
479 span_err!(self.tcx.sess, msg.span, E0020,
480 "{} in a constant expression",
481 msg.description())
85aaf69f
SL
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).
d9579d0f 498 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
85aaf69f 499 // propagated from another error, so erroring again would be just noise.
c1a9b12d 500 let tc = node_ty.type_contents(self.tcx);
d9579d0f
AL
501 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
502 outer = outer | ConstQualif::NOT_CONST;
85aaf69f
SL
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.
d9579d0f
AL
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);
85aaf69f
SL
514 }
515 }
516 Some(ast::MutMutable) => {
517 // `&mut expr` means expr could be mutated, unless it's zero-sized.
d9579d0f 518 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
85aaf69f 519 if self.mode == Mode::Var {
d9579d0f
AL
520 outer = outer | ConstQualif::NOT_CONST;
521 self.add_qualif(ConstQualif::MUTABLE_MEM);
85aaf69f
SL
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 }
d9579d0f
AL
528 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
529 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
85aaf69f
SL
530 }
531 }
532 None => {}
1a4d82fc 533 }
85aaf69f
SL
534 self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
535 // Don't propagate certain flags.
d9579d0f 536 self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
1a4d82fc
JJ
537 }
538}
539
85aaf69f
SL
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.
546fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
547 e: &ast::Expr, node_ty: Ty<'tcx>) {
548 match node_ty.sty {
62682a34 549 ty::TyStruct(did, _) |
c1a9b12d 550 ty::TyEnum(did, _) if v.tcx.has_dtor(did) => {
d9579d0f 551 v.add_qualif(ConstQualif::NEEDS_DROP);
85aaf69f
SL
552 if v.mode != Mode::Var {
553 v.tcx.sess.span_err(e.span,
554 &format!("{}s are not allowed to have destructors",
c34b1796 555 v.msg()));
85aaf69f
SL
556 }
557 }
558 _ => {}
559 }
223e47cc 560
85aaf69f 561 let method_call = ty::MethodCall::expr(e.id);
1a4d82fc 562 match e.node {
85aaf69f
SL
563 ast::ExprUnary(..) |
564 ast::ExprBinary(..) |
c1a9b12d 565 ast::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
d9579d0f 566 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f
SL
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(..) |
1a4d82fc 573 ast::ExprUnary(ast::UnUniq, _) => {
d9579d0f 574 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f
SL
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 }
1a4d82fc 579 }
62682a34 580 ast::ExprUnary(op, ref inner) => {
c1a9b12d 581 match v.tcx.node_id_to_type(inner.id).sty {
62682a34
SL
582 ty::TyRawPtr(_) => {
583 assert!(op == ast::UnDeref);
584
d9579d0f 585 v.add_qualif(ConstQualif::NOT_CONST);
62682a34
SL
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 }
85aaf69f
SL
590 }
591 _ => {}
223e47cc 592 }
1a4d82fc 593 }
62682a34 594 ast::ExprBinary(op, ref lhs, _) => {
c1a9b12d 595 match v.tcx.node_id_to_type(lhs.id).sty {
62682a34
SL
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 }
85aaf69f 606 }
62682a34 607 _ => {}
223e47cc 608 }
62682a34
SL
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 }
85aaf69f 620 }
62682a34 621 _ => {}
223e47cc 622 }
1a4d82fc 623 }
c34b1796
AL
624 ast::ExprPath(..) => {
625 let def = v.tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
85aaf69f
SL
626 match def {
627 Some(def::DefVariant(_, _, _)) => {
628 // Count the discriminator or function pointer.
d9579d0f 629 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
85aaf69f
SL
630 }
631 Some(def::DefStruct(_)) => {
62682a34 632 if let ty::TyBareFn(..) = node_ty.sty {
85aaf69f 633 // Count the function pointer.
d9579d0f 634 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
85aaf69f
SL
635 }
636 }
c34b1796 637 Some(def::DefFn(..)) | Some(def::DefMethod(..)) => {
85aaf69f 638 // Count the function pointer.
d9579d0f 639 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
85aaf69f
SL
640 }
641 Some(def::DefStatic(..)) => {
642 match v.mode {
643 Mode::Static | Mode::StaticMut => {}
62682a34 644 Mode::Const | Mode::ConstFn => {
85aaf69f 645 span_err!(v.tcx.sess, e.span, E0013,
62682a34
SL
646 "{}s cannot refer to other statics, insert \
647 an intermediate constant instead", v.msg());
85aaf69f 648 }
d9579d0f 649 Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
85aaf69f
SL
650 }
651 }
d9579d0f 652 Some(def::DefConst(did)) |
c1a9b12d 653 Some(def::DefAssociatedConst(did)) => {
d9579d0f
AL
654 if let Some(expr) = const_eval::lookup_const_by_id(v.tcx, did,
655 Some(e.id)) {
85aaf69f
SL
656 let inner = v.global_expr(Mode::Const, expr);
657 v.add_qualif(inner);
658 } else {
d9579d0f
AL
659 v.tcx.sess.span_bug(e.span,
660 "DefConst or DefAssociatedConst \
661 doesn't point to a constant");
85aaf69f
SL
662 }
663 }
62682a34
SL
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 }
1a4d82fc 668 def => {
d9579d0f 669 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f
SL
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 }
1a4d82fc 676 }
223e47cc 677 }
1a4d82fc
JJ
678 }
679 ast::ExprCall(ref callee, _) => {
85aaf69f
SL
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 }
c34b1796 691 let def = v.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
62682a34
SL
692 let is_const = match def {
693 Some(def::DefStruct(..)) => true,
85aaf69f
SL
694 Some(def::DefVariant(..)) => {
695 // Count the discriminator.
d9579d0f 696 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
62682a34 697 true
85aaf69f 698 }
62682a34
SL
699 Some(def::DefFn(did, _)) => {
700 v.handle_const_fn_call(e, did, node_ty)
223e47cc 701 }
c1a9b12d
SL
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 }
62682a34
SL
710 _ => false
711 };
712 if !is_const {
d9579d0f 713 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f 714 if v.mode != Mode::Var {
62682a34
SL
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());
85aaf69f 719 }
62682a34
SL
720 }
721 }
722 ast::ExprMethodCall(..) => {
c1a9b12d
SL
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
62682a34
SL
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());
1a4d82fc 734 }
223e47cc
LB
735 }
736 }
85aaf69f
SL
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() {
d9579d0f 740 v.add_qualif(ConstQualif::MUTABLE_MEM);
85aaf69f
SL
741 }
742 }
743
744 ast::ExprLit(_) |
745 ast::ExprAddrOf(..) => {
d9579d0f 746 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
85aaf69f
SL
747 }
748
749 ast::ExprRepeat(..) => {
d9579d0f 750 v.add_qualif(ConstQualif::PREFER_IN_PLACE);
85aaf69f
SL
751 }
752
753 ast::ExprClosure(..) => {
62682a34 754 // Paths in constant contexts cannot refer to local variables,
85aaf69f 755 // as there are none, and thus closures can't have upvars there.
c1a9b12d 756 if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
85aaf69f
SL
757 assert!(v.mode == Mode::Var,
758 "global closures can't capture anything");
d9579d0f 759 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f
SL
760 }
761 }
762
62682a34 763 ast::ExprBlock(_) |
85aaf69f 764 ast::ExprIndex(..) |
1a4d82fc
JJ
765 ast::ExprField(..) |
766 ast::ExprTupField(..) |
85aaf69f
SL
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(..) |
223e47cc 781
85aaf69f
SL
782 // More control flow (also not very meaningful).
783 ast::ExprBreak(_) |
784 ast::ExprAgain(_) |
785 ast::ExprRet(_) |
223e47cc 786
85aaf69f
SL
787 // Miscellaneous expressions that could be implemented.
788 ast::ExprRange(..) |
789
62682a34 790 // Expressions with side-effects.
85aaf69f
SL
791 ast::ExprAssign(..) |
792 ast::ExprAssignOp(..) |
793 ast::ExprInlineAsm(_) |
794 ast::ExprMac(_) => {
d9579d0f 795 v.add_qualif(ConstQualif::NOT_CONST);
85aaf69f
SL
796 if v.mode != Mode::Var {
797 span_err!(v.tcx.sess, e.span, E0019,
798 "{} contains unimplemented expression type", v.msg());
1a4d82fc 799 }
223e47cc 800 }
85aaf69f
SL
801 }
802}
803
c1a9b12d
SL
804/// Check the adjustments of an expression
805fn 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
85aaf69f
SL
823pub fn check_crate(tcx: &ty::ctxt) {
824 visit::walk_crate(&mut CheckCrateVisitor {
825 tcx: tcx,
826 mode: Mode::Var,
d9579d0f 827 qualif: ConstQualif::NOT_CONST,
85aaf69f
SL
828 rvalue_borrows: NodeMap()
829 }, tcx.map.krate());
223e47cc 830
85aaf69f
SL
831 tcx.sess.abort_if_errors();
832}
833
834impl<'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.
62682a34
SL
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");
85aaf69f
SL
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,
9346a6ac
AL
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
85aaf69f
SL
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 {
62682a34
SL
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.
85aaf69f 900 match cmt.ty.sty {
62682a34 901 ty::TyArray(_, _) | ty::TySlice(_) => break,
85aaf69f
SL
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 }
223e47cc 930 }
85aaf69f
SL
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) {}
223e47cc 950}