1
Fork 0

Rollup merge of #65949 - ecstatic-morse:promote-only-pass, r=eddyb

Move promotion into its own pass

**edited**

This adds a `PromoteTemps` pass, which runs after the old `QualifyAndPromoteConsts` pass, that *only* does promotion (no const-checking). Everything related to promotion has been removed from `QualifyAndPromoteConstants`: it no longer even visits the body of a non-const `fn`.

As a result we no longer need to keep the `BitSet` of promotable locals that was returned by `mir_const_qualif`. Rvalue-static promotion in a `const` is now done in `promote_consts`, and it operates on a set of `Candidate`s instead. This will allow me–in a later PR–to create promoted MIR fragments for `const`s when necessary, which could resolve some shortcomings of the current approach (removing `StorageDead`).

r? @eddyb
This commit is contained in:
Mazdak Farrokhzad 2019-11-09 07:18:27 +01:00 committed by GitHub
commit aaceeded9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 186 additions and 197 deletions

View file

@ -93,7 +93,7 @@ rustc_queries! {
/// Maps DefId's that have an associated `mir::Body` to the result /// Maps DefId's that have an associated `mir::Body` to the result
/// of the MIR qualify_consts pass. The actual meaning of /// of the MIR qualify_consts pass. The actual meaning of
/// the value isn't known except to the pass itself. /// the value isn't known except to the pass itself.
query mir_const_qualif(key: DefId) -> (u8, &'tcx BitSet<mir::Local>) { query mir_const_qualif(key: DefId) -> u8 {
desc { |tcx| "const checking `{}`", tcx.def_path_str(key) } desc { |tcx| "const checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() } cache_on_disk_if { key.is_local() }
} }

View file

@ -42,7 +42,6 @@ use crate::util::common::ErrorReported;
use crate::util::profiling::ProfileCategory::*; use crate::util::profiling::ProfileCategory::*;
use rustc_data_structures::svh::Svh; use rustc_data_structures::svh::Svh;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::StableVec; use rustc_data_structures::stable_hasher::StableVec;

View file

@ -32,7 +32,6 @@ use syntax::parse::parser::emit_unclosed_delims;
use syntax::source_map::Spanned; use syntax::source_map::Spanned;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
use syntax_pos::{Span, FileName}; use syntax_pos::{Span, FileName};
use rustc_index::bit_set::BitSet;
macro_rules! provide { macro_rules! provide {
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident, (<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
@ -122,9 +121,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
} }
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) } optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) } promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
mir_const_qualif => { mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
(cdata.mir_const_qualif(def_id.index), tcx.arena.alloc(BitSet::new_empty(0)))
}
fn_sig => { cdata.fn_sig(def_id.index, tcx) } fn_sig => { cdata.fn_sig(def_id.index, tcx) }
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) } is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }

View file

