Skip to content

Commit 6d312d7

Browse files
committed
MIR required_consts, mentioned_items: ensure we do not forget to fill these lists
1 parent 70591dc commit 6d312d7

File tree

11 files changed

+113
-44
lines changed

11 files changed

+113
-44
lines changed

compiler/rustc_const_eval/src/interpret/eval_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
888888
body: &'tcx mir::Body<'tcx>,
889889
) -> InterpResult<'tcx> {
890890
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
891-
for &const_ in &body.required_consts {
891+
for &const_ in body.required_consts() {
892892
let c =
893893
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
894894
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {

compiler/rustc_middle/src/mir/mod.rs

+42-6
Original file line numberDiff line numberDiff line change
@@ -383,23 +383,25 @@ pub struct Body<'tcx> {
383383

384384
/// Constants that are required to evaluate successfully for this MIR to be well-formed.
385385
/// We hold in this field all the constants we are not able to evaluate yet.
386+
/// `None` indicates that the list has not been computed yet.
386387
///
387388
/// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a
388389
/// function have successfully evaluated if the function ever gets executed at runtime.
389-
pub required_consts: Vec<ConstOperand<'tcx>>,
390+
pub required_consts: Option<Vec<ConstOperand<'tcx>>>,
390391

391392
/// Further items that were mentioned in this function and hence *may* become monomorphized,
392393
/// depending on optimizations. We use this to avoid optimization-dependent compile errors: the
393394
/// collector recursively traverses all "mentioned" items and evaluates all their
394395
/// `required_consts`.
396+
/// `None` indicates that the list has not been computed yet.
395397
///
396398
/// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee.
397399
/// All that's relevant is that this set is optimization-level-independent, and that it includes
398400
/// everything that the collector would consider "used". (For example, we currently compute this
399401
/// set after drop elaboration, so some drop calls that can never be reached are not considered
400402
/// "mentioned".) See the documentation of `CollectionMode` in
401403
/// `compiler/rustc_monomorphize/src/collector.rs` for more context.
402-
pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
404+
pub mentioned_items: Option<Vec<Spanned<MentionedItem<'tcx>>>>,
403405

404406
/// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
405407
///
@@ -477,8 +479,8 @@ impl<'tcx> Body<'tcx> {
477479
spread_arg: None,
478480
var_debug_info,
479481
span,
480-
required_consts: Vec::new(),
481-
mentioned_items: Vec::new(),
482+
required_consts: None,
483+
mentioned_items: None,
482484
is_polymorphic: false,
483485
injection_phase: None,
484486
tainted_by_errors,
@@ -507,8 +509,8 @@ impl<'tcx> Body<'tcx> {
507509
arg_count: 0,
508510
spread_arg: None,
509511
span: DUMMY_SP,
510-
required_consts: Vec::new(),
511-
mentioned_items: Vec::new(),
512+
required_consts: None,
513+
mentioned_items: None,
512514
var_debug_info: Vec::new(),
513515
is_polymorphic: false,
514516
injection_phase: None,
@@ -785,6 +787,40 @@ impl<'tcx> Body<'tcx> {
785787
// No inlined `SourceScope`s, or all of them were `#[track_caller]`.
786788
caller_location.unwrap_or_else(|| from_span(source_info.span))
787789
}
790+
791+
#[track_caller]
792+
pub fn set_required_consts(&mut self, required_consts: Vec<ConstOperand<'tcx>>) {
793+
assert!(
794+
self.required_consts.is_none(),
795+
"required_consts for {:?} have already been set",
796+
self.source.def_id()
797+
);
798+
self.required_consts = Some(required_consts);
799+
}
800+
#[track_caller]
801+
pub fn required_consts(&self) -> &[ConstOperand<'tcx>] {
802+
match &self.required_consts {
803+
Some(l) => l,
804+
None => panic!("required_consts for {:?} have not yet been set", self.source.def_id()),
805+
}
806+
}
807+
808+
#[track_caller]
809+
pub fn set_mentioned_items(&mut self, mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>) {
810+
assert!(
811+
self.mentioned_items.is_none(),
812+
"mentioned_items for {:?} have already been set",
813+
self.source.def_id()
814+
);
815+
self.mentioned_items = Some(mentioned_items);
816+
}
817+
#[track_caller]
818+
pub fn mentioned_items(&self) -> &[Spanned<MentionedItem<'tcx>>] {
819+
match &self.mentioned_items {
820+
Some(l) => l,
821+
None => panic!("mentioned_items for {:?} have not yet been set", self.source.def_id()),
822+
}
823+
}
788824
}
789825

790826
impl<'tcx> Index<BasicBlock> for Body<'tcx> {

compiler/rustc_middle/src/mir/visit.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1066,9 +1066,11 @@ macro_rules! super_body {
10661066

10671067
$self.visit_span($(& $mutability)? $body.span);
10681068

1069-
for const_ in &$($mutability)? $body.required_consts {
1070-
let location = Location::START;
1071-
$self.visit_const_operand(const_, location);
1069+
if let Some(required_consts) = &$($mutability)? $body.required_consts {
1070+
for const_ in required_consts {
1071+
let location = Location::START;
1072+
$self.visit_const_operand(const_, location);
1073+
}
10721074
}
10731075
}
10741076
}

compiler/rustc_mir_build/src/build/custom/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ pub(super) fn build_custom_mir<'tcx>(
5454
spread_arg: None,
5555
var_debug_info: Vec::new(),
5656
span,
57-
required_consts: Vec::new(),
58-
mentioned_items: Vec::new(),
57+
required_consts: None,
58+
mentioned_items: None,
5959
is_polymorphic: false,
6060
tainted_by_errors: None,
6161
injection_phase: None,

compiler/rustc_mir_transform/src/inline.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,8 @@ impl<'tcx> Inliner<'tcx> {
744744
// Copy required constants from the callee_body into the caller_body. Although we are only
745745
// pushing unevaluated consts to `required_consts`, here they may have been evaluated
746746
// because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again.
747-
caller_body.required_consts.extend(
748-
callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()),
747+
caller_body.required_consts.as_mut().unwrap().extend(
748+
callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()),
749749
);
750750
// Now that we incorporated the callee's `required_consts`, we can remove the callee from
751751
// `mentioned_items` -- but we have to take their `mentioned_items` in return. This does
@@ -755,12 +755,11 @@ impl<'tcx> Inliner<'tcx> {
755755
// We need to reconstruct the `required_item` for the callee so that we can find and
756756
// remove it.
757757
let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx));
758-
if let Some(idx) =
759-
caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
760-
{
758+
let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap();
759+
if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) {
761760
// We found the callee, so remove it and add its items instead.
762-
caller_body.mentioned_items.remove(idx);
763-
caller_body.mentioned_items.extend(callee_body.mentioned_items);
761+
caller_mentioned_items.remove(idx);
762+
caller_mentioned_items.extend(callee_body.mentioned_items());
764763
} else {
765764
// If we can't find the callee, there's no point in adding its items. Probably it
766765
// already got removed by being inlined elsewhere in the same function, so we already

compiler/rustc_mir_transform/src/lib.rs

+24-12
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,10 @@ use rustc_hir::def::DefKind;
2828
use rustc_hir::def_id::LocalDefId;
2929
use rustc_hir::intravisit::{self, Visitor};
3030
use rustc_index::IndexVec;
31-
use rustc_middle::mir::visit::Visitor as _;
3231
use rustc_middle::mir::{
33-
traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs,
34-
LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue,
35-
SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK,
32+
AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl,
33+
MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
34+
Statement, StatementKind, TerminatorKind, START_BLOCK,
3635
};
3736
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
3837
use rustc_middle::util::Providers;
@@ -339,12 +338,15 @@ fn mir_promoted(
339338

340339
// Collect `required_consts` *before* promotion, so if there are any consts being promoted
341340
// we still add them to the list in the outer MIR body.
342-
let mut required_consts = Vec::new();
343-
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
344-
for (bb, bb_data) in traversal::reverse_postorder(&body) {
345-
required_consts_visitor.visit_basic_block_data(bb, bb_data);
341+
RequiredConstsVisitor::compute_required_consts(&mut body);
342+
// If this has an associated by-move async closure body, that doesn't get run through these
343+
// passes itself, it gets "tagged along" by the pass manager. `RequiredConstsVisitor` is not
344+
// a regular pass so we have to also apply it manually to the other body.
345+
if let Some(coroutine) = body.coroutine.as_mut() {
346+
if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
347+
RequiredConstsVisitor::compute_required_consts(by_move_body);
348+
}
346349
}
347-
body.required_consts = required_consts;
348350

349351
// What we need to run borrowck etc.
350352
let promote_pass = promote_consts::PromoteTemps::default();
@@ -561,9 +563,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
561563
tcx,
562564
body,
563565
&[
564-
// Before doing anything, remember which items are being mentioned so that the set of items
565-
// visited does not depend on the optimization level.
566-
&mentioned_items::MentionedItems,
567566
// Add some UB checks before any UB gets optimized away.
568567
&check_alignment::CheckAlignment,
569568
// Before inlining: trim down MIR with passes to reduce inlining work.
@@ -657,6 +656,19 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
657656
return body;
658657
}
659658

659+
// Before doing anything, remember which items are being mentioned so that the set of items
660+
// visited does not depend on the optimization level.
661+
// We do not use `run_passes` for this as that might skip the pass if `injection_phase` is set.
662+
mentioned_items::MentionedItems.run_pass(tcx, &mut body);
663+
// If this has an associated by-move async closure body, that doesn't get run through these
664+
// passes itself, it gets "tagged along" by the pass manager. Since we're not using the pass
665+
// manager we have to do this by hand.
666+
if let Some(coroutine) = body.coroutine.as_mut() {
667+
if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
668+
mentioned_items::MentionedItems.run_pass(tcx, by_move_body);
669+
}
670+
}
671+
660672
// If `mir_drops_elaborated_and_const_checked` found that the current body has unsatisfiable
661673
// predicates, it will shrink the MIR to a single `unreachable` terminator.
662674
// More generally, if MIR is a lone `unreachable`, there is nothing to optimize.

compiler/rustc_mir_transform/src/mentioned_items.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ impl<'tcx> MirPass<'tcx> for MentionedItems {
2323
}
2424

2525
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
26-
debug_assert!(body.mentioned_items.is_empty());
2726
let mut mentioned_items = Vec::new();
2827
MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
29-
body.mentioned_items = mentioned_items;
28+
body.set_mentioned_items(mentioned_items);
3029
}
3130
}
3231

compiler/rustc_mir_transform/src/promote_consts.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,9 @@ struct Promoter<'a, 'tcx> {
702702
temps: &'a mut IndexVec<Local, TempState>,
703703
extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
704704

705+
/// Used to assemble the required_consts list while building the promoted.
706+
required_consts: Vec<ConstOperand<'tcx>>,
707+
705708
/// If true, all nested temps are also kept in the
706709
/// source MIR, not moved to the promoted MIR.
707710
keep_original: bool,
@@ -924,11 +927,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
924927
let span = self.promoted.span;
925928
self.assign(RETURN_PLACE, rvalue, span);
926929

927-
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
930+
// Now that we did promotion, we know whether we'll want to add this to `required_consts` of
931+
// the surrounding MIR body.
928932
if self.add_to_required {
929-
self.source.required_consts.push(promoted_op);
933+
self.source.required_consts.as_mut().unwrap().push(promoted_op);
930934
}
931935

936+
self.promoted.set_required_consts(self.required_consts);
937+
932938
self.promoted
933939
}
934940
}
@@ -947,7 +953,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
947953

948954
fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
949955
if constant.const_.is_required_const() {
950-
self.promoted.required_consts.push(*constant);
956+
self.required_consts.push(*constant);
951957
}
952958

953959
// Skipping `super_constant` as the visitor is otherwise only looking for locals.
@@ -1011,9 +1017,9 @@ fn promote_candidates<'tcx>(
10111017
extra_statements: &mut extra_statements,
10121018
keep_original: false,
10131019
add_to_required: false,
1020+
required_consts: Vec::new(),
10141021
};
10151022

1016-
// `required_consts` of the promoted itself gets filled while building the MIR body.
10171023
let mut promoted = promoter.promote_candidate(candidate, promotions.len());
10181024
promoted.source.promoted = Some(promotions.next_index());
10191025
promotions.push(promoted);

compiler/rustc_mir_transform/src/required_consts.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
use rustc_middle::mir::visit::Visitor;
2-
use rustc_middle::mir::{ConstOperand, Location};
2+
use rustc_middle::mir::{traversal, Body, ConstOperand, Location};
33

44
pub struct RequiredConstsVisitor<'a, 'tcx> {
55
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
66
}
77

88
impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
9-
pub fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
9+
fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
1010
RequiredConstsVisitor { required_consts }
1111
}
12+
13+
pub fn compute_required_consts(body: &mut Body<'tcx>) {
14+
let mut required_consts = Vec::new();
15+
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
16+
for (bb, bb_data) in traversal::reverse_postorder(&body) {
17+
required_consts_visitor.visit_basic_block_data(bb, bb_data);
18+
}
19+
body.set_required_consts(required_consts);
20+
}
1221
}
1322

