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