Skip to content

Commit f205b14

Browse files
committed
compute required_consts before promotion, and add promoteds that may fail
1 parent e949b49 commit f205b14

File tree

2 files changed

+44
-24
lines changed

2 files changed

+44
-24
lines changed

compiler/rustc_mir_transform/src/lib.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,15 @@ fn mir_promoted(
343343
body.tainted_by_errors = Some(error_reported);
344344
}
345345

346+
// Collect `required_consts` *before* promotion, so if there are any consts being promoted
347+
// we still add them to the list in the outer MIR body.
348+
let mut required_consts = Vec::new();
349+
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
350+
for (bb, bb_data) in traversal::reverse_postorder(&body) {
351+
required_consts_visitor.visit_basic_block_data(bb, bb_data);
352+
}
353+
body.required_consts = required_consts;
354+
346355
// What we need to run borrowck etc.
347356
let promote_pass = promote_consts::PromoteTemps::default();
348357
pm::run_passes(
@@ -352,14 +361,6 @@ fn mir_promoted(
352361
Some(MirPhase::Analysis(AnalysisPhase::Initial)),
353362
);
354363

355-
// Promotion generates new consts; we run this after promotion to ensure they are accounted for.
356-
let mut required_consts = Vec::new();
357-
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
358-
for (bb, bb_data) in traversal::reverse_postorder(&body) {
359-
required_consts_visitor.visit_basic_block_data(bb, bb_data);
360-
}
361-
body.required_consts = required_consts;
362-
363364
let promoted = promote_pass.promoted_fragments.into_inner();
364365
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
365366
}

compiler/rustc_mir_transform/src/promote_consts.rs

+35-16
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
6161

6262
let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates);
6363

64-
let promoted = promote_candidates(body, tcx, temps, promotable_candidates);
64+
let promoted = promote_candidates(body, tcx, ccx.const_kind, temps, promotable_candidates);
6565
self.promoted_fragments.set(promoted);
6666
}
6767
}
@@ -657,15 +657,9 @@ impl<'tcx> Validator<'_, 'tcx> {
657657
}
658658
}
659659

660-
// Ideally, we'd stop here and reject the rest.
661-
// But for backward compatibility, we have to accept some promotion in const/static
662-
// initializers. Inline consts are explicitly excluded, they are more recent so we have no
663-
// backwards compatibility reason to allow more promotion inside of them.
664-
let promote_all_fn = matches!(
665-
self.const_kind,
666-
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { inline: false })
667-
);
668-
if !promote_all_fn {
660+
// Ideally, we'd stop here and reject the rest. But for backward compatibility, we have to
661+
// accept some promotion in const/static initializers.
662+
if !promote_all_fn(self.ccx.const_kind) {
669663
return Err(Unpromotable);
670664
}
671665
// Make sure the callee is a `const fn`.
@@ -687,6 +681,18 @@ impl<'tcx> Validator<'_, 'tcx> {
687681
}
688682
}
689683

684+
/// Decides whether in this context we are okay with promoting all functions (and not just
685+
/// `#[rustc_promotable]`).
686+
/// For backwards compatibility, we do that in static/const initializers.
687+
fn promote_all_fn(const_kind: Option<hir::ConstContext>) -> bool {
688+
// Inline consts are explicitly excluded, they are more recent so we have no
689+
// backwards compatibility reason to allow more promotion inside of them.
690+
matches!(
691+
const_kind,
692+
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { inline: false })
693+
)
694+
}
695+
690696
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
691697
fn validate_candidates(
692698
ccx: &ConstCx<'_, '_>,
@@ -712,6 +718,9 @@ struct Promoter<'a, 'tcx> {
712718
/// If true, all nested temps are also kept in the
713719
/// source MIR, not moved to the promoted MIR.
714720
keep_original: bool,
721+
722+
/// If true, add the new const (the promoted) to the required_consts of the parent MIR.
723+
add_to_required: bool,
715724
}
716725

717726
impl<'a, 'tcx> Promoter<'a, 'tcx> {
@@ -858,11 +867,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
858867
let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def));
859868
let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) };
860869

861-
Operand::Constant(Box::new(ConstOperand {
862-
span,
863-
user_ty: None,
864-
const_: Const::Unevaluated(uneval, ty),
865-
}))
870+
ConstOperand { span, user_ty: None, const_: Const::Unevaluated(uneval, ty) }
866871
};
867872

868873
let blocks = self.source.basic_blocks.as_mut();
@@ -898,11 +903,15 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
898903
let promoted_ref = local_decls.push(promoted_ref);
899904
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
900905

906+
let promoted_operand = promoted_operand(ref_ty, span);
907+
if self.add_to_required {
908+
self.source.required_consts.push(promoted_operand);
909+
}
901910
let promoted_ref_statement = Statement {
902911
source_info: statement.source_info,
903912
kind: StatementKind::Assign(Box::new((
904913
Place::from(promoted_ref),
905-
Rvalue::Use(promoted_operand(ref_ty, span)),
914+
Rvalue::Use(Operand::Constant(Box::new(promoted_operand))),
906915
))),
907916
};
908917
self.extra_statements.push((loc, promoted_ref_statement));
@@ -945,6 +954,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
945954
fn promote_candidates<'tcx>(
946955
body: &mut Body<'tcx>,
947956
tcx: TyCtxt<'tcx>,
957+
ccx_const_kind: Option<hir::ConstContext>,
948958
mut temps: IndexVec<Local, TempState>,
949959
candidates: Vec<Candidate>,
950960
) -> IndexVec<Promoted, Body<'tcx>> {
@@ -984,6 +994,11 @@ fn promote_candidates<'tcx>(
984994
None,
985995
body.tainted_by_errors,
986996
);
997+
// We keep `required_consts` of the new MIR body empty. All consts mentioned here have
998+
// already been added to the parent MIR's `required_consts` (that is computed before
999+
// promotion), and no matter where this promoted const ends up, our parent MIR must be
1000+
// somewhere in the reachable dependency chain so we can rely on its required consts being
1001+
// evaluated.
9871002
promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
9881003

9891004
let promoter = Promoter {
@@ -993,6 +1008,10 @@ fn promote_candidates<'tcx>(
9931008
temps: &mut temps,
9941009
extra_statements: &mut extra_statements,
9951010
keep_original: false,
1011+
// If we promote all functions, some of these promoteds could fail, so we better make
1012+
// sure they get all checked as `required_consts`. Otherwise, as an optimization we
1013+
// *don't* add the promoteds to the parent's `required_consts`.
1014+
add_to_required: promote_all_fn(ccx_const_kind),
9961015
};
9971016

9981017
let mut promoted = promoter.promote_candidate(candidate, promotions.len());

0 commit comments

Comments
 (0)