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::{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;
44 // Not constant at all - non-`const fn` calls, asm!,
45 // pointer comparisons, ptr-to-int casts, etc.
46 const NOT_CONST = 1 << 2;
48 // Refers to temporaries which cannot be promoted as
49 // promote_consts decided they weren't simple enough.
50 const NOT_PROMOTABLE = 1 << 3;
54 impl<'a, 'tcx> Qualif {
55 /// Compute the qualifications for the given type.
58 tcx: TyCtxt<'a, 'tcx, 'tcx>,
59 param_env: ty::ParamEnv<'tcx>,
61 let mut qualif = Self::empty();
62 if !ty.is_freeze(tcx, param_env, DUMMY_SP) {
63 qualif = qualif | Qualif::MUTABLE_INTERIOR;
65 if ty.needs_drop(tcx, param_env) {
66 qualif = qualif | Qualif::NEEDS_DROP;
71 /// Remove flags which are impossible for the given type.
75 tcx: TyCtxt<'a, 'tcx, 'tcx>,
76 param_env: ty::ParamEnv<'tcx>,
78 let ty_qualif = Self::any_value_of_ty(ty, tcx, param_env);
79 if !ty_qualif.contains(Qualif::MUTABLE_INTERIOR) {
80 *self = *self - Qualif::MUTABLE_INTERIOR;
82 if !ty_qualif.contains(Qualif::NEEDS_DROP) {
83 *self = *self - Qualif::NEEDS_DROP;
88 /// What kind of item we are in.
89 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
98 impl fmt::Display for Mode {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 Mode::Const => write!(f, "constant"),
102 Mode::Static | Mode::StaticMut => write!(f, "static"),
103 Mode::ConstFn => write!(f, "constant function"),
104 Mode::Fn => write!(f, "function")
109 struct Qualifier<'a, 'tcx> {
110 tcx: TyCtxt<'a, 'tcx, 'tcx>,
111 param_env: ty::ParamEnv<'tcx>,
115 local_qualif: &'a IndexVec<Local, Qualif>,
118 impl<'a, 'tcx> Qualifier<'a, 'tcx> {
119 fn qualify_any_value_of_ty(&self, ty: Ty<'tcx>) -> Qualif {
120 Qualif::any_value_of_ty(ty, self.tcx, self.param_env)
123 fn qualify_local(&self, local: Local) -> Qualif {
124 self.local_qualif[local]
127 fn qualify_projection_elem(&self, proj: &PlaceElem<'tcx>) -> Qualif {
129 ProjectionElem::Deref |
130 ProjectionElem::Subslice { .. } |
131 ProjectionElem::Field(..) |
132 ProjectionElem::ConstantIndex { .. } |
133 ProjectionElem::Downcast(..) => Qualif::empty(),
135 ProjectionElem::Index(local) => self.qualify_local(local),
139 fn qualify_place(&self, place: &Place<'tcx>) -> Qualif {
141 Place::Local(local) => self.qualify_local(local),
142 Place::Promoted(_) => bug!("qualifying already promoted MIR"),
143 Place::Static(ref global) => {
145 .get_attrs(global.def_id)
147 .any(|attr| attr.check_name("thread_local")) {
148 return Qualif::NOT_CONST;
151 // Only allow statics (not consts) to refer to other statics.
152 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
158 Place::Projection(ref proj) => {
160 self.qualify_place(&proj.base) |
161 self.qualify_projection_elem(&proj.elem);
163 ProjectionElem::Deref |
164 ProjectionElem::Downcast(..) => qualif | Qualif::NOT_CONST,
166 ProjectionElem::ConstantIndex {..} |
167 ProjectionElem::Subslice {..} |
168 ProjectionElem::Field(..) |
169 ProjectionElem::Index(_) => {
170 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
171 if let Some(def) = base_ty.ty_adt_def() {
174 Mode::Fn => qualif = qualif | Qualif::NOT_CONST,
184 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
185 qualif.restrict(ty, self.tcx, self.param_env);
193 fn qualify_operand(&self, operand: &Operand<'tcx>) -> Qualif {
195 Operand::Copy(ref place) |
196 Operand::Move(ref place) => self.qualify_place(place),
198 Operand::Constant(ref constant) => {
199 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
200 // Don't peek inside trait associated constants.
201 if self.tcx.trait_of_item(*def_id).is_some() {
202 self.qualify_any_value_of_ty(constant.ty)
204 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
206 let mut qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
208 // Just in case the type is more specific than
209 // the definition, e.g., impl associated const
210 // with type parameters, take it into account.
211 qualif.restrict(constant.ty, self.tcx, self.param_env);
221 fn qualify_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Qualif {
223 Rvalue::NullaryOp(NullOp::SizeOf, _) => Qualif::empty(),
225 Rvalue::Use(ref operand) |
226 Rvalue::Repeat(ref operand, _) |
227 Rvalue::UnaryOp(UnOp::Neg, ref operand) |
228 Rvalue::UnaryOp(UnOp::Not, ref operand) |
229 Rvalue::Cast(CastKind::ReifyFnPointer, ref operand, _) |
230 Rvalue::Cast(CastKind::UnsafeFnPointer, ref operand, _) |
231 Rvalue::Cast(CastKind::ClosureFnPointer, ref operand, _) |
232 Rvalue::Cast(CastKind::Unsize, ref operand, _) => {
233 self.qualify_operand(operand)
236 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
237 self.qualify_operand(lhs) | self.qualify_operand(rhs)
240 Rvalue::Discriminant(ref place) |
241 Rvalue::Len(ref place) => self.qualify_place(place),
243 Rvalue::Ref(_, kind, ref place) => {
244 let mut reborrow_qualif = None;
245 if let Place::Projection(ref proj) = *place {
246 if let ProjectionElem::Deref = proj.elem {
247 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
248 if let ty::Ref(..) = base_ty.sty {
249 reborrow_qualif = Some(self.qualify_place(&proj.base));
254 let mut qualif = reborrow_qualif.unwrap_or_else(|| {
255 self.qualify_place(place)
258 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
260 if let BorrowKind::Mut { .. } = kind {
261 // In theory, any zero-sized value could be borrowed
262 // mutably without consequences. However, only &mut []
263 // is allowed right now, and only in functions.
264 let allowed = if self.mode == Mode::StaticMut {
265 // Inside a `static mut`, &mut [...] is also allowed.
267 ty::Array(..) | ty::Slice(_) => true,
270 } else if let ty::Array(_, len) = ty.sty {
271 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
272 // seems unnecessary, given that this is merely a ZST.
273 len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn
279 qualif = qualif | Qualif::MUTABLE_INTERIOR;
286 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
287 let mut qualif = self.qualify_operand(operand);
289 let operand_ty = operand.ty(self.mir, self.tcx);
290 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
291 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
292 match (cast_in, cast_out) {
293 (CastTy::Ptr(_), CastTy::Int(_)) |
294 (CastTy::FnPtr, CastTy::Int(_)) => {
295 if let Mode::Fn = self.mode {
296 // in normal functions, mark such casts as not promotable
297 qualif = qualif | Qualif::NOT_CONST;
306 Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
307 let mut qualif = self.qualify_operand(lhs) | self.qualify_operand(rhs);
309 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
310 assert!(op == BinOp::Eq || op == BinOp::Ne ||
311 op == BinOp::Le || op == BinOp::Lt ||
312 op == BinOp::Ge || op == BinOp::Gt ||
313 op == BinOp::Offset);
315 if let Mode::Fn = self.mode {
316 // raw pointer operations are not allowed inside promoteds
317 qualif = qualif | Qualif::NOT_CONST;
324 Rvalue::NullaryOp(NullOp::Box, _) => Qualif::NOT_CONST,
326 Rvalue::Aggregate(ref kind, ref operands) => {
327 let mut qualif = operands.iter().map(|o| self.qualify_operand(o))
328 .fold(Qualif::empty(), |a, b| a | b);
330 if let AggregateKind::Adt(def, ..) = **kind {
331 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
332 let ty = rvalue.ty(self.mir, self.tcx);
333 qualif = qualif | self.qualify_any_value_of_ty(ty);
334 assert!(qualif.contains(Qualif::MUTABLE_INTERIOR));
337 if def.has_dtor(self.tcx) {
338 qualif = qualif | Qualif::NEEDS_DROP;
347 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
348 Some(def_id) == self.tcx.lang_items().panic_fn() ||
349 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
354 callee: &Operand<'tcx>,
355 args: &[Operand<'tcx>],
358 let fn_ty = callee.ty(self.mir, self.tcx);
359 let mut is_promotable_const_fn = false;
360 let is_const_fn = match fn_ty.sty {
361 ty::FnDef(def_id, _) => {
362 match self.tcx.fn_sig(def_id).abi() {
364 Abi::PlatformIntrinsic => {
365 assert!(!self.tcx.is_const_fn(def_id));
366 match &self.tcx.item_name(def_id).as_str()[..] {
385 | "add_with_overflow"
386 | "sub_with_overflow"
387 | "mul_with_overflow"
397 // Never promote runtime `const fn` calls of
398 // functions without `#[rustc_promotable]`.
399 if self.tcx.is_promotable_const_fn(def_id) {
400 is_promotable_const_fn = true;
403 if self.mode == Mode::Fn {
404 self.tcx.is_const_fn(def_id)
406 self.tcx.is_const_fn(def_id) ||
407 self.is_const_panic_fn(def_id) ||
408 self.tcx.is_unstable_const_fn(def_id).is_some()
416 // Bail out on oon-`const fn` calls or if the callee had errors.
417 if !is_const_fn || self.qualify_operand(callee).intersects(Qualif::NOT_CONST) {
418 return Qualif::NOT_CONST;
421 // Bail out if any arguments had errors.
423 if self.qualify_operand(arg).intersects(Qualif::NOT_CONST) {
424 return Qualif::NOT_CONST;
428 // Be conservative about the returned value of a const fn.
429 let qualif = self.qualify_any_value_of_ty(return_ty);
430 if !is_promotable_const_fn && self.mode == Mode::Fn {
431 qualif | Qualif::NOT_PROMOTABLE
438 struct Checker<'a, 'tcx> {
439 tcx: TyCtxt<'a, 'tcx, 'tcx>,
440 param_env: ty::ParamEnv<'tcx>,
445 rpo: ReversePostorder<'a, 'tcx>,
447 local_qualif: IndexVec<Local, Qualif>,
448 temp_promotion_state: IndexVec<Local, TempState>,
449 promotion_candidates: Vec<Candidate>,
452 macro_rules! unleash_miri {
454 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
455 $this.tcx.sess.span_warn($this.span, "skipping const checks");
461 impl<'a, 'tcx> Checker<'a, 'tcx> {
462 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
467 assert!(def_id.is_local());
468 let mut rpo = traversal::reverse_postorder(mir);
469 let temps = promote_consts::collect_temps(mir, &mut rpo);
472 let param_env = tcx.param_env(def_id);
474 let local_qualif = mir.local_decls.iter_enumerated().map(|(local, decl)| {
475 match mir.local_kind(local) {
477 Qualif::any_value_of_ty(decl.ty, tcx, param_env) |
478 Qualif::NOT_PROMOTABLE
481 LocalKind::Var if mode == Mode::Fn => Qualif::NOT_CONST,
483 LocalKind::Temp if !temps[local].is_promotable() => {
484 Qualif::NOT_PROMOTABLE
487 _ => Qualif::empty(),
500 temp_promotion_state: temps,
501 promotion_candidates: vec![]
505 fn qualifier(&'a self) -> Qualifier<'a, 'tcx> {
508 param_env: self.param_env,
511 local_qualif: &self.local_qualif,
515 // FIXME(eddyb) we could split the errors into meaningful
516 // categories, but enabling full miri would make that
517 // slightly pointless (even with feature-gating).
518 fn not_const(&mut self) {
520 if self.mode != Mode::Fn {
521 let mut err = struct_span_err!(
525 "{} contains unimplemented expression type",
528 if self.tcx.sess.teach(&err.get_code().unwrap()) {
529 err.note("A function call isn't allowed in the const's initialization expression \
530 because the expression's value must be known at compile-time.");
531 err.note("Remember: you can't use a function call inside a const's initialization \
532 expression! However, you can use it anywhere else.");
538 /// Assigns an rvalue/call qualification to the given destination.
539 fn assign(&mut self, dest: &Place<'tcx>, qualif: Qualif, location: Location) {
540 trace!("assign: {:?} <- {:?}", dest, qualif);
542 // Only handle promotable temps in non-const functions.
543 if self.mode == Mode::Fn {
544 if let Place::Local(index) = *dest {
545 if self.mir.local_kind(index) == LocalKind::Temp
546 && self.temp_promotion_state[index].is_promotable() {
547 debug!("store to promotable temp {:?} ({:?})", index, qualif);
548 let slot = &mut self.local_qualif[index];
549 if !slot.is_empty() {
550 span_bug!(self.span, "multiple assignments to {:?}", dest);
561 // We treat all locals equal in constants
562 Place::Local(index) => break *index,
563 // projections are transparent for assignments
564 // we qualify the entire destination at once, even if just a field would have
565 // stricter qualification
566 Place::Projection(proj) => {
567 // Catch more errors in the destination. `visit_place` also checks various
568 // projection rules like union field access and raw pointer deref
571 PlaceContext::MutatingUse(MutatingUseContext::Store),
576 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
577 Place::Static(..) => {
578 // Catch more errors in the destination. `visit_place` also checks that we
579 // do not try to access statics from constants or try to mutate statics
582 PlaceContext::MutatingUse(MutatingUseContext::Store),
589 debug!("store to var {:?}", index);
590 // this is overly restrictive, because even full assignments do not clear the qualif
591 // While we could special case full assignments, this would be inconsistent with
592 // aggregates where we overwrite all fields via assignments, which would not get
594 let slot = &mut self.local_qualif[index];
595 *slot = *slot | qualif;
597 // Ensure we keep the `NOT_PROMOTABLE` flag is preserved.
598 // NOTE(eddyb) this is actually unnecessary right now, as
599 // we never replace the local's qualif (but we might in
600 // the future) - also, if `NOT_PROMOTABLE` only matters
601 // for `Mode::Fn`, then this is also pointless.
602 if self.mir.local_kind(index) == LocalKind::Temp {
603 if !self.temp_promotion_state[index].is_promotable() {
604 *slot = *slot | Qualif::NOT_PROMOTABLE;
609 /// Check a whole const, static initializer or const fn.
610 fn check_const(&mut self) -> (Qualif, Lrc<BitSet<Local>>) {
611 debug!("const-checking {} {:?}", self.mode, self.def_id);
615 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
616 let mut bb = START_BLOCK;
618 seen_blocks.insert(bb.index());
620 self.visit_basic_block_data(bb, &mir[bb]);
622 let target = match mir[bb].terminator().kind {
623 TerminatorKind::Goto { target } |
624 TerminatorKind::Drop { target, .. } |
625 TerminatorKind::Assert { target, .. } |
626 TerminatorKind::Call { destination: Some((_, target)), .. } => {
630 // Non-terminating calls cannot produce any value.
631 TerminatorKind::Call { destination: None, .. } => {
635 TerminatorKind::SwitchInt {..} |
636 TerminatorKind::DropAndReplace { .. } |
637 TerminatorKind::Resume |
638 TerminatorKind::Abort |
639 TerminatorKind::GeneratorDrop |
640 TerminatorKind::Yield { .. } |
641 TerminatorKind::Unreachable |
642 TerminatorKind::FalseEdges { .. } |
643 TerminatorKind::FalseUnwind { .. } => None,
645 TerminatorKind::Return => {
652 Some(target) if !seen_blocks.contains(target.index()) => {
662 let mut qualif = self.local_qualif[RETURN_PLACE];
664 // Account for errors in consts by using the
665 // conservative type qualification instead.
666 if qualif.intersects(Qualif::NOT_CONST) {
667 qualif = self.qualifier().qualify_any_value_of_ty(mir.return_ty());
671 // Collect all the temps we need to promote.
672 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
674 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
675 for candidate in &self.promotion_candidates {
677 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
678 match self.mir[bb].statements[stmt_idx].kind {
679 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
680 promoted_temps.insert(index);
685 Candidate::Argument { .. } => {}
689 (qualif, Lrc::new(promoted_temps))
693 /// Checks MIR for const-correctness, using `Qualifier`
694 /// for value qualifications, and accumulates writes of
695 /// rvalue/call results to locals, in `local_qualif`.
696 /// For functions (constant or not), it also records
697 /// candidates for promotion in `promotion_candidates`.
698 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
699 fn visit_place(&mut self,
701 context: PlaceContext<'tcx>,
702 location: Location) {
703 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
704 self.super_place(place, context, location);
707 Place::Promoted(_) => {}
708 Place::Static(ref global) => {
710 .get_attrs(global.def_id)
712 .any(|attr| attr.check_name("thread_local")) {
713 if self.mode != Mode::Fn {
714 span_err!(self.tcx.sess, self.span, E0625,
715 "thread-local statics cannot be \
716 accessed at compile-time");
721 // Only allow statics (not consts) to refer to other statics.
722 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
723 if self.mode == Mode::Static && context.is_mutating_use() {
724 // this is not strictly necessary as miri will also bail out
725 // For interior mutability we can't really catch this statically as that
726 // goes through raw pointers and intermediate temporaries, so miri has
727 // to catch this anyway
728 self.tcx.sess.span_err(
730 "cannot mutate statics in the initializer of another static",
737 if self.mode != Mode::Fn {
738 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
739 "{}s cannot refer to statics, use \
740 a constant instead", self.mode);
741 if self.tcx.sess.teach(&err.get_code().unwrap()) {
743 "Static and const variables can refer to other const variables. But a \
744 const variable cannot refer to a static variable."
747 "To fix this, the value can be extracted as a const and then used."
753 Place::Projection(ref proj) => {
755 ProjectionElem::Deref => {
756 if context.is_mutating_use() {
757 // `not_const` errors out in const contexts
760 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
764 if let ty::RawPtr(_) = base_ty.sty {
765 if !self.tcx.features().const_raw_ptr_deref {
767 &self.tcx.sess.parse_sess, "const_raw_ptr_deref",
768 self.span, GateIssue::Language,
770 "dereferencing raw pointers in {}s is unstable",
780 ProjectionElem::ConstantIndex {..} |
781 ProjectionElem::Subslice {..} |
782 ProjectionElem::Field(..) |
783 ProjectionElem::Index(_) => {
784 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
785 if let Some(def) = base_ty.ty_adt_def() {
789 if !self.tcx.features().const_fn_union {
791 &self.tcx.sess.parse_sess, "const_fn_union",
792 self.span, GateIssue::Language,
793 "unions in const fn are unstable",
808 ProjectionElem::Downcast(..) => {
816 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
817 debug!("visit_operand: operand={:?} location={:?}", operand, location);
818 self.super_operand(operand, location);
821 Operand::Move(ref place) => {
822 // Mark the consumed locals to indicate later drops are noops.
823 if let Place::Local(local) = *place {
824 let slot = &mut self.local_qualif[local];
825 *slot = *slot - Qualif::NEEDS_DROP;
829 Operand::Constant(_) => {}
833 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
834 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
836 // Check nested operands and places.
837 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
838 // Special-case reborrows.
839 let mut is_reborrow = false;
840 if let Place::Projection(ref proj) = *place {
841 if let ProjectionElem::Deref = proj.elem {
842 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
843 if let ty::Ref(..) = base_ty.sty {
850 let ctx = match kind {
851 BorrowKind::Shared => PlaceContext::NonMutatingUse(
852 NonMutatingUseContext::SharedBorrow(region),
854 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
855 NonMutatingUseContext::ShallowBorrow(region),
857 BorrowKind::Unique => PlaceContext::NonMutatingUse(
858 NonMutatingUseContext::UniqueBorrow(region),
860 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
861 MutatingUseContext::Borrow(region),
864 self.super_place(place, ctx, location);
866 self.super_rvalue(rvalue, location);
869 self.super_rvalue(rvalue, location);
875 Rvalue::UnaryOp(UnOp::Neg, _) |
876 Rvalue::UnaryOp(UnOp::Not, _) |
877 Rvalue::NullaryOp(NullOp::SizeOf, _) |
878 Rvalue::CheckedBinaryOp(..) |
879 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
880 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
881 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
882 Rvalue::Cast(CastKind::Unsize, ..) |
883 Rvalue::Discriminant(..) |
886 Rvalue::Aggregate(..) => {}
888 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
889 let operand_ty = operand.ty(self.mir, self.tcx);
890 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
891 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
892 match (cast_in, cast_out) {
893 (CastTy::Ptr(_), CastTy::Int(_)) |
894 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::Fn => {
896 if !self.tcx.features().const_raw_ptr_to_usize_cast {
897 // in const fn and constants require the feature gate
898 // FIXME: make it unsafe inside const fn and constants
900 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
901 self.span, GateIssue::Language,
903 "casting pointers to integers in {}s is unstable",
913 Rvalue::BinaryOp(op, ref lhs, _) => {
914 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
915 assert!(op == BinOp::Eq || op == BinOp::Ne ||
916 op == BinOp::Le || op == BinOp::Lt ||
917 op == BinOp::Ge || op == BinOp::Gt ||
918 op == BinOp::Offset);
921 if self.mode != Mode::Fn && !self.tcx.features().const_compare_raw_pointers {
922 // require the feature gate inside constants and const fn
923 // FIXME: make it unsafe to use these operations
925 &self.tcx.sess.parse_sess,
926 "const_compare_raw_pointers",
929 &format!("comparing raw pointers inside {}", self.mode),
935 Rvalue::NullaryOp(NullOp::Box, _) => {
937 if self.mode != Mode::Fn {
938 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
939 "allocations are not allowed in {}s", self.mode);
940 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
941 if self.tcx.sess.teach(&err.get_code().unwrap()) {
943 "The value of statics and constants must be known at compile time, \
944 and they live for the entire lifetime of a program. Creating a boxed \
945 value allocates memory on the heap at runtime, and therefore cannot \
946 be done at compile time."
955 fn visit_terminator_kind(&mut self,
957 kind: &TerminatorKind<'tcx>,
958 location: Location) {
959 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
960 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
961 if let Some((ref dest, _)) = *destination {
962 let ty = dest.ty(self.mir, self.tcx).to_ty(self.tcx);
963 let qualif = self.qualifier().qualify_call(func, args, ty);
964 self.assign(dest, qualif, location);
967 let fn_ty = func.ty(self.mir, self.tcx);
968 let mut callee_def_id = None;
969 let mut is_shuffle = false;
971 ty::FnDef(def_id, _) => {
972 callee_def_id = Some(def_id);
973 match self.tcx.fn_sig(def_id).abi() {
975 Abi::PlatformIntrinsic => {
976 assert!(!self.tcx.is_const_fn(def_id));
977 match &self.tcx.item_name(def_id).as_str()[..] {
978 // special intrinsic that can be called diretly without an intrinsic
979 // feature gate needs a language feature gate
981 // never promote transmute calls
982 if self.mode != Mode::Fn {
983 // const eval transmute calls only with the feature gate
984 if !self.tcx.features().const_transmute {
986 &self.tcx.sess.parse_sess, "const_transmute",
987 self.span, GateIssue::Language,
988 &format!("The use of std::mem::transmute() \
989 is gated in {}s", self.mode));
994 name if name.starts_with("simd_shuffle") => {
998 // no need to check feature gates, intrinsics are only callable
999 // from the libstd or with forever unstable feature gates
1004 // In normal functions no calls are feature-gated.
1005 if self.mode != Mode::Fn {
1006 let unleash_miri = self
1011 .unleash_the_miri_inside_of_you;
1012 if self.tcx.is_const_fn(def_id) || unleash_miri {
1013 // stable const fns or unstable const fns
1014 // with their feature gate active
1015 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1016 } else if self.qualifier().is_const_panic_fn(def_id) {
1017 // Check the const_panic feature gate.
1018 // FIXME: cannot allow this inside `allow_internal_unstable`
1019 // because that would make `panic!` insta stable in constants,
1020 // since the macro is marked with the attribute.
1021 if !self.tcx.features().const_panic {
1022 // Don't allow panics in constants without the feature gate.
1024 &self.tcx.sess.parse_sess,
1027 GateIssue::Language,
1028 &format!("panicking in {}s is unstable", self.mode),
1031 } else if let Some(feature)
1032 = self.tcx.is_unstable_const_fn(def_id) {
1033 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1034 // functions without the feature gate active in this crate in
1035 // order to report a better error message than the one below.
1036 if !self.span.allows_unstable(&feature.as_str()) {
1037 let mut err = self.tcx.sess.struct_span_err(self.span,
1038 &format!("`{}` is not yet stable as a const fn",
1039 self.tcx.item_path_str(def_id)));
1040 if nightly_options::is_nightly_build() {
1042 "add `#![feature({})]` to the \
1043 crate attributes to enable",
1049 let mut err = struct_span_err!(
1053 "calls in {}s are limited to constant functions, \
1054 tuple structs and tuple variants",
1064 if self.mode != Mode::Fn {
1065 let mut err = self.tcx.sess.struct_span_err(
1067 &format!("function pointers are not allowed in const fn"));
1076 if self.mode == Mode::Fn {
1077 let constant_args = callee_def_id.and_then(|id| {
1078 args_required_const(self.tcx, id)
1079 }).unwrap_or_default();
1080 for (i, arg) in args.iter().enumerate() {
1081 if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1085 let candidate = Candidate::Argument { bb, index: i };
1086 // Since the argument is required to be constant,
1087 // we care about constness, not promotability.
1088 // If we checked for promotability, we'd miss out on
1089 // the results of function calls (which are never promoted
1090 // in runtime code).
1091 // This is not a problem, because the argument explicitly
1092 // requests constness, in contrast to regular promotion
1093 // which happens even without the user requesting it.
1094 // We can error out with a hard error if the argument is not
1096 let arg_qualif = self.qualifier().qualify_operand(arg);
1097 if (arg_qualif - Qualif::NOT_PROMOTABLE).is_empty() {
1098 debug!("visit_terminator_kind: candidate={:?}", candidate);
1099 self.promotion_candidates.push(candidate);
1102 span_err!(self.tcx.sess, self.span, E0526,
1103 "shuffle indices are not constant");
1105 self.tcx.sess.span_err(self.span,
1106 &format!("argument {} is required to be a constant",
1113 // Check callee and argument operands.
1114 self.visit_operand(func, location);
1116 self.visit_operand(arg, location);
1118 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1119 self.super_terminator_kind(bb, kind, location);
1121 // Deny *any* live drops anywhere other than functions.
1122 if self.mode != Mode::Fn {
1123 unleash_miri!(self);
1124 // HACK(eddyb): emulate a bit of dataflow analysis,
1125 // conservatively, that drop elaboration will do.
1126 let needs_drop = if let Place::Local(local) = *place {
1127 if self.local_qualif[local].contains(Qualif::NEEDS_DROP) {
1128 Some(self.mir.local_decls[local].source_info.span)
1136 if let Some(span) = needs_drop {
1137 // Double-check the type being dropped, to minimize false positives.
1138 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1139 if ty.needs_drop(self.tcx, self.param_env) {
1140 struct_span_err!(self.tcx.sess, span, E0493,
1141 "destructors cannot be evaluated at compile-time")
1142 .span_label(span, format!("{}s cannot evaluate destructors",
1149 // Qualify any operands inside other terminators.
1150 self.super_terminator_kind(bb, kind, location);
1154 fn visit_assign(&mut self,
1157 rvalue: &Rvalue<'tcx>,
1158 location: Location) {
1159 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1160 let mut qualif = self.qualifier().qualify_rvalue(rvalue);
1162 if let Rvalue::Ref(_, kind, ref place) = *rvalue {
1163 // Getting `MUTABLE_INTERIOR` from `qualify_rvalue` means
1164 // the borrowed place is disallowed from being borrowed,
1165 // due to either a mutable borrow (with some exceptions),
1166 // or an shared borrow of a value with interior mutability.
1167 // Then `MUTABLE_INTERIOR` is replaced with `NOT_CONST`,
1168 // to avoid duplicate errors (e.g. from reborrowing).
1169 if qualif.contains(Qualif::MUTABLE_INTERIOR) {
1170 qualif = (qualif - Qualif::MUTABLE_INTERIOR) | Qualif::NOT_CONST;
1172 if self.mode != Mode::Fn {
1173 if let BorrowKind::Mut { .. } = kind {
1174 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
1175 "references in {}s may only refer \
1176 to immutable values", self.mode);
1177 err.span_label(self.span, format!("{}s require immutable values",
1179 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1180 err.note("References in statics and constants may only refer to \
1181 immutable values.\n\n\
1182 Statics are shared everywhere, and if they refer to \
1183 mutable data one might violate memory safety since \
1184 holding multiple mutable references to shared data is \
1186 If you really want global mutable state, try using \
1187 static mut or a global UnsafeCell.");
1191 span_err!(self.tcx.sess, self.span, E0492,
1192 "cannot borrow a constant which may contain \
1193 interior mutability, create a static instead");
1197 // We might have a candidate for promotion.
1198 let candidate = Candidate::Ref(location);
1199 // We can only promote interior borrows of promotable temps.
1200 let mut place = place;
1201 while let Place::Projection(ref proj) = *place {
1202 if proj.elem == ProjectionElem::Deref {
1207 debug!("qualify_consts: promotion candidate: place={:?}", place);
1208 if let Place::Local(local) = *place {
1209 if self.mir.local_kind(local) == LocalKind::Temp {
1210 debug!("qualify_consts: promotion candidate: local={:?}", local);
1211 let qualif = self.local_qualif[local];
1212 // The borrowed place doesn't have `MUTABLE_INTERIOR`
1213 // (from `qualify_rvalue`), so we can safely ignore
1214 // `MUTABLE_INTERIOR` from the local's qualifications.
1215 // This allows borrowing fields which don't have
1216 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
1217 // `let _: &'static _ = &(Cell::new(1), 2).1;`
1218 debug!("qualify_consts: promotion candidate: qualif={:?}", qualif);
1219 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
1220 debug!("qualify_consts: promotion candidate: {:?}", candidate);
1221 self.promotion_candidates.push(candidate);
1228 self.assign(dest, qualif, location);
1230 self.visit_rvalue(rvalue, location);
1233 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1234 debug!("visit_source_info: source_info={:?}", source_info);
1235 self.span = source_info.span;
1238 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1239 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1240 match statement.kind {
1241 StatementKind::Assign(..) => {
1242 self.super_statement(bb, statement, location);
1244 // FIXME(eddyb) should these really do nothing?
1245 StatementKind::FakeRead(..) |
1246 StatementKind::SetDiscriminant { .. } |
1247 StatementKind::StorageLive(_) |
1248 StatementKind::StorageDead(_) |
1249 StatementKind::InlineAsm {..} |
1250 StatementKind::Retag { .. } |
1251 StatementKind::AscribeUserType(..) |
1252 StatementKind::Nop => {}
1256 fn visit_terminator(&mut self,
1258 terminator: &Terminator<'tcx>,
1259 location: Location) {
1260 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1261 self.super_terminator(bb, terminator, location);
1265 pub fn provide(providers: &mut Providers<'_>) {
1266 *providers = Providers {
1272 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1274 -> (u8, Lrc<BitSet<Local>>) {
1275 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1276 // cannot yet be stolen), because `mir_validated()`, which steals
1277 // from `mir_const(), forces this query to execute before
1278 // performing the steal.
1279 let mir = &tcx.mir_const(def_id).borrow();
1281 if mir.return_ty().references_error() {
1282 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1283 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1286 let mut checker = Checker::new(tcx, def_id, mir, Mode::Const);
1287 let (qualif, promoted_temps) = checker.check_const();
1288 (qualif.bits(), promoted_temps)
1291 pub struct QualifyAndPromoteConstants;
1293 impl MirPass for QualifyAndPromoteConstants {
1294 fn run_pass<'a, 'tcx>(&self,
1295 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1296 src: MirSource<'tcx>,
1297 mir: &mut Mir<'tcx>) {
1298 // There's not really any point in promoting errorful MIR.
1299 if mir.return_ty().references_error() {
1300 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1304 if src.promoted.is_some() {
1308 let def_id = src.def_id();
1309 let id = tcx.hir().as_local_hir_id(def_id).unwrap();
1310 let mut const_promoted_temps = None;
1311 let mode = match tcx.hir().body_owner_kind_by_hir_id(id) {
1312 hir::BodyOwnerKind::Closure => Mode::Fn,
1313 hir::BodyOwnerKind::Fn => {
1314 if tcx.is_const_fn(def_id) {
1320 hir::BodyOwnerKind::Const => {
1321 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1324 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1325 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1328 debug!("run_pass: mode={:?}", mode);
1329 if mode == Mode::Fn || mode == Mode::ConstFn {
1330 // This is ugly because Checker holds onto mir,
1331 // which can't be mutated until its scope ends.
1332 let (temps, candidates) = {
1333 let mut checker = Checker::new(tcx, def_id, mir, mode);
1334 if mode == Mode::ConstFn {
1335 if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
1336 checker.check_const();
1337 } else if tcx.is_min_const_fn(def_id) {
1338 // enforce `min_const_fn` for stable const fns
1339 use super::qualify_min_const_fn::is_min_const_fn;
1340 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1341 tcx.sess.span_err(span, &err);
1343 // this should not produce any errors, but better safe than sorry
1345 checker.check_const();
1348 // Enforce a constant-like CFG for `const fn`.
1349 checker.check_const();
1352 while let Some((bb, data)) = checker.rpo.next() {
1353 checker.visit_basic_block_data(bb, data);
1357 (checker.temp_promotion_state, checker.promotion_candidates)
1360 // Do the actual promotion, now that we know what's viable.
1361 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1363 if !mir.control_flow_destroyed.is_empty() {
1364 let mut locals = mir.vars_iter();
1365 if let Some(local) = locals.next() {
1366 let span = mir.local_decls[local].source_info.span;
1367 let mut error = tcx.sess.struct_span_err(
1370 "new features like let bindings are not permitted in {}s \
1371 which also use short circuiting operators",
1375 for (span, kind) in mir.control_flow_destroyed.iter() {
1378 &format!("use of {} here does not actually short circuit due to \
1379 the const evaluator presently not being able to do control flow. \
1380 See https://github.com/rust-lang/rust/issues/49146 for more \
1381 information.", kind),
1384 for local in locals {
1385 let span = mir.local_decls[local].source_info.span;
1388 "more locals defined here",
1394 let promoted_temps = if mode == Mode::Const {
1395 // Already computed by `mir_const_qualif`.
1396 const_promoted_temps.unwrap()
1398 Checker::new(tcx, def_id, mir, mode).check_const().1
1401 // In `const` and `static` everything without `StorageDead`
1402 // is `'static`, we don't have to create promoted MIR fragments,
1403 // just remove `Drop` and `StorageDead` on "promoted" locals.
1404 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1405 for block in mir.basic_blocks_mut() {
1406 block.statements.retain(|statement| {
1407 match statement.kind {
1408 StatementKind::StorageDead(index) => {
1409 !promoted_temps.contains(index)
1414 let terminator = block.terminator_mut();
1415 match terminator.kind {
1416 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1417 if promoted_temps.contains(index) {
1418 terminator.kind = TerminatorKind::Goto {
1428 // Statics must be Sync.
1429 if mode == Mode::Static {
1430 // `#[thread_local]` statics don't have to be `Sync`.
1431 for attr in &tcx.get_attrs(def_id)[..] {
1432 if attr.check_name("thread_local") {
1436 let ty = mir.return_ty();
1437 tcx.infer_ctxt().enter(|infcx| {
1438 let param_env = ty::ParamEnv::empty();
1439 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1440 let mut fulfillment_cx = traits::FulfillmentContext::new();
1441 fulfillment_cx.register_bound(&infcx,
1444 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1446 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1447 infcx.report_fulfillment_errors(&err, None, false);
1454 fn args_required_const(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1455 let attrs = tcx.get_attrs(def_id);
1456 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1457 let mut ret = FxHashSet::default();
1458 for meta in attr.meta_item_list()? {
1459 match meta.literal()?.node {
1460 LitKind::Int(a, _) => { ret.insert(a as usize); }