@ -955,7 +955,7 @@ impl EncodeContext<'tcx> {
record!(self.per_def.kind[def_id] <- match impl_item.kind { record!(self.per_def.kind[def_id] <- match impl_item.kind {
ty::AssocKind::Const => { ty::AssocKind::Const => {
if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind { if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id).0; let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
EntryKind::AssocConst(container, EntryKind::AssocConst(container,
ConstQualif { mir }, ConstQualif { mir },
@ -1089,7 +1089,7 @@ impl EncodeContext<'tcx> {
hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic, hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic,
hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic, hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic,
hir::ItemKind::Const(_, body_id) => { hir::ItemKind::Const(_, body_id) => {
let mir = self.tcx.at(item.span).mir_const_qualif(def_id).0; let mir = self.tcx.at(item.span).mir_const_qualif(def_id);
EntryKind::Const( EntryKind::Const(
ConstQualif { mir }, ConstQualif { mir },
self.encode_rendered_const_for_body(body_id) self.encode_rendered_const_for_body(body_id)
@ -1368,7 +1368,7 @@ impl EncodeContext<'tcx> {
let id = self.tcx.hir().as_local_hir_id(def_id).unwrap(); let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
let body_id = self.tcx.hir().body_owned_by(id); let body_id = self.tcx.hir().body_owned_by(id);
let const_data = self.encode_rendered_const_for_body(body_id); let const_data = self.encode_rendered_const_for_body(body_id);
let mir = self.tcx.mir_const_qualif(def_id).0; let mir = self.tcx.mir_const_qualif(def_id);
record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data)); record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
record!(self.per_def.visibility[def_id] <- ty::Visibility::Public); record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);

View file

@ -123,7 +123,7 @@ pub trait Qualif {
if cx.tcx.trait_of_item(def_id).is_some() { if cx.tcx.trait_of_item(def_id).is_some() {
Self::in_any_value_of_ty(cx, constant.literal.ty) Self::in_any_value_of_ty(cx, constant.literal.ty)
} else { } else {
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id); let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
let qualif = QualifSet(bits).contains::<Self>(); let qualif = QualifSet(bits).contains::<Self>();

View file

@ -210,13 +210,14 @@ fn mir_validated(
} }
let mut body = tcx.mir_const(def_id).steal(); let mut body = tcx.mir_const(def_id).steal();
let qualify_and_promote_pass = qualify_consts::QualifyAndPromoteConstants::default(); let promote_pass = promote_consts::PromoteTemps::default();
run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[ run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
// What we need to run borrowck etc. // What we need to run borrowck etc.
&qualify_and_promote_pass, &qualify_consts::QualifyAndPromoteConstants::default(),
&promote_pass,
&simplify::SimplifyCfg::new("qualify-consts"), &simplify::SimplifyCfg::new("qualify-consts"),
]); ]);
let promoted = qualify_and_promote_pass.promoted.into_inner(); let promoted = promote_pass.promoted_fragments.into_inner();
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
} }

View file

@ -17,7 +17,7 @@ use rustc::mir::*;
use rustc::mir::interpret::ConstValue; use rustc::mir::interpret::ConstValue;
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor}; use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder; use rustc::mir::traversal::ReversePostorder;
use rustc::ty::{self, List, TyCtxt}; use rustc::ty::{self, List, TyCtxt, TypeFoldable};
use rustc::ty::subst::InternalSubsts; use rustc::ty::subst::InternalSubsts;
use rustc::ty::cast::CastTy; use rustc::ty::cast::CastTy;
use syntax::ast::LitKind; use syntax::ast::LitKind;
@ -25,12 +25,68 @@ use syntax::symbol::sym;
use syntax_pos::{Span, DUMMY_SP}; use syntax_pos::{Span, DUMMY_SP};
use rustc_index::vec::{IndexVec, Idx}; use rustc_index::vec::{IndexVec, Idx};
use rustc_index::bit_set::HybridBitSet;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use std::cell::Cell;
use std::{iter, mem, usize}; use std::{iter, mem, usize};
use crate::transform::{MirPass, MirSource};
use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn}; use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
/// A `MirPass` for promotion.
///
/// In this case, "promotion" entails the following:
/// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
/// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
/// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
///
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
/// newly created `StaticKind::Promoted`.
#[derive(Default)]
pub struct PromoteTemps<'tcx> {
pub promoted_fragments: Cell<IndexVec<Promoted, Body<'tcx>>>,
}
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
// There's not really any point in promoting errorful MIR.
//
// This does not include MIR that failed const-checking, which we still try to promote.
if body.return_ty().references_error() {
tcx.sess.delay_span_bug(body.span, "PromoteTemps: MIR had errors");
return;
}
if src.promoted.is_some() {
return;
}
let def_id = src.def_id();
let item = Item::new(tcx, def_id, body);
let mut rpo = traversal::reverse_postorder(body);
let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
// For now, lifetime extension is done in `const` and `static`s without creating promoted
// MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
// not work inside loops when they are allowed in `const`s.
//
// FIXME: use promoted MIR fragments everywhere?
let promoted_fragments = if should_create_promoted_mir_fragments(item.const_kind) {
promote_candidates(def_id, body, tcx, temps, promotable_candidates)
} else {
// FIXME: promote const array initializers in consts.
remove_drop_and_storage_dead_on_promoted_locals(tcx, body, &promotable_candidates);
IndexVec::new()
};
self.promoted_fragments.set(promoted_fragments);
}
}
/// State of a temporary during collection and promotion. /// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState { pub enum TempState {
@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
// is gone - we can always promote constants even if they // is gone - we can always promote constants even if they
// fail to pass const-checking, as compilation would've // fail to pass const-checking, as compilation would've
// errored independently and promotion can't change that. // errored independently and promotion can't change that.
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id); let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
if bits == super::qualify_consts::QUALIF_ERROR_BIT { if bits == super::qualify_consts::QUALIF_ERROR_BIT {
self.tcx.sess.delay_span_bug( self.tcx.sess.delay_span_bug(
constant.span, constant.span,
@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag); should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
should_promote && !feature_flag should_promote && !feature_flag
} }
fn should_create_promoted_mir_fragments(const_kind: Option<ConstKind>) -> bool {
match const_kind {
Some(ConstKind::ConstFn) | None => true,
Some(ConstKind::Const) | Some(ConstKind::Static) | Some(ConstKind::StaticMut) => false,
}
}
/// In `const` and `static` everything without `StorageDead`
/// is `'static`, we don't have to create promoted MIR fragments,
/// just remove `Drop` and `StorageDead` on "promoted" locals.
fn remove_drop_and_storage_dead_on_promoted_locals(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
promotable_candidates: &[Candidate],
) {
debug!("run_pass: promotable_candidates={:?}", promotable_candidates);
// Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
// simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
//
// FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
if body.is_cfg_cyclic() {
tcx.sess.delay_span_bug(body.span, "Control-flow cycle detected in `const`");
return;
}
// The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
let mut requires_lifetime_extension = HybridBitSet::new_empty(body.local_decls.len());
promotable_candidates
.iter()
.filter_map(|c| {
match c {
Candidate::Ref(loc) => Some(loc),
Candidate::Repeat(_) | Candidate::Argument { .. } => None,
}
})
.map(|&Location { block, statement_index }| {
// FIXME: store the `Local` for each `Candidate` when it is created.
let place = match &body[block].statements[statement_index].kind {
StatementKind::Assign(box ( _, Rvalue::Ref(_, _, place))) => place,
_ => bug!("`Candidate::Ref` without corresponding assignment"),
};
match place.base {
PlaceBase::Local(local) => local,
PlaceBase::Static(_) => bug!("`Candidate::Ref` for a non-local"),
}
})
.for_each(|local| {
requires_lifetime_extension.insert(local);
});
// Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
// lifetime extension.
for block in body.basic_blocks_mut() {
block.statements.retain(|statement| {
match statement.kind {
StatementKind::StorageDead(index) => !requires_lifetime_extension.contains(index),
_ => true
}
});
let terminator = block.terminator_mut();
match &terminator.kind {
TerminatorKind::Drop {
location,
target,
..
} => {
if let Some(index) = location.as_local() {
if requires_lifetime_extension.contains(index) {
terminator.kind = TerminatorKind::Goto { target: *target };
}
}
}
_ => {}
}
}
}

View file

@ -15,7 +15,6 @@ use rustc::ty::cast::CastTy;
use rustc::ty::query::Providers; use rustc::ty::query::Providers;
use rustc::mir::*; use rustc::mir::*;
use rustc::mir::interpret::ConstValue; use rustc::mir::interpret::ConstValue;
use rustc::mir::traversal::ReversePostorder;
use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext}; use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
use rustc::middle::lang_items; use rustc::middle::lang_items;
use rustc::session::config::nightly_options; use rustc::session::config::nightly_options;
@ -31,7 +30,6 @@ use std::usize;
use rustc::hir::HirId; use rustc::hir::HirId;
use crate::transform::{MirPass, MirSource}; use crate::transform::{MirPass, MirSource};
use super::promote_consts::{self, Candidate, TempState};
use crate::transform::check_consts::ops::{self, NonConstOp}; use crate::transform::check_consts::ops::{self, NonConstOp};
/// What kind of item we are in. /// What kind of item we are in.
@ -258,7 +256,7 @@ trait Qualif {
if cx.tcx.trait_of_item(def_id).is_some() { if cx.tcx.trait_of_item(def_id).is_some() {
Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false) Self::in_any_value_of_ty(cx, constant.literal.ty).unwrap_or(false)
} else { } else {
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id); let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX]; let qualif = PerQualif::decode_from_bits(bits).0[Self::IDX];
@ -477,10 +475,6 @@ struct Checker<'a, 'tcx> {
span: Span, span: Span,
def_id: DefId, def_id: DefId,
rpo: ReversePostorder<'a, 'tcx>,
temp_promotion_state: IndexVec<Local, TempState>,
unchecked_promotion_candidates: Vec<Candidate>,
/// If `true`, do not emit errors to the user, merely collect them in `errors`. /// If `true`, do not emit errors to the user, merely collect them in `errors`.
suppress_errors: bool, suppress_errors: bool,
@ -509,10 +503,6 @@ impl Deref for Checker<'a, 'tcx> {
impl<'a, 'tcx> Checker<'a, 'tcx> { impl<'a, 'tcx> Checker<'a, 'tcx> {
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self { fn new(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>, mode: Mode) -> Self {
assert!(def_id.is_local()); assert!(def_id.is_local());
let mut rpo = traversal::reverse_postorder(body);
let (temps, unchecked_promotion_candidates) =
promote_consts::collect_temps_and_candidates(tcx, body, &mut rpo);
rpo.reset();
let param_env = tcx.param_env(def_id); let param_env = tcx.param_env(def_id);
@ -539,9 +529,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
cx, cx,
span: body.span, span: body.span,
def_id, def_id,
rpo,
temp_promotion_state: temps,
unchecked_promotion_candidates,
errors: vec![], errors: vec![],
suppress_errors: false, suppress_errors: false,
} }
@ -662,14 +649,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
let kind = self.body.local_kind(index); let kind = self.body.local_kind(index);
debug!("store to {:?} {:?}", kind, index); debug!("store to {:?} {:?}", kind, index);
// Only handle promotable temps in non-const functions.
if self.mode == Mode::NonConstFn {
if kind != LocalKind::Temp ||
!self.temp_promotion_state[index].is_promotable() {
return;
}
}
// this is overly restrictive, because even full assignments do not clear the qualif // this is overly restrictive, because even full assignments do not clear the qualif
// While we could special case full assignments, this would be inconsistent with // While we could special case full assignments, this would be inconsistent with
// aggregates where we overwrite all fields via assignments, which would not get // aggregates where we overwrite all fields via assignments, which would not get
@ -682,7 +661,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
} }
/// Check a whole const, static initializer or const fn. /// Check a whole const, static initializer or const fn.
fn check_const(&mut self) -> (u8, &'tcx BitSet<Local>) { fn check_const(&mut self) -> u8 {
use crate::transform::check_consts as new_checker; use crate::transform::check_consts as new_checker;
debug!("const-checking {} {:?}", self.mode, self.def_id); debug!("const-checking {} {:?}", self.mode, self.def_id);
@ -704,7 +683,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len()); let mut seen_blocks = BitSet::new_empty(body.basic_blocks().len());
let mut bb = START_BLOCK; let mut bb = START_BLOCK;
let mut has_controlflow_error = false;
loop { loop {
seen_blocks.insert(bb.index()); seen_blocks.insert(bb.index());
@ -745,7 +723,6 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
bb = target; bb = target;
} }
_ => { _ => {
has_controlflow_error = true;
self.not_const(ops::Loop); self.not_const(ops::Loop);
validator.check_op(ops::Loop); validator.check_op(ops::Loop);
break; break;
@ -772,51 +749,7 @@ impl<'a, 'tcx> Checker<'a, 'tcx> {
} }
} }
// Collect all the temps we need to promote. self.qualifs_in_local(RETURN_PLACE).encode_to_bits()
let mut promoted_temps = BitSet::new_empty(self.temp_promotion_state.len());
// 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 {
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);
for candidate in promotion_candidates {
match candidate {
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
if let StatementKind::Assign(box( _, Rvalue::Ref(_, _, place)))
= &self.body[bb].statements[stmt_idx].kind
{
if let PlaceBase::Local(local) = place.base {
promoted_temps.insert(local);
}
}
}
// Only rvalue-static promotion requires extending the lifetime of the promoted
// local.
Candidate::Argument { .. } | Candidate::Repeat(_) => {}
}
}
let qualifs = self.qualifs_in_local(RETURN_PLACE);
(qualifs.encode_to_bits(), self.tcx.arena.alloc(promoted_temps))
} }
} }
@ -1346,7 +1279,7 @@ pub fn provide(providers: &mut Providers<'_>) {
// in `promote_consts`, see the comment in `validate_operand`. // in `promote_consts`, see the comment in `validate_operand`.
pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2; pub(super) const QUALIF_ERROR_BIT: u8 = 1 << 2;
fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) { fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
// N.B., this `borrow()` is guaranteed to be valid (i.e., the value // N.B., this `borrow()` is guaranteed to be valid (i.e., the value
// cannot yet be stolen), because `mir_validated()`, which steals // cannot yet be stolen), because `mir_validated()`, which steals
// from `mir_const(), forces this query to execute before // from `mir_const(), forces this query to execute before
@ -1355,7 +1288,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> (u8, &BitSet<Local>) {
if body.return_ty().references_error() { if body.return_ty().references_error() {
tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors"); tcx.sess.delay_span_bug(body.span, "mir_const_qualif: MIR had errors");
return (QUALIF_ERROR_BIT, tcx.arena.alloc(BitSet::new_empty(0))); return QUALIF_ERROR_BIT;
} }
Checker::new(tcx, def_id, body, Mode::Const).check_const() Checker::new(tcx, def_id, body, Mode::Const).check_const()
@ -1391,56 +1324,34 @@ impl<'tcx> MirPass<'tcx> for QualifyAndPromoteConstants<'tcx> {
let mode = determine_mode(tcx, hir_id, def_id); let mode = determine_mode(tcx, hir_id, def_id);
debug!("run_pass: mode={:?}", mode); debug!("run_pass: mode={:?}", mode);
if let Mode::NonConstFn | Mode::ConstFn = mode { if let Mode::NonConstFn = mode {
// No need to const-check a non-const `fn` now that we don't do promotion here.
return;
} else if let Mode::ConstFn = mode {
let mut checker = Checker::new(tcx, def_id, body, mode); let mut checker = Checker::new(tcx, def_id, body, mode);
if let Mode::ConstFn = mode { let use_min_const_fn_checks =
let use_min_const_fn_checks = !tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you &&
!tcx.sess.opts.debugging_opts.unleash_the_miri_inside_of_you && tcx.is_min_const_fn(def_id);
tcx.is_min_const_fn(def_id); if use_min_const_fn_checks {
if use_min_const_fn_checks { // Enforce `min_const_fn` for stable `const fn`s.
// Enforce `min_const_fn` for stable `const fn`s. use super::qualify_min_const_fn::is_min_const_fn;
use super::qualify_min_const_fn::is_min_const_fn; if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) {
if let Err((span, err)) = is_min_const_fn(tcx, def_id, body) { error_min_const_fn_violation(tcx, span, err);
error_min_const_fn_violation(tcx, span, err); return;
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.
}
// 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. // `check_const` should not produce any errors, but better safe than sorry
let temps = checker.temp_promotion_state; // FIXME(#53819)
let candidates = promote_consts::validate_candidates( // Enforce a constant-like CFG for `const fn`.
tcx, checker.check_const();
body,
def_id,
&temps,
&checker.unchecked_promotion_candidates,
);
// Do the actual promotion, now that we know what's viable.
self.promoted.set(
promote_consts::promote_candidates(def_id, body, tcx, temps, candidates)
);
} else { } else {
check_short_circuiting_in_const_local(tcx, body, mode); check_short_circuiting_in_const_local(tcx, body, mode);
let promoted_temps = match mode { match mode {
Mode::Const => tcx.mir_const_qualif(def_id).1, Mode::Const => tcx.mir_const_qualif(def_id),
_ => Checker::new(tcx, def_id, body, mode).check_const().1, _ => Checker::new(tcx, def_id, body, mode).check_const(),
}; };
remove_drop_and_storage_dead_on_promoted_locals(body, promoted_temps);
} }
if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) { if mode == Mode::Static && !tcx.has_attr(def_id, sym::thread_local) {
@ -1501,40 +1412,6 @@ fn check_short_circuiting_in_const_local(tcx: TyCtxt<'_>, body: &mut Body<'tcx>,
} }
} }
/// In `const` and `static` everything without `StorageDead`
/// is `'static`, we don't have to create promoted MIR fragments,
/// just remove `Drop` and `StorageDead` on "promoted" locals.
fn remove_drop_and_storage_dead_on_promoted_locals(
body: &mut Body<'tcx>,
promoted_temps: &BitSet<Local>,
) {
debug!("run_pass: promoted_temps={:?}", promoted_temps);
for block in body.basic_blocks_mut() {
block.statements.retain(|statement| {
match statement.kind {
StatementKind::StorageDead(index) => !promoted_temps.contains(index),
_ => true
}
});
let terminator = block.terminator_mut();
match &terminator.kind {
TerminatorKind::Drop {
location,
target,
..
} => {
if let Some(index) = location.as_local() {
if promoted_temps.contains(index) {
terminator.kind = TerminatorKind::Goto { target: *target };
}
}
}
_ => {}
}
}
}
fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) { fn check_static_is_sync(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, hir_id: HirId) {
let ty = body.return_ty(); let ty = body.return_ty();
tcx.infer_ctxt().enter(|infcx| { tcx.infer_ctxt().enter(|infcx| {

View file

@ -39,7 +39,7 @@ fn main() {
// END RUST SOURCE // END RUST SOURCE
// //
// START rustc.full_tested_match.QualifyAndPromoteConstants.after.mir // START rustc.full_tested_match.PromoteTemps.after.mir
// bb0: { // bb0: {
// ... // ...
// _2 = std::option::Option::<i32>::Some(const 42i32,); // _2 = std::option::Option::<i32>::Some(const 42i32,);
@ -108,9 +108,9 @@ fn main() {
// _0 = (); // _0 = ();
// return; // return;
// } // }
// END rustc.full_tested_match.QualifyAndPromoteConstants.after.mir // END rustc.full_tested_match.PromoteTemps.after.mir
// //
// START rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir // START rustc.full_tested_match2.PromoteTemps.before.mir
// bb0: { // bb0: {
// ... // ...
// _2 = std::option::Option::<i32>::Some(const 42i32,); // _2 = std::option::Option::<i32>::Some(const 42i32,);
@ -179,9 +179,9 @@ fn main() {
// _0 = (); // _0 = ();
// return; // return;
// } // }
// END rustc.full_tested_match2.QualifyAndPromoteConstants.before.mir // END rustc.full_tested_match2.PromoteTemps.before.mir
// //
// START rustc.main.QualifyAndPromoteConstants.before.mir // START rustc.main.PromoteTemps.before.mir
// bb0: { // bb0: {
// ... // ...
// _2 = std::option::Option::<i32>::Some(const 1i32,); // _2 = std::option::Option::<i32>::Some(const 1i32,);
@ -276,4 +276,4 @@ fn main() {
// _0 = (); // _0 = ();
// return; // return;
// } // }
// END rustc.main.QualifyAndPromoteConstants.before.mir // END rustc.main.PromoteTemps.before.mir

View file

@ -136,7 +136,6 @@ const fn no_rpit() -> impl std::fmt::Debug {} //~ ERROR `impl Trait` in const fn
const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized` const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {} //~ ERROR trait bounds other than `Sized`
const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() } const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
//~^ ERROR trait bounds other than `Sized` //~^ ERROR trait bounds other than `Sized`
//~| ERROR cannot return reference to temporary value
const fn no_unsafe() { unsafe {} } const fn no_unsafe() { unsafe {} }

View file

@ -286,17 +286,8 @@ LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
= help: add `#![feature(const_fn)]` to the crate attributes to enable = help: add `#![feature(const_fn)]` to the crate attributes to enable
error[E0515]: cannot return reference to temporary value
--> $DIR/min_const_fn.rs:137:63
|
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^--
| ||
| |temporary value created here
| returns a reference to data owned by the current function
error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable error[E0723]: trait bounds other than `Sized` on const fn parameters are unstable
--> $DIR/min_const_fn.rs:143:41 --> $DIR/min_const_fn.rs:142:41
| |
LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 } LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -305,7 +296,7 @@ LL | const fn really_no_traits_i_mean_it() { (&() as &dyn std::fmt::Debug, ()).1
= help: add `#![feature(const_fn)]` to the crate attributes to enable = help: add `#![feature(const_fn)]` to the crate attributes to enable
error[E0723]: function pointers in const fn are unstable error[E0723]: function pointers in const fn are unstable
--> $DIR/min_const_fn.rs:146:21 --> $DIR/min_const_fn.rs:145:21
| |
LL | const fn no_fn_ptrs(_x: fn()) {} LL | const fn no_fn_ptrs(_x: fn()) {}
| ^^ | ^^
@ -314,7 +305,7 @@ LL | const fn no_fn_ptrs(_x: fn()) {}
= help: add `#![feature(const_fn)]` to the crate attributes to enable = help: add `#![feature(const_fn)]` to the crate attributes to enable
error[E0723]: function pointers in const fn are unstable error[E0723]: function pointers in const fn are unstable
--> $DIR/min_const_fn.rs:148:27 --> $DIR/min_const_fn.rs:147:27
| |
LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo } LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
| ^^^^ | ^^^^
@ -322,7 +313,7 @@ LL | const fn no_fn_ptrs2() -> fn() { fn foo() {} foo }
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
= help: add `#![feature(const_fn)]` to the crate attributes to enable = help: add `#![feature(const_fn)]` to the crate attributes to enable
error: aborting due to 37 previous errors error: aborting due to 36 previous errors
Some errors have detailed explanations: E0493, E0515, E0723. Some errors have detailed explanations: E0493, E0723.
For more information about an error, try `rustc --explain E0493`. For more information about an error, try `rustc --explain E0493`.

View file

@ -11,6 +11,5 @@ const fn no_inner_dyn_trait2(x: Hide) {
} }
const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) } const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
//~^ ERROR trait bounds other than `Sized` //~^ ERROR trait bounds other than `Sized`
//~| ERROR temporary value dropped while borrowed
fn main() {} fn main() {}

View file

@ -16,16 +16,6 @@ LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
= help: add `#![feature(const_fn)]` to the crate attributes to enable = help: add `#![feature(const_fn)]` to the crate attributes to enable
error[E0716]: temporary value dropped while borrowed error: aborting due to 2 previous errors
--> $DIR/min_const_fn_dyn.rs:12:67
|
LL | const fn no_inner_dyn_trait_ret() -> Hide { Hide(HasDyn { field: &0 }) }
| -^ - temporary value is freed at the end of this statement
| ||
| |creates a temporary which is freed while still in use
| cast requires that borrow lasts for `'static`
error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0723`.
Some errors have detailed explanations: E0716, E0723.
For more information about an error, try `rustc --explain E0716`.