1 //! A pass that qualifies constness of temporaries in constants,
2 //! static initializers and functions and also drives promotion.
4 //! The Qualif flags below can be used to also provide better
5 //! diagnostics as to why a constant rvalue wasn't promoted.
7 use rustc_data_structures::bit_set::BitSet;
8 use rustc_data_structures::indexed_vec::IndexVec;
9 use rustc_data_structures::fx::FxHashSet;
10 use rustc_data_structures::sync::Lrc;
11 use rustc_target::spec::abi::Abi;
13 use rustc::hir::def_id::DefId;
14 use rustc::traits::{self, TraitEngine};
15 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
16 use rustc::ty::cast::CastTy;
17 use rustc::ty::query::Providers;
19 use rustc::mir::traversal::ReversePostorder;
20 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
21 use rustc::middle::lang_items;
22 use rustc::session::config::nightly_options;
23 use syntax::ast::LitKind;
24 use syntax::feature_gate::{UnstableFeatures, emit_feature_err, GateIssue};
25 use syntax_pos::{Span, DUMMY_SP};
30 use crate::transform::{MirPass, MirSource};
31 use super::promote_consts::{self, Candidate, TempState};
34 // Borrows of temporaries can be promoted only if
35 // they have none of these qualifications, with
36 // the exception of `STATIC_REF` (in statics only).
38 // Constant containing interior mutability (UnsafeCell).
39 const MUTABLE_INTERIOR = 1 << 0;
41 // Constant containing an ADT that implements Drop.
42 const NEEDS_DROP = 1 << 1;
45 const FN_ARGUMENT = 1 << 2;
47 // Not constant at all - non-`const fn` calls, asm!,
48 // pointer comparisons, ptr-to-int casts, etc.
49 const NOT_CONST = 1 << 3;
51 // Refers to temporaries which cannot be promoted as
52 // promote_consts decided they weren't simple enough.
53 const NOT_PROMOTABLE = 1 << 4;
55 // Const items can only have MUTABLE_INTERIOR
56 // and NOT_PROMOTABLE without producing an error.
57 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
58 !Qualif::NOT_PROMOTABLE.bits;
62 impl<'a, 'tcx> Qualif {
63 /// Remove flags which are impossible for the given type.
64 fn restrict(&mut self, ty: Ty<'tcx>,
65 tcx: TyCtxt<'a, 'tcx, 'tcx>,
66 param_env: ty::ParamEnv<'tcx>) {
67 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
68 *self = *self - Qualif::MUTABLE_INTERIOR;
70 if !ty.needs_drop(tcx, param_env) {
71 *self = *self - Qualif::NEEDS_DROP;
76 /// What kind of item we are in.
77 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
86 impl fmt::Display for Mode {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 Mode::Const => write!(f, "constant"),
90 Mode::Static | Mode::StaticMut => write!(f, "static"),
91 Mode::ConstFn => write!(f, "constant function"),
92 Mode::Fn => write!(f, "function")
97 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
102 rpo: ReversePostorder<'a, 'tcx>,
103 tcx: TyCtxt<'a, 'gcx, 'tcx>,
104 param_env: ty::ParamEnv<'tcx>,
105 local_qualif: IndexVec<Local, Option<Qualif>>,
107 temp_promotion_state: IndexVec<Local, TempState>,
108 promotion_candidates: Vec<Candidate>
111 macro_rules! unleash_miri {
113 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
114 $this.tcx.sess.span_warn($this.span, "skipping const checks");
120 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
121 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
125 -> Qualifier<'a, 'tcx, 'tcx> {
126 assert!(def_id.is_local());
127 let mut rpo = traversal::reverse_postorder(mir);
128 let temps = promote_consts::collect_temps(mir, &mut rpo);
131 let param_env = tcx.param_env(def_id);
133 let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
134 for arg in mir.args_iter() {
135 let mut qualif = Qualif::NEEDS_DROP;
136 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
137 local_qualif[arg] = Some(qualif);
149 qualif: Qualif::empty(),
150 temp_promotion_state: temps,
151 promotion_candidates: vec![]
155 // FIXME(eddyb) we could split the errors into meaningful
156 // categories, but enabling full miri would make that
157 // slightly pointless (even with feature-gating).
158 fn not_const(&mut self) {
160 self.add(Qualif::NOT_CONST);
161 if self.mode != Mode::Fn {
162 let mut err = struct_span_err!(
166 "{} contains unimplemented expression type",
169 if self.tcx.sess.teach(&err.get_code().unwrap()) {
170 err.note("A function call isn't allowed in the const's initialization expression \
171 because the expression's value must be known at compile-time.");
172 err.note("Remember: you can't use a function call inside a const's initialization \
173 expression! However, you can use it anywhere else.");
179 /// Adds the given qualification to `self.qualif`.
180 fn add(&mut self, qualif: Qualif) {
181 self.qualif = self.qualif | qualif;
184 /// Adds 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);
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();
200 /// Assign the current qualification to the given destination.
201 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
202 trace!("assign: {:?}", dest);
203 let qualif = self.qualif;
204 let span = self.span;
205 let store = |slot: &mut Option<Qualif>| {
207 span_bug!(span, "multiple assignments to {:?}", dest);
209 *slot = Some(qualif);
212 // Only handle promotable temps in non-const functions.
213 if self.mode == Mode::Fn {
214 if let Place::Local(index) = *dest {
215 if self.mir.local_kind(index) == LocalKind::Temp
216 && self.temp_promotion_state[index].is_promotable() {
217 debug!("store to promotable temp {:?} ({:?})", index, qualif);
218 store(&mut self.local_qualif[index]);
227 // We treat all locals equal in constants
228 Place::Local(index) => break *index,
229 // projections are transparent for assignments
230 // we qualify the entire destination at once, even if just a field would have
231 // stricter qualification
232 Place::Projection(proj) => {
233 // Catch more errors in the destination. `visit_place` also checks various
234 // projection rules like union field access and raw pointer deref
237 PlaceContext::MutatingUse(MutatingUseContext::Store),
242 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
243 Place::Static(..) => {
244 // Catch more errors in the destination. `visit_place` also checks that we
245 // do not try to access statics from constants or try to mutate statics
248 PlaceContext::MutatingUse(MutatingUseContext::Store),
255 debug!("store to var {:?}", index);
256 match &mut self.local_qualif[index] {
257 // this is overly restrictive, because even full assignments do not clear the qualif
258 // While we could special case full assignments, this would be inconsistent with
259 // aggregates where we overwrite all fields via assignments, which would not get
261 Some(ref mut qualif) => *qualif = *qualif | self.qualif,
262 // insert new qualification
263 qualif @ None => *qualif = Some(self.qualif),
267 /// Qualify a whole const, static initializer or const fn.
268 fn qualify_const(&mut self) -> (Qualif, Lrc<BitSet<Local>>) {
269 debug!("qualifying {} {:?}", self.mode, self.def_id);
273 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
274 let mut bb = START_BLOCK;
276 seen_blocks.insert(bb.index());
278 self.visit_basic_block_data(bb, &mir[bb]);
280 let target = match mir[bb].terminator().kind {
281 TerminatorKind::Goto { target } |
282 TerminatorKind::Drop { target, .. } |
283 TerminatorKind::Assert { target, .. } |
284 TerminatorKind::Call { destination: Some((_, target)), .. } => {
288 // Non-terminating calls cannot produce any value.
289 TerminatorKind::Call { destination: None, .. } => {
293 TerminatorKind::SwitchInt {..} |
294 TerminatorKind::DropAndReplace { .. } |
295 TerminatorKind::Resume |
296 TerminatorKind::Abort |
297 TerminatorKind::GeneratorDrop |
298 TerminatorKind::Yield { .. } |
299 TerminatorKind::Unreachable |
300 TerminatorKind::FalseEdges { .. } |
301 TerminatorKind::FalseUnwind { .. } => None,
303 TerminatorKind::Return => {
310 Some(target) if !seen_blocks.contains(target.index()) => {
320 self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
322 // Account for errors in consts by using the
323 // conservative type qualification instead.
324 if self.qualif.intersects(Qualif::CONST_ERROR) {
325 self.qualif = Qualif::empty();
326 let return_ty = mir.return_ty();
327 self.add_type(return_ty);
331 // Collect all the temps we need to promote.
332 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
334 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
335 for candidate in &self.promotion_candidates {
337 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
338 match self.mir[bb].statements[stmt_idx].kind {
339 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
340 promoted_temps.insert(index);
345 Candidate::Argument { .. } => {}
349 (self.qualif, Lrc::new(promoted_temps))
352 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
353 Some(def_id) == self.tcx.lang_items().panic_fn() ||
354 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
358 /// Accumulates an Rvalue or Call's effects in self.qualif.
359 /// For functions (constant or not), it also records
360 /// candidates for promotion in promotion_candidates.
361 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
362 fn visit_local(&mut self,
364 _: PlaceContext<'tcx>,
366 debug!("visit_local: local={:?}", local);
367 let kind = self.mir.local_kind(local);
369 LocalKind::ReturnPointer => {
372 LocalKind::Var if self.mode == Mode::Fn => {
373 self.add(Qualif::NOT_CONST);
378 if let LocalKind::Arg = kind {
379 self.add(Qualif::FN_ARGUMENT);
382 if !self.temp_promotion_state[local].is_promotable() {
383 debug!("visit_local: (not promotable) local={:?}", local);
384 self.add(Qualif::NOT_PROMOTABLE);
387 if let Some(qualif) = self.local_qualif[local] {
396 fn visit_place(&mut self,
398 context: PlaceContext<'tcx>,
399 location: Location) {
400 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
402 Place::Local(ref local) => self.visit_local(local, context, location),
403 Place::Promoted(_) => bug!("promoting already promoted MIR"),
404 Place::Static(ref global) => {
406 .get_attrs(global.def_id)
408 .any(|attr| attr.check_name("thread_local")) {
409 if self.mode != Mode::Fn {
410 span_err!(self.tcx.sess, self.span, E0625,
411 "thread-local statics cannot be \
412 accessed at compile-time");
414 self.add(Qualif::NOT_CONST);
418 // Only allow statics (not consts) to refer to other statics.
419 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
420 if self.mode == Mode::Static && context.is_mutating_use() {
421 // this is not strictly necessary as miri will also bail out
422 // For interior mutability we can't really catch this statically as that
423 // goes through raw pointers and intermediate temporaries, so miri has
424 // to catch this anyway
425 self.tcx.sess.span_err(
427 "cannot mutate statics in the initializer of another static",
433 self.add(Qualif::NOT_CONST);
435 if self.mode != Mode::Fn {
436 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
437 "{}s cannot refer to statics, use \
438 a constant instead", self.mode);
439 if self.tcx.sess.teach(&err.get_code().unwrap()) {
441 "Static and const variables can refer to other const variables. But a \
442 const variable cannot refer to a static variable."
445 "To fix this, the value can be extracted as a const and then used."
451 Place::Projection(ref proj) => {
453 this.super_place(place, context, location);
455 ProjectionElem::Deref => {
456 if context.is_mutating_use() {
457 // `not_const` errors out in const contexts
460 // just make sure this doesn't get promoted
461 this.add(Qualif::NOT_CONST);
463 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
467 if let ty::RawPtr(_) = base_ty.sty {
468 if !this.tcx.features().const_raw_ptr_deref {
470 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
471 this.span, GateIssue::Language,
473 "dereferencing raw pointers in {}s is unstable",
483 ProjectionElem::ConstantIndex {..} |
484 ProjectionElem::Subslice {..} |
485 ProjectionElem::Field(..) |
486 ProjectionElem::Index(_) => {
487 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
488 if let Some(def) = base_ty.ty_adt_def() {
491 Mode::Fn => this.not_const(),
493 if !this.tcx.features().const_fn_union {
495 &this.tcx.sess.parse_sess, "const_fn_union",
496 this.span, GateIssue::Language,
497 "unions in const fn are unstable",
510 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
511 this.qualif.restrict(ty, this.tcx, this.param_env);
514 ProjectionElem::Downcast(..) => {
523 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
524 debug!("visit_operand: operand={:?} location={:?}", operand, location);
525 self.super_operand(operand, location);
529 Operand::Move(_) => {
530 // Mark the consumed locals to indicate later drops are noops.
531 if let Operand::Move(Place::Local(local)) = *operand {
532 self.local_qualif[local] = self.local_qualif[local].map(|q|
533 q - Qualif::NEEDS_DROP
537 Operand::Constant(ref constant) => {
538 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
539 // Don't peek inside trait associated constants.
540 if self.tcx.trait_of_item(*def_id).is_some() {
541 self.add_type(constant.ty);
543 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
545 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
548 // Just in case the type is more specific than
549 // the definition, e.g., impl associated const
550 // with type parameters, take it into account.
551 self.qualif.restrict(constant.ty, self.tcx, self.param_env);
558 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
559 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
560 // Recurse through operands and places.
561 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
562 let mut is_reborrow = false;
563 if let Place::Projection(ref proj) = *place {
564 if let ProjectionElem::Deref = proj.elem {
565 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
566 if let ty::Ref(..) = base_ty.sty {
573 let ctx = match kind {
574 BorrowKind::Shared =>
575 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
576 BorrowKind::Shallow =>
577 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
578 BorrowKind::Unique =>
579 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
580 BorrowKind::Mut { .. } =>
581 PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
583 self.super_place(place, ctx, location);
585 self.super_rvalue(rvalue, location);
588 self.super_rvalue(rvalue, location);
594 Rvalue::UnaryOp(UnOp::Neg, _) |
595 Rvalue::UnaryOp(UnOp::Not, _) |
596 Rvalue::NullaryOp(NullOp::SizeOf, _) |
597 Rvalue::CheckedBinaryOp(..) |
598 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
599 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
600 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
601 Rvalue::Cast(CastKind::Unsize, ..) |
602 Rvalue::Discriminant(..) |
605 Rvalue::Ref(_, kind, ref place) => {
606 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
608 // Default to forbidding the borrow and/or its promotion,
609 // due to the potential for direct or interior mutability,
610 // and only proceed by setting `forbidden_mut` to `false`.
611 let mut forbidden_mut = true;
613 if let BorrowKind::Mut { .. } = kind {
614 // In theory, any zero-sized value could be borrowed
615 // mutably without consequences. However, only &mut []
616 // is allowed right now, and only in functions.
617 if self.mode == Mode::StaticMut {
618 // Inside a `static mut`, &mut [...] is also allowed.
620 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
623 } else if let ty::Array(_, len) = ty.sty {
624 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
625 // seems unnecessary, given that this is merely a ZST.
626 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
627 forbidden_mut = false;
633 self.add(Qualif::NOT_CONST);
634 if self.mode != Mode::Fn {
635 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
636 "references in {}s may only refer \
637 to immutable values", self.mode);
638 err.span_label(self.span, format!("{}s require immutable values",
640 if self.tcx.sess.teach(&err.get_code().unwrap()) {
641 err.note("References in statics and constants may only refer to \
642 immutable values.\n\n\
643 Statics are shared everywhere, and if they refer to \
644 mutable data one might violate memory safety since \
645 holding multiple mutable references to shared data is \
647 If you really want global mutable state, try using \
648 static mut or a global UnsafeCell.");
654 // Constants cannot be borrowed if they contain interior mutability as
655 // it means that our "silent insertion of statics" could change
656 // initializer values (very bad).
657 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
658 // A reference of a MUTABLE_INTERIOR place is instead
659 // NOT_CONST (see `if forbidden_mut` below), to avoid
660 // duplicate errors (from reborrowing, for example).
661 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
662 if self.mode != Mode::Fn {
663 span_err!(self.tcx.sess, self.span, E0492,
664 "cannot borrow a constant which may contain \
665 interior mutability, create a static instead");
668 // We allow immutable borrows of frozen data.
669 forbidden_mut = false;
673 debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
676 self.add(Qualif::NOT_CONST);
678 // We might have a candidate for promotion.
679 let candidate = Candidate::Ref(location);
680 // We can only promote interior borrows of promotable temps.
681 let mut place = place;
682 while let Place::Projection(ref proj) = *place {
683 if proj.elem == ProjectionElem::Deref {
688 debug!("visit_rvalue: place={:?}", place);
689 if let Place::Local(local) = *place {
690 if self.mir.local_kind(local) == LocalKind::Temp {
691 debug!("visit_rvalue: local={:?}", local);
692 if let Some(qualif) = self.local_qualif[local] {
693 // `forbidden_mut` is false, so we can safely ignore
694 // `MUTABLE_INTERIOR` from the local's qualifications.
695 // This allows borrowing fields which don't have
696 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
697 // `let _: &'static _ = &(Cell::new(1), 2).1;`
698 debug!("visit_rvalue: qualif={:?}", qualif);
699 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
700 debug!("visit_rvalue: candidate={:?}", candidate);
701 self.promotion_candidates.push(candidate);
709 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
710 let operand_ty = operand.ty(self.mir, self.tcx);
711 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
712 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
713 match (cast_in, cast_out) {
714 (CastTy::Ptr(_), CastTy::Int(_)) |
715 (CastTy::FnPtr, CastTy::Int(_)) => {
717 if let Mode::Fn = self.mode {
718 // in normal functions, mark such casts as not promotable
719 self.add(Qualif::NOT_CONST);
720 } else if !self.tcx.features().const_raw_ptr_to_usize_cast {
721 // in const fn and constants require the feature gate
722 // FIXME: make it unsafe inside const fn and constants
724 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
725 self.span, GateIssue::Language,
727 "casting pointers to integers in {}s is unstable",
737 Rvalue::BinaryOp(op, ref lhs, _) => {
738 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
739 assert!(op == BinOp::Eq || op == BinOp::Ne ||
740 op == BinOp::Le || op == BinOp::Lt ||
741 op == BinOp::Ge || op == BinOp::Gt ||
742 op == BinOp::Offset);
745 if let Mode::Fn = self.mode {
746 // raw pointer operations are not allowed inside promoteds
747 self.add(Qualif::NOT_CONST);
748 } else if !self.tcx.features().const_compare_raw_pointers {
749 // require the feature gate inside constants and const fn
750 // FIXME: make it unsafe to use these operations
752 &self.tcx.sess.parse_sess,
753 "const_compare_raw_pointers",
756 &format!("comparing raw pointers inside {}", self.mode),
762 Rvalue::NullaryOp(NullOp::Box, _) => {
764 self.add(Qualif::NOT_CONST);
765 if self.mode != Mode::Fn {
766 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
767 "allocations are not allowed in {}s", self.mode);
768 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
769 if self.tcx.sess.teach(&err.get_code().unwrap()) {
771 "The value of statics and constants must be known at compile time, \
772 and they live for the entire lifetime of a program. Creating a boxed \
773 value allocates memory on the heap at runtime, and therefore cannot \
774 be done at compile time."
781 Rvalue::Aggregate(ref kind, _) => {
782 if let AggregateKind::Adt(def, ..) = **kind {
783 if def.has_dtor(self.tcx) {
784 self.add(Qualif::NEEDS_DROP);
787 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
788 let ty = rvalue.ty(self.mir, self.tcx);
790 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
797 fn visit_terminator_kind(&mut self,
799 kind: &TerminatorKind<'tcx>,
800 location: Location) {
801 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
802 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
803 self.visit_operand(func, location);
805 let fn_ty = func.ty(self.mir, self.tcx);
806 let mut callee_def_id = None;
807 let mut is_shuffle = false;
808 let mut is_const_fn = false;
809 let mut is_promotable_const_fn = false;
811 ty::FnDef(def_id, _) => {
812 callee_def_id = Some(def_id);
813 match self.tcx.fn_sig(def_id).abi() {
815 Abi::PlatformIntrinsic => {
816 assert!(!self.tcx.is_const_fn(def_id));
817 match &self.tcx.item_name(def_id).as_str()[..] {
836 | "add_with_overflow"
837 | "sub_with_overflow"
838 | "mul_with_overflow"
841 // no need to check feature gates, intrinsics are only callable
842 // from the libstd or with forever unstable feature gates
843 => is_const_fn = true,
844 // special intrinsic that can be called diretly without an intrinsic
845 // feature gate needs a language feature gate
847 // never promote transmute calls
848 if self.mode != Mode::Fn {
850 // const eval transmute calls only with the feature gate
851 if !self.tcx.features().const_transmute {
853 &self.tcx.sess.parse_sess, "const_transmute",
854 self.span, GateIssue::Language,
855 &format!("The use of std::mem::transmute() \
856 is gated in {}s", self.mode));
861 name if name.starts_with("simd_shuffle") => {
869 // In normal functions we only care about promotion.
870 if self.mode == Mode::Fn {
871 // Never promote const fn calls of
872 // functions without `#[rustc_promotable]`.
873 if self.tcx.is_promotable_const_fn(def_id) {
875 is_promotable_const_fn = true;
876 } else if self.tcx.is_const_fn(def_id) {
880 // stable const fns or unstable const fns with their feature gate
882 let unleash_miri = self
887 .unleash_the_miri_inside_of_you;
888 if self.tcx.is_const_fn(def_id) || unleash_miri {
890 } else if self.is_const_panic_fn(def_id) {
891 // Check the const_panic feature gate.
892 // FIXME: cannot allow this inside `allow_internal_unstable`
893 // because that would make `panic!` insta stable in constants,
894 // since the macro is marked with the attribute.
895 if self.tcx.features().const_panic {
898 // Don't allow panics in constants without the feature gate.
900 &self.tcx.sess.parse_sess,
904 &format!("panicking in {}s is unstable", self.mode),
907 } else if let Some(feature)
908 = self.tcx.is_unstable_const_fn(def_id) {
909 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
910 // functions without the feature gate active in this crate in
911 // order to report a better error message than the one below.
912 if self.span.allows_unstable(&feature.as_str()) {
913 // `allow_internal_unstable` can make such calls stable.
916 let mut err = self.tcx.sess.struct_span_err(self.span,
917 &format!("`{}` is not yet stable as a const fn",
918 self.tcx.item_path_str(def_id)));
919 if nightly_options::is_nightly_build() {
921 "add `#![feature({})]` to the \
922 crate attributes to enable",
928 // FIXME(#57563): remove this check when const fn stabilizes.
929 let (msg, note) = if let UnstableFeatures::Disallow =
930 self.tcx.sess.opts.unstable_features {
931 (format!("calls in {}s are limited to \
932 tuple structs and tuple variants",
934 Some("a limited form of compile-time function \
935 evaluation is available on a nightly \
936 compiler via `const fn`"))
938 (format!("calls in {}s are limited \
939 to constant functions, \
940 tuple structs and tuple variants",
944 let mut err = struct_span_err!(
951 if let Some(note) = note {
952 err.span_note(self.span, note);
961 if self.mode != Mode::Fn {
962 let mut err = self.tcx.sess.struct_span_err(
964 &format!("function pointers are not allowed in const fn"));
975 let constant_arguments = callee_def_id.and_then(|id| {
976 args_required_const(self.tcx, id)
978 for (i, arg) in args.iter().enumerate() {
980 this.visit_operand(arg, location);
981 if this.mode != Mode::Fn {
984 let candidate = Candidate::Argument { bb, index: i };
985 if is_shuffle && i == 2 {
986 if this.qualif.is_empty() {
987 debug!("visit_terminator_kind: candidate={:?}", candidate);
988 this.promotion_candidates.push(candidate);
990 span_err!(this.tcx.sess, this.span, E0526,
991 "shuffle indices are not constant");
996 let constant_arguments = match constant_arguments.as_ref() {
1000 if !constant_arguments.contains(&i) {
1003 // Since the argument is required to be constant,
1004 // we care about constness, not promotability.
1005 // If we checked for promotability, we'd miss out on
1006 // the results of function calls (which are never promoted
1007 // in runtime code).
1008 // This is not a problem, because the argument explicitly
1009 // requests constness, in contrast to regular promotion
1010 // which happens even without the user requesting it.
1011 // We can error out with a hard error if the argument is not
1013 if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
1014 debug!("visit_terminator_kind: candidate={:?}", candidate);
1015 this.promotion_candidates.push(candidate);
1017 this.tcx.sess.span_err(this.span,
1018 &format!("argument {} is required to be a constant",
1024 // non-const fn calls
1026 self.qualif = Qualif::NOT_CONST;
1027 if self.mode != Mode::Fn {
1028 self.tcx.sess.delay_span_bug(
1030 "should have reported an error about non-const fn calls in constants",
1035 if let Some((ref dest, _)) = *destination {
1036 // Avoid propagating irrelevant callee/argument qualifications.
1037 if self.qualif.intersects(Qualif::CONST_ERROR) {
1038 self.qualif = Qualif::NOT_CONST;
1040 // Be conservative about the returned value of a const fn.
1042 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
1043 if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
1044 self.qualif = Qualif::NOT_PROMOTABLE;
1046 self.qualif = Qualif::empty();
1050 self.assign(dest, location);
1052 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1053 self.super_terminator_kind(bb, kind, location);
1055 // Deny *any* live drops anywhere other than functions.
1056 if self.mode != Mode::Fn {
1057 unleash_miri!(self);
1058 // HACK(eddyb): emulate a bit of dataflow analysis,
1059 // conservatively, that drop elaboration will do.
1060 let needs_drop = if let Place::Local(local) = *place {
1061 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1062 Some(self.mir.local_decls[local].source_info.span)
1070 if let Some(span) = needs_drop {
1071 // Double-check the type being dropped, to minimize false positives.
1072 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1073 if ty.needs_drop(self.tcx, self.param_env) {
1074 struct_span_err!(self.tcx.sess, span, E0493,
1075 "destructors cannot be evaluated at compile-time")
1076 .span_label(span, format!("{}s cannot evaluate destructors",
1083 // Qualify any operands inside other terminators.
1084 self.super_terminator_kind(bb, kind, location);
1088 fn visit_assign(&mut self,
1091 rvalue: &Rvalue<'tcx>,
1092 location: Location) {
1093 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1094 self.visit_rvalue(rvalue, location);
1096 self.assign(dest, location);
1099 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1100 debug!("visit_source_info: source_info={:?}", source_info);
1101 self.span = source_info.span;
1104 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1105 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1107 this.visit_source_info(&statement.source_info);
1108 match statement.kind {
1109 StatementKind::Assign(ref place, ref rvalue) => {
1110 this.visit_assign(bb, place, rvalue, location);
1112 StatementKind::FakeRead(..) |
1113 StatementKind::SetDiscriminant { .. } |
1114 StatementKind::StorageLive(_) |
1115 StatementKind::StorageDead(_) |
1116 StatementKind::InlineAsm {..} |
1117 StatementKind::Retag { .. } |
1118 StatementKind::AscribeUserType(..) |
1119 StatementKind::Nop => {}
1124 fn visit_terminator(&mut self,
1126 terminator: &Terminator<'tcx>,
1127 location: Location) {
1128 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1129 self.nest(|this| this.super_terminator(bb, terminator, location));
1133 pub fn provide(providers: &mut Providers<'_>) {
1134 *providers = Providers {
1140 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1142 -> (u8, Lrc<BitSet<Local>>) {
1143 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1144 // cannot yet be stolen), because `mir_validated()`, which steals
1145 // from `mir_const(), forces this query to execute before
1146 // performing the steal.
1147 let mir = &tcx.mir_const(def_id).borrow();
1149 if mir.return_ty().references_error() {
1150 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1151 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1154 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1155 let (qualif, promoted_temps) = qualifier.qualify_const();
1156 (qualif.bits(), promoted_temps)
1159 pub struct QualifyAndPromoteConstants;
1161 impl MirPass for QualifyAndPromoteConstants {
1162 fn run_pass<'a, 'tcx>(&self,
1163 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1164 src: MirSource<'tcx>,
1165 mir: &mut Mir<'tcx>) {
1166 // There's not really any point in promoting errorful MIR.
1167 if mir.return_ty().references_error() {
1168 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1172 if src.promoted.is_some() {
1176 let def_id = src.def_id();
1177 let id = tcx.hir().as_local_node_id(def_id).unwrap();
1178 let mut const_promoted_temps = None;
1179 let mode = match tcx.hir().body_owner_kind(id) {
1180 hir::BodyOwnerKind::Closure => Mode::Fn,
1181 hir::BodyOwnerKind::Fn => {
1182 if tcx.is_const_fn(def_id) {
1188 hir::BodyOwnerKind::Const => {
1189 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1192 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1193 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1196 debug!("run_pass: mode={:?}", mode);
1197 if mode == Mode::Fn || mode == Mode::ConstFn {
1198 // This is ugly because Qualifier holds onto mir,
1199 // which can't be mutated until its scope ends.
1200 let (temps, candidates) = {
1201 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1202 if mode == Mode::ConstFn {
1203 if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
1204 qualifier.qualify_const();
1205 } else if tcx.is_min_const_fn(def_id) {
1206 // enforce `min_const_fn` for stable const fns
1207 use super::qualify_min_const_fn::is_min_const_fn;
1208 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1209 tcx.sess.span_err(span, &err);
1211 // this should not produce any errors, but better safe than sorry
1213 qualifier.qualify_const();
1216 // Enforce a constant-like CFG for `const fn`.
1217 qualifier.qualify_const();
1220 while let Some((bb, data)) = qualifier.rpo.next() {
1221 qualifier.visit_basic_block_data(bb, data);
1225 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1228 // Do the actual promotion, now that we know what's viable.
1229 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1231 if !mir.control_flow_destroyed.is_empty() {
1232 let mut locals = mir.vars_iter();
1233 if let Some(local) = locals.next() {
1234 let span = mir.local_decls[local].source_info.span;
1235 let mut error = tcx.sess.struct_span_err(
1238 "new features like let bindings are not permitted in {}s \
1239 which also use short circuiting operators",
1243 for (span, kind) in mir.control_flow_destroyed.iter() {
1246 &format!("use of {} here does not actually short circuit due to \
1247 the const evaluator presently not being able to do control flow. \
1248 See https://github.com/rust-lang/rust/issues/49146 for more \
1249 information.", kind),
1252 for local in locals {
1253 let span = mir.local_decls[local].source_info.span;
1256 "more locals defined here",
1262 let promoted_temps = if mode == Mode::Const {
1263 // Already computed by `mir_const_qualif`.
1264 const_promoted_temps.unwrap()
1266 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1269 // In `const` and `static` everything without `StorageDead`
1270 // is `'static`, we don't have to create promoted MIR fragments,
1271 // just remove `Drop` and `StorageDead` on "promoted" locals.
1272 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1273 for block in mir.basic_blocks_mut() {
1274 block.statements.retain(|statement| {
1275 match statement.kind {
1276 StatementKind::StorageDead(index) => {
1277 !promoted_temps.contains(index)
1282 let terminator = block.terminator_mut();
1283 match terminator.kind {
1284 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1285 if promoted_temps.contains(index) {
1286 terminator.kind = TerminatorKind::Goto {
1296 // Statics must be Sync.
1297 if mode == Mode::Static {
1298 // `#[thread_local]` statics don't have to be `Sync`.
1299 for attr in &tcx.get_attrs(def_id)[..] {
1300 if attr.check_name("thread_local") {
1304 let ty = mir.return_ty();
1305 tcx.infer_ctxt().enter(|infcx| {
1306 let param_env = ty::ParamEnv::empty();
1307 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1308 let mut fulfillment_cx = traits::FulfillmentContext::new();
1309 fulfillment_cx.register_bound(&infcx,
1312 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1314 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1315 infcx.report_fulfillment_errors(&err, None, false);
1322 fn args_required_const(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1323 let attrs = tcx.get_attrs(def_id);
1324 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1325 let mut ret = FxHashSet::default();
1326 for meta in attr.meta_item_list()? {
1327 match meta.literal()?.node {
1328 LitKind::Int(a, _) => { ret.insert(a as usize); }