1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A pass that qualifies constness of temporaries in constants,
12 //! static initializers and functions and also drives promotion.
14 //! The Qualif flags below can be used to also provide better
15 //! diagnostics as to why a constant rvalue wasn't promoted.
17 use rustc_data_structures::bit_set::BitSet;
18 use rustc_data_structures::indexed_vec::IndexVec;
19 use rustc_data_structures::fx::FxHashSet;
21 use rustc::hir::def_id::DefId;
22 use rustc::mir::interpret::ConstValue;
23 use rustc::traits::{self, TraitEngine};
24 use rustc::ty::{self, TyCtxt, Ty, TypeFoldable};
25 use rustc::ty::cast::CastTy;
26 use rustc::ty::query::Providers;
28 use rustc::mir::traversal::ReversePostorder;
29 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
30 use rustc::middle::lang_items;
31 use rustc_target::spec::abi::Abi;
32 use syntax::ast::LitKind;
33 use syntax::feature_gate::{UnstableFeatures, feature_err, emit_feature_err, GateIssue};
34 use syntax_pos::{Span, DUMMY_SP};
37 use rustc_data_structures::sync::Lrc;
40 use transform::{MirPass, MirSource};
41 use super::promote_consts::{self, Candidate, TempState};
44 // Borrows of temporaries can be promoted only if
45 // they have none of these qualifications, with
46 // the exception of `STATIC_REF` (in statics only).
48 // Constant containing interior mutability (UnsafeCell).
49 const MUTABLE_INTERIOR = 1 << 0;
51 // Constant containing an ADT that implements Drop.
52 const NEEDS_DROP = 1 << 1;
55 const FN_ARGUMENT = 1 << 2;
57 // Not constant at all - non-`const fn` calls, asm!,
58 // pointer comparisons, ptr-to-int casts, etc.
59 const NOT_CONST = 1 << 3;
61 // Refers to temporaries which cannot be promoted as
62 // promote_consts decided they weren't simple enough.
63 const NOT_PROMOTABLE = 1 << 4;
65 // Const items can only have MUTABLE_INTERIOR
66 // and NOT_PROMOTABLE without producing an error.
67 const CONST_ERROR = !Qualif::MUTABLE_INTERIOR.bits &
68 !Qualif::NOT_PROMOTABLE.bits;
72 impl<'a, 'tcx> Qualif {
73 /// Remove flags which are impossible for the given type.
74 fn restrict(&mut self, ty: Ty<'tcx>,
75 tcx: TyCtxt<'a, 'tcx, 'tcx>,
76 param_env: ty::ParamEnv<'tcx>) {
77 if ty.is_freeze(tcx, param_env, DUMMY_SP) {
78 *self = *self - Qualif::MUTABLE_INTERIOR;
80 if !ty.needs_drop(tcx, param_env) {
81 *self = *self - Qualif::NEEDS_DROP;
86 /// What kind of item we are in.
87 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
96 impl fmt::Display for Mode {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 Mode::Const => write!(f, "constant"),
100 Mode::Static | Mode::StaticMut => write!(f, "static"),
101 Mode::ConstFn => write!(f, "constant function"),
102 Mode::Fn => write!(f, "function")
107 struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
112 rpo: ReversePostorder<'a, 'tcx>,
113 tcx: TyCtxt<'a, 'gcx, 'tcx>,
114 param_env: ty::ParamEnv<'tcx>,
115 local_qualif: IndexVec<Local, Option<Qualif>>,
117 const_fn_arg_vars: BitSet<Local>,
118 temp_promotion_state: IndexVec<Local, TempState>,
119 promotion_candidates: Vec<Candidate>
122 impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
123 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
127 -> Qualifier<'a, 'tcx, 'tcx> {
128 assert!(def_id.is_local());
129 let mut rpo = traversal::reverse_postorder(mir);
130 let temps = promote_consts::collect_temps(mir, &mut rpo);
133 let param_env = tcx.param_env(def_id);
135 let mut local_qualif = IndexVec::from_elem(None, &mir.local_decls);
136 for arg in mir.args_iter() {
137 let mut qualif = Qualif::NEEDS_DROP;
138 qualif.restrict(mir.local_decls[arg].ty, tcx, param_env);
139 local_qualif[arg] = Some(qualif);
151 qualif: Qualif::empty(),
152 const_fn_arg_vars: BitSet::new_empty(mir.local_decls.len()),
153 temp_promotion_state: temps,
154 promotion_candidates: vec![]
158 // FIXME(eddyb) we could split the errors into meaningful
159 // categories, but enabling full miri would make that
160 // slightly pointless (even with feature-gating).
161 fn not_const(&mut self) {
162 self.add(Qualif::NOT_CONST);
163 if self.mode != Mode::Fn {
164 let mut err = struct_span_err!(
168 "{} contains unimplemented expression type",
171 if self.tcx.sess.teach(&err.get_code().unwrap()) {
172 err.note("A function call isn't allowed in the const's initialization expression \
173 because the expression's value must be known at compile-time.");
174 err.note("Remember: you can't use a function call inside a const's initialization \
175 expression! However, you can use it anywhere else.");
181 /// Error about extra statements in a constant.
182 fn statement_like(&mut self) {
183 self.add(Qualif::NOT_CONST);
184 if self.mode != Mode::Fn {
185 let mut err = feature_err(
186 &self.tcx.sess.parse_sess,
190 &format!("statements in {}s are unstable", self.mode),
192 if self.tcx.sess.teach(&err.get_code().unwrap()) {
193 err.note("Blocks in constants may only contain items (such as constant, function \
194 definition, etc...) and a tail expression.");
195 err.help("To avoid it, you have to replace the non-item object.");
201 /// Add the given qualification to self.qualif.
202 fn add(&mut self, qualif: Qualif) {
203 self.qualif = self.qualif | qualif;
206 /// Add the given type's qualification to self.qualif.
207 fn add_type(&mut self, ty: Ty<'tcx>) {
208 self.add(Qualif::MUTABLE_INTERIOR | Qualif::NEEDS_DROP);
209 self.qualif.restrict(ty, self.tcx, self.param_env);
212 /// Within the provided closure, self.qualif will start
213 /// out empty, and its value after the closure returns will
214 /// be combined with the value before the call to nest.
215 fn nest<F: FnOnce(&mut Self)>(&mut self, f: F) {
216 let original = self.qualif;
217 self.qualif = Qualif::empty();
222 /// Assign the current qualification to the given destination.
223 fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
224 trace!("assign: {:?}", dest);
225 let qualif = self.qualif;
226 let span = self.span;
227 let store = |slot: &mut Option<Qualif>| {
229 span_bug!(span, "multiple assignments to {:?}", dest);
231 *slot = Some(qualif);
234 // Only handle promotable temps in non-const functions.
235 if self.mode == Mode::Fn {
236 if let Place::Local(index) = *dest {
237 if self.mir.local_kind(index) == LocalKind::Temp
238 && self.temp_promotion_state[index].is_promotable() {
239 debug!("store to promotable temp {:?} ({:?})", index, qualif);
240 store(&mut self.local_qualif[index]);
246 if self.const_let_allowed() {
250 // with `const_let` active, we treat all locals equal
251 Place::Local(index) => break *index,
252 // projections are transparent for assignments
253 // we qualify the entire destination at once, even if just a field would have
254 // stricter qualification
255 Place::Projection(proj) => {
256 // Catch more errors in the destination. `visit_place` also checks various
257 // projection rules like union field access and raw pointer deref
260 PlaceContext::MutatingUse(MutatingUseContext::Store),
265 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
266 Place::Static(..) => {
267 // Catch more errors in the destination. `visit_place` also checks that we
268 // do not try to access statics from constants or try to mutate statics
271 PlaceContext::MutatingUse(MutatingUseContext::Store),
278 debug!("store to var {:?}", index);
279 match &mut self.local_qualif[index] {
280 // this is overly restrictive, because even full assignments do not clear the qualif
281 // While we could special case full assignments, this would be inconsistent with
282 // aggregates where we overwrite all fields via assignments, which would not get
284 Some(ref mut qualif) => *qualif = *qualif | self.qualif,
285 // insert new qualification
286 qualif @ None => *qualif = Some(self.qualif),
292 Place::Local(index) if self.mir.local_kind(index) == LocalKind::Temp ||
293 self.mir.local_kind(index) == LocalKind::ReturnPointer => {
294 debug!("store to {:?} (temp or return pointer)", index);
295 store(&mut self.local_qualif[index])
298 Place::Projection(box Projection {
299 base: Place::Local(index),
300 elem: ProjectionElem::Deref
301 }) if self.mir.local_kind(index) == LocalKind::Temp
302 && self.mir.local_decls[index].ty.is_box()
303 && self.local_qualif[index].map_or(false, |qualif| {
304 qualif.contains(Qualif::NOT_CONST)
306 // Part of `box expr`, we should've errored
307 // already for the Box allocation Rvalue.
310 // This must be an explicit assignment.
312 // Catch more errors in the destination.
315 PlaceContext::MutatingUse(MutatingUseContext::Store),
318 self.statement_like();
323 fn const_let_allowed(&self) -> bool {
324 self.tcx.features().const_let
327 /// Qualify a whole const, static initializer or const fn.
328 fn qualify_const(&mut self) -> (Qualif, Lrc<BitSet<Local>>) {
329 debug!("qualifying {} {:?}", self.mode, self.def_id);
333 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
334 let mut bb = START_BLOCK;
336 seen_blocks.insert(bb.index());
338 self.visit_basic_block_data(bb, &mir[bb]);
340 let target = match mir[bb].terminator().kind {
341 TerminatorKind::Goto { target } |
342 TerminatorKind::Drop { target, .. } |
343 TerminatorKind::Assert { target, .. } |
344 TerminatorKind::Call { destination: Some((_, target)), .. } => {
348 // Non-terminating calls cannot produce any value.
349 TerminatorKind::Call { destination: None, .. } => {
353 TerminatorKind::SwitchInt {..} |
354 TerminatorKind::DropAndReplace { .. } |
355 TerminatorKind::Resume |
356 TerminatorKind::Abort |
357 TerminatorKind::GeneratorDrop |
358 TerminatorKind::Yield { .. } |
359 TerminatorKind::Unreachable |
360 TerminatorKind::FalseEdges { .. } |
361 TerminatorKind::FalseUnwind { .. } => None,
363 TerminatorKind::Return => {
364 if !self.const_let_allowed() {
365 // Check for unused values. This usually means
366 // there are extra statements in the AST.
367 for temp in mir.temps_iter() {
368 if self.local_qualif[temp].is_none() {
372 let state = self.temp_promotion_state[temp];
373 if let TempState::Defined { location, uses: 0 } = state {
374 let data = &mir[location.block];
375 let stmt_idx = location.statement_index;
377 // Get the span for the initialization.
378 let source_info = if stmt_idx < data.statements.len() {
379 data.statements[stmt_idx].source_info
381 data.terminator().source_info
383 self.span = source_info.span;
385 // Treat this as a statement in the AST.
386 self.statement_like();
390 // Make sure there are no extra unassigned variables.
391 self.qualif = Qualif::NOT_CONST;
392 for index in mir.vars_iter() {
393 if !self.const_fn_arg_vars.contains(index) {
394 debug!("unassigned variable {:?}", index);
395 self.assign(&Place::Local(index), Location {
397 statement_index: usize::MAX,
409 Some(target) if !seen_blocks.contains(target.index()) => {
419 self.qualif = self.local_qualif[RETURN_PLACE].unwrap_or(Qualif::NOT_CONST);
421 // Account for errors in consts by using the
422 // conservative type qualification instead.
423 if self.qualif.intersects(Qualif::CONST_ERROR) {
424 self.qualif = Qualif::empty();
425 let return_ty = mir.return_ty();
426 self.add_type(return_ty);
430 // Collect all the temps we need to promote.
431 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
433 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
434 for candidate in &self.promotion_candidates {
436 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
437 match self.mir[bb].statements[stmt_idx].kind {
438 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
439 promoted_temps.insert(index);
444 Candidate::Argument { .. } => {}
448 (self.qualif, Lrc::new(promoted_temps))
451 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
452 Some(def_id) == self.tcx.lang_items().panic_fn() ||
453 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
457 /// Accumulates an Rvalue or Call's effects in self.qualif.
458 /// For functions (constant or not), it also records
459 /// candidates for promotion in promotion_candidates.
460 impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
461 fn visit_local(&mut self,
463 _: PlaceContext<'tcx>,
465 debug!("visit_local: local={:?}", local);
466 let kind = self.mir.local_kind(local);
468 LocalKind::ReturnPointer => {
471 LocalKind::Var if !self.const_let_allowed() => {
472 if self.mode != Mode::Fn {
473 emit_feature_err(&self.tcx.sess.parse_sess, "const_let",
474 self.span, GateIssue::Language,
475 &format!("let bindings in {}s are unstable",self.mode));
477 self.add(Qualif::NOT_CONST);
482 if let LocalKind::Arg = kind {
483 self.add(Qualif::FN_ARGUMENT);
486 if !self.temp_promotion_state[local].is_promotable() {
487 debug!("visit_local: (not promotable) local={:?}", local);
488 self.add(Qualif::NOT_PROMOTABLE);
491 if let Some(qualif) = self.local_qualif[local] {
500 fn visit_place(&mut self,
502 context: PlaceContext<'tcx>,
503 location: Location) {
504 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
506 Place::Local(ref local) => self.visit_local(local, context, location),
507 Place::Promoted(_) => bug!("promoting already promoted MIR"),
508 Place::Static(ref global) => {
510 .get_attrs(global.def_id)
512 .any(|attr| attr.check_name("thread_local")) {
513 if self.mode != Mode::Fn {
514 span_err!(self.tcx.sess, self.span, E0625,
515 "thread-local statics cannot be \
516 accessed at compile-time");
518 self.add(Qualif::NOT_CONST);
522 // Only allow statics (not consts) to refer to other statics.
523 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
524 if context.is_mutating_use() {
525 // this is not strictly necessary as miri will also bail out
526 // For interior mutability we can't really catch this statically as that
527 // goes through raw pointers and intermediate temporaries, so miri has
528 // to catch this anyway
529 self.tcx.sess.span_err(
531 "cannot mutate statics in the initializer of another static",
536 self.add(Qualif::NOT_CONST);
538 if self.mode != Mode::Fn {
539 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
540 "{}s cannot refer to statics, use \
541 a constant instead", self.mode);
542 if self.tcx.sess.teach(&err.get_code().unwrap()) {
544 "Static and const variables can refer to other const variables. But a \
545 const variable cannot refer to a static variable."
548 "To fix this, the value can be extracted as a const and then used."
554 Place::Projection(ref proj) => {
556 this.super_place(place, context, location);
558 ProjectionElem::Deref => {
559 if context.is_mutating_use() {
560 // `not_const` errors out in const contexts
563 // just make sure this doesn't get promoted
564 this.qualif.add(Qualif::NOT_CONST);
566 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
570 if let ty::RawPtr(_) = base_ty.sty {
571 if !this.tcx.features().const_raw_ptr_deref {
573 &this.tcx.sess.parse_sess, "const_raw_ptr_deref",
574 this.span, GateIssue::Language,
576 "dereferencing raw pointers in {}s is unstable",
586 ProjectionElem::Field(..) |
587 ProjectionElem::Index(_) => {
588 let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
589 if let Some(def) = base_ty.ty_adt_def() {
592 Mode::Fn => this.not_const(),
594 if !this.tcx.features().const_fn_union {
596 &this.tcx.sess.parse_sess, "const_fn_union",
597 this.span, GateIssue::Language,
598 "unions in const fn are unstable",
611 let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
612 this.qualif.restrict(ty, this.tcx, this.param_env);
615 ProjectionElem::ConstantIndex {..} |
616 ProjectionElem::Subslice {..} |
617 ProjectionElem::Downcast(..) => {
626 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
627 debug!("visit_operand: operand={:?} location={:?}", operand, location);
628 self.super_operand(operand, location);
632 Operand::Move(_) => {
633 // Mark the consumed locals to indicate later drops are noops.
634 if let Operand::Move(Place::Local(local)) = *operand {
635 self.local_qualif[local] = self.local_qualif[local].map(|q|
636 q - Qualif::NEEDS_DROP
640 Operand::Constant(ref constant) => {
641 if let ConstValue::Unevaluated(def_id, _) = constant.literal.val {
642 // Don't peek inside trait associated constants.
643 if self.tcx.trait_of_item(def_id).is_some() {
644 self.add_type(constant.literal.ty);
646 let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
648 let qualif = Qualif::from_bits(bits).expect("invalid mir_const_qualif");
651 // Just in case the type is more specific than
652 // the definition, e.g. impl associated const
653 // with type parameters, take it into account.
654 self.qualif.restrict(constant.literal.ty, self.tcx, self.param_env);
661 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
662 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
663 // Recurse through operands and places.
664 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
665 let mut is_reborrow = false;
666 if let Place::Projection(ref proj) = *place {
667 if let ProjectionElem::Deref = proj.elem {
668 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
669 if let ty::Ref(..) = base_ty.sty {
676 let ctx = match kind {
677 BorrowKind::Shared =>
678 PlaceContext::NonMutatingUse(NonMutatingUseContext::SharedBorrow(region)),
679 BorrowKind::Shallow =>
680 PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow(region)),
681 BorrowKind::Unique =>
682 PlaceContext::NonMutatingUse(NonMutatingUseContext::UniqueBorrow(region)),
683 BorrowKind::Mut { .. } =>
684 PlaceContext::MutatingUse(MutatingUseContext::Borrow(region)),
686 self.super_place(place, ctx, location);
688 self.super_rvalue(rvalue, location);
691 self.super_rvalue(rvalue, location);
697 Rvalue::UnaryOp(UnOp::Neg, _) |
698 Rvalue::UnaryOp(UnOp::Not, _) |
699 Rvalue::NullaryOp(NullOp::SizeOf, _) |
700 Rvalue::CheckedBinaryOp(..) |
701 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
702 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
703 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
704 Rvalue::Cast(CastKind::Unsize, ..) |
705 Rvalue::Discriminant(..) |
708 Rvalue::Ref(_, kind, ref place) => {
709 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
711 // Default to forbidding the borrow and/or its promotion,
712 // due to the potential for direct or interior mutability,
713 // and only proceed by setting `forbidden_mut` to `false`.
714 let mut forbidden_mut = true;
716 if let BorrowKind::Mut { .. } = kind {
717 // In theory, any zero-sized value could be borrowed
718 // mutably without consequences. However, only &mut []
719 // is allowed right now, and only in functions.
720 if self.mode == Mode::StaticMut {
721 // Inside a `static mut`, &mut [...] is also allowed.
723 ty::Array(..) | ty::Slice(_) => forbidden_mut = false,
726 } else if let ty::Array(_, len) = ty.sty {
727 // FIXME(eddyb) the `self.mode == Mode::Fn` condition
728 // seems unnecessary, given that this is merely a ZST.
729 if len.unwrap_usize(self.tcx) == 0 && self.mode == Mode::Fn {
730 forbidden_mut = false;
735 self.add(Qualif::NOT_CONST);
736 if self.mode != Mode::Fn {
737 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
738 "references in {}s may only refer \
739 to immutable values", self.mode);
740 err.span_label(self.span, format!("{}s require immutable values",
742 if self.tcx.sess.teach(&err.get_code().unwrap()) {
743 err.note("References in statics and constants may only refer to \
744 immutable values.\n\n\
745 Statics are shared everywhere, and if they refer to \
746 mutable data one might violate memory safety since \
747 holding multiple mutable references to shared data is \
749 If you really want global mutable state, try using \
750 static mut or a global UnsafeCell.");
756 // Constants cannot be borrowed if they contain interior mutability as
757 // it means that our "silent insertion of statics" could change
758 // initializer values (very bad).
759 if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
760 // A reference of a MUTABLE_INTERIOR place is instead
761 // NOT_CONST (see `if forbidden_mut` below), to avoid
762 // duplicate errors (from reborrowing, for example).
763 self.qualif = self.qualif - Qualif::MUTABLE_INTERIOR;
764 if self.mode != Mode::Fn {
765 span_err!(self.tcx.sess, self.span, E0492,
766 "cannot borrow a constant which may contain \
767 interior mutability, create a static instead");
770 // We allow immutable borrows of frozen data.
771 forbidden_mut = false;
775 debug!("visit_rvalue: forbidden_mut={:?}", forbidden_mut);
777 self.add(Qualif::NOT_CONST);
779 // We might have a candidate for promotion.
780 let candidate = Candidate::Ref(location);
781 // We can only promote interior borrows of promotable temps.
782 let mut place = place;
783 while let Place::Projection(ref proj) = *place {
784 if proj.elem == ProjectionElem::Deref {
789 debug!("visit_rvalue: place={:?}", place);
790 if let Place::Local(local) = *place {
791 if self.mir.local_kind(local) == LocalKind::Temp {
792 debug!("visit_rvalue: local={:?}", local);
793 if let Some(qualif) = self.local_qualif[local] {
794 // `forbidden_mut` is false, so we can safely ignore
795 // `MUTABLE_INTERIOR` from the local's qualifications.
796 // This allows borrowing fields which don't have
797 // `MUTABLE_INTERIOR`, from a type that does, e.g.:
798 // `let _: &'static _ = &(Cell::new(1), 2).1;`
799 debug!("visit_rvalue: qualif={:?}", qualif);
800 if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
801 debug!("visit_rvalue: candidate={:?}", candidate);
802 self.promotion_candidates.push(candidate);
810 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
811 let operand_ty = operand.ty(self.mir, self.tcx);
812 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
813 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
814 match (cast_in, cast_out) {
815 (CastTy::Ptr(_), CastTy::Int(_)) |
816 (CastTy::FnPtr, CastTy::Int(_)) => {
817 if let Mode::Fn = self.mode {
818 // in normal functions, mark such casts as not promotable
819 self.add(Qualif::NOT_CONST);
820 } else if !self.tcx.features().const_raw_ptr_to_usize_cast {
821 // in const fn and constants require the feature gate
822 // FIXME: make it unsafe inside const fn and constants
824 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
825 self.span, GateIssue::Language,
827 "casting pointers to integers in {}s is unstable",
837 Rvalue::BinaryOp(op, ref lhs, _) => {
838 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
839 assert!(op == BinOp::Eq || op == BinOp::Ne ||
840 op == BinOp::Le || op == BinOp::Lt ||
841 op == BinOp::Ge || op == BinOp::Gt ||
842 op == BinOp::Offset);
844 if let Mode::Fn = self.mode {
845 // raw pointer operations are not allowed inside promoteds
846 self.add(Qualif::NOT_CONST);
847 } else if !self.tcx.features().const_compare_raw_pointers {
848 // require the feature gate inside constants and const fn
849 // FIXME: make it unsafe to use these operations
851 &self.tcx.sess.parse_sess,
852 "const_compare_raw_pointers",
855 &format!("comparing raw pointers inside {}", self.mode),
861 Rvalue::NullaryOp(NullOp::Box, _) => {
862 self.add(Qualif::NOT_CONST);
863 if self.mode != Mode::Fn {
864 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
865 "allocations are not allowed in {}s", self.mode);
866 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
867 if self.tcx.sess.teach(&err.get_code().unwrap()) {
869 "The value of statics and constants must be known at compile time, \
870 and they live for the entire lifetime of a program. Creating a boxed \
871 value allocates memory on the heap at runtime, and therefore cannot \
872 be done at compile time."
879 Rvalue::Aggregate(ref kind, _) => {
880 if let AggregateKind::Adt(def, ..) = **kind {
881 if def.has_dtor(self.tcx) {
882 self.add(Qualif::NEEDS_DROP);
885 if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
886 let ty = rvalue.ty(self.mir, self.tcx);
888 assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
895 fn visit_terminator_kind(&mut self,
897 kind: &TerminatorKind<'tcx>,
898 location: Location) {
899 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
900 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
901 self.visit_operand(func, location);
903 let fn_ty = func.ty(self.mir, self.tcx);
904 let mut callee_def_id = None;
905 let mut is_shuffle = false;
906 let mut is_const_fn = false;
907 let mut is_promotable_const_fn = false;
908 if let ty::FnDef(def_id, _) = fn_ty.sty {
909 callee_def_id = Some(def_id);
910 match self.tcx.fn_sig(def_id).abi() {
912 Abi::PlatformIntrinsic => {
913 assert!(!self.tcx.is_const_fn(def_id));
914 match &self.tcx.item_name(def_id).as_str()[..] {
933 | "add_with_overflow"
934 | "sub_with_overflow"
935 | "mul_with_overflow"
936 // no need to check feature gates, intrinsics are only callable from the
937 // libstd or with forever unstable feature gates
938 => is_const_fn = true,
939 // special intrinsic that can be called diretly without an intrinsic
940 // feature gate needs a language feature gate
942 // never promote transmute calls
943 if self.mode != Mode::Fn {
945 // const eval transmute calls only with the feature gate
946 if !self.tcx.features().const_transmute {
948 &self.tcx.sess.parse_sess, "const_transmute",
949 self.span, GateIssue::Language,
950 &format!("The use of std::mem::transmute() \
951 is gated in {}s", self.mode));
956 name if name.starts_with("simd_shuffle") => {
964 // in normal functions we only care about promotion
965 if self.mode == Mode::Fn {
966 // never promote const fn calls of
967 // functions without #[rustc_promotable]
968 if self.tcx.is_promotable_const_fn(def_id) {
970 is_promotable_const_fn = true;
971 } else if self.tcx.is_const_fn(def_id) {
975 // stable const fn or unstable const fns with their feature gate
977 if self.tcx.is_const_fn(def_id) {
979 } else if self.is_const_panic_fn(def_id) {
980 // check the const_panic feature gate
981 // FIXME: cannot allow this inside `allow_internal_unstable` because
982 // that would make `panic!` insta stable in constants, since the
983 // macro is marked with the attr
984 if self.tcx.features().const_panic {
987 // don't allow panics in constants without the feature gate
989 &self.tcx.sess.parse_sess,
993 &format!("panicking in {}s is unstable", self.mode),
996 } else if let Some(feature) = self.tcx.is_unstable_const_fn(def_id) {
997 // check `#[unstable]` const fns or `#[rustc_const_unstable]`
998 // functions without the feature gate active in this crate to report
999 // a better error message than the one below
1000 if self.span.allows_unstable() {
1001 // `allow_internal_unstable` can make such calls stable
1004 let mut err = self.tcx.sess.struct_span_err(self.span,
1005 &format!("`{}` is not yet stable as a const fn",
1006 self.tcx.item_path_str(def_id)));
1008 "in Nightly builds, add `#![feature({})]` \
1009 to the crate attributes to enable",
1014 // FIXME(#24111) Remove this check when const fn stabilizes
1015 let (msg, note) = if let UnstableFeatures::Disallow =
1016 self.tcx.sess.opts.unstable_features {
1017 (format!("calls in {}s are limited to \
1018 tuple structs and tuple variants",
1020 Some("a limited form of compile-time function \
1021 evaluation is available on a nightly \
1022 compiler via `const fn`"))
1024 (format!("calls in {}s are limited \
1025 to constant functions, \
1026 tuple structs and tuple variants",
1030 let mut err = struct_span_err!(
1037 if let Some(note) = note {
1038 err.span_note(self.span, note);
1047 let constant_arguments = callee_def_id.and_then(|id| {
1048 args_required_const(self.tcx, id)
1050 for (i, arg) in args.iter().enumerate() {
1052 this.visit_operand(arg, location);
1053 if this.mode != Mode::Fn {
1056 let candidate = Candidate::Argument { bb, index: i };
1057 if is_shuffle && i == 2 {
1058 if this.qualif.is_empty() {
1059 debug!("visit_terminator_kind: candidate={:?}", candidate);
1060 this.promotion_candidates.push(candidate);
1062 span_err!(this.tcx.sess, this.span, E0526,
1063 "shuffle indices are not constant");
1068 let constant_arguments = match constant_arguments.as_ref() {
1072 if !constant_arguments.contains(&i) {
1075 // Since the argument is required to be constant,
1076 // we care about constness, not promotability.
1077 // If we checked for promotability, we'd miss out on
1078 // the results of function calls (which are never promoted
1080 // This is not a problem, because the argument explicitly
1081 // requests constness, in contrast to regular promotion
1082 // which happens even without the user requesting it.
1083 // We can error out with a hard error if the argument is not
1085 if (this.qualif - Qualif::NOT_PROMOTABLE).is_empty() {
1086 debug!("visit_terminator_kind: candidate={:?}", candidate);
1087 this.promotion_candidates.push(candidate);
1089 this.tcx.sess.span_err(this.span,
1090 &format!("argument {} is required to be a constant",
1096 // non-const fn calls.
1098 self.qualif = Qualif::NOT_CONST;
1099 if self.mode != Mode::Fn {
1100 self.tcx.sess.delay_span_bug(
1102 "should have reported an error about non-const fn calls in constants",
1107 if let Some((ref dest, _)) = *destination {
1108 // Avoid propagating irrelevant callee/argument qualifications.
1109 if self.qualif.intersects(Qualif::CONST_ERROR) {
1110 self.qualif = Qualif::NOT_CONST;
1112 // Be conservative about the returned value of a const fn.
1114 let ty = dest.ty(self.mir, tcx).to_ty(tcx);
1115 if is_const_fn && !is_promotable_const_fn && self.mode == Mode::Fn {
1116 self.qualif = Qualif::NOT_PROMOTABLE;
1118 self.qualif = Qualif::empty();
1122 self.assign(dest, location);
1124 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1125 self.super_terminator_kind(bb, kind, location);
1127 // Deny *any* live drops anywhere other than functions.
1128 if self.mode != Mode::Fn {
1129 // HACK(eddyb) Emulate a bit of dataflow analysis,
1130 // conservatively, that drop elaboration will do.
1131 let needs_drop = if let Place::Local(local) = *place {
1132 if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
1133 Some(self.mir.local_decls[local].source_info.span)
1141 if let Some(span) = needs_drop {
1142 // Double-check the type being dropped, to minimize false positives.
1143 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1144 if ty.needs_drop(self.tcx, self.param_env) {
1145 struct_span_err!(self.tcx.sess, span, E0493,
1146 "destructors cannot be evaluated at compile-time")
1147 .span_label(span, format!("{}s cannot evaluate destructors",
1154 // Qualify any operands inside other terminators.
1155 self.super_terminator_kind(bb, kind, location);
1159 fn visit_assign(&mut self,
1162 rvalue: &Rvalue<'tcx>,
1163 location: Location) {
1164 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1165 self.visit_rvalue(rvalue, location);
1167 // Check the allowed const fn argument forms.
1168 if let (Mode::ConstFn, &Place::Local(index)) = (self.mode, dest) {
1169 if self.mir.local_kind(index) == LocalKind::Var &&
1170 self.const_fn_arg_vars.insert(index) &&
1171 !self.const_let_allowed() {
1172 // Direct use of an argument is permitted.
1174 Rvalue::Use(Operand::Copy(Place::Local(local))) |
1175 Rvalue::Use(Operand::Move(Place::Local(local))) => {
1176 if self.mir.local_kind(local) == LocalKind::Arg {
1182 // Avoid a generic error for other uses of arguments.
1183 if self.qualif.contains(Qualif::FN_ARGUMENT) {
1184 let decl = &self.mir.local_decls[index];
1185 let mut err = feature_err(
1186 &self.tcx.sess.parse_sess,
1188 decl.source_info.span,
1189 GateIssue::Language,
1190 "arguments of constant functions can only be immutable by-value bindings"
1192 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1193 err.note("Constant functions are not allowed to mutate anything. Thus, \
1194 binding to an argument with a mutable pattern is not allowed.");
1195 err.note("Remove any mutable bindings from the argument list to fix this \
1196 error. In case you need to mutate the argument, try lazily \
1197 initializing a global variable instead of using a const fn, or \
1198 refactoring the code to a functional style to avoid mutation if \
1207 self.assign(dest, location);
1210 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1211 debug!("visit_source_info: source_info={:?}", source_info);
1212 self.span = source_info.span;
1215 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1216 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1218 this.visit_source_info(&statement.source_info);
1219 match statement.kind {
1220 StatementKind::Assign(ref place, ref rvalue) => {
1221 this.visit_assign(bb, place, rvalue, location);
1223 StatementKind::FakeRead(..) |
1224 StatementKind::SetDiscriminant { .. } |
1225 StatementKind::StorageLive(_) |
1226 StatementKind::StorageDead(_) |
1227 StatementKind::InlineAsm {..} |
1228 StatementKind::Retag { .. } |
1229 StatementKind::EscapeToRaw { .. } |
1230 StatementKind::AscribeUserType(..) |
1231 StatementKind::Nop => {}
1236 fn visit_terminator(&mut self,
1238 terminator: &Terminator<'tcx>,
1239 location: Location) {
1240 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1241 self.nest(|this| this.super_terminator(bb, terminator, location));
1245 pub fn provide(providers: &mut Providers) {
1246 *providers = Providers {
1252 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1254 -> (u8, Lrc<BitSet<Local>>) {
1255 // NB: This `borrow()` is guaranteed to be valid (i.e., the value
1256 // cannot yet be stolen), because `mir_validated()`, which steals
1257 // from `mir_const(), forces this query to execute before
1258 // performing the steal.
1259 let mir = &tcx.mir_const(def_id).borrow();
1261 if mir.return_ty().references_error() {
1262 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1263 return (Qualif::NOT_CONST.bits(), Lrc::new(BitSet::new_empty(0)));
1266 let mut qualifier = Qualifier::new(tcx, def_id, mir, Mode::Const);
1267 let (qualif, promoted_temps) = qualifier.qualify_const();
1268 (qualif.bits(), promoted_temps)
1271 pub struct QualifyAndPromoteConstants;
1273 impl MirPass for QualifyAndPromoteConstants {
1274 fn run_pass<'a, 'tcx>(&self,
1275 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1277 mir: &mut Mir<'tcx>) {
1278 // There's not really any point in promoting errorful MIR.
1279 if mir.return_ty().references_error() {
1280 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1284 if src.promoted.is_some() {
1288 let def_id = src.def_id;
1289 let id = tcx.hir.as_local_node_id(def_id).unwrap();
1290 let mut const_promoted_temps = None;
1291 let mode = match tcx.hir.body_owner_kind(id) {
1292 hir::BodyOwnerKind::Fn => {
1293 if tcx.is_const_fn(def_id) {
1299 hir::BodyOwnerKind::Const => {
1300 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1303 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1304 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1307 debug!("run_pass: mode={:?}", mode);
1308 if mode == Mode::Fn || mode == Mode::ConstFn {
1309 // This is ugly because Qualifier holds onto mir,
1310 // which can't be mutated until its scope ends.
1311 let (temps, candidates) = {
1312 let mut qualifier = Qualifier::new(tcx, def_id, mir, mode);
1313 if mode == Mode::ConstFn {
1314 if tcx.is_min_const_fn(def_id) {
1315 // enforce `min_const_fn` for stable const fns
1316 use super::qualify_min_const_fn::is_min_const_fn;
1317 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1318 tcx.sess.span_err(span, &err);
1320 // this should not produce any errors, but better safe than sorry
1322 qualifier.qualify_const();
1325 // Enforce a constant-like CFG for `const fn`.
1326 qualifier.qualify_const();
1329 while let Some((bb, data)) = qualifier.rpo.next() {
1330 qualifier.visit_basic_block_data(bb, data);
1334 (qualifier.temp_promotion_state, qualifier.promotion_candidates)
1337 // Do the actual promotion, now that we know what's viable.
1338 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1340 if !mir.control_flow_destroyed.is_empty() {
1341 let mut locals = mir.vars_iter();
1342 if let Some(local) = locals.next() {
1343 let span = mir.local_decls[local].source_info.span;
1344 let mut error = tcx.sess.struct_span_err(
1347 "new features like let bindings are not permitted in {} \
1348 which also use short circuiting operators",
1352 for (span, kind) in mir.control_flow_destroyed.iter() {
1355 &format!("use of {} here", kind),
1358 for local in locals {
1359 let span = mir.local_decls[local].source_info.span;
1362 "more locals defined here",
1368 let promoted_temps = if mode == Mode::Const {
1369 // Already computed by `mir_const_qualif`.
1370 const_promoted_temps.unwrap()
1372 Qualifier::new(tcx, def_id, mir, mode).qualify_const().1
1375 // In `const` and `static` everything without `StorageDead`
1376 // is `'static`, we don't have to create promoted MIR fragments,
1377 // just remove `Drop` and `StorageDead` on "promoted" locals.
1378 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1379 for block in mir.basic_blocks_mut() {
1380 block.statements.retain(|statement| {
1381 match statement.kind {
1382 StatementKind::StorageDead(index) => {
1383 !promoted_temps.contains(index)
1388 let terminator = block.terminator_mut();
1389 match terminator.kind {
1390 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1391 if promoted_temps.contains(index) {
1392 terminator.kind = TerminatorKind::Goto {
1402 // Statics must be Sync.
1403 if mode == Mode::Static {
1404 // `#[thread_local]` statics don't have to be `Sync`.
1405 for attr in &tcx.get_attrs(def_id)[..] {
1406 if attr.check_name("thread_local") {
1410 let ty = mir.return_ty();
1411 tcx.infer_ctxt().enter(|infcx| {
1412 let param_env = ty::ParamEnv::empty();
1413 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1414 let mut fulfillment_cx = traits::FulfillmentContext::new();
1415 fulfillment_cx.register_bound(&infcx,
1418 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1420 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1421 infcx.report_fulfillment_errors(&err, None, false);
1428 fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
1429 let attrs = tcx.get_attrs(def_id);
1430 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1431 let mut ret = FxHashSet::default();
1432 for meta in attr.meta_item_list()? {
1433 match meta.literal()?.node {
1434 LitKind::Int(a, _) => { ret.insert(a as usize); }