Auto merge of #66642 - ecstatic-morse:promotion-in-const, r=eddyb
Create promoted MIR fragments for `const` and `static`s Resolves #65732. The previous strategy of removing `Drop` and `StorageDead` for promoted locals only worked for rvalue lifetime extension and only if no `loop`s were present. This PR applies the approach currently used for `fn` and `const fn`s to `const` and `statics`. This may have some performance impacts. r? @eddyb
This commit is contained in:
commit
bbb664a99c
5 changed files with 48 additions and 129 deletions
|
@ -24,7 +24,6 @@ use syntax::symbol::sym;
|
|||
use syntax_pos::{Span, DUMMY_SP};
|
||||
|
||||
use rustc_index::vec::{IndexVec, Idx};
|
||||
use rustc_index::bit_set::HybridBitSet;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
@ -35,10 +34,8 @@ 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.
|
||||
/// Promotion is the extraction of promotable temps into separate MIR bodies. This pass also emits
|
||||
/// errors when promotion of `#[rustc_args_required_const]` arguments fails.
|
||||
///
|
||||
/// After this pass is run, `promoted_fragments` will hold the MIR body corresponding to each
|
||||
/// newly created `StaticKind::Promoted`.
|
||||
|
@ -63,26 +60,13 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
|
|||
|
||||
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);
|
||||
let promoted = promote_candidates(def_id, body, tcx, temps, promotable_candidates);
|
||||
self.promoted_fragments.set(promoted);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1178,83 +1162,3 @@ 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
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
// ignore-tidy-linelength
|
||||
// ignore-compare-mode-nll
|
||||
#![feature(const_in_array_repeat_expressions, nll)]
|
||||
#![allow(warnings)]
|
||||
|
||||
// Some type that is not copyable.
|
||||
struct Bar;
|
||||
|
||||
const fn type_no_copy() -> Option<Bar> {
|
||||
None
|
||||
}
|
||||
|
||||
const fn type_copy() -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
fn no_copy() {
|
||||
const ARR: [Option<Bar>; 2] = [type_no_copy(); 2];
|
||||
//~^ ERROR the trait bound `std::option::Option<Bar>: std::marker::Copy` is not satisfied [E0277]
|
||||
}
|
||||
|
||||
fn copy() {
|
||||
const ARR: [u32; 2] = [type_copy(); 2];
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,23 @@
|
|||
// run-pass
|
||||
|
||||
#![allow(unused)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
|
||||
// Some type that is not copyable.
|
||||
struct Bar;
|
||||
|
||||
const fn type_no_copy() -> Option<Bar> {
|
||||
None
|
||||
}
|
||||
|
||||
const fn type_copy() -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
const _: [u32; 2] = [type_copy(); 2];
|
||||
|
||||
// This is allowed because all promotion contexts use the explicit rules for promotability when
|
||||
// inside an explicit const context.
|
||||
const _: [Option<Bar>; 2] = [type_no_copy(); 2];
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,18 @@
|
|||
#![feature(const_in_array_repeat_expressions)]
|
||||
|
||||
// Some type that is not copyable.
|
||||
struct Bar;
|
||||
|
||||
const fn no_copy() -> Option<Bar> {
|
||||
None
|
||||
}
|
||||
|
||||
const fn copy() -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _: [u32; 2] = [copy(); 2];
|
||||
let _: [Option<Bar>; 2] = [no_copy(); 2];
|
||||
//~^ ERROR the trait bound `std::option::Option<Bar>: std::marker::Copy` is not satisfied
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
error[E0277]: the trait bound `std::option::Option<Bar>: std::marker::Copy` is not satisfied
|
||||
--> $DIR/const-fns.rs:18:35
|
||||
--> $DIR/fn-call-in-non-const.rs:16:31
|
||||
|
|
||||
LL | const ARR: [Option<Bar>; 2] = [type_no_copy(); 2];
|
||||
| ^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option<Bar>`
|
||||
LL | let _: [Option<Bar>; 2] = [no_copy(); 2];
|
||||
| ^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `std::option::Option<Bar>`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<std::option::Option<T> as std::marker::Copy>
|
Loading…
Add table
Add a link
Reference in a new issue