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