]> git.proxmox.com Git - rustc.git/blob - src/librustc_passes/consts.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc_passes / consts.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 rustc::dep_graph::DepNode;
28 use rustc::ty::cast::CastKind;
29 use rustc_const_eval::{ConstEvalErr, lookup_const_fn_by_id, compare_lit_exprs};
30 use rustc_const_eval::{eval_const_expr_partial, lookup_const_by_id};
31 use rustc_const_eval::ErrKind::{IndexOpFeatureGated, UnimplementedConstVal, MiscCatchAll, Math};
32 use rustc_const_eval::ErrKind::{ErroneousReferencedConstant, MiscBinaryOp, NonConstPath};
33 use rustc_const_eval::ErrKind::UnresolvedPath;
34 use rustc_const_eval::EvalHint::ExprTypeChecked;
35 use rustc_const_math::{ConstMathErr, Op};
36 use rustc::hir::def::Def;
37 use rustc::hir::def_id::DefId;
38 use rustc::middle::expr_use_visitor as euv;
39 use rustc::middle::mem_categorization as mc;
40 use rustc::middle::mem_categorization::Categorization;
41 use rustc::ty::{self, Ty, TyCtxt};
42 use rustc::traits::Reveal;
43 use rustc::util::common::ErrorReported;
44 use rustc::util::nodemap::NodeMap;
45 use rustc::middle::const_qualif::ConstQualif;
46 use rustc::lint::builtin::CONST_ERR;
47
48 use rustc::hir::{self, PatKind};
49 use syntax::ast;
50 use syntax_pos::Span;
51 use rustc::hir::intravisit::{self, FnKind, Visitor};
52
53 use std::collections::hash_map::Entry;
54 use std::cmp::Ordering;
55
56 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
57 enum Mode {
58 Const,
59 ConstFn,
60 Static,
61 StaticMut,
62
63 // An expression that occurs outside of any constant context
64 // (i.e. `const`, `static`, array lengths, etc.). The value
65 // can be variable at runtime, but will be promotable to
66 // static memory if we can prove it is actually constant.
67 Var,
68 }
69
70 struct CheckCrateVisitor<'a, 'tcx: 'a> {
71 tcx: TyCtxt<'a, 'tcx, 'tcx>,
72 mode: Mode,
73 qualif: ConstQualif,
74 rvalue_borrows: NodeMap<hir::Mutability>,
75 }
76
77 impl<'a, 'gcx> CheckCrateVisitor<'a, 'gcx> {
78 fn with_mode<F, R>(&mut self, mode: Mode, f: F) -> R
79 where F: FnOnce(&mut CheckCrateVisitor<'a, 'gcx>) -> R
80 {
81 let (old_mode, old_qualif) = (self.mode, self.qualif);
82 self.mode = mode;
83 self.qualif = ConstQualif::empty();
84 let r = f(self);
85 self.mode = old_mode;
86 self.qualif = old_qualif;
87 r
88 }
89
90 fn with_euv<F, R>(&mut self, item_id: Option<ast::NodeId>, f: F) -> R
91 where F: for<'b, 'tcx> FnOnce(&mut euv::ExprUseVisitor<'b, 'gcx, 'tcx>) -> R
92 {
93 let param_env = match item_id {
94 Some(item_id) => ty::ParameterEnvironment::for_item(self.tcx, item_id),
95 None => self.tcx.empty_parameter_environment(),
96 };
97
98 self.tcx
99 .infer_ctxt(None, Some(param_env), Reveal::NotSpecializable)
100 .enter(|infcx| f(&mut euv::ExprUseVisitor::new(self, &infcx)))
101 }
102
103 fn global_expr(&mut self, mode: Mode, expr: &hir::Expr) -> ConstQualif {
104 assert!(mode != Mode::Var);
105 match self.tcx.const_qualif_map.borrow_mut().entry(expr.id) {
106 Entry::Occupied(entry) => return *entry.get(),
107 Entry::Vacant(entry) => {
108 // Prevent infinite recursion on re-entry.
109 entry.insert(ConstQualif::empty());
110 }
111 }
112 if let Err(err) = eval_const_expr_partial(self.tcx, expr, ExprTypeChecked, None) {
113 match err.kind {
114 UnimplementedConstVal(_) => {}
115 IndexOpFeatureGated => {}
116 ErroneousReferencedConstant(_) => {}
117 _ => {
118 self.tcx.sess.add_lint(CONST_ERR,
119 expr.id,
120 expr.span,
121 format!("constant evaluation error: {}. This will \
122 become a HARD ERROR in the future",
123 err.description().into_oneline()))
124 }
125 }
126 }
127 self.with_mode(mode, |this| {
128 this.with_euv(None, |euv| euv.consume_expr(expr));
129 this.visit_expr(expr);
130 this.qualif
131 })
132 }
133
134 fn fn_like(&mut self,
135 fk: FnKind,
136 fd: &hir::FnDecl,
137 b: &hir::Block,
138 s: Span,
139 fn_id: ast::NodeId)
140 -> ConstQualif {
141 match self.tcx.const_qualif_map.borrow_mut().entry(fn_id) {
142 Entry::Occupied(entry) => return *entry.get(),
143 Entry::Vacant(entry) => {
144 // Prevent infinite recursion on re-entry.
145 entry.insert(ConstQualif::empty());
146 }
147 }
148
149 let mode = match fk {
150 FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _, _) => Mode::ConstFn,
151 FnKind::Method(_, m, _, _) => {
152 if m.constness == hir::Constness::Const {
153 Mode::ConstFn
154 } else {
155 Mode::Var
156 }
157 }
158 _ => Mode::Var,
159 };
160
161 let qualif = self.with_mode(mode, |this| {
162 this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
163 intravisit::walk_fn(this, fk, fd, b, s, fn_id);
164 this.qualif
165 });
166
167 // Keep only bits that aren't affected by function body (NON_ZERO_SIZED),
168 // and bits that don't change semantics, just optimizations (PREFER_IN_PLACE).
169 let qualif = qualif & (ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
170
171 self.tcx.const_qualif_map.borrow_mut().insert(fn_id, qualif);
172 qualif
173 }
174
175 fn add_qualif(&mut self, qualif: ConstQualif) {
176 self.qualif = self.qualif | qualif;
177 }
178
179 /// Returns true if the call is to a const fn or method.
180 fn handle_const_fn_call(&mut self, _expr: &hir::Expr, def_id: DefId, ret_ty: Ty<'gcx>) -> bool {
181 if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
182 let qualif = self.fn_like(fn_like.kind(),
183 fn_like.decl(),
184 fn_like.body(),
185 fn_like.span(),
186 fn_like.id());
187 self.add_qualif(qualif);
188
189 if ret_ty.type_contents(self.tcx).interior_unsafe() {
190 self.add_qualif(ConstQualif::MUTABLE_MEM);
191 }
192
193 true
194 } else {
195 false
196 }
197 }
198
199 fn record_borrow(&mut self, id: ast::NodeId, mutbl: hir::Mutability) {
200 match self.rvalue_borrows.entry(id) {
201 Entry::Occupied(mut entry) => {
202 // Merge the two borrows, taking the most demanding
203 // one, mutability-wise.
204 if mutbl == hir::MutMutable {
205 entry.insert(mutbl);
206 }
207 }
208 Entry::Vacant(entry) => {
209 entry.insert(mutbl);
210 }
211 }
212 }
213 }
214
215 impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
216 fn visit_item(&mut self, i: &hir::Item) {
217 debug!("visit_item(item={})", self.tcx.map.node_to_string(i.id));
218 assert_eq!(self.mode, Mode::Var);
219 match i.node {
220 hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
221 self.global_expr(Mode::Static, &expr);
222 }
223 hir::ItemStatic(_, hir::MutMutable, ref expr) => {
224 self.global_expr(Mode::StaticMut, &expr);
225 }
226 hir::ItemConst(_, ref expr) => {
227 self.global_expr(Mode::Const, &expr);
228 }
229 hir::ItemEnum(ref enum_definition, _) => {
230 for var in &enum_definition.variants {
231 if let Some(ref ex) = var.node.disr_expr {
232 self.global_expr(Mode::Const, &ex);
233 }
234 }
235 }
236 _ => {
237 intravisit::walk_item(self, i);
238 }
239 }
240 }
241
242 fn visit_trait_item(&mut self, t: &'v hir::TraitItem) {
243 match t.node {
244 hir::ConstTraitItem(_, ref default) => {
245 if let Some(ref expr) = *default {
246 self.global_expr(Mode::Const, &expr);
247 } else {
248 intravisit::walk_trait_item(self, t);
249 }
250 }
251 _ => self.with_mode(Mode::Var, |v| intravisit::walk_trait_item(v, t)),
252 }
253 }
254
255 fn visit_impl_item(&mut self, i: &'v hir::ImplItem) {
256 match i.node {
257 hir::ImplItemKind::Const(_, ref expr) => {
258 self.global_expr(Mode::Const, &expr);
259 }
260 _ => self.with_mode(Mode::Var, |v| intravisit::walk_impl_item(v, i)),
261 }
262 }
263
264 fn visit_fn(&mut self,
265 fk: FnKind<'v>,
266 fd: &'v hir::FnDecl,
267 b: &'v hir::Block,
268 s: Span,
269 fn_id: ast::NodeId) {
270 self.fn_like(fk, fd, b, s, fn_id);
271 }
272
273 fn visit_pat(&mut self, p: &hir::Pat) {
274 match p.node {
275 PatKind::Lit(ref lit) => {
276 self.global_expr(Mode::Const, &lit);
277 }
278 PatKind::Range(ref start, ref end) => {
279 self.global_expr(Mode::Const, &start);
280 self.global_expr(Mode::Const, &end);
281
282 match compare_lit_exprs(self.tcx, p.span, start, end) {
283 Ok(Ordering::Less) |
284 Ok(Ordering::Equal) => {}
285 Ok(Ordering::Greater) => {
286 span_err!(self.tcx.sess,
287 start.span,
288 E0030,
289 "lower range bound must be less than or equal to upper");
290 }
291 Err(ErrorReported) => {}
292 }
293 }
294 _ => intravisit::walk_pat(self, p),
295 }
296 }
297
298 fn visit_block(&mut self, block: &hir::Block) {
299 // Check all statements in the block
300 for stmt in &block.stmts {
301 match stmt.node {
302 hir::StmtDecl(ref decl, _) => {
303 match decl.node {
304 hir::DeclLocal(_) => {}
305 // Item statements are allowed
306 hir::DeclItem(_) => continue,
307 }
308 }
309 hir::StmtExpr(_, _) => {}
310 hir::StmtSemi(_, _) => {}
311 }
312 self.add_qualif(ConstQualif::NOT_CONST);
313 }
314 intravisit::walk_block(self, block);
315 }
316
317 fn visit_expr(&mut self, ex: &hir::Expr) {
318 let mut outer = self.qualif;
319 self.qualif = ConstQualif::empty();
320
321 let node_ty = self.tcx.node_id_to_type(ex.id);
322 check_expr(self, ex, node_ty);
323 check_adjustments(self, ex);
324
325 // Special-case some expressions to avoid certain flags bubbling up.
326 match ex.node {
327 hir::ExprCall(ref callee, ref args) => {
328 for arg in args {
329 self.visit_expr(&arg)
330 }
331
332 let inner = self.qualif;
333 self.visit_expr(&callee);
334 // The callee's size doesn't count in the call.
335 let added = self.qualif - inner;
336 self.qualif = inner | (added - ConstQualif::NON_ZERO_SIZED);
337 }
338 hir::ExprRepeat(ref element, _) => {
339 self.visit_expr(&element);
340 // The count is checked elsewhere (typeck).
341 let count = match node_ty.sty {
342 ty::TyArray(_, n) => n,
343 _ => bug!(),
344 };
345 // [element; 0] is always zero-sized.
346 if count == 0 {
347 self.qualif.remove(ConstQualif::NON_ZERO_SIZED | ConstQualif::PREFER_IN_PLACE);
348 }
349 }
350 hir::ExprMatch(ref discr, ref arms, _) => {
351 // Compute the most demanding borrow from all the arms'
352 // patterns and set that on the discriminator.
353 let mut borrow = None;
354 for pat in arms.iter().flat_map(|arm| &arm.pats) {
355 let pat_borrow = self.rvalue_borrows.remove(&pat.id);
356 match (borrow, pat_borrow) {
357 (None, _) |
358 (_, Some(hir::MutMutable)) => {
359 borrow = pat_borrow;
360 }
361 _ => {}
362 }
363 }
364 if let Some(mutbl) = borrow {
365 self.record_borrow(discr.id, mutbl);
366 }
367 intravisit::walk_expr(self, ex);
368 }
369 _ => intravisit::walk_expr(self, ex),
370 }
371
372 // Handle borrows on (or inside the autorefs of) this expression.
373 match self.rvalue_borrows.remove(&ex.id) {
374 Some(hir::MutImmutable) => {
375 // Constants cannot be borrowed if they contain interior mutability as
376 // it means that our "silent insertion of statics" could change
377 // initializer values (very bad).
378 // If the type doesn't have interior mutability, then `ConstQualif::MUTABLE_MEM` has
379 // propagated from another error, so erroring again would be just noise.
380 let tc = node_ty.type_contents(self.tcx);
381 if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
382 outer = outer | ConstQualif::NOT_CONST;
383 }
384 // If the reference has to be 'static, avoid in-place initialization
385 // as that will end up pointing to the stack instead.
386 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
387 self.qualif = self.qualif - ConstQualif::PREFER_IN_PLACE;
388 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
389 }
390 }
391 Some(hir::MutMutable) => {
392 // `&mut expr` means expr could be mutated, unless it's zero-sized.
393 if self.qualif.intersects(ConstQualif::NON_ZERO_SIZED) {
394 if self.mode == Mode::Var {
395 outer = outer | ConstQualif::NOT_CONST;
396 self.add_qualif(ConstQualif::MUTABLE_MEM);
397 }
398 }
399 if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
400 self.add_qualif(ConstQualif::HAS_STATIC_BORROWS);
401 }
402 }
403 None => {}
404 }
405
406 if self.mode == Mode::Var && !self.qualif.intersects(ConstQualif::NOT_CONST) {
407 match eval_const_expr_partial(self.tcx, ex, ExprTypeChecked, None) {
408 Ok(_) => {}
409 Err(ConstEvalErr { kind: UnimplementedConstVal(_), .. }) |
410 Err(ConstEvalErr { kind: MiscCatchAll, .. }) |
411 Err(ConstEvalErr { kind: MiscBinaryOp, .. }) |
412 Err(ConstEvalErr { kind: NonConstPath, .. }) |
413 Err(ConstEvalErr { kind: UnresolvedPath, .. }) |
414 Err(ConstEvalErr { kind: ErroneousReferencedConstant(_), .. }) |
415 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shr)), .. }) |
416 Err(ConstEvalErr { kind: Math(ConstMathErr::Overflow(Op::Shl)), .. }) |
417 Err(ConstEvalErr { kind: IndexOpFeatureGated, .. }) => {}
418 Err(msg) => {
419 self.tcx.sess.add_lint(CONST_ERR,
420 ex.id,
421 msg.span,
422 msg.description().into_oneline().into_owned())
423 }
424 }
425 }
426
427 self.tcx.const_qualif_map.borrow_mut().insert(ex.id, self.qualif);
428 // Don't propagate certain flags.
429 self.qualif = outer | (self.qualif - ConstQualif::HAS_STATIC_BORROWS);
430 }
431 }
432
433 /// This function is used to enforce the constraints on
434 /// const/static items. It walks through the *value*
435 /// of the item walking down the expression and evaluating
436 /// every nested expression. If the expression is not part
437 /// of a const/static item, it is qualified for promotion
438 /// instead of producing errors.
439 fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) {
440 match node_ty.sty {
441 ty::TyStruct(def, _) |
442 ty::TyEnum(def, _) if def.has_dtor() => {
443 v.add_qualif(ConstQualif::NEEDS_DROP);
444 }
445 _ => {}
446 }
447
448 let method_call = ty::MethodCall::expr(e.id);
449 match e.node {
450 hir::ExprUnary(..) |
451 hir::ExprBinary(..) |
452 hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
453 v.add_qualif(ConstQualif::NOT_CONST);
454 }
455 hir::ExprBox(_) => {
456 v.add_qualif(ConstQualif::NOT_CONST);
457 }
458 hir::ExprUnary(op, ref inner) => {
459 match v.tcx.node_id_to_type(inner.id).sty {
460 ty::TyRawPtr(_) => {
461 assert!(op == hir::UnDeref);
462
463 v.add_qualif(ConstQualif::NOT_CONST);
464 }
465 _ => {}
466 }
467 }
468 hir::ExprBinary(op, ref lhs, _) => {
469 match v.tcx.node_id_to_type(lhs.id).sty {
470 ty::TyRawPtr(_) => {
471 assert!(op.node == hir::BiEq || op.node == hir::BiNe ||
472 op.node == hir::BiLe || op.node == hir::BiLt ||
473 op.node == hir::BiGe || op.node == hir::BiGt);
474
475 v.add_qualif(ConstQualif::NOT_CONST);
476 }
477 _ => {}
478 }
479 }
480 hir::ExprCast(ref from, _) => {
481 debug!("Checking const cast(id={})", from.id);
482 match v.tcx.cast_kinds.borrow().get(&from.id) {
483 None => span_bug!(e.span, "no kind for cast"),
484 Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
485 v.add_qualif(ConstQualif::NOT_CONST);
486 }
487 _ => {}
488 }
489 }
490 hir::ExprPath(..) => {
491 match v.tcx.expect_def(e.id) {
492 Def::Variant(..) => {
493 // Count the discriminator or function pointer.
494 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
495 }
496 Def::Struct(..) => {
497 if let ty::TyFnDef(..) = node_ty.sty {
498 // Count the function pointer.
499 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
500 }
501 }
502 Def::Fn(..) | Def::Method(..) => {
503 // Count the function pointer.
504 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
505 }
506 Def::Static(..) => {
507 match v.mode {
508 Mode::Static | Mode::StaticMut => {}
509 Mode::Const | Mode::ConstFn => {}
510 Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
511 }
512 }
513 Def::Const(did) | Def::AssociatedConst(did) => {
514 let substs = Some(v.tcx.node_id_item_substs(e.id).substs);
515 if let Some((expr, _)) = lookup_const_by_id(v.tcx, did, substs) {
516 let inner = v.global_expr(Mode::Const, expr);
517 v.add_qualif(inner);
518 }
519 }
520 Def::Local(..) if v.mode == Mode::ConstFn => {
521 // Sadly, we can't determine whether the types are zero-sized.
522 v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
523 }
524 _ => {
525 v.add_qualif(ConstQualif::NOT_CONST);
526 }
527 }
528 }
529 hir::ExprCall(ref callee, _) => {
530 let mut callee = &**callee;
531 loop {
532 callee = match callee.node {
533 hir::ExprBlock(ref block) => match block.expr {
534 Some(ref tail) => &tail,
535 None => break
536 },
537 _ => break
538 };
539 }
540 // The callee is an arbitrary expression, it doesn't necessarily have a definition.
541 let is_const = match v.tcx.expect_def_or_none(callee.id) {
542 Some(Def::Struct(..)) => true,
543 Some(Def::Variant(..)) => {
544 // Count the discriminator.
545 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
546 true
547 }
548 Some(Def::Fn(did)) => {
549 v.handle_const_fn_call(e, did, node_ty)
550 }
551 Some(Def::Method(did)) => {
552 match v.tcx.impl_or_trait_item(did).container() {
553 ty::ImplContainer(_) => {
554 v.handle_const_fn_call(e, did, node_ty)
555 }
556 ty::TraitContainer(_) => false
557 }
558 }
559 _ => false
560 };
561 if !is_const {
562 v.add_qualif(ConstQualif::NOT_CONST);
563 }
564 }
565 hir::ExprMethodCall(..) => {
566 let method = v.tcx.tables.borrow().method_map[&method_call];
567 let is_const = match v.tcx.impl_or_trait_item(method.def_id).container() {
568 ty::ImplContainer(_) => v.handle_const_fn_call(e, method.def_id, node_ty),
569 ty::TraitContainer(_) => false
570 };
571 if !is_const {
572 v.add_qualif(ConstQualif::NOT_CONST);
573 }
574 }
575 hir::ExprStruct(..) => {
576 // unsafe_cell_type doesn't necessarily exist with no_core
577 if Some(v.tcx.expect_def(e.id).def_id()) == v.tcx.lang_items.unsafe_cell_type() {
578 v.add_qualif(ConstQualif::MUTABLE_MEM);
579 }
580 }
581
582 hir::ExprLit(_) |
583 hir::ExprAddrOf(..) => {
584 v.add_qualif(ConstQualif::NON_ZERO_SIZED);
585 }
586
587 hir::ExprRepeat(..) => {
588 v.add_qualif(ConstQualif::PREFER_IN_PLACE);
589 }
590
591 hir::ExprClosure(..) => {
592 // Paths in constant contexts cannot refer to local variables,
593 // as there are none, and thus closures can't have upvars there.
594 if v.tcx.with_freevars(e.id, |fv| !fv.is_empty()) {
595 assert!(v.mode == Mode::Var,
596 "global closures can't capture anything");
597 v.add_qualif(ConstQualif::NOT_CONST);
598 }
599 }
600
601 hir::ExprBlock(_) |
602 hir::ExprIndex(..) |
603 hir::ExprField(..) |
604 hir::ExprTupField(..) |
605 hir::ExprVec(_) |
606 hir::ExprType(..) |
607 hir::ExprTup(..) => {}
608
609 // Conditional control flow (possible to implement).
610 hir::ExprMatch(..) |
611 hir::ExprIf(..) |
612
613 // Loops (not very meaningful in constants).
614 hir::ExprWhile(..) |
615 hir::ExprLoop(..) |
616
617 // More control flow (also not very meaningful).
618 hir::ExprBreak(_) |
619 hir::ExprAgain(_) |
620 hir::ExprRet(_) |
621
622 // Expressions with side-effects.
623 hir::ExprAssign(..) |
624 hir::ExprAssignOp(..) |
625 hir::ExprInlineAsm(..) => {
626 v.add_qualif(ConstQualif::NOT_CONST);
627 }
628 }
629 }
630
631 /// Check the adjustments of an expression
632 fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr) {
633 match v.tcx.tables.borrow().adjustments.get(&e.id) {
634 None |
635 Some(&ty::adjustment::AdjustNeverToAny(..)) |
636 Some(&ty::adjustment::AdjustReifyFnPointer) |
637 Some(&ty::adjustment::AdjustUnsafeFnPointer) |
638 Some(&ty::adjustment::AdjustMutToConstPointer) => {}
639
640 Some(&ty::adjustment::AdjustDerefRef(ty::adjustment::AutoDerefRef { autoderefs, .. })) => {
641 if (0..autoderefs as u32)
642 .any(|autoderef| v.tcx.is_overloaded_autoderef(e.id, autoderef)) {
643 v.add_qualif(ConstQualif::NOT_CONST);
644 }
645 }
646 }
647 }
648
649 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
650 tcx.visit_all_items_in_krate(DepNode::CheckConst,
651 &mut CheckCrateVisitor {
652 tcx: tcx,
653 mode: Mode::Var,
654 qualif: ConstQualif::NOT_CONST,
655 rvalue_borrows: NodeMap(),
656 });
657 tcx.sess.abort_if_errors();
658 }
659
660 impl<'a, 'gcx, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'gcx> {
661 fn consume(&mut self,
662 _consume_id: ast::NodeId,
663 _consume_span: Span,
664 cmt: mc::cmt,
665 _mode: euv::ConsumeMode) {
666 let mut cur = &cmt;
667 loop {
668 match cur.cat {
669 Categorization::StaticItem => {
670 break;
671 }
672 Categorization::Deref(ref cmt, _, _) |
673 Categorization::Downcast(ref cmt, _) |
674 Categorization::Interior(ref cmt, _) => cur = cmt,
675
676 Categorization::Rvalue(..) |
677 Categorization::Upvar(..) |
678 Categorization::Local(..) => break,
679 }
680 }
681 }
682 fn borrow(&mut self,
683 borrow_id: ast::NodeId,
684 _borrow_span: Span,
685 cmt: mc::cmt<'tcx>,
686 _loan_region: ty::Region,
687 bk: ty::BorrowKind,
688 loan_cause: euv::LoanCause) {
689 // Kind of hacky, but we allow Unsafe coercions in constants.
690 // These occur when we convert a &T or *T to a *U, as well as
691 // when making a thin pointer (e.g., `*T`) into a fat pointer
692 // (e.g., `*Trait`).
693 match loan_cause {
694 euv::LoanCause::AutoUnsafe => {
695 return;
696 }
697 _ => {}
698 }
699
700 let mut cur = &cmt;
701 loop {
702 match cur.cat {
703 Categorization::Rvalue(..) => {
704 if loan_cause == euv::MatchDiscriminant {
705 // Ignore the dummy immutable borrow created by EUV.
706 break;
707 }
708 let mutbl = bk.to_mutbl_lossy();
709 if mutbl == hir::MutMutable && self.mode == Mode::StaticMut {
710 // Mutable slices are the only `&mut` allowed in
711 // globals, but only in `static mut`, nowhere else.
712 // FIXME: This exception is really weird... there isn't
713 // any fundamental reason to restrict this based on
714 // type of the expression. `&mut [1]` has exactly the
715 // same representation as &mut 1.
716 match cmt.ty.sty {
717 ty::TyArray(_, _) |
718 ty::TySlice(_) => break,
719 _ => {}
720 }
721 }
722 self.record_borrow(borrow_id, mutbl);
723 break;
724 }
725 Categorization::StaticItem => {
726 break;
727 }
728 Categorization::Deref(ref cmt, _, _) |
729 Categorization::Downcast(ref cmt, _) |
730 Categorization::Interior(ref cmt, _) => {
731 cur = cmt;
732 }
733
734 Categorization::Upvar(..) |
735 Categorization::Local(..) => break,
736 }
737 }
738 }
739
740 fn decl_without_init(&mut self, _id: ast::NodeId, _span: Span) {}
741 fn mutate(&mut self,
742 _assignment_id: ast::NodeId,
743 _assignment_span: Span,
744 _assignee_cmt: mc::cmt,
745 _mode: euv::MutateMode) {
746 }
747
748 fn matched_pat(&mut self, _: &hir::Pat, _: mc::cmt, _: euv::MatchMode) {}
749
750 fn consume_pat(&mut self, _consume_pat: &hir::Pat, _cmt: mc::cmt, _mode: euv::ConsumeMode) {}
751 }