1423
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {

compiler/rustc_mir_transform/src/shim.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ fn new_body<'tcx>(
305305
arg_count: usize,
306306
span: Span,
307307
) -> Body<'tcx> {
308-
Body::new(
308+
let mut body = Body::new(
309309
source,
310310
basic_blocks,
311311
IndexVec::from_elem_n(
@@ -326,7 +326,10 @@ fn new_body<'tcx>(
326326
None,
327327
// FIXME(compiler-errors): is this correct?
328328
None,
329-
)
329+
);
330+
// Shims do not directly mention any consts.
331+
body.set_required_consts(Vec::new());
332+
body
330333
}
331334

332335
pub struct DropShimElaborator<'a, 'tcx> {
@@ -969,13 +972,16 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
969972
};
970973

971974
let source = MirSource::item(ctor_id);
972-
let body = new_body(
975+
let mut body = new_body(
973976
source,
974977
IndexVec::from_elem_n(start_block, 1),
975978
local_decls,
976979
sig.inputs().len(),
977980
span,
978981
);
982+
// A constructor doesn't mention any other items (and we don't run the usual optimization passes
983+
// so this would otherwise not get filled).
984+
body.set_mentioned_items(Vec::new());
979985

980986
crate::pass_manager::dump_mir_for_phase_change(tcx, &body);
981987

compiler/rustc_monomorphize/src/collector.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1229,15 +1229,15 @@ fn collect_items_of_instance<'tcx>(
12291229

12301230
// Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
12311231
// them errors.
1232-
for const_op in &body.required_consts {
1232+
for const_op in body.required_consts() {
12331233
if let Some(val) = collector.eval_constant(const_op) {
12341234
collect_const_value(tcx, val, mentioned_items);
12351235
}
12361236
}
12371237

12381238
// Always gather mentioned items. We try to avoid processing items that we have already added to
12391239
// `used_items` above.
1240-
for item in &body.mentioned_items {
1240+
for item in body.mentioned_items() {
12411241
if !collector.used_mentioned_items.contains(&item.node) {
12421242
let item_mono = collector.monomorphize(item.node);
12431243
visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items);

0 commit comments

Comments
 (0)