Skip to content

Commit f9ed219

Browse files
Create promoted MIR fragments in const and statics
The previous strategy of removing `Drop` and `StorageDead` for promoted locals only worked for rvalue lifetime extension. We now use the same implementation for promotion across all kinds of items.
1 parent fcf4bee commit f9ed219

File tree

1 file changed

+4
-100
lines changed

1 file changed

+4
-100
lines changed

src/librustc_mir/transform/promote_consts.rs

+4-100
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use syntax::symbol::sym;
2424
use syntax_pos::{Span, DUMMY_SP};
2525

2626
use rustc_index::vec::{IndexVec, Idx};
27-
use rustc_index::bit_set::HybridBitSet;
2827
use rustc_target::spec::abi::Abi;
2928

3029
use std::cell::Cell;
@@ -35,10 +34,8 @@ use crate::transform::check_consts::{qualifs, Item, ConstKind, is_lang_panic_fn}
3534

3635
/// A `MirPass` for promotion.
3736
///
38-
/// In this case, "promotion" entails the following:
39-
/// - Extract promotable temps in `fn` and `const fn` into their own MIR bodies.
40-
/// - Extend lifetimes in `const` and `static` by removing `Drop` and `StorageDead`.
41-
/// - Emit errors if the requirements of `#[rustc_args_required_const]` are not met.
37+
/// Promotion is the extraction of promotable temps into separate MIR bodies. This pass also emits
38+
/// errors when promotion of `#[rustc_args_required_const]` arguments fails.
4239
///
4340
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
4441
/// newly created `StaticKind::Promoted`.
@@ -63,26 +60,13 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
6360

6461
let def_id = src.def_id();
6562

66-
let item = Item::new(tcx, def_id, body);
6763
let mut rpo = traversal::reverse_postorder(body);
6864
let (temps, all_candidates) = collect_temps_and_candidates(tcx, body, &mut rpo);
6965

7066
let promotable_candidates = validate_candidates(tcx, body, def_id, &temps, &all_candidates);
7167

72-
// For now, lifetime extension is done in `const` and `static`s without creating promoted
73-
// MIR fragments by removing `Drop` and `StorageDead` for each referent. However, this will
74-
// not work inside loops when they are allowed in `const`s.
75-
//
76-
// FIXME: use promoted MIR fragments everywhere?
77-
let promoted_fragments = if should_create_promoted_mir_fragments(item.const_kind) {
78-
promote_candidates(def_id, body, tcx, temps, promotable_candidates)
79-
} else {
80-
// FIXME: promote const array initializers in consts.
81-
remove_drop_and_storage_dead_on_promoted_locals(tcx, body, &promotable_candidates);
82-
IndexVec::new()
83-
};
84-
85-
self.promoted_fragments.set(promoted_fragments);
68+
let promoted = promote_candidates(def_id, body, tcx, temps, promotable_candidates);
69+
self.promoted_fragments.set(promoted);
8670
}
8771
}
8872

@@ -1188,83 +1172,3 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
11881172
should_promote={:?} feature_flag={:?}", mir_def_id, should_promote, feature_flag);
11891173
should_promote && !feature_flag
11901174
}
1191-
1192-
fn should_create_promoted_mir_fragments(const_kind: Option<ConstKind>) -> bool {
1193-
match const_kind {
1194-
Some(ConstKind::ConstFn) | None => true,
1195-
Some(ConstKind::Const) | Some(ConstKind::Static) | Some(ConstKind::StaticMut) => false,
1196-
}
1197-
}
1198-
1199-
/// In `const` and `static` everything without `StorageDead`
1200-
/// is `'static`, we don't have to create promoted MIR fragments,
1201-
/// just remove `Drop` and `StorageDead` on "promoted" locals.
1202-
fn remove_drop_and_storage_dead_on_promoted_locals(
1203-
tcx: TyCtxt<'tcx>,
1204-
body: &mut Body<'tcx>,
1205-
promotable_candidates: &[Candidate],
1206-
) {
1207-
debug!("run_pass: promotable_candidates={:?}", promotable_candidates);
1208-
1209-
// Removing `StorageDead` will cause errors for temps declared inside a loop body. For now we
1210-
// simply skip promotion if a loop exists, since loops are not yet allowed in a `const`.
1211-
//
1212-
// FIXME: Just create MIR fragments for `const`s instead of using this hackish approach?
1213-
if body.is_cfg_cyclic() {
1214-
tcx.sess.delay_span_bug(body.span, "Control-flow cycle detected in `const`");
1215-
return;
1216-
}
1217-
1218-
// The underlying local for promotion contexts like `&temp` and `&(temp.proj)`.
1219-
let mut requires_lifetime_extension = HybridBitSet::new_empty(body.local_decls.len());
1220-
1221-
promotable_candidates
1222-
.iter()
1223-
.filter_map(|c| {
1224-
match c {
1225-
Candidate::Ref(loc) => Some(loc),
1226-
Candidate::Repeat(_) | Candidate::Argument { .. } => None,
1227-
}
1228-
})
1229-
.map(|&Location { block, statement_index }| {
1230-
// FIXME: store the `Local` for each `Candidate` when it is created.
1231-
let place = match &body[block].statements[statement_index].kind {
1232-
StatementKind::Assign(box ( _, Rvalue::Ref(_, _, place))) => place,
1233-
_ => bug!("`Candidate::Ref` without corresponding assignment"),
1234-
};
1235-
1236-
match place.base {
1237-
PlaceBase::Local(local) => local,
1238-
PlaceBase::Static(_) => bug!("`Candidate::Ref` for a non-local"),
1239-
}
1240-
})
1241-
.for_each(|local| {
1242-
requires_lifetime_extension.insert(local);
1243-
});
1244-
1245-
// Remove `Drop` terminators and `StorageDead` statements for all promotable temps that require
1246-
// lifetime extension.
1247-
for block in body.basic_blocks_mut() {
1248-
block.statements.retain(|statement| {
1249-
match statement.kind {
1250-
StatementKind::StorageDead(index) => !requires_lifetime_extension.contains(index),
1251-
_ => true
1252-
}
1253-
});
1254-
let terminator = block.terminator_mut();
1255-
match &terminator.kind {
1256-
TerminatorKind::Drop {
1257-
location,
1258-
target,
1259-
..
1260-
} => {
1261-
if let Some(index) = location.as_local() {
1262-
if requires_lifetime_extension.contains(index) {
1263-
terminator.kind = TerminatorKind::Goto { target: *target };
1264-
}
1265-
}
1266-
}
1267-
_ => {}
1268-
}
1269-
}
1270-
}

0 commit comments

Comments
 (0)