Skip to content

Commit aaceede

Browse files
authored
Rollup merge of rust-lang#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
2 parents 475c713 + a3b0369 commit aaceede

File tree

13 files changed

+186
-197
lines changed

13 files changed

+186
-197
lines changed

src/librustc/query/mod.rs

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

src/librustc/ty/query/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ use crate::util::common::ErrorReported;
4242
use crate::util::profiling::ProfileCategory::*;
4343

4444
use rustc_data_structures::svh::Svh;
45-
use rustc_index::bit_set::BitSet;
4645
use rustc_index::vec::IndexVec;
4746
use rustc_data_structures::fx::{FxIndexMap, FxHashMap, FxHashSet};
4847
use rustc_data_structures::stable_hasher::StableVec;

src/librustc_metadata/rmeta/decoder/cstore_impl.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use syntax::parse::parser::emit_unclosed_delims;
3232
use syntax::source_map::Spanned;
3333
use syntax::symbol::Symbol;
3434
use syntax_pos::{Span, FileName};
35-
use rustc_index::bit_set::BitSet;
3635

3736
macro_rules! provide {
3837
(<$lt:tt> $tcx:ident, $def_id:ident, $other:ident, $cdata:ident,
@@ -122,9 +121,7 @@ provide! { <'tcx> tcx, def_id, other, cdata,
122121
}
123122
optimized_mir => { tcx.arena.alloc(cdata.get_optimized_mir(tcx, def_id.index)) }
124123
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
125-
mir_const_qualif => {
126-
(cdata.mir_const_qualif(def_id.index), tcx.arena.alloc(BitSet::new_empty(0)))
127-
}
124+
mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
128125
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
129126
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
130127
is_const_fn_raw => { cdata.is_const_fn_raw(def_id.index) }

src/librustc_metadata/rmeta/encoder.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ impl EncodeContext<'tcx> {
955955
record!(self.per_def.kind[def_id] <- match impl_item.kind {
956956
ty::AssocKind::Const => {
957957
if let hir::ImplItemKind::Const(_, body_id) = ast_item.kind {
958-
let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id).0;
958+
let mir = self.tcx.at(ast_item.span).mir_const_qualif(def_id);
959959

960960
EntryKind::AssocConst(container,
961961
ConstQualif { mir },
@@ -1089,7 +1089,7 @@ impl EncodeContext<'tcx> {
10891089
hir::ItemKind::Static(_, hir::MutMutable, _) => EntryKind::MutStatic,
10901090
hir::ItemKind::Static(_, hir::MutImmutable, _) => EntryKind::ImmStatic,
10911091
hir::ItemKind::Const(_, body_id) => {
1092-
let mir = self.tcx.at(item.span).mir_const_qualif(def_id).0;
1092+
let mir = self.tcx.at(item.span).mir_const_qualif(def_id);
10931093
EntryKind::Const(
10941094
ConstQualif { mir },
10951095
self.encode_rendered_const_for_body(body_id)
@@ -1368,7 +1368,7 @@ impl EncodeContext<'tcx> {
13681368
let id = self.tcx.hir().as_local_hir_id(def_id).unwrap();
13691369
let body_id = self.tcx.hir().body_owned_by(id);
13701370
let const_data = self.encode_rendered_const_for_body(body_id);
1371-
let mir = self.tcx.mir_const_qualif(def_id).0;
1371+
let mir = self.tcx.mir_const_qualif(def_id);
13721372

13731373
record!(self.per_def.kind[def_id] <- EntryKind::Const(ConstQualif { mir }, const_data));
13741374
record!(self.per_def.visibility[def_id] <- ty::Visibility::Public);

src/librustc_mir/transform/check_consts/qualifs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub trait Qualif {
123123
if cx.tcx.trait_of_item(def_id).is_some() {
124124
Self::in_any_value_of_ty(cx, constant.literal.ty)
125125
} else {
126-
let (bits, _) = cx.tcx.at(constant.span).mir_const_qualif(def_id);
126+
let bits = cx.tcx.at(constant.span).mir_const_qualif(def_id);
127127

128128
let qualif = QualifSet(bits).contains::<Self>();
129129

src/librustc_mir/transform/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,14 @@ fn mir_validated(
210210
}
211211

212212
let mut body = tcx.mir_const(def_id).steal();
213-
let qualify_and_promote_pass = qualify_consts::QualifyAndPromoteConstants::default();
213+
let promote_pass = promote_consts::PromoteTemps::default();
214214
run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
215215
// What we need to run borrowck etc.
216-
&qualify_and_promote_pass,
216+
&qualify_consts::QualifyAndPromoteConstants::default(),
217+
&promote_pass,
217218
&simplify::SimplifyCfg::new("qualify-consts"),
218219
]);
219-
let promoted = qualify_and_promote_pass.promoted.into_inner();
220+
let promoted = promote_pass.promoted_fragments.into_inner();
220221
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
221222
}
222223

src/librustc_mir/transform/promote_consts.rs

+138-2
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,76 @@ use rustc::mir::*;
1717
use rustc::mir::interpret::ConstValue;
1818
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
1919
use rustc::mir::traversal::ReversePostorder;
20-
use rustc::ty::{self, List, TyCtxt};
20+
use rustc::ty::{self, List, TyCtxt, TypeFoldable};
2121
use rustc::ty::subst::InternalSubsts;
2222
use rustc::ty::cast::CastTy;
2323
use syntax::ast::LitKind;
2424
use syntax::symbol::sym;
2525
use syntax_pos::{Span, DUMMY_SP};
2626

2727
use rustc_index::vec::{IndexVec, Idx};
28+
use rustc_index::bit_set::HybridBitSet;
2829
use rustc_target::spec::abi::Abi;
2930

31+
use std::cell::Cell;
3032
use std::{iter, mem, usize};
3133

34+
use crate::transform::{MirPass, MirSource};
3235
use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn};
3336

37+
/// A `MirPass` for promotion.
38+
///
39+
/// In this case, "promotion" entails the following:
40+
/// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
41+
/// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
42+
/// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
43+
///
44+
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
45+
/// newly created `StaticKind::Promoted`.
46+
#[derive(Default)]
47+
pub struct PromoteTemps<'tcx> {
48+
pub promoted_fragments: Cell<IndexVec<Promoted, Body<'tcx>>>,
49+
}
50+
51+
impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
52+
fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
53+
// There's not really any point in promoting errorful MIR.
54+
//
55+
// This does not include MIR that failed const-checking, which we still try to promote.
56+
if body.return_ty().references_error() {
57+
tcx.sess.delay_span_bug(body.span, "PromoteTemps: MIR had errors");
58+
return;
59+
}
60+
61+
if src.promoted.is_some() {
62+
return;
63+
}
64+
65+
let def_id = src.def_id();
66+
67+
let item = Item::new(tcx, def_id, body);
68+
let mut rpo = traversal::reverse_postorder(body);
69+
let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
70+
71+
let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
72+
73+
// For now, lifetime extension is done in `const` and `static`s without creating promoted
74+
// MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
75+
// not work inside loops when they are allowed in `const`s.
76+
//
77+
// FIXME: use promoted MIR fragments everywhere?
78+
let promoted_fragments = if should_create_promoted_mir_fragments(item.const_kind) {
79+
promote_candidates(def_id, body, tcx, temps, promotable_candidates)
80+
} else {
81+
// FIXME: promote const array initializers in consts.
82+
remove_drop_and_storage_dead_on_promoted_locals(tcx, body, &promotable_candidates);
83+
IndexVec::new()
84+
};
85+
86+
self.promoted_fragments.set(promoted_fragments);
87+
}
88+
}
89+
3490
/// State of a temporary during collection and promotion.
3591
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
3692
pub enum TempState {
@@ -538,7 +594,7 @@ impl<'tcx> Validator<'_, 'tcx> {
538594
// is gone - we can always promote constants even if they
539595
// fail to pass const-checking, as compilation would've
540596
// errored independently and promotion can't change that.
541-
let (bits, _) = self.tcx.at(constant.span).mir_const_qualif(def_id);
597+
let bits = self.tcx.at(constant.span).mir_const_qualif(def_id);
542598
if bits == super::qualify_consts::QUALIF_ERROR_BIT {
543599
self.tcx.sess.delay_span_bug(
544600
constant.span,
@@ -1154,3 +1210,83 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
11541210
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
11551211
should_promote && !feature_flag
11561212
}
1213+
1214+
fn should_create_promoted_mir_fragments(const_kind: Option<ConstKind>) -> bool {
1215+
match const_kind {
1216+
Some(ConstKind::ConstFn) | None => true,
1217+
Some(ConstKind::Const) | Some(ConstKind::Static) | Some(ConstKind::StaticMut) => false,
1218+
}
1219+
}
1220+
1221+
/// In `const` and `static` everything without `StorageDead`
1222+
/// is `'static`, we don't have to create promoted MIR fragments,
1223+
/// just remove `Drop` and `StorageDead` on "promoted" locals.
1224+
fn remove_drop_and_storage_dead_on_promoted_locals(
1225+
tcx: TyCtxt<'tcx>,
1226+
body: &mut Body<'tcx>,
1227+
promotable_candidates: &[Candidate],
1228+
) {
1229+
debug!("run_pass: promotable_candidates={:?}", promotable_candidates);
1230+
1231+
// Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
1232+
// simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
1233+
//
1234+
// FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
1235+
if body.is_cfg_cyclic() {
1236+
tcx.sess.delay_span_bug(body.span, "Control-flow cycle detected in `const`");
1237+
return;
1238+
}
1239+
1240+
// The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
1241+
let mut requires_lifetime_extension = HybridBitSet::new_empty(body.local_decls.len());
1242+
1243+
promotable_candidates
1244+
.iter()
1245+
.filter_map(|c| {
1246+
match c {
1247+
Candidate::Ref(loc) => Some(loc),
1248+
Candidate::Repeat(_) | Candidate::Argument { .. } => None,
1249+
}
1250+
})
1251+
.map(|&Location { block, statement_index }| {
1252+
// FIXME: store the `Local` for each `Candidate` when it is created.
1253+
let place = match &body[block].statements[statement_index].kind {
1254+
StatementKind::Assign(box ( _, Rvalue::Ref(_, _, place))) => place,
1255+
_ => bug!("`Candidate::Ref` without corresponding assignment"),
1256+
};
1257+
1258+
match place.base {
1259+
PlaceBase::Local(local) => local,
1260+
PlaceBase::Static(_) => bug!("`Candidate::Ref` for a non-local"),
1261+
}
1262+
})
1263+
.for_each(|local| {
1264+
requires_lifetime_extension.insert(local);
1265+
});
1266+
1267+
// Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
1268+
// lifetime extension.
1269+
for block in body.basic_blocks_mut() {
1270+
block.statements.retain(|statement| {
1271+
match statement.kind {
1272+
StatementKind::StorageDead(index) => !requires_lifetime_extension.contains(index),
1273+
_ => true
1274+
}
1275+
});
1276+
let terminator = block.terminator_mut();
1277+
match &terminator.kind {
1278+
TerminatorKind::Drop {
1279+
location,
1280+
target,
1281+
..
1282+
} => {
1283+
if let Some(index) = location.as_local() {
1284+
if requires_lifetime_extension.contains(index) {
1285+
terminator.kind = TerminatorKind::Goto { target: *target };
1286+
}
1287+
}
1288+
}
1289+
_ => {}
1290+
}
1291+
}
1292+
}

0 commit comments

Comments
 (0)