]> git.proxmox.com Git - rustc.git/blame - src/librustc_mir/transform/qualify_consts.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc_mir / transform / qualify_consts.rs
CommitLineData
a7813a04
XL
1// Copyright 2016 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//! A pass that qualifies constness of temporaries in constants,
12//! static initializers and functions and also drives promotion.
13//!
14//! The Qualif flags below can be used to also provide better
15//! diagnostics as to why a constant rvalue wasn't promoted.
16
17use rustc_data_structures::bitvec::BitVector;
3157f602 18use rustc_data_structures::indexed_vec::{IndexVec, Idx};
5bcae85e 19use rustc::dep_graph::DepNode;
a7813a04
XL
20use rustc::hir;
21use rustc::hir::def_id::DefId;
22use rustc::hir::intravisit::FnKind;
23use rustc::hir::map::blocks::FnLikeNode;
5bcae85e 24use rustc::traits::{self, Reveal};
a7813a04
XL
25use rustc::ty::{self, TyCtxt, Ty};
26use rustc::ty::cast::CastTy;
27use rustc::mir::repr::*;
28use rustc::mir::mir_map::MirMap;
3157f602
XL
29use rustc::mir::traversal::{self, ReversePostorder};
30use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource};
a7813a04
XL
31use rustc::mir::visit::{LvalueContext, Visitor};
32use rustc::util::nodemap::DefIdMap;
33use syntax::abi::Abi;
a7813a04 34use syntax::feature_gate::UnstableFeatures;
3157f602 35use syntax_pos::Span;
a7813a04
XL
36
37use std::collections::hash_map::Entry;
38use std::fmt;
39
40use build::Location;
a7813a04
XL
41
42use super::promote_consts::{self, Candidate, TempState};
43
44bitflags! {
45 flags Qualif: u8 {
46 // Const item's qualification while recursing.
47 // Recursive consts are an error.
48 const RECURSIVE = 1 << 0,
49
50 // Constant containing interior mutability (UnsafeCell).
51 const MUTABLE_INTERIOR = 1 << 1,
52
53 // Constant containing an ADT that implements Drop.
54 const NEEDS_DROP = 1 << 2,
55
56 // Function argument.
57 const FN_ARGUMENT = 1 << 3,
58
59 // Static lvalue or move from a static.
60 const STATIC = 1 << 4,
61
62 // Reference to a static.
63 const STATIC_REF = 1 << 5,
64
65 // Not constant at all - non-`const fn` calls, asm!,
66 // pointer comparisons, ptr-to-int casts, etc.
67 const NOT_CONST = 1 << 6,
68
69 // Refers to temporaries which cannot be promoted as
70 // promote_consts decided they weren't simple enough.
71 const NOT_PROMOTABLE = 1 << 7,
72
73 // Borrows of temporaries can be promoted only
74 // if they have none of the above qualifications.
75 const NEVER_PROMOTE = !0,
76
77 // Const items can only have MUTABLE_INTERIOR
78 // and NOT_PROMOTABLE without producing an error.
79 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
80 !Qualif::NOT_PROMOTABLE.bits
81 }
82}
83
84impl<'a, 'tcx> Qualif {
85 /// Remove flags which are impossible for the given type.
86 fn restrict(&mut self, ty: Ty<'tcx>,
87 tcx: TyCtxt<'a, 'tcx, 'tcx>,
88 param_env: &ty::ParameterEnvironment<'tcx>) {
89 if !ty.type_contents(tcx).interior_unsafe() {
90 *self = *self - Qualif::MUTABLE_INTERIOR;
91 }
92 if !tcx.type_needs_drop_given_env(ty, param_env) {
93 *self = *self - Qualif::NEEDS_DROP;
94 }
95 }
96}
97
98/// What kind of item we are in.
99#[derive(Copy, Clone, PartialEq, Eq)]
100enum Mode {
101 Const,
102 Static,
103 StaticMut,
104 ConstFn,
105 Fn
106}
107
108impl fmt::Display for Mode {
109 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110 match *self {
111 Mode::Const => write!(f, "constant"),
112 Mode::Static | Mode::StaticMut => write!(f, "static"),
113 Mode::ConstFn => write!(f, "constant function"),
114 Mode::Fn => write!(f, "function")
115 }
116 }
117}
118
119fn is_const_fn(tcx: TyCtxt, def_id: DefId) -> bool {
120 if let Some(node_id) = tcx.map.as_local_node_id(def_id) {
121 let fn_like = FnLikeNode::from_node(tcx.map.get(node_id));
122 match fn_like.map(|f| f.kind()) {
123 Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => {
124 c == hir::Constness::Const
125 }
126 Some(FnKind::Method(_, m, _, _)) => {
127 m.constness == hir::Constness::Const
128 }
129 _ => false
130 }
131 } else {
132 tcx.sess.cstore.is_const_fn(def_id)
133 }
134}
135
136struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
137 mode: Mode,
138 span: Span,
139 def_id: DefId,
140 mir: &'a Mir<'tcx>,
141 rpo: ReversePostorder<'a, 'tcx>,
142 tcx: TyCtxt<'a, 'gcx, 'tcx>,
143 param_env: ty::ParameterEnvironment<'tcx>,
144 qualif_map: &'a mut DefIdMap<Qualif>,
145 mir_map: Option<&'a MirMap<'tcx>>,
3157f602 146 temp_qualif: IndexVec<Temp, Option<Qualif>>,
a7813a04
XL
147 return_qualif: Option<Qualif>,
148 qualif: Qualif,
149 const_fn_arg_vars: BitVector,
150 location: Location,
3157f602 151 temp_promotion_state: IndexVec<Temp, TempState>,
a7813a04
XL
152 promotion_candidates: Vec<Candidate>
153}
154
155impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
156 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
157 param_env: ty::ParameterEnvironment<'tcx>,
158 qualif_map: &'a mut DefIdMap<Qualif>,
159 mir_map: Option<&'a MirMap<'tcx>>,
160 def_id: DefId,
161 mir: &'a Mir<'tcx>,
162 mode: Mode)
163 -> Qualifier<'a, 'tcx, 'tcx> {
164 let mut rpo = traversal::reverse_postorder(mir);
165 let temps = promote_consts::collect_temps(mir, &mut rpo);
166 rpo.reset();
167 Qualifier {
168 mode: mode,
169 span: mir.span,
170 def_id: def_id,
171 mir: mir,
172 rpo: rpo,
173 tcx: tcx,
174 param_env: param_env,
175 qualif_map: qualif_map,
176 mir_map: mir_map,
3157f602 177 temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
a7813a04
XL
178 return_qualif: None,
179 qualif: Qualif::empty(),
180 const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
181 location: Location {
182 block: START_BLOCK,
183 statement_index: 0
184 },
185 temp_promotion_state: temps,
186 promotion_candidates: vec![]
187 }
188 }
189
190 // FIXME(eddyb) we could split the errors into meaningful
191 // categories, but enabling full miri would make that
192 // slightly pointless (even with feature-gating).
193 fn not_const(&mut self) {
194 self.add(Qualif::NOT_CONST);
195 if self.mode != Mode::Fn {
196 span_err!(self.tcx.sess, self.span, E0019,
197 "{} contains unimplemented expression type", self.mode);
198 }
199 }
200
201 /// Error about extra statements in a constant.
202 fn statement_like(&mut self) {
203 self.add(Qualif::NOT_CONST);
204 if self.mode != Mode::Fn {
205 span_err!(self.tcx.sess, self.span, E0016,
206 "blocks in {}s are limited to items and tail expressions",
207 self.mode);
208 }
209 }
210
211 /// Add the given qualification to self.qualif.
212 fn add(&mut self, qualif: Qualif) {
213 self.qualif = self.qualif | qualif;
214 }
215
216 /// Add the given type's qualification to self.qualif.
217 fn add_type(&mut self, ty: Ty<'tcx>) {
218 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
219 self.qualif.restrict(ty, self.tcx, &self.param_env);
220 }
221
222 /// Within the provided closure, self.qualif will start
223 /// out empty, and its value after the closure returns will
224 /// be combined with the value before the call to nest.
225 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
226 let original = self.qualif;
227 self.qualif = Qualif::empty();
228 f(self);
229 self.add(original);
230 }
231
232 /// Check for NEEDS_DROP (from an ADT or const fn call) and
233 /// error, unless we're in a function, or the feature-gate
234 /// for globals with destructors is enabled.
235 fn deny_drop(&self) {
236 if self.mode == Mode::Fn || !self.qualif.intersects(Qualif::NEEDS_DROP) {
237 return;
238 }
239
240 // Static and const fn's allow destructors, but they're feature-gated.
241 let msg = if self.mode != Mode::Const {
242 // Feature-gate for globals with destructors is enabled.
243 if self.tcx.sess.features.borrow().drop_types_in_const {
244 return;
245 }
246
247 // This comes from a macro that has #[allow_internal_unstable].
248 if self.tcx.sess.codemap().span_allows_unstable(self.span) {
249 return;
250 }
251
252 format!("destructors in {}s are an unstable feature",
253 self.mode)
254 } else {
255 format!("{}s are not allowed to have destructors",
256 self.mode)
257 };
258
259 let mut err =
260 struct_span_err!(self.tcx.sess, self.span, E0493, "{}", msg);
261 if self.mode != Mode::Const {
262 help!(&mut err,
263 "in Nightly builds, add `#![feature(drop_types_in_const)]` \
264 to the crate attributes to enable");
265 }
266 err.emit();
267 }
268
269 /// Check if an Lvalue with the current qualifications could
270 /// be consumed, by either an operand or a Deref projection.
271 fn try_consume(&mut self) -> bool {
272 if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
273 let msg = if self.mode == Mode::Static ||
274 self.mode == Mode::StaticMut {
275 "cannot refer to other statics by value, use the \
276 address-of operator or a constant instead"
277 } else {
278 "cannot refer to statics by value, use a constant instead"
279 };
280 span_err!(self.tcx.sess, self.span, E0394, "{}", msg);
281
282 // Replace STATIC with NOT_CONST to avoid further errors.
283 self.qualif = self.qualif - Qualif::STATIC;
284 self.add(Qualif::NOT_CONST);
285
286 false
287 } else {
288 true
289 }
290 }
291
292 /// Assign the current qualification to the given destination.
293 fn assign(&mut self, dest: &Lvalue<'tcx>) {
294 let qualif = self.qualif;
295 let span = self.span;
296 let store = |slot: &mut Option<Qualif>| {
297 if slot.is_some() {
298 span_bug!(span, "multiple assignments to {:?}", dest);
299 }
300 *slot = Some(qualif);
301 };
302
303 // Only handle promotable temps in non-const functions.
304 if self.mode == Mode::Fn {
305 if let Lvalue::Temp(index) = *dest {
3157f602
XL
306 if self.temp_promotion_state[index].is_promotable() {
307 store(&mut self.temp_qualif[index]);
a7813a04
XL
308 }
309 }
310 return;
311 }
312
313 match *dest {
3157f602 314 Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
a7813a04
XL
315 Lvalue::ReturnPointer => store(&mut self.return_qualif),
316
317 Lvalue::Projection(box Projection {
318 base: Lvalue::Temp(index),
319 elem: ProjectionElem::Deref
3157f602
XL
320 }) if self.mir.temp_decls[index].ty.is_unique()
321 && self.temp_qualif[index].map_or(false, |qualif| {
a7813a04
XL
322 qualif.intersects(Qualif::NOT_CONST)
323 }) => {
324 // Part of `box expr`, we should've errored
325 // already for the Box allocation Rvalue.
326 }
327
328 // This must be an explicit assignment.
329 _ => {
330 // Catch more errors in the destination.
331 self.visit_lvalue(dest, LvalueContext::Store);
332 self.statement_like();
333 }
334 }
335 }
336
a7813a04
XL
337 /// Qualify a whole const, static initializer or const fn.
338 fn qualify_const(&mut self) -> Qualif {
339 let mir = self.mir;
340
3157f602 341 let mut seen_blocks = BitVector::new(mir.basic_blocks().len());
a7813a04
XL
342 let mut bb = START_BLOCK;
343 loop {
344 seen_blocks.insert(bb.index());
345
346 self.visit_basic_block_data(bb, &mir[bb]);
347
348 let target = match mir[bb].terminator().kind {
349 TerminatorKind::Goto { target } |
350 // Drops are considered noops.
351 TerminatorKind::Drop { target, .. } |
3157f602 352 TerminatorKind::Assert { target, .. } |
a7813a04
XL
353 TerminatorKind::Call { destination: Some((_, target)), .. } => {
354 Some(target)
355 }
356
357 // Non-terminating calls cannot produce any value.
358 TerminatorKind::Call { destination: None, .. } => {
359 return Qualif::empty();
360 }
361
3157f602 362 TerminatorKind::If {..} |
a7813a04
XL
363 TerminatorKind::Switch {..} |
364 TerminatorKind::SwitchInt {..} |
3157f602
XL
365 TerminatorKind::DropAndReplace { .. } |
366 TerminatorKind::Resume |
367 TerminatorKind::Unreachable => None,
a7813a04
XL
368
369 TerminatorKind::Return => {
370 // Check for unused values. This usually means
371 // there are extra statements in the AST.
3157f602
XL
372 for temp in mir.temp_decls.indices() {
373 if self.temp_qualif[temp].is_none() {
a7813a04
XL
374 continue;
375 }
376
3157f602 377 let state = self.temp_promotion_state[temp];
a7813a04
XL
378 if let TempState::Defined { location, uses: 0 } = state {
379 let data = &mir[location.block];
380 let stmt_idx = location.statement_index;
381
382 // Get the span for the initialization.
3157f602
XL
383 let source_info = if stmt_idx < data.statements.len() {
384 data.statements[stmt_idx].source_info
a7813a04 385 } else {
3157f602
XL
386 data.terminator().source_info
387 };
388 self.span = source_info.span;
a7813a04
XL
389
390 // Treat this as a statement in the AST.
391 self.statement_like();
392 }
393 }
394
395 // Make sure there are no extra unassigned variables.
396 self.qualif = Qualif::NOT_CONST;
397 for index in 0..mir.var_decls.len() {
398 if !self.const_fn_arg_vars.contains(index) {
3157f602 399 self.assign(&Lvalue::Var(Var::new(index)));
a7813a04
XL
400 }
401 }
402
403 break;
404 }
405 };
406
407 match target {
408 // No loops allowed.
409 Some(target) if !seen_blocks.contains(target.index()) => {
410 bb = target;
411 }
412 _ => {
413 self.not_const();
414 break;
415 }
416 }
417 }
418
5bcae85e 419 let return_ty = mir.return_ty;
a7813a04
XL
420 self.qualif = self.return_qualif.unwrap_or(Qualif::NOT_CONST);
421
422 match self.mode {
423 Mode::StaticMut => {
424 // Check for destructors in static mut.
425 self.add_type(return_ty);
426 self.deny_drop();
427 }
428 _ => {
429 // Account for errors in consts by using the
430 // conservative type qualification instead.
431 if self.qualif.intersects(Qualif::CONST_ERROR) {
432 self.qualif = Qualif::empty();
433 self.add_type(return_ty);
434 }
435 }
436 }
437 self.qualif
438 }
439}
440
441/// Accumulates an Rvalue or Call's effects in self.qualif.
442/// For functions (constant or not), it also records
443/// candidates for promotion in promotion_candidates.
444impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
445 fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
446 match *lvalue {
447 Lvalue::Arg(_) => {
448 self.add(Qualif::FN_ARGUMENT);
449 }
450 Lvalue::Var(_) => {
451 self.add(Qualif::NOT_CONST);
452 }
453 Lvalue::Temp(index) => {
3157f602 454 if !self.temp_promotion_state[index].is_promotable() {
a7813a04
XL
455 self.add(Qualif::NOT_PROMOTABLE);
456 }
457
3157f602 458 if let Some(qualif) = self.temp_qualif[index] {
a7813a04
XL
459 self.add(qualif);
460 } else {
461 self.not_const();
462 }
463 }
464 Lvalue::Static(_) => {
465 self.add(Qualif::STATIC);
466 if self.mode == Mode::Const || self.mode == Mode::ConstFn {
467 span_err!(self.tcx.sess, self.span, E0013,
468 "{}s cannot refer to statics, use \
469 a constant instead", self.mode);
470 }
471 }
472 Lvalue::ReturnPointer => {
473 self.not_const();
474 }
475 Lvalue::Projection(ref proj) => {
476 self.nest(|this| {
477 this.super_lvalue(lvalue, context);
478 match proj.elem {
479 ProjectionElem::Deref => {
480 if !this.try_consume() {
481 return;
482 }
483
484 if this.qualif.intersects(Qualif::STATIC_REF) {
485 this.qualif = this.qualif - Qualif::STATIC_REF;
486 this.add(Qualif::STATIC);
487 }
488
5bcae85e 489 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
a7813a04
XL
490 if let ty::TyRawPtr(_) = base_ty.sty {
491 this.add(Qualif::NOT_CONST);
492 if this.mode != Mode::Fn {
493 span_err!(this.tcx.sess, this.span, E0396,
494 "raw pointers cannot be dereferenced in {}s",
495 this.mode);
496 }
497 }
498 }
499
500 ProjectionElem::Field(..) |
501 ProjectionElem::Index(_) => {
502 if this.mode != Mode::Fn &&
503 this.qualif.intersects(Qualif::STATIC) {
504 span_err!(this.tcx.sess, this.span, E0494,
505 "cannot refer to the interior of another \
506 static, use a constant instead");
507 }
5bcae85e 508 let ty = lvalue.ty(this.mir, this.tcx).to_ty(this.tcx);
a7813a04
XL
509 this.qualif.restrict(ty, this.tcx, &this.param_env);
510 }
511
512 ProjectionElem::ConstantIndex {..} |
3157f602 513 ProjectionElem::Subslice {..} |
a7813a04
XL
514 ProjectionElem::Downcast(..) => {
515 this.not_const()
516 }
517 }
518 });
519 }
520 }
521 }
522
523 fn visit_operand(&mut self, operand: &Operand<'tcx>) {
524 match *operand {
525 Operand::Consume(_) => {
526 self.nest(|this| {
527 this.super_operand(operand);
528 this.try_consume();
529 });
530 }
531 Operand::Constant(ref constant) => {
532 // Only functions and methods can have these types.
533 if let ty::TyFnDef(..) = constant.ty.sty {
534 return;
535 }
536
537 if let Literal::Item { def_id, substs } = constant.literal {
538 // Don't peek inside generic (associated) constants.
539 if !substs.types.is_empty() {
540 self.add_type(constant.ty);
541 } else {
542 let qualif = qualify_const_item_cached(self.tcx,
543 self.qualif_map,
544 self.mir_map,
545 def_id);
546 self.add(qualif);
547 }
548
549 // FIXME(eddyb) check recursive constants here,
550 // instead of rustc_passes::static_recursion.
551 if self.qualif.intersects(Qualif::RECURSIVE) {
552 span_bug!(constant.span,
553 "recursive constant wasn't caught earlier");
554 }
555
556 // Let `const fn` transitively have destructors,
557 // but they do get stopped in `const` or `static`.
558 if self.mode != Mode::ConstFn {
559 self.deny_drop();
560 }
561 }
562 }
563 }
564 }
565
566 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
567 // Recurse through operands and lvalues.
568 self.super_rvalue(rvalue);
569
570 match *rvalue {
571 Rvalue::Use(_) |
572 Rvalue::Repeat(..) |
573 Rvalue::UnaryOp(..) |
3157f602 574 Rvalue::CheckedBinaryOp(..) |
a7813a04
XL
575 Rvalue::Cast(CastKind::ReifyFnPointer, _, _) |
576 Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
577 Rvalue::Cast(CastKind::Unsize, _, _) => {}
578
579 Rvalue::Len(_) => {
580 // Static lvalues in consts would have errored already,
581 // don't treat length checks as reads from statics.
582 self.qualif = self.qualif - Qualif::STATIC;
583 }
584
585 Rvalue::Ref(_, kind, ref lvalue) => {
586 // Static lvalues in consts would have errored already,
587 // only keep track of references to them here.
588 if self.qualif.intersects(Qualif::STATIC) {
589 self.qualif = self.qualif - Qualif::STATIC;
590 self.add(Qualif::STATIC_REF);
591 }
592
5bcae85e 593 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
a7813a04
XL
594 if kind == BorrowKind::Mut {
595 // In theory, any zero-sized value could be borrowed
596 // mutably without consequences. However, only &mut []
597 // is allowed right now, and only in functions.
598 let allow = if self.mode == Mode::StaticMut {
599 // Inside a `static mut`, &mut [...] is also allowed.
600 match ty.sty {
601 ty::TyArray(..) | ty::TySlice(_) => {
602 // Mutating can expose drops, be conservative.
603 self.add_type(ty);
604 self.deny_drop();
605 true
606 }
607 _ => false
608 }
609 } else if let ty::TyArray(_, 0) = ty.sty {
610 self.mode == Mode::Fn
611 } else {
612 false
613 };
614
615 if !allow {
616 self.add(Qualif::NOT_CONST);
617 if self.mode != Mode::Fn {
5bcae85e
SL
618 struct_span_err!(self.tcx.sess, self.span, E0017,
619 "references in {}s may only refer \
620 to immutable values", self.mode)
621 .span_label(self.span, &format!("{}s require immutable values",
622 self.mode))
623 .emit();
a7813a04
XL
624 }
625 }
626 } else {
627 // Constants cannot be borrowed if they contain interior mutability as
628 // it means that our "silent insertion of statics" could change
629 // initializer values (very bad).
630 if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
631 // Replace MUTABLE_INTERIOR with NOT_CONST to avoid
632 // duplicate errors (from reborrowing, for example).
633 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
634 self.add(Qualif::NOT_CONST);
635 if self.mode != Mode::Fn {
636 span_err!(self.tcx.sess, self.span, E0492,
637 "cannot borrow a constant which contains \
638 interior mutability, create a static instead");
639 }
640 }
641 }
642
643 // We might have a candidate for promotion.
644 let candidate = Candidate::Ref(self.location);
645 if self.mode == Mode::Fn || self.mode == Mode::ConstFn {
646 if !self.qualif.intersects(Qualif::NEVER_PROMOTE) {
647 // We can only promote direct borrows of temps.
648 if let Lvalue::Temp(_) = *lvalue {
649 self.promotion_candidates.push(candidate);
650 }
651 }
652 }
653 }
654
655 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
5bcae85e 656 let operand_ty = operand.ty(self.mir, self.tcx);
a7813a04
XL
657 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
658 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
659 match (cast_in, cast_out) {
660 (CastTy::Ptr(_), CastTy::Int(_)) |
661 (CastTy::FnPtr, CastTy::Int(_)) => {
662 self.add(Qualif::NOT_CONST);
663 if self.mode != Mode::Fn {
664 span_err!(self.tcx.sess, self.span, E0018,
665 "raw pointers cannot be cast to integers in {}s",
666 self.mode);
667 }
668 }
669 _ => {}
670 }
671 }
672
673 Rvalue::BinaryOp(op, ref lhs, _) => {
5bcae85e 674 if let ty::TyRawPtr(_) = lhs.ty(self.mir, self.tcx).sty {
a7813a04
XL
675 assert!(op == BinOp::Eq || op == BinOp::Ne ||
676 op == BinOp::Le || op == BinOp::Lt ||
677 op == BinOp::Ge || op == BinOp::Gt);
678
679 self.add(Qualif::NOT_CONST);
680 if self.mode != Mode::Fn {
681 span_err!(self.tcx.sess, self.span, E0395,
682 "raw pointers cannot be compared in {}s",
683 self.mode);
684 }
685 }
686 }
687
688 Rvalue::Box(_) => {
689 self.add(Qualif::NOT_CONST);
690 if self.mode != Mode::Fn {
5bcae85e
SL
691 struct_span_err!(self.tcx.sess, self.span, E0010,
692 "allocations are not allowed in {}s", self.mode)
693 .span_label(self.span, &format!("allocation not allowed in {}s", self.mode))
694 .emit();
a7813a04
XL
695 }
696 }
697
698 Rvalue::Aggregate(ref kind, _) => {
699 if let AggregateKind::Adt(def, _, _) = *kind {
700 if def.has_dtor() {
701 self.add(Qualif::NEEDS_DROP);
702 self.deny_drop();
703 }
704
705 if Some(def.did) == self.tcx.lang_items.unsafe_cell_type() {
5bcae85e 706 let ty = rvalue.ty(self.mir, self.tcx).unwrap();
a7813a04
XL
707 self.add_type(ty);
708 assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
709 // Even if the value inside may not need dropping,
710 // mutating it would change that.
711 if !self.qualif.intersects(Qualif::NOT_CONST) {
712 self.deny_drop();
713 }
714 }
715 }
716 }
717
a7813a04
XL
718 Rvalue::InlineAsm {..} => {
719 self.not_const();
720 }
721 }
722 }
723
724 fn visit_terminator_kind(&mut self, bb: BasicBlock, kind: &TerminatorKind<'tcx>) {
725 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
726 self.visit_operand(func);
727
5bcae85e 728 let fn_ty = func.ty(self.mir, self.tcx);
a7813a04
XL
729 let (is_shuffle, is_const_fn) = match fn_ty.sty {
730 ty::TyFnDef(def_id, _, f) => {
731 (f.abi == Abi::PlatformIntrinsic &&
732 self.tcx.item_name(def_id).as_str().starts_with("simd_shuffle"),
733 is_const_fn(self.tcx, def_id))
734 }
735 _ => (false, false)
736 };
737
738 for (i, arg) in args.iter().enumerate() {
739 self.nest(|this| {
740 this.visit_operand(arg);
741 if is_shuffle && i == 2 && this.mode == Mode::Fn {
742 let candidate = Candidate::ShuffleIndices(bb);
743 if !this.qualif.intersects(Qualif::NEVER_PROMOTE) {
744 this.promotion_candidates.push(candidate);
745 } else {
746 span_err!(this.tcx.sess, this.span, E0526,
747 "shuffle indices are not constant");
748 }
749 }
750 });
751 }
752
753 // Const fn calls.
754 if is_const_fn {
755 // We are in a const or static initializer,
756 if self.mode != Mode::Fn &&
757
758 // feature-gate is not enabled,
759 !self.tcx.sess.features.borrow().const_fn &&
760
761 // this doesn't come from a crate with the feature-gate enabled,
762 self.def_id.is_local() &&
763
764 // this doesn't come from a macro that has #[allow_internal_unstable]
765 !self.tcx.sess.codemap().span_allows_unstable(self.span)
766 {
767 let mut err = self.tcx.sess.struct_span_err(self.span,
768 "const fns are an unstable feature");
769 help!(&mut err,
770 "in Nightly builds, add `#![feature(const_fn)]` \
771 to the crate attributes to enable");
772 err.emit();
773 }
774 } else {
775 self.qualif = Qualif::NOT_CONST;
776 if self.mode != Mode::Fn {
777 // FIXME(#24111) Remove this check when const fn stabilizes
778 let (msg, note) = if let UnstableFeatures::Disallow =
779 self.tcx.sess.opts.unstable_features {
780 (format!("calls in {}s are limited to \
781 struct and enum constructors",
782 self.mode),
783 Some("a limited form of compile-time function \
784 evaluation is available on a nightly \
785 compiler via `const fn`"))
786 } else {
787 (format!("calls in {}s are limited \
788 to constant functions, \
789 struct and enum constructors",
790 self.mode),
791 None)
792 };
793 let mut err = struct_span_err!(self.tcx.sess, self.span, E0015, "{}", msg);
794 if let Some(note) = note {
795 err.span_note(self.span, note);
796 }
797 err.emit();
798 }
799 }
800
801 if let Some((ref dest, _)) = *destination {
802 // Avoid propagating irrelevant callee/argument qualifications.
803 if self.qualif.intersects(Qualif::CONST_ERROR) {
804 self.qualif = Qualif::NOT_CONST;
805 } else {
806 // Be conservative about the returned value of a const fn.
807 let tcx = self.tcx;
5bcae85e 808 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
a7813a04
XL
809 self.qualif = Qualif::empty();
810 self.add_type(ty);
811
812 // Let `const fn` transitively have destructors,
813 // but they do get stopped in `const` or `static`.
814 if self.mode != Mode::ConstFn {
815 self.deny_drop();
816 }
817 }
818 self.assign(dest);
819 }
820 } else {
821 // Qualify any operands inside other terminators.
822 self.super_terminator_kind(bb, kind);
823 }
824 }
825
826 fn visit_assign(&mut self, _: BasicBlock, dest: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) {
827 self.visit_rvalue(rvalue);
828
829 // Check the allowed const fn argument forms.
830 if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
3157f602 831 if self.const_fn_arg_vars.insert(index.index()) {
a7813a04
XL
832 // Direct use of an argument is permitted.
833 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
834 return;
835 }
836
837 // Avoid a generic error for other uses of arguments.
838 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
3157f602
XL
839 let decl = &self.mir.var_decls[index];
840 span_err!(self.tcx.sess, decl.source_info.span, E0022,
a7813a04
XL
841 "arguments of constant functions can only \
842 be immutable by-value bindings");
843 return;
844 }
845 }
846 }
847
848 self.assign(dest);
849 }
850
3157f602
XL
851 fn visit_source_info(&mut self, source_info: &SourceInfo) {
852 self.span = source_info.span;
853 }
854
a7813a04
XL
855 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
856 assert_eq!(self.location.block, bb);
5bcae85e
SL
857 self.nest(|this| {
858 this.visit_source_info(&statement.source_info);
859 match statement.kind {
860 StatementKind::Assign(ref lvalue, ref rvalue) => {
861 this.visit_assign(bb, lvalue, rvalue);
862 }
863 StatementKind::SetDiscriminant { .. } |
864 StatementKind::StorageLive(_) |
865 StatementKind::StorageDead(_) => {}
866 }
867 });
a7813a04
XL
868 self.location.statement_index += 1;
869 }
870
871 fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
872 assert_eq!(self.location.block, bb);
a7813a04
XL
873 self.nest(|this| this.super_terminator(bb, terminator));
874 }
875
876 fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
877 self.location.statement_index = 0;
878 self.location.block = bb;
879 self.super_basic_block_data(bb, data);
880 }
881}
882
883fn qualify_const_item_cached<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
884 qualif_map: &mut DefIdMap<Qualif>,
885 mir_map: Option<&MirMap<'tcx>>,
886 def_id: DefId)
887 -> Qualif {
888 match qualif_map.entry(def_id) {
889 Entry::Occupied(entry) => return *entry.get(),
890 Entry::Vacant(entry) => {
891 // Guard against `const` recursion.
892 entry.insert(Qualif::RECURSIVE);
893 }
894 }
895
896 let extern_mir;
897 let param_env_and_mir = if def_id.is_local() {
5bcae85e
SL
898 mir_map.and_then(|map| map.map.get(&def_id)).map(|mir| {
899 let node_id = tcx.map.as_local_node_id(def_id).unwrap();
a7813a04
XL
900 (ty::ParameterEnvironment::for_item(tcx, node_id), mir)
901 })
902 } else if let Some(mir) = tcx.sess.cstore.maybe_get_item_mir(tcx, def_id) {
903 // These should only be monomorphic constants.
904 extern_mir = mir;
905 Some((tcx.empty_parameter_environment(), &extern_mir))
906 } else {
907 None
908 };
909
910 let (param_env, mir) = param_env_and_mir.unwrap_or_else(|| {
911 bug!("missing constant MIR for {}", tcx.item_path_str(def_id))
912 });
913
914 let mut qualifier = Qualifier::new(tcx, param_env, qualif_map, mir_map,
915 def_id, mir, Mode::Const);
916 let qualif = qualifier.qualify_const();
917 qualifier.qualif_map.insert(def_id, qualif);
918 qualif
919}
920
921pub struct QualifyAndPromoteConstants;
922
923impl Pass for QualifyAndPromoteConstants {}
924
925impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
3157f602
XL
926 fn run_pass<'a>(&mut self,
927 tcx: TyCtxt<'a, 'tcx, 'tcx>,
928 map: &mut MirMap<'tcx>,
929 hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
a7813a04
XL
930 let mut qualif_map = DefIdMap();
931
932 // First, visit `const` items, potentially recursing, to get
933 // accurate MUTABLE_INTERIOR and NEEDS_DROP qualifications.
5bcae85e
SL
934 let keys = map.map.keys();
935 for &def_id in &keys {
936 let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
937 let id = tcx.map.as_local_node_id(def_id).unwrap();
a7813a04
XL
938 let src = MirSource::from_node(tcx, id);
939 if let MirSource::Const(_) = src {
940 qualify_const_item_cached(tcx, &mut qualif_map, Some(map), def_id);
941 }
942 }
943
944 // Then, handle everything else, without recursing,
945 // as the MIR map is not shared, since promotion
946 // in functions (including `const fn`) mutates it.
5bcae85e
SL
947 for &def_id in &keys {
948 let _task = tcx.dep_graph.in_task(DepNode::Mir(def_id));
949 let id = tcx.map.as_local_node_id(def_id).unwrap();
a7813a04
XL
950 let src = MirSource::from_node(tcx, id);
951 let mode = match src {
952 MirSource::Fn(_) => {
953 if is_const_fn(tcx, def_id) {
954 Mode::ConstFn
955 } else {
956 Mode::Fn
957 }
958 }
959 MirSource::Const(_) => continue,
960 MirSource::Static(_, hir::MutImmutable) => Mode::Static,
961 MirSource::Static(_, hir::MutMutable) => Mode::StaticMut,
962 MirSource::Promoted(..) => bug!()
963 };
964 let param_env = ty::ParameterEnvironment::for_item(tcx, id);
965
5bcae85e 966 let mir = map.map.get_mut(&def_id).unwrap();
3157f602
XL
967 for hook in &mut *hooks {
968 hook.on_mir_pass(tcx, src, mir, self, false);
969 }
970
a7813a04
XL
971 if mode == Mode::Fn || mode == Mode::ConstFn {
972 // This is ugly because Qualifier holds onto mir,
973 // which can't be mutated until its scope ends.
974 let (temps, candidates) = {
975 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
976 None, def_id, mir, mode);
977 if mode == Mode::ConstFn {
978 // Enforce a constant-like CFG for `const fn`.
979 qualifier.qualify_const();
980 } else {
981 while let Some((bb, data)) = qualifier.rpo.next() {
982 qualifier.visit_basic_block_data(bb, data);
983 }
984 }
985
986 (qualifier.temp_promotion_state,
987 qualifier.promotion_candidates)
988 };
989
990 // Do the actual promotion, now that we know what's viable.
991 promote_consts::promote_candidates(mir, tcx, temps, candidates);
992 } else {
993 let mut qualifier = Qualifier::new(tcx, param_env, &mut qualif_map,
994 None, def_id, mir, mode);
995 qualifier.qualify_const();
996 }
997
3157f602
XL
998 for hook in &mut *hooks {
999 hook.on_mir_pass(tcx, src, mir, self, true);
1000 }
1001
a7813a04
XL
1002 // Statics must be Sync.
1003 if mode == Mode::Static {
5bcae85e
SL
1004 let ty = mir.return_ty;
1005 tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
a7813a04
XL
1006 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1007 let mut fulfillment_cx = traits::FulfillmentContext::new();
1008 fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
1009 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1010 infcx.report_fulfillment_errors(&err);
1011 }
1012
1013 if let Err(errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
1014 infcx.report_fulfillment_errors_as_warnings(&errors, id);
1015 }
1016 });
1017 }
1018 }
1019 }
1020}