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