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, feature_err, emit_feature_err, GateIssue};
25 use syntax_pos::{Span, DUMMY_SP};
30 use 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 const_fn_arg_vars: BitSet<Local>,
108 temp_promotion_state: IndexVec<Local, TempState>,
109 promotion_candidates: Vec<Candidate>
112 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
113 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
117 -> Qualifier<'a, 'tcx, 'tcx> {
118 assert!(def_id.is_local());
119 let mut rpo = traversal::reverse_postorder(mir);
120 let temps = promote_consts::collect_temps(mir, &mut rpo);
123 let param_env = tcx.param_env(def_id);
125 let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
126 for arg in mir.args_iter() {
127 let mut qualif = Qualif::NEEDS_DROP;
128 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
129 local_qualif[arg] = Some(qualif);
141 qualif: Qualif::empty(),
142 const_fn_arg_vars: BitSet::new_empty(mir.local_decls.len()),
143 temp_promotion_state: temps,
144 promotion_candidates: vec![]
148 // FIXME(eddyb) we could split the errors into meaningful
149 // categories, but enabling full miri would make that
150 // slightly pointless (even with feature-gating).
151 fn not_const(&mut self) {
152 self.add(Qualif::NOT_CONST);
153 if self.mode != Mode::Fn {
154 let mut err = struct_span_err!(
158 "{} contains unimplemented expression type",
161 if self.tcx.sess.teach(&err.get_code().unwrap()) {
162 err.note("A function call isn't allowed in the const's initialization expression \
163 because the expression's value must be known at compile-time.");
164 err.note("Remember: you can't use a function call inside a const's initialization \
165 expression! However, you can use it anywhere else.");
171 /// Error about extra statements in a constant.
172 fn statement_like(&mut self) {
173 self.add(Qualif::NOT_CONST);
174 if self.mode != Mode::Fn {
175 let mut err = feature_err(
176 &self.tcx.sess.parse_sess,
180 &format!("statements in {}s are unstable", self.mode),
182 if self.tcx.sess.teach(&err.get_code().unwrap()) {
183 err.note("Blocks in constants may only contain items (such as constant, function \
184 definition, etc...) and a tail expression.");
185 err.help("To avoid it, you have to replace the non-item object.");
191 /// Add the given qualification to self.qualif.
192 fn add(&mut self, qualif: Qualif) {
193 self.qualif = self.qualif | qualif;
196 /// Add the given type's qualification to self.qualif.
197 fn add_type(&mut self, ty: Ty<'tcx>) {
198 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
199 self.qualif.restrict(ty, self.tcx, self.param_env);
202 /// Within the provided closure, self.qualif will start
203 /// out empty, and its value after the closure returns will
204 /// be combined with the value before the call to nest.
205 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
206 let original = self.qualif;
207 self.qualif = Qualif::empty();
212 /// Assign the current qualification to the given destination.
213 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
214 trace!("assign: {:?}", dest);
215 let qualif = self.qualif;
216 let span = self.span;
217 let store = |slot: &mut Option<Qualif>| {
219 span_bug!(span, "multiple assignments to {:?}", dest);
221 *slot = Some(qualif);
224 // Only handle promotable temps in non-const functions.
225 if self.mode == Mode::Fn {
226 if let Place::Local(index) = *dest {
227 if self.mir.local_kind(index) == LocalKind::Temp
228 && self.temp_promotion_state[index].is_promotable() {
229 debug!("store to promotable temp {:?} ({:?})", index, qualif);
230 store(&mut self.local_qualif[index]);
236 if self.tcx.features().const_let {
240 // with `const_let` active, we treat all locals equal
241 Place::Local(index) => break *index,
242 // projections are transparent for assignments
243 // we qualify the entire destination at once, even if just a field would have
244 // stricter qualification
245 Place::Projection(proj) => {
246 // Catch more errors in the destination. `visit_place` also checks various
247 // projection rules like union field access and raw pointer deref
250 PlaceContext::MutatingUse(MutatingUseContext::Store),
255 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
256 Place::Static(..) => {
257 // Catch more errors in the destination. `visit_place` also checks that we
258 // do not try to access statics from constants or try to mutate statics
261 PlaceContext::MutatingUse(MutatingUseContext::Store),
268 debug!("store to var {:?}", index);
269 match &mut self.local_qualif[index] {
270 // this is overly restrictive, because even full assignments do not clear the qualif
271 // While we could special case full assignments, this would be inconsistent with
272 // aggregates where we overwrite all fields via assignments, which would not get
274 Some(ref mut qualif) => *qualif = *qualif | self.qualif,
275 // insert new qualification
276 qualif @ None => *qualif = Some(self.qualif),
282 Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp ||
283 self.mir.local_kind(index) == LocalKind::ReturnPointer => {
284 debug!("store to {:?} (temp or return pointer)", index);
285 store(&mut self.local_qualif[index])
288 Place::Projection(box Projection {
289 base: Place::Local(index),
290 elem: ProjectionElem::Deref
291 }) if self.mir.local_kind(index) == LocalKind::Temp
292 && self.mir.local_decls[index].ty.is_box()
293 && self.local_qualif[index].map_or(false, |qualif| {
294 qualif.contains(Qualif::NOT_CONST)
296 // Part of `box expr`, we should've errored
297 // already for the Box allocation Rvalue.
300 // This must be an explicit assignment.
302 // Catch more errors in the destination.
305 PlaceContext::MutatingUse(MutatingUseContext::Store),
308 self.statement_like();
313 /// Qualify a whole const, static initializer or const fn.
314 fn qualify_const(&mut self) -> (Qualif, Lrc<BitSet<Local>>) {
315 debug!("qualifying {} {:?}", self.mode, self.def_id);
319 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
320 let mut bb = START_BLOCK;
322 seen_blocks.insert(bb.index());
324 self.visit_basic_block_data(bb, &mir[bb]);
326 let target = match mir[bb].terminator().kind {
327 TerminatorKind::Goto { target } |
328 TerminatorKind::Drop { target, .. } |
329 TerminatorKind::Assert { target, .. } |
330 TerminatorKind::Call { destination: Some((_, target)), .. } => {
334 // Non-terminating calls cannot produce any value.
335 TerminatorKind::Call { destination: None, .. } => {
339 TerminatorKind::SwitchInt {..} |
340 TerminatorKind::DropAndReplace { .. } |
341 TerminatorKind::Resume |
342 TerminatorKind::Abort |
343 TerminatorKind::GeneratorDrop |
344 TerminatorKind::Yield { .. } |
345 TerminatorKind::Unreachable |
346 TerminatorKind::FalseEdges { .. } |
347 TerminatorKind::FalseUnwind { .. } => None,
349 TerminatorKind::Return => {
350 if !self.tcx.features().const_let {
351 // Check for unused values. This usually means
352 // there are extra statements in the AST.
353 for temp in mir.temps_iter() {
354 if self.local_qualif[temp].is_none() {
358 let state = self.temp_promotion_state[temp];
359 if let TempState::Defined { location, uses: 0 } = state {
360 let data = &mir[location.block];
361 let stmt_idx = location.statement_index;
363 // Get the span for the initialization.
364 let source_info = if stmt_idx < data.statements.len() {
365 data.statements[stmt_idx].source_info
367 data.terminator().source_info
369 self.span = source_info.span;
371 // Treat this as a statement in the AST.
372 self.statement_like();
376 // Make sure there are no extra unassigned variables.
377 self.qualif = Qualif::NOT_CONST;
378 for index in mir.vars_iter() {
379 if !self.const_fn_arg_vars.contains(index) {
380 debug!("unassigned variable {:?}", index);
381 self.assign(&Place::Local(index), Location {
383 statement_index: usize::MAX,
395 Some(target) if !seen_blocks.contains(target.index()) => {
405 self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
407 // Account for errors in consts by using the
408 // conservative type qualification instead.
409 if self.qualif.intersects(Qualif::CONST_ERROR) {
410 self.qualif = Qualif::empty();
411 let return_ty = mir.return_ty();
412 self.add_type(return_ty);
416 // Collect all the temps we need to promote.
417 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
419 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
420 for candidate in &self.promotion_candidates {
422 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
423 match self.mir[bb].statements[stmt_idx].kind {
424 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
425 promoted_temps.insert(index);
430 Candidate::Argument { .. } => {}
434 (self.qualif, Lrc::new(promoted_temps))
437 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
438 Some(def_id) == self.tcx.lang_items().panic_fn() ||
439 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
443 /// Accumulates an Rvalue or Call's effects in self.qualif.
444 /// For functions (constant or not), it also records
445 /// candidates for promotion in promotion_candidates.
446 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
447 fn visit_local(&mut self,
449 _: PlaceContext<'tcx>,
451 debug!("visit_local: local={:?}", local);
452 let kind = self.mir.local_kind(local);
454 LocalKind::ReturnPointer => {
457 LocalKind::Var if !self.tcx.features().const_let => {
458 if self.mode != Mode::Fn {
459 emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
460 self.span, GateIssue::Language,
461 &format!("let bindings in {}s are unstable",self.mode));
463 self.add(Qualif::NOT_CONST);
468 if let LocalKind::Arg = kind {
469 self.add(Qualif::FN_ARGUMENT);
472 if !self.temp_promotion_state[local].is_promotable() {
473 debug!("visit_local: (not promotable) local={:?}", local);
474 self.add(Qualif::NOT_PROMOTABLE);
477 if let Some(qualif) = self.local_qualif[local] {
486 fn visit_place(&mut self,
488 context: PlaceContext<'tcx>,
489 location: Location) {
490 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
492 Place::Local(ref local) => self.visit_local(local, context, location),
493 Place::Promoted(_) => bug!("promoting already promoted MIR"),
494 Place::Static(ref global) => {
496 .get_attrs(global.def_id)
498 .any(|attr| attr.check_name("thread_local")) {
499 if self.mode != Mode::Fn {
500 span_err!(self.tcx.sess, self.span, E0625,
501 "thread-local statics cannot be \
502 accessed at compile-time");
504 self.add(Qualif::NOT_CONST);
508 // Only allow statics (not consts) to refer to other statics.
509 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
510 if self.mode == Mode::Static && context.is_mutating_use() {
511 // this is not strictly necessary as miri will also bail out
512 // For interior mutability we can't really catch this statically as that
513 // goes through raw pointers and intermediate temporaries, so miri has
514 // to catch this anyway
515 self.tcx.sess.span_err(
517 "cannot mutate statics in the initializer of another static",
522 self.add(Qualif::NOT_CONST);
524 if self.mode != Mode::Fn {
525 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
526 "{}s cannot refer to statics, use \
527 a constant instead", self.mode);
528 if self.tcx.sess.teach(&err.get_code().unwrap()) {
530 "Static and const variables can refer to other const variables. But a \
531 const variable cannot refer to a static variable."
534 "To fix this, the value can be extracted as a const and then used."
540 Place::Projection(ref proj) => {
542 this.super_place(place, context, location);
544 ProjectionElem::Deref => {
545 if context.is_mutating_use() {
546 // `not_const` errors out in const contexts
549 // just make sure this doesn't get promoted
550 this.add(Qualif::NOT_CONST);
552 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
556 if let ty::RawPtr(_) = base_ty.sty {
557 if !this.tcx.features().const_raw_ptr_deref {
559 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
560 this.span, GateIssue::Language,
562 "dereferencing raw pointers in {}s is unstable",
572 ProjectionElem::Field(..) |
573 ProjectionElem::Index(_) => {
574 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
575 if let Some(def) = base_ty.ty_adt_def() {
578 Mode::Fn => this.not_const(),
580 if !this.tcx.features().const_fn_union {
582 &this.tcx.sess.parse_sess, "const_fn_union",
583 this.span, GateIssue::Language,
584 "unions in const fn are unstable",
597 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
598 this.qualif.restrict(ty, this.tcx, this.param_env);
601 ProjectionElem::ConstantIndex {..} |
602 ProjectionElem::Subslice {..} |
603 ProjectionElem::Downcast(..) => {
612 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
613 debug!("visit_operand: operand={:?} location={:?}", operand, location);
614 self.super_operand(operand, location);
618 Operand::Move(_) => {
619 // Mark the consumed locals to indicate later drops are noops.
620 if let Operand::Move(Place::Local(local)) = *operand {
621 self.local_qualif[local] = self.local_qualif[local].map(|q|
622 q - Qualif::NEEDS_DROP
626 Operand::Constant(ref constant) => {
627 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
628 // Don't peek inside trait associated constants.
629 if self.tcx.trait_of_item(*def_id).is_some() {
630 self.add_type(constant.ty);
632 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(*def_id);
634 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
637 // Just in case the type is more specific than
638 // the definition, e.g., impl associated const
639 // with type parameters, take it into account.
640 self.qualif.restrict(constant.ty, self.tcx, self.param_env);
647 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
648 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
649 // Recurse through operands and places.
650 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
651 let mut is_reborrow = false;
652 if let Place::Projection(ref proj) = *place {
653 if let ProjectionElem::Deref = proj.elem {
654 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
655 if let ty::Ref(..) = base_ty.sty {
662 let ctx = match kind {
663 BorrowKind::Shared =>
664 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
665 BorrowKind::Shallow =>
666 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
667 BorrowKind::Unique =>
668 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
669 BorrowKind::Mut { .. } =>
670 PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
672 self.super_place(place, ctx, location);
674 self.super_rvalue(rvalue, location);
677 self.super_rvalue(rvalue, location);
683 Rvalue::UnaryOp(UnOp::Neg, _) |
684 Rvalue::UnaryOp(UnOp::Not, _) |
685 Rvalue::NullaryOp(NullOp::SizeOf, _) |
686 Rvalue::CheckedBinaryOp(..) |
687 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
688 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
689 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
690 Rvalue::Cast(CastKind::Unsize, ..) |
691 Rvalue::Discriminant(..) |
694 Rvalue::Ref(_, kind, ref place) => {
695 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
697 // Default to forbidding the borrow and/or its promotion,
698 // due to the potential for direct or interior mutability,
699 // and only proceed by setting `forbidden_mut` to `false`.
700 let mut forbidden_mut = true;
702 if let BorrowKind::Mut { .. } = kind {
703 // In theory, any zero-sized value could be borrowed
704 // mutably without consequences. However, only &mut []
705 // is allowed right now, and only in functions.
706 if self.mode == Mode::StaticMut {
707 // Inside a `static mut`, &mut [...] is also allowed.
709 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
712 } else if let ty::Array(_, len) = ty.sty {
713 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
714 // seems unnecessary, given that this is merely a ZST.
715 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
716 forbidden_mut = false;
721 self.add(Qualif::NOT_CONST);
722 if self.mode != Mode::Fn {
723 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
724 "references in {}s may only refer \
725 to immutable values", self.mode);
726 err.span_label(self.span, format!("{}s require immutable values",
728 if self.tcx.sess.teach(&err.get_code().unwrap()) {
729 err.note("References in statics and constants may only refer to \
730 immutable values.\n\n\
731 Statics are shared everywhere, and if they refer to \
732 mutable data one might violate memory safety since \
733 holding multiple mutable references to shared data is \
735 If you really want global mutable state, try using \
736 static mut or a global UnsafeCell.");
742 // Constants cannot be borrowed if they contain interior mutability as
743 // it means that our "silent insertion of statics" could change
744 // initializer values (very bad).
745 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
746 // A reference of a MUTABLE_INTERIOR place is instead
747 // NOT_CONST (see `if forbidden_mut` below), to avoid
748 // duplicate errors (from reborrowing, for example).
749 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
750 if self.mode != Mode::Fn {
751 span_err!(self.tcx.sess, self.span, E0492,
752 "cannot borrow a constant which may contain \
753 interior mutability, create a static instead");
756 // We allow immutable borrows of frozen data.
757 forbidden_mut = false;
761 debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
763 self.add(Qualif::NOT_CONST);
765 // We might have a candidate for promotion.
766 let candidate = Candidate::Ref(location);
767 // We can only promote interior borrows of promotable temps.
768 let mut place = place;
769 while let Place::Projection(ref proj) = *place {
770 if proj.elem == ProjectionElem::Deref {
775 debug!("visit_rvalue: place={:?}", place);
776 if let Place::Local(local) = *place {
777 if self.mir.local_kind(local) == LocalKind::Temp {
778 debug!("visit_rvalue: local={:?}", local);
779 if let Some(qualif) = self.local_qualif[local] {
780 // `forbidden_mut` is false, so we can safely ignore
781 // `MUTABLE_INTERIOR` from the local's qualifications.
782 // This allows borrowing fields which don't have
783 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
784 // `let _: &'static _ = &(Cell::new(1), 2).1;`
785 debug!("visit_rvalue: qualif={:?}", qualif);
786 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
787 debug!("visit_rvalue: candidate={:?}", candidate);
788 self.promotion_candidates.push(candidate);
796 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
797 let operand_ty = operand.ty(self.mir, self.tcx);
798 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
799 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
800 match (cast_in, cast_out) {
801 (CastTy::Ptr(_), CastTy::Int(_)) |
802 (CastTy::FnPtr, CastTy::Int(_)) => {
803 if let Mode::Fn = self.mode {
804 // in normal functions, mark such casts as not promotable
805 self.add(Qualif::NOT_CONST);
806 } else if !self.tcx.features().const_raw_ptr_to_usize_cast {
807 // in const fn and constants require the feature gate
808 // FIXME: make it unsafe inside const fn and constants
810 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
811 self.span, GateIssue::Language,
813 "casting pointers to integers in {}s is unstable",
823 Rvalue::BinaryOp(op, ref lhs, _) => {
824 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
825 assert!(op == BinOp::Eq || op == BinOp::Ne ||
826 op == BinOp::Le || op == BinOp::Lt ||
827 op == BinOp::Ge || op == BinOp::Gt ||
828 op == BinOp::Offset);
830 if let Mode::Fn = self.mode {
831 // raw pointer operations are not allowed inside promoteds
832 self.add(Qualif::NOT_CONST);
833 } else if !self.tcx.features().const_compare_raw_pointers {
834 // require the feature gate inside constants and const fn
835 // FIXME: make it unsafe to use these operations
837 &self.tcx.sess.parse_sess,
838 "const_compare_raw_pointers",
841 &format!("comparing raw pointers inside {}", self.mode),
847 Rvalue::NullaryOp(NullOp::Box, _) => {
848 self.add(Qualif::NOT_CONST);
849 if self.mode != Mode::Fn {
850 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
851 "allocations are not allowed in {}s", self.mode);
852 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
853 if self.tcx.sess.teach(&err.get_code().unwrap()) {
855 "The value of statics and constants must be known at compile time, \
856 and they live for the entire lifetime of a program. Creating a boxed \
857 value allocates memory on the heap at runtime, and therefore cannot \
858 be done at compile time."
865 Rvalue::Aggregate(ref kind, _) => {
866 if let AggregateKind::Adt(def, ..) = **kind {
867 if def.has_dtor(self.tcx) {
868 self.add(Qualif::NEEDS_DROP);
871 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
872 let ty = rvalue.ty(self.mir, self.tcx);
874 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
881 fn visit_terminator_kind(&mut self,
883 kind: &TerminatorKind<'tcx>,
884 location: Location) {
885 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
886 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
887 self.visit_operand(func, location);
889 let fn_ty = func.ty(self.mir, self.tcx);
890 let mut callee_def_id = None;
891 let mut is_shuffle = false;
892 let mut is_const_fn = false;
893 let mut is_promotable_const_fn = false;
895 ty::FnDef(def_id, _) => {
896 callee_def_id = Some(def_id);
897 match self.tcx.fn_sig(def_id).abi() {
899 Abi::PlatformIntrinsic => {
900 assert!(!self.tcx.is_const_fn(def_id));
901 match &self.tcx.item_name(def_id).as_str()[..] {
920 | "add_with_overflow"
921 | "sub_with_overflow"
922 | "mul_with_overflow"
923 // no need to check feature gates, intrinsics are only callable
924 // from the libstd or with forever unstable feature gates
925 => is_const_fn = true,
926 // special intrinsic that can be called diretly without an intrinsic
927 // feature gate needs a language feature gate
929 // never promote transmute calls
930 if self.mode != Mode::Fn {
932 // const eval transmute calls only with the feature gate
933 if !self.tcx.features().const_transmute {
935 &self.tcx.sess.parse_sess, "const_transmute",
936 self.span, GateIssue::Language,
937 &format!("The use of std::mem::transmute() \
938 is gated in {}s", self.mode));
943 name if name.starts_with("simd_shuffle") => {
951 // In normal functions we only care about promotion.
952 if self.mode == Mode::Fn {
953 // Never promote const fn calls of
954 // functions without `#[rustc_promotable]`.
955 if self.tcx.is_promotable_const_fn(def_id) {
957 is_promotable_const_fn = true;
958 } else if self.tcx.is_const_fn(def_id) {
962 // stable const fns or unstable const fns with their feature gate
964 if self.tcx.is_const_fn(def_id) {
966 } else if self.is_const_panic_fn(def_id) {
967 // Check the const_panic feature gate.
968 // FIXME: cannot allow this inside `allow_internal_unstable`
969 // because that would make `panic!` insta stable in constants,
970 // since the macro is marked with the attribute.
971 if self.tcx.features().const_panic {
974 // Don't allow panics in constants without the feature gate.
976 &self.tcx.sess.parse_sess,
980 &format!("panicking in {}s is unstable", self.mode),
983 } else if let Some(feature)
984 = self.tcx.is_unstable_const_fn(def_id) {
985 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
986 // functions without the feature gate active in this crate in
987 // order to report a better error message than the one below.
988 if self.span.allows_unstable() {
989 // `allow_internal_unstable` can make such calls stable.
992 let mut err = self.tcx.sess.struct_span_err(self.span,
993 &format!("`{}` is not yet stable as a const fn",
994 self.tcx.item_path_str(def_id)));
995 if nightly_options::is_nightly_build() {
997 "add `#![feature({})]` to the \
998 crate attributes to enable",
1004 // FIXME(#24111): remove this check when const fn stabilizes.
1005 let (msg, note) = if let UnstableFeatures::Disallow =
1006 self.tcx.sess.opts.unstable_features {
1007 (format!("calls in {}s are limited to \
1008 tuple structs and tuple variants",
1010 Some("a limited form of compile-time function \
1011 evaluation is available on a nightly \
1012 compiler via `const fn`"))
1014 (format!("calls in {}s are limited \
1015 to constant functions, \
1016 tuple structs and tuple variants",
1020 let mut err = struct_span_err!(
1027 if let Some(note) = note {
1028 err.span_note(self.span, note);
1037 if self.mode != Mode::Fn {
1038 let mut err = self.tcx.sess.struct_span_err(
1040 &format!("function pointers are not allowed in const fn"));
1051 let constant_arguments = callee_def_id.and_then(|id| {
1052 args_required_const(self.tcx, id)
1054 for (i, arg) in args.iter().enumerate() {
1056 this.visit_operand(arg, location);
1057 if this.mode != Mode::Fn {
1060 let candidate = Candidate::Argument { bb, index: i };
1061 if is_shuffle && i == 2 {
1062 if this.qualif.is_empty() {
1063 debug!("visit_terminator_kind: candidate={:?}", candidate);
1064 this.promotion_candidates.push(candidate);
1066 span_err!(this.tcx.sess, this.span, E0526,
1067 "shuffle indices are not constant");
1072 let constant_arguments = match constant_arguments.as_ref() {
1076 if !constant_arguments.contains(&i) {
1079 // Since the argument is required to be constant,
1080 // we care about constness, not promotability.
1081 // If we checked for promotability, we'd miss out on
1082 // the results of function calls (which are never promoted
1083 // in runtime code).
1084 // This is not a problem, because the argument explicitly
1085 // requests constness, in contrast to regular promotion
1086 // which happens even without the user requesting it.
1087 // We can error out with a hard error if the argument is not
1089 if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
1090 debug!("visit_terminator_kind: candidate={:?}", candidate);
1091 this.promotion_candidates.push(candidate);
1093 this.tcx.sess.span_err(this.span,
1094 &format!("argument {} is required to be a constant",
1100 // non-const fn calls
1102 self.qualif = Qualif::NOT_CONST;
1103 if self.mode != Mode::Fn {
1104 self.tcx.sess.delay_span_bug(
1106 "should have reported an error about non-const fn calls in constants",
1111 if let Some((ref dest, _)) = *destination {
1112 // Avoid propagating irrelevant callee/argument qualifications.
1113 if self.qualif.intersects(Qualif::CONST_ERROR) {
1114 self.qualif = Qualif::NOT_CONST;
1116 // Be conservative about the returned value of a const fn.
1118 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
1119 if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
1120 self.qualif = Qualif::NOT_PROMOTABLE;
1122 self.qualif = Qualif::empty();
1126 self.assign(dest, location);
1128 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1129 self.super_terminator_kind(bb, kind, location);
1131 // Deny *any* live drops anywhere other than functions.
1132 if self.mode != Mode::Fn {
1133 // HACK(eddyb): emulate a bit of dataflow analysis,
1134 // conservatively, that drop elaboration will do.
1135 let needs_drop = if let Place::Local(local) = *place {
1136 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1137 Some(self.mir.local_decls[local].source_info.span)
1145 if let Some(span) = needs_drop {
1146 // Double-check the type being dropped, to minimize false positives.
1147 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1148 if ty.needs_drop(self.tcx, self.param_env) {
1149 struct_span_err!(self.tcx.sess, span, E0493,
1150 "destructors cannot be evaluated at compile-time")
1151 .span_label(span, format!("{}s cannot evaluate destructors",
1158 // Qualify any operands inside other terminators.
1159 self.super_terminator_kind(bb, kind, location);
1163 fn visit_assign(&mut self,
1166 rvalue: &Rvalue<'tcx>,
1167 location: Location) {
1168 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1169 self.visit_rvalue(rvalue, location);
1171 // Check the allowed const fn argument forms.
1172 if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1173 if self.mir.local_kind(index) == LocalKind::Var &&
1174 self.const_fn_arg_vars.insert(index) &&
1175 !self.tcx.features().const_let {
1176 // Direct use of an argument is permitted.
1178 Rvalue::Use(Operand::Copy(Place::Local(local))) |
1179 Rvalue::Use(Operand::Move(Place::Local(local))) => {
1180 if self.mir.local_kind(local) == LocalKind::Arg {
1186 // Avoid a generic error for other uses of arguments.
1187 if self.qualif.contains(Qualif::FN_ARGUMENT) {
1188 let decl = &self.mir.local_decls[index];
1189 let mut err = feature_err(
1190 &self.tcx.sess.parse_sess,
1192 decl.source_info.span,
1193 GateIssue::Language,
1194 "arguments of constant functions can only be immutable by-value bindings"
1196 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1197 err.note("Constant functions are not allowed to mutate anything. Thus, \
1198 binding to an argument with a mutable pattern is not allowed.");
1199 err.note("Remove any mutable bindings from the argument list to fix this \
1200 error. In case you need to mutate the argument, try lazily \
1201 initializing a global variable instead of using a const fn, or \
1202 refactoring the code to a functional style to avoid mutation if \
1211 self.assign(dest, location);
1214 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1215 debug!("visit_source_info: source_info={:?}", source_info);
1216 self.span = source_info.span;
1219 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1220 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1222 this.visit_source_info(&statement.source_info);
1223 match statement.kind {
1224 StatementKind::Assign(ref place, ref rvalue) => {
1225 this.visit_assign(bb, place, rvalue, location);
1227 StatementKind::FakeRead(..) |
1228 StatementKind::SetDiscriminant { .. } |
1229 StatementKind::StorageLive(_) |
1230 StatementKind::StorageDead(_) |
1231 StatementKind::InlineAsm {..} |
1232 StatementKind::Retag { .. } |
1233 StatementKind::AscribeUserType(..) |
1234 StatementKind::Nop => {}
1239 fn visit_terminator(&mut self,
1241 terminator: &Terminator<'tcx>,
1242 location: Location) {
1243 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1244 self.nest(|this| this.super_terminator(bb, terminator, location));
1248 pub fn provide(providers: &mut Providers) {
1249 *providers = Providers {
1255 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1257 -> (u8, Lrc<BitSet<Local>>) {
1258 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1259 // cannot yet be stolen), because `mir_validated()`, which steals
1260 // from `mir_const(), forces this query to execute before
1261 // performing the steal.
1262 let mir = &tcx.mir_const(def_id).borrow();
1264 if mir.return_ty().references_error() {
1265 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1266 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1269 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1270 let (qualif, promoted_temps) = qualifier.qualify_const();
1271 (qualif.bits(), promoted_temps)
1274 pub struct QualifyAndPromoteConstants;
1276 impl MirPass for QualifyAndPromoteConstants {
1277 fn run_pass<'a, 'tcx>(&self,
1278 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1280 mir: &mut Mir<'tcx>) {
1281 // There's not really any point in promoting errorful MIR.
1282 if mir.return_ty().references_error() {
1283 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1287 if src.promoted.is_some() {
1291 let def_id = src.def_id;
1292 let id = tcx.hir().as_local_node_id(def_id).unwrap();
1293 let mut const_promoted_temps = None;
1294 let mode = match tcx.hir().body_owner_kind(id) {
1295 hir::BodyOwnerKind::Fn => {
1296 if tcx.is_const_fn(def_id) {
1302 hir::BodyOwnerKind::Const => {
1303 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1306 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1307 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1310 debug!("run_pass: mode={:?}", mode);
1311 if mode == Mode::Fn || mode == Mode::ConstFn {
1312 // This is ugly because Qualifier holds onto mir,
1313 // which can't be mutated until its scope ends.
1314 let (temps, candidates) = {
1315 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1316 if mode == Mode::ConstFn {
1317 if tcx.is_min_const_fn(def_id) {
1318 // enforce `min_const_fn` for stable const fns
1319 use super::qualify_min_const_fn::is_min_const_fn;
1320 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1321 tcx.sess.span_err(span, &err);
1323 // this should not produce any errors, but better safe than sorry
1325 qualifier.qualify_const();
1328 // Enforce a constant-like CFG for `const fn`.
1329 qualifier.qualify_const();
1332 while let Some((bb, data)) = qualifier.rpo.next() {
1333 qualifier.visit_basic_block_data(bb, data);
1337 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1340 // Do the actual promotion, now that we know what's viable.
1341 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1343 if !mir.control_flow_destroyed.is_empty() {
1344 let mut locals = mir.vars_iter();
1345 if let Some(local) = locals.next() {
1346 let span = mir.local_decls[local].source_info.span;
1347 let mut error = tcx.sess.struct_span_err(
1350 "new features like let bindings are not permitted in {}s \
1351 which also use short circuiting operators",
1355 for (span, kind) in mir.control_flow_destroyed.iter() {
1358 &format!("use of {} here does not actually short circuit due to \
1359 the const evaluator presently not being able to do control flow. \
1360 See https://github.com/rust-lang/rust/issues/49146 for more \
1361 information.", kind),
1364 for local in locals {
1365 let span = mir.local_decls[local].source_info.span;
1368 "more locals defined here",
1374 let promoted_temps = if mode == Mode::Const {
1375 // Already computed by `mir_const_qualif`.
1376 const_promoted_temps.unwrap()
1378 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1381 // In `const` and `static` everything without `StorageDead`
1382 // is `'static`, we don't have to create promoted MIR fragments,
1383 // just remove `Drop` and `StorageDead` on "promoted" locals.
1384 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1385 for block in mir.basic_blocks_mut() {
1386 block.statements.retain(|statement| {
1387 match statement.kind {
1388 StatementKind::StorageDead(index) => {
1389 !promoted_temps.contains(index)
1394 let terminator = block.terminator_mut();
1395 match terminator.kind {
1396 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1397 if promoted_temps.contains(index) {
1398 terminator.kind = TerminatorKind::Goto {
1408 // Statics must be Sync.
1409 if mode == Mode::Static {
1410 // `#[thread_local]` statics don't have to be `Sync`.
1411 for attr in &tcx.get_attrs(def_id)[..] {
1412 if attr.check_name("thread_local") {
1416 let ty = mir.return_ty();
1417 tcx.infer_ctxt().enter(|infcx| {
1418 let param_env = ty::ParamEnv::empty();
1419 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1420 let mut fulfillment_cx = traits::FulfillmentContext::new();
1421 fulfillment_cx.register_bound(&infcx,
1424 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1426 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1427 infcx.report_fulfillment_errors(&err, None, false);
1434 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1435 let attrs = tcx.get_attrs(def_id);
1436 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1437 let mut ret = FxHashSet::default();
1438 for meta in attr.meta_item_list()? {
1439 match meta.literal()?.node {
1440 LitKind::Int(a, _) => { ret.insert(a as usize); }