1
Fork 0

Auto merge of #66066 - ecstatic-morse:remove-promotion-from-qualify-consts, r=eddyb

Remove promotion candidate gathering and checking from `qualify_consts.rs`

This makes promotion candidate gathering and checking the exclusive domain of `promote_consts`, but the `QualifyAndPromoteConsts` pass is still responsible for both const-checking and creating promoted MIR fragments.

This should not be merged until the beta branches on Nov. 5.

r? @eddyb
This commit is contained in:
bors 2019-11-08 07:55:53 +00:00
commit 76ade3e8ac
2 changed files with 79 additions and 439 deletions

View file

@ -6,7 +6,6 @@
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use rustc::hir;
use rustc::hir::def_id::DefId;
@ -20,7 +19,6 @@ use rustc::mir::traversal::ReversePostorder;
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::middle::lang_items;
use rustc::session::config::nightly_options;
use syntax::ast::LitKind;
use syntax::feature_gate::{emit_feature_err, GateIssue};
use syntax::symbol::sym;
use syntax_pos::{Span, DUMMY_SP};
@ -71,7 +69,7 @@ impl fmt::Display for Mode {
}
}
const QUALIF_COUNT: usize = 4;
const QUALIF_COUNT: usize = 2;
// FIXME(eddyb) once we can use const generics, replace this array with
// something like `IndexVec` but for fixed-size arrays (`IndexArray`?).
@ -80,20 +78,20 @@ struct PerQualif<T>([T; QUALIF_COUNT]);
impl<T: Clone> PerQualif<T> {
fn new(x: T) -> Self {
PerQualif([x.clone(), x.clone(), x.clone(), x])
PerQualif([x.clone(), x])
}
}
impl<T> PerQualif<T> {
fn as_mut(&mut self) -> PerQualif<&mut T> {
let [x0, x1, x2, x3] = &mut self.0;
PerQualif([x0, x1, x2, x3])
let [x0, x1] = &mut self.0;
PerQualif([x0, x1])
}
fn zip<U>(self, other: PerQualif<U>) -> PerQualif<(T, U)> {
let [x0, x1, x2, x3] = self.0;
let [y0, y1, y2, y3] = other.0;
PerQualif([(x0, y0), (x1, y1), (x2, y2), (x3, y3)])
let [x0, x1] = self.0;
let [y0, y1] = other.0;
PerQualif([(x0, y0), (x1, y1)])
}
}
@ -429,195 +427,6 @@ impl Qualif for NeedsDrop {
}
}
/// Not promotable at all - non-`const fn` calls, `asm!`,
/// pointer comparisons, ptr-to-int casts, etc.
/// Inside a const context all constness rules apply, so promotion simply has to follow the regular
/// constant rules (modulo interior mutability or `Drop` rules which are handled `HasMutInterior`
/// and `NeedsDrop` respectively). Basically this duplicates the checks that the const-checking
/// visitor enforces by emitting errors when working in const context.
struct IsNotPromotable;
impl Qualif for IsNotPromotable {
const IDX: usize = 2;
fn in_static(cx: &ConstCx<'_, 'tcx>, static_: &Static<'tcx>) -> bool {
match static_.kind {
StaticKind::Promoted(_, _) => unreachable!(),
StaticKind::Static => {
// Only allow statics (not consts) to refer to other statics.
// FIXME(eddyb) does this matter at all for promotion?
let allowed = cx.mode == Mode::Static || cx.mode == Mode::StaticMut;
!allowed ||
cx.tcx.get_attrs(static_.def_id).iter().any(
|attr| attr.check_name(sym::thread_local)
)
}
}
}
fn in_projection(
cx: &ConstCx<'_, 'tcx>,
place: PlaceRef<'_, 'tcx>,
) -> bool {
if let [proj_base @ .., elem] = place.projection {
match elem {
ProjectionElem::Deref |
ProjectionElem::Downcast(..) => return true,
ProjectionElem::ConstantIndex {..} |
ProjectionElem::Subslice {..} |
ProjectionElem::Index(_) => {}
ProjectionElem::Field(..) => {
if cx.mode == Mode::NonConstFn {
let base_ty = Place::ty_from(place.base, proj_base, cx.body, cx.tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No promotion of union field accesses.
if def.is_union() {
return true;
}
}
}
}
}
Self::in_projection_structurally(cx, place)
} else {
bug!("This should be called if projection is not empty");
}
}
fn in_rvalue(cx: &ConstCx<'_, 'tcx>, rvalue: &Rvalue<'tcx>) -> bool {
match *rvalue {
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if cx.mode == Mode::NonConstFn => {
let operand_ty = operand.ty(cx.body, cx.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
(CastTy::Ptr(_), CastTy::Int(_)) |
(CastTy::FnPtr, CastTy::Int(_)) => {
// in normal functions, mark such casts as not promotable
return true;
}
_ => {}
}
}
Rvalue::BinaryOp(op, ref lhs, _) if cx.mode == Mode::NonConstFn => {
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(cx.body, cx.tcx).kind {
assert!(op == BinOp::Eq || op == BinOp::Ne ||
op == BinOp::Le || op == BinOp::Lt ||
op == BinOp::Ge || op == BinOp::Gt ||
op == BinOp::Offset);
// raw pointer operations are not allowed inside promoteds
return true;
}
}
Rvalue::NullaryOp(NullOp::Box, _) => return true,
_ => {}
}
Self::in_rvalue_structurally(cx, rvalue)
}
fn in_call(
cx: &ConstCx<'_, 'tcx>,
callee: &Operand<'tcx>,
args: &[Operand<'tcx>],
_return_ty: Ty<'tcx>,
) -> bool {
let fn_ty = callee.ty(cx.body, cx.tcx);
match fn_ty.kind {
ty::FnDef(def_id, _) => {
match cx.tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic |
Abi::PlatformIntrinsic => {
assert!(!cx.tcx.is_const_fn(def_id));
match &*cx.tcx.item_name(def_id).as_str() {
| "size_of"
| "min_align_of"
| "needs_drop"
| "type_id"
| "bswap"
| "bitreverse"
| "ctpop"
| "cttz"
| "cttz_nonzero"
| "ctlz"
| "ctlz_nonzero"
| "wrapping_add"
| "wrapping_sub"
| "wrapping_mul"
| "unchecked_shl"
| "unchecked_shr"
| "rotate_left"
| "rotate_right"
| "add_with_overflow"
| "sub_with_overflow"
| "mul_with_overflow"
| "saturating_add"
| "saturating_sub"
| "transmute"
| "simd_insert"
| "simd_extract"
| "ptr_offset_from"
=> return true,
_ => {}
}
}
_ => {
let is_const_fn =
cx.tcx.is_const_fn(def_id) ||
cx.tcx.is_unstable_const_fn(def_id).is_some() ||
cx.is_const_panic_fn(def_id);
if !is_const_fn {
return true;
}
}
}
}
_ => return true,
}
Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
}
}
/// Refers to temporaries which cannot be promoted *implicitly*.
/// Explicit promotion happens e.g. for constant arguments declared via `rustc_args_required_const`.
/// Implicit promotion has almost the same rules, except that disallows `const fn` except for
/// those marked `#[rustc_promotable]`. This is to avoid changing a legitimate run-time operation
/// into a failing compile-time operation e.g. due to addresses being compared inside the function.
struct IsNotImplicitlyPromotable;
impl Qualif for IsNotImplicitlyPromotable {
const IDX: usize = 3;
fn in_call(
cx: &ConstCx<'_, 'tcx>,
callee: &Operand<'tcx>,
args: &[Operand<'tcx>],
_return_ty: Ty<'tcx>,
) -> bool {
if cx.mode == Mode::NonConstFn {
if let ty::FnDef(def_id, _) = callee.ty(cx.body, cx.tcx).kind {
// Never promote runtime `const fn` calls of
// functions without `#[rustc_promotable]`.
if !cx.tcx.is_promotable_const_fn(def_id) {
return true;
}
}
}
Self::in_operand(cx, callee) || args.iter().any(|arg| Self::in_operand(cx, arg))
}
}
// Ensure the `IDX` values are sequential (`0..QUALIF_COUNT`).
macro_rules! static_assert_seq_qualifs {
($i:expr => $first:ident $(, $rest:ident)*) => {
@ -632,7 +441,7 @@ macro_rules! static_assert_seq_qualifs {
};
}
static_assert_seq_qualifs!(
0 => HasMutInterior, NeedsDrop, IsNotPromotable, IsNotImplicitlyPromotable
0 => HasMutInterior, NeedsDrop
);
impl ConstCx<'_, 'tcx> {
@ -640,9 +449,6 @@ impl ConstCx<'_, 'tcx> {
let mut qualifs = PerQualif::default();
qualifs[HasMutInterior] = HasMutInterior::in_any_value_of_ty(self, ty).unwrap_or(false);
qualifs[NeedsDrop] = NeedsDrop::in_any_value_of_ty(self, ty).unwrap_or(false);
qualifs[IsNotPromotable] = IsNotPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
qualifs[IsNotImplicitlyPromotable] =
IsNotImplicitlyPromotable::in_any_value_of_ty(self, ty).unwrap_or(false);
qualifs
}
@ -650,8 +456,6 @@ impl ConstCx<'_, 'tcx> {
let mut qualifs = PerQualif::default();
qualifs[HasMutInterior] = HasMutInterior::in_local(self, local);
qualifs[NeedsDrop] = NeedsDrop::in_local(self, local);
qualifs[IsNotPromotable] = IsNotPromotable::in_local(self, local);
qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_local(self, local);
qualifs
}
@ -659,8 +463,6 @@ impl ConstCx<'_, 'tcx> {
let mut qualifs = PerQualif::default();
qualifs[HasMutInterior] = HasMutInterior::in_value(self, source);
qualifs[NeedsDrop] = NeedsDrop::in_value(self, source);
qualifs[IsNotPromotable] = IsNotPromotable::in_value(self, source);
qualifs[IsNotImplicitlyPromotable] = IsNotImplicitlyPromotable::in_value(self, source);
qualifs
}
}
@ -678,7 +480,6 @@ struct Checker<'a, 'tcx> {
rpo: ReversePostorder<'a, 'tcx>,
temp_promotion_state: IndexVec<Local, TempState>,
promotion_candidates: Vec<Candidate>,
unchecked_promotion_candidates: Vec<Candidate>,
/// If `true`, do not emit errors to the user, merely collect them in `errors`.
@ -732,14 +533,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
}
}
}
if !temps[local].is_promotable() {
cx.per_local[IsNotPromotable].insert(local);
}
if let LocalKind::Var = body.local_kind(local) {
// Sanity check to prevent implicit and explicit promotion of
// named locals
assert!(cx.per_local[IsNotPromotable].contains(local));
}
}
Checker {
@ -748,7 +541,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
def_id,
rpo,
temp_promotion_state: temps,
promotion_candidates: vec![],
unchecked_promotion_candidates,
errors: vec![],
suppress_errors: false,
@ -794,16 +586,15 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
let mut qualifs = self.qualifs_in_value(source);
match source {
ValueSource::Rvalue(&Rvalue::Ref(_, kind, ref place)) => {
ValueSource::Rvalue(&Rvalue::Ref(_, kind, _)) => {
// Getting `true` from `HasMutInterior::in_rvalue` means
// the borrowed place is disallowed from being borrowed,
// due to either a mutable borrow (with some exceptions),
// or an shared borrow of a value with interior mutability.
// Then `HasMutInterior` is replaced with `IsNotPromotable`,
// Then `HasMutInterior` is cleared
// to avoid duplicate errors (e.g. from reborrowing).
if qualifs[HasMutInterior] {
qualifs[HasMutInterior] = false;
qualifs[IsNotPromotable] = true;
debug!("suppress_errors: {}", self.suppress_errors);
if self.mode.requires_const_checking() && !self.suppress_errors {
@ -833,57 +624,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
}
}
}
} else if let BorrowKind::Mut { .. } | BorrowKind::Shared = kind {
// Don't promote BorrowKind::Shallow borrows, as they don't
// reach codegen.
// FIXME(eddyb) the two other kinds of borrow (`Shallow` and `Unique`)
// aren't promoted here but *could* be promoted as part of a larger
// value because `IsNotPromotable` isn't being set for them,
// need to figure out what is the intended behavior.
// We might have a candidate for promotion.
let candidate = Candidate::Ref(location);
// Start by traversing to the "base", with non-deref projections removed.
let deref_proj =
place.projection.iter().rev().find(|&elem| *elem == ProjectionElem::Deref);
debug!(
"qualify_consts: promotion candidate: place={:?} {:?}",
place.base, deref_proj
);
// We can only promote interior borrows of promotable temps (non-temps
// don't get promoted anyway).
// (If we bailed out of the loop due to a `Deref` above, we will definitely
// not enter the conditional here.)
if let (PlaceBase::Local(local), None) = (&place.base, deref_proj) {
if self.body.local_kind(*local) == LocalKind::Temp {
debug!("qualify_consts: promotion candidate: local={:?}", local);
// The borrowed place doesn't have `HasMutInterior`
// (from `in_rvalue`), so we can safely ignore
// `HasMutInterior` from the local's qualifications.
// This allows borrowing fields which don't have
// `HasMutInterior`, from a type that does, e.g.:
// `let _: &'static _ = &(Cell::new(1), 2).1;`
let mut local_qualifs = self.qualifs_in_local(*local);
// Any qualifications, except HasMutInterior (see above), disqualify
// from promotion.
// This is, in particular, the "implicit promotion" version of
// the check making sure that we don't run drop glue during const-eval.
local_qualifs[HasMutInterior] = false;
if !local_qualifs.0.iter().any(|&qualif| qualif) {
debug!("qualify_consts: promotion candidate: {:?}", candidate);
self.promotion_candidates.push(candidate);
}
}
}
}
},
ValueSource::Rvalue(&Rvalue::Repeat(ref operand, _)) => {
debug!("assign: self.cx.mode={:?} self.def_id={:?} location={:?} operand={:?}",
self.cx.mode, self.def_id, location, operand);
if self.should_promote_repeat_expression(operand) &&
self.tcx.features().const_in_array_repeat_expressions {
self.promotion_candidates.push(Candidate::Repeat(location));
}
},
_ => {},
@ -939,18 +679,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
per_local.insert(index);
}
}
// Ensure the `IsNotPromotable` qualification is preserved.
// NOTE(eddyb) this is actually unnecessary right now, as
// we never replace the local's qualif, but we might in
// the future, and so it serves to catch changes that unset
// important bits (in which case, asserting `contains` could
// be replaced with calling `insert` to re-set the bit).
if kind == LocalKind::Temp {
if !self.temp_promotion_state[index].is_promotable() {
assert!(self.cx.per_local[IsNotPromotable].contains(index));
}
}
}
/// Check a whole const, static initializer or const fn.
@ -1047,24 +775,25 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
// Collect all the temps we need to promote.
let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
// HACK(eddyb) don't try to validate promotion candidates if any
// parts of the control-flow graph were skipped due to an error.
let promotion_candidates = if has_controlflow_error {
let unleash_miri = self
.tcx
.sess
.opts
.debugging_opts
.unleash_the_miri_inside_of_you;
if !unleash_miri {
self.tcx.sess.delay_span_bug(
body.span,
"check_const: expected control-flow error(s)",
);
}
self.promotion_candidates.clone()
// HACK: if parts of the control-flow graph were skipped due to an error, don't try to
// promote anything, since that can cause errors in a `const` if e.g. rvalue static
// promotion is attempted within a loop body.
let unleash_miri = self.tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you;
let promotion_candidates = if has_controlflow_error && !unleash_miri {
self.tcx.sess.delay_span_bug(
body.span,
"check_const: expected control-flow error(s)",
);
vec![]
} else {
self.valid_promotion_candidates()
promote_consts::validate_candidates(
self.tcx,
self.body,
self.def_id,
&self.temp_promotion_state,
&self.unchecked_promotion_candidates,
)
};
debug!("qualify_const: promotion_candidates={:?}", promotion_candidates);
@ -1086,68 +815,9 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
}
}
let mut qualifs = self.qualifs_in_local(RETURN_PLACE);
// Account for errors in consts by using the
// conservative type qualification instead.
if qualifs[IsNotPromotable] {
qualifs = self.qualifs_in_any_value_of_ty(body.return_ty());
}
let qualifs = self.qualifs_in_local(RETURN_PLACE);
(qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
}
/// Get the subset of `unchecked_promotion_candidates` that are eligible
/// for promotion.
// FIXME(eddyb) replace the old candidate gathering with this.
fn valid_promotion_candidates(&self) -> Vec<Candidate> {
// Sanity-check the promotion candidates.
let candidates = promote_consts::validate_candidates(
self.tcx,
self.body,
self.def_id,
&self.temp_promotion_state,
&self.unchecked_promotion_candidates,
);
if candidates != self.promotion_candidates {
let report = |msg, candidate| {
let span = match candidate {
Candidate::Ref(loc) |
Candidate::Repeat(loc) => self.body.source_info(loc).span,
Candidate::Argument { bb, .. } => {
self.body[bb].terminator().source_info.span
}
};
self.tcx.sess.span_err(span, &format!("{}: {:?}", msg, candidate));
};
for &c in &self.promotion_candidates {
if !candidates.contains(&c) {
report("invalidated old candidate", c);
}
}
for &c in &candidates {
if !self.promotion_candidates.contains(&c) {
report("extra new candidate", c);
}
}
bug!("promotion candidate validation mismatches (see above)");
}
candidates
}
/// Returns `true` if the operand of a repeat expression is promotable.
fn should_promote_repeat_expression(&self, operand: &Operand<'tcx>) -> bool {
let not_promotable = IsNotImplicitlyPromotable::in_operand(self, operand) ||
IsNotPromotable::in_operand(self, operand);
debug!("should_promote_repeat_expression: operand={:?} not_promotable={:?}",
operand, not_promotable);
!not_promotable
}
}
impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
@ -1459,11 +1129,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
}
let fn_ty = func.ty(self.body, self.tcx);
let mut callee_def_id = None;
let mut is_shuffle = false;
match fn_ty.kind {
ty::FnDef(def_id, _) => {
callee_def_id = Some(def_id);
match self.tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic |
Abi::PlatformIntrinsic => {
@ -1487,10 +1154,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
}
}
name if name.starts_with("simd_shuffle") => {
is_shuffle = true;
}
// no need to check feature gates, intrinsics are only callable
// from the libstd or with forever unstable feature gates
_ => {}
@ -1578,36 +1241,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
}
}
// No need to do anything in constants and statics, as everything is "constant" anyway
// so promotion would be useless.
if self.mode != Mode::Static && self.mode != Mode::Const {
let constant_args = callee_def_id.and_then(|id| {
args_required_const(self.tcx, id)
}).unwrap_or_default();
for (i, arg) in args.iter().enumerate() {
if !(is_shuffle && i == 2 || constant_args.contains(&i)) {
continue;
}
let candidate = Candidate::Argument { bb: location.block, index: i };
// Since the argument is required to be constant,
// we care about constness, not promotability.
// If we checked for promotability, we'd miss out on
// the results of function calls (which are never promoted
// in runtime code).
// This is not a problem, because the argument explicitly
// requests constness, in contrast to regular promotion
// which happens even without the user requesting it.
//
// `promote_consts` is responsible for emitting the error if
// the argument is not promotable.
if !IsNotPromotable::in_operand(self, arg) {
debug!("visit_terminator_kind: candidate={:?}", candidate);
self.promotion_candidates.push(candidate);
}
}
}
// Check callee and argument operands.
self.visit_operand(func, location);
for arg in args {
@ -1711,7 +1344,7 @@ pub fn provide(providers: &mut Providers<'_>) {
// FIXME(eddyb) this is only left around for the validation logic
// in `promote_consts`, see the comment in `validate_operand`.
pub(super) const QUALIF_ERROR_BIT: u8 = 1 << IsNotPromotable::IDX;
pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value
@ -1759,39 +1392,42 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
debug!("run_pass: mode={:?}", mode);
if let Mode::NonConstFn | Mode::ConstFn = mode {
// This is ugly because Checker holds onto mir,
// which can't be mutated until its scope ends.
let (temps, candidates) = {
let mut checker = Checker::new(tcx, def_id, body, mode);
if let Mode::ConstFn = mode {
let use_min_const_fn_checks =
!tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
tcx.is_min_const_fn(def_id);
if use_min_const_fn_checks {
// Enforce `min_const_fn` for stable `const fn`s.
use super::qualify_min_const_fn::is_min_const_fn;
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
error_min_const_fn_violation(tcx, span, err);
return;
}
// `check_const` should not produce any errors, but better safe than sorry
// FIXME(#53819)
// NOTE(eddyb) `check_const` is actually needed for promotion inside
// `min_const_fn` functions.
let mut checker = Checker::new(tcx, def_id, body, mode);
if let Mode::ConstFn = mode {
let use_min_const_fn_checks =
!tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
tcx.is_min_const_fn(def_id);
if use_min_const_fn_checks {
// Enforce `min_const_fn` for stable `const fn`s.
use super::qualify_min_const_fn::is_min_const_fn;
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
error_min_const_fn_violation(tcx, span, err);
return;
}
// Enforce a constant-like CFG for `const fn`.
checker.check_const();
} else {
while let Some((bb, data)) = checker.rpo.next() {
checker.visit_basic_block_data(bb, data);
}
// `check_const` should not produce any errors, but better safe than sorry
// FIXME(#53819)
// NOTE(eddyb) `check_const` is actually needed for promotion inside
// `min_const_fn` functions.
}
let promotion_candidates = checker.valid_promotion_candidates();
(checker.temp_promotion_state, promotion_candidates)
};
// Enforce a constant-like CFG for `const fn`.
checker.check_const();
} else {
while let Some((bb, data)) = checker.rpo.next() {
checker.visit_basic_block_data(bb, data);
}
}
// Promote only the promotable candidates.
let temps = checker.temp_promotion_state;
let candidates = promote_consts::validate_candidates(
tcx,
body,
def_id,
&temps,
&checker.unchecked_promotion_candidates,
);
// Do the actual promotion, now that we know what's viable.
self.promoted.set(
@ -1912,19 +1548,6 @@ fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId)
});
}
fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<FxHashSet<usize>> {
let attrs = tcx.get_attrs(def_id);
let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
let mut ret = FxHashSet::default();
for meta in attr.meta_item_list()? {
match meta.literal()?.kind {
LitKind::Int(a, _) => { ret.insert(a as usize); }
_ => return None,
}
}
Some(ret)
}
fn validator_mismatch(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,

View file

@ -0,0 +1,17 @@
// run-pass
use std::cell::Cell;
const X: Option<Cell<i32>> = None;
const Y: Option<Cell<i32>> = {
let x = None;
x
};
// Ensure that binding the final value of a `const` to a variable does not affect promotion.
#[allow(unused)]
fn main() {
let x: &'static _ = &X;
let y: &'static _ = &Y;
}