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};
28 use std::ops::{Deref, Index, IndexMut};
31 use crate::transform::{MirPass, MirSource};
32 use super::promote_consts::{self, Candidate, TempState};
34 /// What kind of item we are in.
35 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
44 impl fmt::Display for Mode {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 Mode::Const => write!(f, "constant"),
48 Mode::Static | Mode::StaticMut => write!(f, "static"),
49 Mode::ConstFn => write!(f, "constant function"),
50 Mode::Fn => write!(f, "function")
55 const QUALIF_COUNT: usize = 4;
57 // FIXME(eddyb) once we can use const generics, replace this array with
58 // something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
59 #[derive(Copy, Clone, Default)]
60 struct PerQualif<T>([T; QUALIF_COUNT]);
62 impl<T: Clone> PerQualif<T> {
63 fn new(x: T) -> Self {
64 PerQualif([x.clone(), x.clone(), x.clone(), x])
68 impl<T> PerQualif<T> {
69 fn as_mut(&mut self) -> PerQualif<&mut T> {
70 let [x0, x1, x2, x3] = &mut self.0;
71 PerQualif([x0, x1, x2, x3])
74 fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
75 let [x0, x1, x2, x3] = self.0;
76 let [y0, y1, y2, y3] = other.0;
77 PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
81 impl PerQualif<bool> {
82 fn encode_to_bits(self) -> u8 {
83 self.0.iter().enumerate().fold(0, |bits, (i, &qualif)| {
84 bits | ((qualif as u8) << i)
88 fn decode_from_bits(bits: u8) -> Self {
89 let mut qualifs = Self::default();
90 for (i, qualif) in qualifs.0.iter_mut().enumerate() {
91 *qualif = (bits & (1 << i)) != 0;
97 impl<Q: Qualif, T> Index<Q> for PerQualif<T> {
100 fn index(&self, _: Q) -> &T {
105 impl<Q: Qualif, T> IndexMut<Q> for PerQualif<T> {
106 fn index_mut(&mut self, _: Q) -> &mut T {
111 struct ConstCx<'a, 'tcx> {
112 tcx: TyCtxt<'a, 'tcx, 'tcx>,
113 param_env: ty::ParamEnv<'tcx>,
117 per_local: PerQualif<BitSet<Local>>,
120 impl<'a, 'tcx> ConstCx<'a, 'tcx> {
121 fn is_const_panic_fn(&self, def_id: DefId) -> bool {
122 Some(def_id) == self.tcx.lang_items().panic_fn() ||
123 Some(def_id) == self.tcx.lang_items().begin_panic_fn()
127 #[derive(Copy, Clone, Debug)]
128 enum ValueSource<'a, 'tcx> {
129 Rvalue(&'a Rvalue<'tcx>),
131 callee: &'a Operand<'tcx>,
132 args: &'a [Operand<'tcx>],
140 /// Return the qualification that is (conservatively) correct for any value
141 /// of the type, or `None` if the qualification is not value/type-based.
142 fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> Option<bool> {
146 /// Return a mask for the qualification, given a type. This is `false` iff
147 /// no value of that type can have the qualification.
148 fn mask_for_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
149 Self::in_any_value_of_ty(cx, ty).unwrap_or(true)
152 fn in_local(cx: &ConstCx<'_, '_>, local: Local) -> bool {
153 cx.per_local.0[Self::IDX].contains(local)
156 fn in_static(_cx: &ConstCx<'_, 'tcx>, _static: &Static<'tcx>) -> bool {
157 // FIXME(eddyb) should we do anything here for value properties?
161 fn in_projection_structurally(
162 cx: &ConstCx<'_, 'tcx>,
163 proj: &PlaceProjection<'tcx>,
165 let base_qualif = Self::in_place(cx, &proj.base);
166 let qualif = base_qualif && Self::mask_for_ty(
168 proj.base.ty(cx.mir, cx.tcx)
169 .projection_ty(cx.tcx, &proj.elem)
173 ProjectionElem::Deref |
174 ProjectionElem::Subslice { .. } |
175 ProjectionElem::Field(..) |
176 ProjectionElem::ConstantIndex { .. } |
177 ProjectionElem::Downcast(..) => qualif,
179 ProjectionElem::Index(local) => qualif || Self::in_local(cx, local),
183 fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &PlaceProjection<'tcx>) -> bool {
184 Self::in_projection_structurally(cx, proj)
187 fn in_place(cx: &ConstCx<'_, 'tcx>, place: &Place<'tcx>) -> bool {
189 Place::Local(local) => Self::in_local(cx, local),
190 Place::Promoted(_) => bug!("qualifying already promoted MIR"),
191 Place::Static(ref static_) => Self::in_static(cx, static_),
192 Place::Projection(ref proj) => Self::in_projection(cx, proj),
196 fn in_operand(cx: &ConstCx<'_, 'tcx>, operand: &Operand<'tcx>) -> bool {
198 Operand::Copy(ref place) |
199 Operand::Move(ref place) => Self::in_place(cx, place),
201 Operand::Constant(ref constant) => {
202 if let ty::LazyConst::Unevaluated(def_id, _) = constant.literal {
203 // Don't peek inside trait associated constants.
204 if cx.tcx.trait_of_item(*def_id).is_some() {
205 Self::in_any_value_of_ty(cx, constant.ty).unwrap_or(false)
207 let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(*def_id);
209 let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
211 // Just in case the type is more specific than
212 // the definition, e.g., impl associated const
213 // with type parameters, take it into account.
214 qualif && Self::mask_for_ty(cx, constant.ty)
223 fn in_rvalue_structurally(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
225 Rvalue::NullaryOp(..) => false,
227 Rvalue::Discriminant(ref place) |
228 Rvalue::Len(ref place) => Self::in_place(cx, place),
230 Rvalue::Use(ref operand) |
231 Rvalue::Repeat(ref operand, _) |
232 Rvalue::UnaryOp(_, ref operand) |
233 Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, operand),
235 Rvalue::BinaryOp(_, ref lhs, ref rhs) |
236 Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
237 Self::in_operand(cx, lhs) || Self::in_operand(cx, rhs)
240 Rvalue::Ref(_, _, ref place) => {
241 // Special-case reborrows to be more like a copy of the reference.
242 if let Place::Projection(ref proj) = *place {
243 if let ProjectionElem::Deref = proj.elem {
244 let base_ty = proj.base.ty(cx.mir, cx.tcx).to_ty(cx.tcx);
245 if let ty::Ref(..) = base_ty.sty {
246 return Self::in_place(cx, &proj.base);
251 Self::in_place(cx, place)
254 Rvalue::Aggregate(_, ref operands) => {
255 operands.iter().any(|o| Self::in_operand(cx, o))
260 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
261 Self::in_rvalue_structurally(cx, rvalue)
265 cx: &ConstCx<'_, 'tcx>,
266 _callee: &Operand<'tcx>,
267 _args: &[Operand<'tcx>],
270 // Be conservative about the returned value of a const fn.
271 Self::in_any_value_of_ty(cx, return_ty).unwrap_or(false)
274 fn in_value(cx: &ConstCx<'_, 'tcx>, source: ValueSource<'_, 'tcx>) -> bool {
276 ValueSource::Rvalue(rvalue) => Self::in_rvalue(cx, rvalue),
277 ValueSource::Call { callee, args, return_ty } => {
278 Self::in_call(cx, callee, args, return_ty)
284 // Constant containing interior mutability (UnsafeCell).
285 struct HasMutInterior;
287 impl Qualif for HasMutInterior {
288 const IDX: usize = 0;
290 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
291 Some(!ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP))
294 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
296 // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
297 // allowed in constants (and the `Checker` will error), and/or it
298 // won't be promoted, due to `&mut ...` or interior mutability.
299 Rvalue::Ref(_, kind, ref place) => {
300 let ty = place.ty(cx.mir, cx.tcx).to_ty(cx.tcx);
302 if let BorrowKind::Mut { .. } = kind {
303 // In theory, any zero-sized value could be borrowed
304 // mutably without consequences. However, only &mut []
305 // is allowed right now, and only in functions.
306 if cx.mode == Mode::StaticMut {
307 // Inside a `static mut`, &mut [...] is also allowed.
309 ty::Array(..) | ty::Slice(_) => {}
312 } else if let ty::Array(_, len) = ty.sty {
313 // FIXME(eddyb) the `cx.mode == Mode::Fn` condition
314 // seems unnecessary, given that this is merely a ZST.
315 if !(len.unwrap_usize(cx.tcx) == 0 && cx.mode == Mode::Fn) {
324 Rvalue::Aggregate(ref kind, _) => {
325 if let AggregateKind::Adt(def, ..) = **kind {
326 if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
327 let ty = rvalue.ty(cx.mir, cx.tcx);
328 assert_eq!(Self::in_any_value_of_ty(cx, ty), Some(true));
337 Self::in_rvalue_structurally(cx, rvalue)
341 // Constant containing an ADT that implements Drop.
344 impl Qualif for NeedsDrop {
345 const IDX: usize = 1;
347 fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> Option<bool> {
348 Some(ty.needs_drop(cx.tcx, cx.param_env))
351 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
352 if let Rvalue::Aggregate(ref kind, _) = *rvalue {
353 if let AggregateKind::Adt(def, ..) = **kind {
354 if def.has_dtor(cx.tcx) {
360 Self::in_rvalue_structurally(cx, rvalue)
364 // Not constant at all - non-`const fn` calls, asm!,
365 // pointer comparisons, ptr-to-int casts, etc.
368 impl Qualif for IsNotConst {
369 const IDX: usize = 2;
371 fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
372 // Only allow statics (not consts) to refer to other statics.
373 let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
376 cx.tcx.get_attrs(static_.def_id).iter().any(|attr| attr.check_name("thread_local"))
379 fn in_projection(cx: &ConstCx<'_, 'tcx>, proj: &PlaceProjection<'tcx>) -> bool {
381 ProjectionElem::Deref |
382 ProjectionElem::Downcast(..) => return true,
384 ProjectionElem::ConstantIndex {..} |
385 ProjectionElem::Subslice {..} |
386 ProjectionElem::Index(_) => {}
388 ProjectionElem::Field(..) => {
389 if cx.mode == Mode::Fn {
390 let base_ty = proj.base.ty(cx.mir, cx.tcx).to_ty(cx.tcx);
391 if let Some(def) = base_ty.ty_adt_def() {
400 Self::in_projection_structurally(cx, proj)
403 fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
405 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::Fn => {
406 let operand_ty = operand.ty(cx.mir, cx.tcx);
407 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
408 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
409 match (cast_in, cast_out) {
410 (CastTy::Ptr(_), CastTy::Int(_)) |
411 (CastTy::FnPtr, CastTy::Int(_)) => {
412 // in normal functions, mark such casts as not promotable
419 Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::Fn => {
420 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.mir, cx.tcx).sty {
421 assert!(op == BinOp::Eq || op == BinOp::Ne ||
422 op == BinOp::Le || op == BinOp::Lt ||
423 op == BinOp::Ge || op == BinOp::Gt ||
424 op == BinOp::Offset);
426 // raw pointer operations are not allowed inside promoteds
431 Rvalue::NullaryOp(NullOp::Box, _) => return true,
436 Self::in_rvalue_structurally(cx, rvalue)
440 cx: &ConstCx<'_, 'tcx>,
441 callee: &Operand<'tcx>,
442 args: &[Operand<'tcx>],
443 _return_ty: Ty<'tcx>,
445 let fn_ty = callee.ty(cx.mir, cx.tcx);
447 ty::FnDef(def_id, _) => {
448 match cx.tcx.fn_sig(def_id).abi() {
450 Abi::PlatformIntrinsic => {
451 assert!(!cx.tcx.is_const_fn(def_id));
452 match &cx.tcx.item_name(def_id).as_str()[..] {
471 | "add_with_overflow"
472 | "sub_with_overflow"
473 | "mul_with_overflow"
484 cx.tcx.is_const_fn(def_id) ||
485 cx.tcx.is_unstable_const_fn(def_id).is_some() ||
486 cx.is_const_panic_fn(def_id);
496 Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
500 // Refers to temporaries which cannot be promoted as
501 // promote_consts decided they weren't simple enough.
502 struct IsNotPromotable;
504 impl Qualif for IsNotPromotable {
505 const IDX: usize = 3;
508 cx: &ConstCx<'_, 'tcx>,
509 callee: &Operand<'tcx>,
510 _args: &[Operand<'tcx>],
511 _return_ty: Ty<'tcx>,
513 if cx.mode == Mode::Fn {
514 if let ty::FnDef(def_id, _) = callee.ty(cx.mir, cx.tcx).sty {
515 // Never promote runtime `const fn` calls of
516 // functions without `#[rustc_promotable]`.
517 if !cx.tcx.is_promotable_const_fn(def_id) {
523 // FIXME(eddyb) do we need "not promotable" in anything
524 // other than `Mode::Fn` by any chance?
530 // Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
531 macro_rules! static_assert_seq_qualifs {
532 ($i:expr => $first:ident $(, $rest:ident)*) => {
533 static_assert!(SEQ_QUALIFS: {
534 static_assert_seq_qualifs!($i + 1 => $($rest),*);
540 static_assert!(SEQ_QUALIFS: QUALIF_COUNT == $i);
543 static_assert_seq_qualifs!(0 => HasMutInterior, NeedsDrop, IsNotConst, IsNotPromotable);
545 impl ConstCx<'_, 'tcx> {
546 fn qualifs_in_any_value_of_ty(&self, ty: Ty<'tcx>) -> PerQualif<bool> {
547 let mut qualifs = PerQualif::default();
548 qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
549 qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
550 qualifs[IsNotConst] = IsNotConst::in_any_value_of_ty(self, ty).unwrap_or(false);
551 qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
555 fn qualifs_in_local(&self, local: Local) -> PerQualif<bool> {
556 let mut qualifs = PerQualif::default();
557 qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
558 qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
559 qualifs[IsNotConst] = IsNotConst::in_local(self, local);
560 qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
564 fn qualifs_in_value(&self, source: ValueSource<'_, 'tcx>) -> PerQualif<bool> {
565 let mut qualifs = PerQualif::default();
566 qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
567 qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
568 qualifs[IsNotConst] = IsNotConst::in_value(self, source);
569 qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
574 struct Checker<'a, 'tcx> {
575 cx: ConstCx<'a, 'tcx>,
579 rpo: ReversePostorder<'a, 'tcx>,
581 temp_promotion_state: IndexVec<Local, TempState>,
582 promotion_candidates: Vec<Candidate>,
585 macro_rules! unleash_miri {
587 if $this.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
588 $this.tcx.sess.span_warn($this.span, "skipping const checks");
594 impl Deref for Checker<'a, 'tcx> {
595 type Target = ConstCx<'a, 'tcx>;
597 fn deref(&self) -> &Self::Target {
602 impl<'a, 'tcx> Checker<'a, 'tcx> {
603 fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
608 assert!(def_id.is_local());
609 let mut rpo = traversal::reverse_postorder(mir);
610 let temps = promote_consts::collect_temps(mir, &mut rpo);
613 let param_env = tcx.param_env(def_id);
615 let mut cx = ConstCx {
620 per_local: PerQualif::new(BitSet::new_empty(mir.local_decls.len())),
623 for (local, decl) in mir.local_decls.iter_enumerated() {
624 match mir.local_kind(local) {
626 let qualifs = cx.qualifs_in_any_value_of_ty(decl.ty);
627 for (per_local, qualif) in &mut cx.per_local.as_mut().zip(qualifs).0 {
629 per_local.insert(local);
632 cx.per_local[IsNotPromotable].insert(local);
635 LocalKind::Var if mode == Mode::Fn => {
636 cx.per_local[IsNotConst].insert(local);
639 LocalKind::Temp if !temps[local].is_promotable() => {
640 cx.per_local[IsNotPromotable].insert(local);
652 temp_promotion_state: temps,
653 promotion_candidates: vec![]
657 // FIXME(eddyb) we could split the errors into meaningful
658 // categories, but enabling full miri would make that
659 // slightly pointless (even with feature-gating).
660 fn not_const(&mut self) {
662 if self.mode != Mode::Fn {
663 let mut err = struct_span_err!(
667 "{} contains unimplemented expression type",
670 if self.tcx.sess.teach(&err.get_code().unwrap()) {
671 err.note("A function call isn't allowed in the const's initialization expression \
672 because the expression's value must be known at compile-time.");
673 err.note("Remember: you can't use a function call inside a const's initialization \
674 expression! However, you can use it anywhere else.");
680 /// Assigns an rvalue/call qualification to the given destination.
681 fn assign(&mut self, dest: &Place<'tcx>, source: ValueSource<'_, 'tcx>, location: Location) {
682 trace!("assign: {:?} <- {:?}", dest, source);
684 let mut qualifs = self.qualifs_in_value(source);
686 if let ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) = source {
687 // Getting `true` from `HasMutInterior::in_rvalue` means
688 // the borrowed place is disallowed from being borrowed,
689 // due to either a mutable borrow (with some exceptions),
690 // or an shared borrow of a value with interior mutability.
691 // Then `HasMutInterior` is replaced with `IsNotConst`,
692 // to avoid duplicate errors (e.g. from reborrowing).
693 if qualifs[HasMutInterior] {
694 qualifs[HasMutInterior] = false;
695 qualifs[IsNotConst] = true;
697 if self.mode != Mode::Fn {
698 if let BorrowKind::Mut { .. } = kind {
699 let mut err = struct_span_err!(self.tcx.sess, self.span, E0017,
700 "references in {}s may only refer \
701 to immutable values", self.mode);
702 err.span_label(self.span, format!("{}s require immutable values",
704 if self.tcx.sess.teach(&err.get_code().unwrap()) {
705 err.note("References in statics and constants may only refer to \
706 immutable values.\n\n\
707 Statics are shared everywhere, and if they refer to \
708 mutable data one might violate memory safety since \
709 holding multiple mutable references to shared data is \
711 If you really want global mutable state, try using \
712 static mut or a global UnsafeCell.");
716 span_err!(self.tcx.sess, self.span, E0492,
717 "cannot borrow a constant which may contain \
718 interior mutability, create a static instead");
722 // We might have a candidate for promotion.
723 let candidate = Candidate::Ref(location);
724 // We can only promote interior borrows of promotable temps.
725 let mut place = place;
726 while let Place::Projection(ref proj) = *place {
727 if proj.elem == ProjectionElem::Deref {
732 debug!("qualify_consts: promotion candidate: place={:?}", place);
733 if let Place::Local(local) = *place {
734 if self.mir.local_kind(local) == LocalKind::Temp {
735 debug!("qualify_consts: promotion candidate: local={:?}", local);
736 // The borrowed place doesn't have `HasMutInterior`
737 // (from `in_rvalue`), so we can safely ignore
738 // `HasMutInterior` from the local's qualifications.
739 // This allows borrowing fields which don't have
740 // `HasMutInterior`, from a type that does, e.g.:
741 // `let _: &'static _ = &(Cell::new(1), 2).1;`
742 let mut local_qualifs = self.qualifs_in_local(local);
743 local_qualifs[HasMutInterior] = false;
744 if !local_qualifs.0.iter().any(|&qualif| qualif) {
745 debug!("qualify_consts: promotion candidate: {:?}", candidate);
746 self.promotion_candidates.push(candidate);
756 // We treat all locals equal in constants
757 Place::Local(index) => break *index,
758 // projections are transparent for assignments
759 // we qualify the entire destination at once, even if just a field would have
760 // stricter qualification
761 Place::Projection(proj) => {
762 // Catch more errors in the destination. `visit_place` also checks various
763 // projection rules like union field access and raw pointer deref
766 PlaceContext::MutatingUse(MutatingUseContext::Store),
771 Place::Promoted(..) => bug!("promoteds don't exist yet during promotion"),
772 Place::Static(..) => {
773 // Catch more errors in the destination. `visit_place` also checks that we
774 // do not try to access statics from constants or try to mutate statics
777 PlaceContext::MutatingUse(MutatingUseContext::Store),
785 let kind = self.mir.local_kind(index);
786 debug!("store to {:?} {:?}", kind, index);
788 // Only handle promotable temps in non-const functions.
789 if self.mode == Mode::Fn {
790 if kind != LocalKind::Temp ||
791 !self.temp_promotion_state[index].is_promotable() {
796 // this is overly restrictive, because even full assignments do not clear the qualif
797 // While we could special case full assignments, this would be inconsistent with
798 // aggregates where we overwrite all fields via assignments, which would not get
800 for (per_local, qualif) in &mut self.cx.per_local.as_mut().zip(qualifs).0 {
802 per_local.insert(index);
806 // Ensure the `IsNotPromotable` qualification is preserved.
807 // NOTE(eddyb) this is actually unnecessary right now, as
808 // we never replace the local's qualif, but we might in
809 // the future, and so it serves to catch changes that unset
810 // important bits (in which case, asserting `contains` could
811 // be replaced with calling `insert` to re-set the bit).
812 if kind == LocalKind::Temp {
813 if !self.temp_promotion_state[index].is_promotable() {
814 assert!(self.cx.per_local[IsNotPromotable].contains(index));
819 /// Check a whole const, static initializer or const fn.
820 fn check_const(&mut self) -> (u8, Lrc<BitSet<Local>>) {
821 debug!("const-checking {} {:?}", self.mode, self.def_id);
825 let mut seen_blocks = BitSet::new_empty(mir.basic_blocks().len());
826 let mut bb = START_BLOCK;
828 seen_blocks.insert(bb.index());
830 self.visit_basic_block_data(bb, &mir[bb]);
832 let target = match mir[bb].terminator().kind {
833 TerminatorKind::Goto { target } |
834 TerminatorKind::Drop { target, .. } |
835 TerminatorKind::Assert { target, .. } |
836 TerminatorKind::Call { destination: Some((_, target)), .. } => {
840 // Non-terminating calls cannot produce any value.
841 TerminatorKind::Call { destination: None, .. } => {
845 TerminatorKind::SwitchInt {..} |
846 TerminatorKind::DropAndReplace { .. } |
847 TerminatorKind::Resume |
848 TerminatorKind::Abort |
849 TerminatorKind::GeneratorDrop |
850 TerminatorKind::Yield { .. } |
851 TerminatorKind::Unreachable |
852 TerminatorKind::FalseEdges { .. } |
853 TerminatorKind::FalseUnwind { .. } => None,
855 TerminatorKind::Return => {
862 Some(target) if !seen_blocks.contains(target.index()) => {
873 // Collect all the temps we need to promote.
874 let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
876 debug!("qualify_const: promotion_candidates={:?}", self.promotion_candidates);
877 for candidate in &self.promotion_candidates {
879 Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
880 match self.mir[bb].statements[stmt_idx].kind {
881 StatementKind::Assign(_, box Rvalue::Ref(_, _, Place::Local(index))) => {
882 promoted_temps.insert(index);
887 Candidate::Argument { .. } => {}
891 let promoted_temps = Lrc::new(promoted_temps);
893 let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
895 // Account for errors in consts by using the
896 // conservative type qualification instead.
897 if qualifs[IsNotConst] {
898 qualifs = self.qualifs_in_any_value_of_ty(mir.return_ty());
901 (qualifs.encode_to_bits(), promoted_temps)
905 /// Checks MIR for const-correctness, using `ConstCx`
906 /// for value qualifications, and accumulates writes of
907 /// rvalue/call results to locals, in `local_qualif`.
908 /// For functions (constant or not), it also records
909 /// candidates for promotion in `promotion_candidates`.
910 impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
911 fn visit_place(&mut self,
913 context: PlaceContext<'tcx>,
914 location: Location) {
915 debug!("visit_place: place={:?} context={:?} location={:?}", place, context, location);
916 self.super_place(place, context, location);
919 Place::Promoted(_) => {}
920 Place::Static(ref global) => {
922 .get_attrs(global.def_id)
924 .any(|attr| attr.check_name("thread_local")) {
925 if self.mode != Mode::Fn {
926 span_err!(self.tcx.sess, self.span, E0625,
927 "thread-local statics cannot be \
928 accessed at compile-time");
933 // Only allow statics (not consts) to refer to other statics.
934 if self.mode == Mode::Static || self.mode == Mode::StaticMut {
935 if self.mode == Mode::Static && context.is_mutating_use() {
936 // this is not strictly necessary as miri will also bail out
937 // For interior mutability we can't really catch this statically as that
938 // goes through raw pointers and intermediate temporaries, so miri has
939 // to catch this anyway
940 self.tcx.sess.span_err(
942 "cannot mutate statics in the initializer of another static",
949 if self.mode != Mode::Fn {
950 let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
951 "{}s cannot refer to statics, use \
952 a constant instead", self.mode);
953 if self.tcx.sess.teach(&err.get_code().unwrap()) {
955 "Static and const variables can refer to other const variables. But a \
956 const variable cannot refer to a static variable."
959 "To fix this, the value can be extracted as a const and then used."
965 Place::Projection(ref proj) => {
967 ProjectionElem::Deref => {
968 if context.is_mutating_use() {
969 // `not_const` errors out in const contexts
972 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
976 if let ty::RawPtr(_) = base_ty.sty {
977 if !self.tcx.features().const_raw_ptr_deref {
979 &self.tcx.sess.parse_sess, "const_raw_ptr_deref",
980 self.span, GateIssue::Language,
982 "dereferencing raw pointers in {}s is unstable",
992 ProjectionElem::ConstantIndex {..} |
993 ProjectionElem::Subslice {..} |
994 ProjectionElem::Field(..) |
995 ProjectionElem::Index(_) => {
996 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
997 if let Some(def) = base_ty.ty_adt_def() {
1001 if !self.tcx.features().const_fn_union {
1003 &self.tcx.sess.parse_sess, "const_fn_union",
1004 self.span, GateIssue::Language,
1005 "unions in const fn are unstable",
1020 ProjectionElem::Downcast(..) => {
1028 fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
1029 debug!("visit_operand: operand={:?} location={:?}", operand, location);
1030 self.super_operand(operand, location);
1033 Operand::Move(ref place) => {
1034 // Mark the consumed locals to indicate later drops are noops.
1035 if let Place::Local(local) = *place {
1036 self.cx.per_local[NeedsDrop].remove(local);
1040 Operand::Constant(_) => {}
1044 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
1045 debug!("visit_rvalue: rvalue={:?} location={:?}", rvalue, location);
1047 // Check nested operands and places.
1048 if let Rvalue::Ref(region, kind, ref place) = *rvalue {
1049 // Special-case reborrows.
1050 let mut is_reborrow = false;
1051 if let Place::Projection(ref proj) = *place {
1052 if let ProjectionElem::Deref = proj.elem {
1053 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1054 if let ty::Ref(..) = base_ty.sty {
1061 let ctx = match kind {
1062 BorrowKind::Shared => PlaceContext::NonMutatingUse(
1063 NonMutatingUseContext::SharedBorrow(region),
1065 BorrowKind::Shallow => PlaceContext::NonMutatingUse(
1066 NonMutatingUseContext::ShallowBorrow(region),
1068 BorrowKind::Unique => PlaceContext::NonMutatingUse(
1069 NonMutatingUseContext::UniqueBorrow(region),
1071 BorrowKind::Mut { .. } => PlaceContext::MutatingUse(
1072 MutatingUseContext::Borrow(region),
1075 self.super_place(place, ctx, location);
1077 self.super_rvalue(rvalue, location);
1080 self.super_rvalue(rvalue, location);
1085 Rvalue::Repeat(..) |
1086 Rvalue::UnaryOp(UnOp::Neg, _) |
1087 Rvalue::UnaryOp(UnOp::Not, _) |
1088 Rvalue::NullaryOp(NullOp::SizeOf, _) |
1089 Rvalue::CheckedBinaryOp(..) |
1090 Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
1091 Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
1092 Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
1093 Rvalue::Cast(CastKind::Unsize, ..) |
1094 Rvalue::Discriminant(..) |
1097 Rvalue::Aggregate(..) => {}
1099 Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => {
1100 let operand_ty = operand.ty(self.mir, self.tcx);
1101 let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
1102 let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
1103 match (cast_in, cast_out) {
1104 (CastTy::Ptr(_), CastTy::Int(_)) |
1105 (CastTy::FnPtr, CastTy::Int(_)) if self.mode != Mode::Fn => {
1106 unleash_miri!(self);
1107 if !self.tcx.features().const_raw_ptr_to_usize_cast {
1108 // in const fn and constants require the feature gate
1109 // FIXME: make it unsafe inside const fn and constants
1111 &self.tcx.sess.parse_sess, "const_raw_ptr_to_usize_cast",
1112 self.span, GateIssue::Language,
1114 "casting pointers to integers in {}s is unstable",
1124 Rvalue::BinaryOp(op, ref lhs, _) => {
1125 if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.mir, self.tcx).sty {
1126 assert!(op == BinOp::Eq || op == BinOp::Ne ||
1127 op == BinOp::Le || op == BinOp::Lt ||
1128 op == BinOp::Ge || op == BinOp::Gt ||
1129 op == BinOp::Offset);
1131 unleash_miri!(self);
1132 if self.mode != Mode::Fn && !self.tcx.features().const_compare_raw_pointers {
1133 // require the feature gate inside constants and const fn
1134 // FIXME: make it unsafe to use these operations
1136 &self.tcx.sess.parse_sess,
1137 "const_compare_raw_pointers",
1139 GateIssue::Language,
1140 &format!("comparing raw pointers inside {}", self.mode),
1146 Rvalue::NullaryOp(NullOp::Box, _) => {
1147 unleash_miri!(self);
1148 if self.mode != Mode::Fn {
1149 let mut err = struct_span_err!(self.tcx.sess, self.span, E0010,
1150 "allocations are not allowed in {}s", self.mode);
1151 err.span_label(self.span, format!("allocation not allowed in {}s", self.mode));
1152 if self.tcx.sess.teach(&err.get_code().unwrap()) {
1154 "The value of statics and constants must be known at compile time, \
1155 and they live for the entire lifetime of a program. Creating a boxed \
1156 value allocates memory on the heap at runtime, and therefore cannot \
1157 be done at compile time."
1166 fn visit_terminator_kind(&mut self,
1168 kind: &TerminatorKind<'tcx>,
1169 location: Location) {
1170 debug!("visit_terminator_kind: bb={:?} kind={:?} location={:?}", bb, kind, location);
1171 if let TerminatorKind::Call { ref func, ref args, ref destination, .. } = *kind {
1172 if let Some((ref dest, _)) = *destination {
1173 self.assign(dest, ValueSource::Call {
1176 return_ty: dest.ty(self.mir, self.tcx).to_ty(self.tcx),
1180 let fn_ty = func.ty(self.mir, self.tcx);
1181 let mut callee_def_id = None;
1182 let mut is_shuffle = false;
1184 ty::FnDef(def_id, _) => {
1185 callee_def_id = Some(def_id);
1186 match self.tcx.fn_sig(def_id).abi() {
1187 Abi::RustIntrinsic |
1188 Abi::PlatformIntrinsic => {
1189 assert!(!self.tcx.is_const_fn(def_id));
1190 match &self.tcx.item_name(def_id).as_str()[..] {
1191 // special intrinsic that can be called diretly without an intrinsic
1192 // feature gate needs a language feature gate
1194 // never promote transmute calls
1195 if self.mode != Mode::Fn {
1196 // const eval transmute calls only with the feature gate
1197 if !self.tcx.features().const_transmute {
1199 &self.tcx.sess.parse_sess, "const_transmute",
1200 self.span, GateIssue::Language,
1201 &format!("The use of std::mem::transmute() \
1202 is gated in {}s", self.mode));
1207 name if name.starts_with("simd_shuffle") => {
1211 // no need to check feature gates, intrinsics are only callable
1212 // from the libstd or with forever unstable feature gates
1217 // In normal functions no calls are feature-gated.
1218 if self.mode != Mode::Fn {
1219 let unleash_miri = self
1224 .unleash_the_miri_inside_of_you;
1225 if self.tcx.is_const_fn(def_id) || unleash_miri {
1226 // stable const fns or unstable const fns
1227 // with their feature gate active
1228 // FIXME(eddyb) move stability checks from `is_const_fn` here.
1229 } else if self.is_const_panic_fn(def_id) {
1230 // Check the const_panic feature gate.
1231 // FIXME: cannot allow this inside `allow_internal_unstable`
1232 // because that would make `panic!` insta stable in constants,
1233 // since the macro is marked with the attribute.
1234 if !self.tcx.features().const_panic {
1235 // Don't allow panics in constants without the feature gate.
1237 &self.tcx.sess.parse_sess,
1240 GateIssue::Language,
1241 &format!("panicking in {}s is unstable", self.mode),
1244 } else if let Some(feature)
1245 = self.tcx.is_unstable_const_fn(def_id) {
1246 // Check `#[unstable]` const fns or `#[rustc_const_unstable]`
1247 // functions without the feature gate active in this crate in
1248 // order to report a better error message than the one below.
1249 if !self.span.allows_unstable(&feature.as_str()) {
1250 let mut err = self.tcx.sess.struct_span_err(self.span,
1251 &format!("`{}` is not yet stable as a const fn",
1252 self.tcx.item_path_str(def_id)));
1253 if nightly_options::is_nightly_build() {
1255 "add `#![feature({})]` to the \
1256 crate attributes to enable",
1262 let mut err = struct_span_err!(
1266 "calls in {}s are limited to constant functions, \
1267 tuple structs and tuple variants",
1277 if self.mode != Mode::Fn {
1278 let mut err = self.tcx.sess.struct_span_err(
1280 &format!("function pointers are not allowed in const fn"));
1289 if self.mode == Mode::Fn {
1290 let constant_args = callee_def_id.and_then(|id| {
1291 args_required_const(self.tcx, id)
1292 }).unwrap_or_default();
1293 for (i, arg) in args.iter().enumerate() {
1294 if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
1298 let candidate = Candidate::Argument { bb, index: i };
1299 // Since the argument is required to be constant,
1300 // we care about constness, not promotability.
1301 // If we checked for promotability, we'd miss out on
1302 // the results of function calls (which are never promoted
1303 // in runtime code).
1304 // This is not a problem, because the argument explicitly
1305 // requests constness, in contrast to regular promotion
1306 // which happens even without the user requesting it.
1307 // We can error out with a hard error if the argument is not
1309 if !IsNotConst::in_operand(self, arg) {
1310 debug!("visit_terminator_kind: candidate={:?}", candidate);
1311 self.promotion_candidates.push(candidate);
1314 span_err!(self.tcx.sess, self.span, E0526,
1315 "shuffle indices are not constant");
1317 self.tcx.sess.span_err(self.span,
1318 &format!("argument {} is required to be a constant",
1325 // Check callee and argument operands.
1326 self.visit_operand(func, location);
1328 self.visit_operand(arg, location);
1330 } else if let TerminatorKind::Drop { location: ref place, .. } = *kind {
1331 self.super_terminator_kind(bb, kind, location);
1333 // Deny *any* live drops anywhere other than functions.
1334 if self.mode != Mode::Fn {
1335 unleash_miri!(self);
1336 // HACK(eddyb): emulate a bit of dataflow analysis,
1337 // conservatively, that drop elaboration will do.
1338 let needs_drop = if let Place::Local(local) = *place {
1339 if NeedsDrop::in_local(self, local) {
1340 Some(self.mir.local_decls[local].source_info.span)
1348 if let Some(span) = needs_drop {
1349 // Double-check the type being dropped, to minimize false positives.
1350 let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
1351 if ty.needs_drop(self.tcx, self.param_env) {
1352 struct_span_err!(self.tcx.sess, span, E0493,
1353 "destructors cannot be evaluated at compile-time")
1354 .span_label(span, format!("{}s cannot evaluate destructors",
1361 // Qualify any operands inside other terminators.
1362 self.super_terminator_kind(bb, kind, location);
1366 fn visit_assign(&mut self,
1369 rvalue: &Rvalue<'tcx>,
1370 location: Location) {
1371 debug!("visit_assign: dest={:?} rvalue={:?} location={:?}", dest, rvalue, location);
1372 self.assign(dest, ValueSource::Rvalue(rvalue), location);
1374 self.visit_rvalue(rvalue, location);
1377 fn visit_source_info(&mut self, source_info: &SourceInfo) {
1378 debug!("visit_source_info: source_info={:?}", source_info);
1379 self.span = source_info.span;
1382 fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>, location: Location) {
1383 debug!("visit_statement: bb={:?} statement={:?} location={:?}", bb, statement, location);
1384 match statement.kind {
1385 StatementKind::Assign(..) => {
1386 self.super_statement(bb, statement, location);
1388 // FIXME(eddyb) should these really do nothing?
1389 StatementKind::FakeRead(..) |
1390 StatementKind::SetDiscriminant { .. } |
1391 StatementKind::StorageLive(_) |
1392 StatementKind::StorageDead(_) |
1393 StatementKind::InlineAsm {..} |
1394 StatementKind::Retag { .. } |
1395 StatementKind::AscribeUserType(..) |
1396 StatementKind::Nop => {}
1400 fn visit_terminator(&mut self,
1402 terminator: &Terminator<'tcx>,
1403 location: Location) {
1404 debug!("visit_terminator: bb={:?} terminator={:?} location={:?}", bb, terminator, location);
1405 self.super_terminator(bb, terminator, location);
1409 pub fn provide(providers: &mut Providers<'_>) {
1410 *providers = Providers {
1416 fn mir_const_qualif<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
1418 -> (u8, Lrc<BitSet<Local>>) {
1419 // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
1420 // cannot yet be stolen), because `mir_validated()`, which steals
1421 // from `mir_const(), forces this query to execute before
1422 // performing the steal.
1423 let mir = &tcx.mir_const(def_id).borrow();
1425 if mir.return_ty().references_error() {
1426 tcx.sess.delay_span_bug(mir.span, "mir_const_qualif: Mir had errors");
1427 return (1 << IsNotConst::IDX, Lrc::new(BitSet::new_empty(0)));
1430 Checker::new(tcx, def_id, mir, Mode::Const).check_const()
1433 pub struct QualifyAndPromoteConstants;
1435 impl MirPass for QualifyAndPromoteConstants {
1436 fn run_pass<'a, 'tcx>(&self,
1437 tcx: TyCtxt<'a, 'tcx, 'tcx>,
1438 src: MirSource<'tcx>,
1439 mir: &mut Mir<'tcx>) {
1440 // There's not really any point in promoting errorful MIR.
1441 if mir.return_ty().references_error() {
1442 tcx.sess.delay_span_bug(mir.span, "QualifyAndPromoteConstants: Mir had errors");
1446 if src.promoted.is_some() {
1450 let def_id = src.def_id();
1451 let id = tcx.hir().as_local_hir_id(def_id).unwrap();
1452 let mut const_promoted_temps = None;
1453 let mode = match tcx.hir().body_owner_kind_by_hir_id(id) {
1454 hir::BodyOwnerKind::Closure => Mode::Fn,
1455 hir::BodyOwnerKind::Fn => {
1456 if tcx.is_const_fn(def_id) {
1462 hir::BodyOwnerKind::Const => {
1463 const_promoted_temps = Some(tcx.mir_const_qualif(def_id).1);
1466 hir::BodyOwnerKind::Static(hir::MutImmutable) => Mode::Static,
1467 hir::BodyOwnerKind::Static(hir::MutMutable) => Mode::StaticMut,
1470 debug!("run_pass: mode={:?}", mode);
1471 if mode == Mode::Fn || mode == Mode::ConstFn {
1472 // This is ugly because Checker holds onto mir,
1473 // which can't be mutated until its scope ends.
1474 let (temps, candidates) = {
1475 let mut checker = Checker::new(tcx, def_id, mir, mode);
1476 if mode == Mode::ConstFn {
1477 if tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you {
1478 checker.check_const();
1479 } else if tcx.is_min_const_fn(def_id) {
1480 // enforce `min_const_fn` for stable const fns
1481 use super::qualify_min_const_fn::is_min_const_fn;
1482 if let Err((span, err)) = is_min_const_fn(tcx, def_id, mir) {
1483 let mut diag = struct_span_err!(
1487 "{} (see issue #57563)",
1491 "add #![feature(const_fn)] to the crate attributes to enable",
1495 // this should not produce any errors, but better safe than sorry
1497 checker.check_const();
1500 // Enforce a constant-like CFG for `const fn`.
1501 checker.check_const();
1504 while let Some((bb, data)) = checker.rpo.next() {
1505 checker.visit_basic_block_data(bb, data);
1509 (checker.temp_promotion_state, checker.promotion_candidates)
1512 // Do the actual promotion, now that we know what's viable.
1513 promote_consts::promote_candidates(mir, tcx, temps, candidates);
1515 if !mir.control_flow_destroyed.is_empty() {
1516 let mut locals = mir.vars_iter();
1517 if let Some(local) = locals.next() {
1518 let span = mir.local_decls[local].source_info.span;
1519 let mut error = tcx.sess.struct_span_err(
1522 "new features like let bindings are not permitted in {}s \
1523 which also use short circuiting operators",
1527 for (span, kind) in mir.control_flow_destroyed.iter() {
1530 &format!("use of {} here does not actually short circuit due to \
1531 the const evaluator presently not being able to do control flow. \
1532 See https://github.com/rust-lang/rust/issues/49146 for more \
1533 information.", kind),
1536 for local in locals {
1537 let span = mir.local_decls[local].source_info.span;
1540 "more locals defined here",
1546 let promoted_temps = if mode == Mode::Const {
1547 // Already computed by `mir_const_qualif`.
1548 const_promoted_temps.unwrap()
1550 Checker::new(tcx, def_id, mir, mode).check_const().1
1553 // In `const` and `static` everything without `StorageDead`
1554 // is `'static`, we don't have to create promoted MIR fragments,
1555 // just remove `Drop` and `StorageDead` on "promoted" locals.
1556 debug!("run_pass: promoted_temps={:?}", promoted_temps);
1557 for block in mir.basic_blocks_mut() {
1558 block.statements.retain(|statement| {
1559 match statement.kind {
1560 StatementKind::StorageDead(index) => {
1561 !promoted_temps.contains(index)
1566 let terminator = block.terminator_mut();
1567 match terminator.kind {
1568 TerminatorKind::Drop { location: Place::Local(index), target, .. } => {
1569 if promoted_temps.contains(index) {
1570 terminator.kind = TerminatorKind::Goto {
1580 // Statics must be Sync.
1581 if mode == Mode::Static {
1582 // `#[thread_local]` statics don't have to be `Sync`.
1583 for attr in &tcx.get_attrs(def_id)[..] {
1584 if attr.check_name("thread_local") {
1588 let ty = mir.return_ty();
1589 tcx.infer_ctxt().enter(|infcx| {
1590 let param_env = ty::ParamEnv::empty();
1591 let cause = traits::ObligationCause::new(mir.span, id, traits::SharedStatic);
1592 let mut fulfillment_cx = traits::FulfillmentContext::new();
1593 fulfillment_cx.register_bound(&infcx,
1596 tcx.require_lang_item(lang_items::SyncTraitLangItem),
1598 if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
1599 infcx.report_fulfillment_errors(&err, None, false);
1606 fn args_required_const(tcx: TyCtxt<'_, '_, '_>, def_id: DefId) -> Option<FxHashSet<usize>> {
1607 let attrs = tcx.get_attrs(def_id);
1608 let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
1609 let mut ret = FxHashSet::default();
1610 for meta in attr.meta_item_list()? {
1611 match meta.literal()?.node {
1612 LitKind::Int(a, _) => { ret.insert(a as usize); }