Skip to content

Commit b2b617a

Browse files
committed
compute required_consts before promotion, and add promoteds that may fail
1 parent 7183fa0 commit b2b617a

File tree

4 files changed

+44
-68
lines changed

4 files changed

+44
-68
lines changed

compiler/rustc_mir_transform/src/lib.rs

+9-8
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,15 @@ fn mir_promoted(
333333
body.tainted_by_errors = Some(error_reported);
334334
}
335335

336+
// Collect `required_consts` *before* promotion, so if there are any consts being promoted
337+
// we still add them to the list in the outer MIR body.
338+
let mut required_consts = Vec::new();
339+
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
340+
for (bb, bb_data) in traversal::reverse_postorder(&body) {
341+
required_consts_visitor.visit_basic_block_data(bb, bb_data);
342+
}
343+
body.required_consts = required_consts;
344+
336345
// What we need to run borrowck etc.
337346
let promote_pass = promote_consts::PromoteTemps::default();
338347
pm::run_passes(
@@ -342,14 +351,6 @@ fn mir_promoted(
342351
Some(MirPhase::Analysis(AnalysisPhase::Initial)),
343352
);
344353

345-
// Promotion generates new consts; we run this after promotion to ensure they are accounted for.
346-
let mut required_consts = Vec::new();
347-
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
348-
for (bb, bb_data) in traversal::reverse_postorder(&body) {
349-
required_consts_visitor.visit_basic_block_data(bb, bb_data);
350-
}
351-
body.required_consts = required_consts;
352-
353354
let promoted = promote_pass.promoted_fragments.into_inner();
354355
(tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
355356
}

compiler/rustc_mir_transform/src/promote_consts.rs

+34-15
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,6 @@ impl<'tcx> Validator<'_, 'tcx> {
688688
}
689689
}
690690

691-
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
692691
fn validate_candidates(
693692
ccx: &ConstCx<'_, '_>,
694693
temps: &mut IndexSlice<Local, TempState>,
@@ -713,6 +712,10 @@ struct Promoter<'a, 'tcx> {
713712
/// If true, all nested temps are also kept in the
714713
/// source MIR, not moved to the promoted MIR.
715714
keep_original: bool,
715+
716+
/// If true, add the new const (the promoted) to the required_consts of the parent MIR.
717+
/// This is initially false and then set by the visitor when it encounters a `Call` terminator.
718+
add_to_required: bool,
716719
}
717720

718721
impl<'a, 'tcx> Promoter<'a, 'tcx> {
@@ -815,6 +818,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
815818
TerminatorKind::Call {
816819
mut func, mut args, call_source: desugar, fn_span, ..
817820
} => {
821+
// This promoted involves a function call, so it may fail to evaluate.
822+
// Let's make sure it is added to `required_consts` so that that failure cannot get lost.
823+
self.add_to_required = true;
824+
818825
self.visit_operand(&mut func, loc);
819826
for arg in &mut args {
820827
self.visit_operand(&mut arg.node, loc);
@@ -849,7 +856,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
849856

850857
fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) -> Body<'tcx> {
851858
let def = self.source.source.def_id();
852-
let mut rvalue = {
859+
let (mut rvalue, promoted_op) = {
853860
let promoted = &mut self.promoted;
854861
let promoted_id = Promoted::new(next_promoted_id);
855862
let tcx = self.tcx;
@@ -859,11 +866,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
859866
let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def));
860867
let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) };
861868

862-
Operand::Constant(Box::new(ConstOperand {
863-
span,
864-
user_ty: None,
865-
const_: Const::Unevaluated(uneval, ty),
866-
}))
869+
ConstOperand { span, user_ty: None, const_: Const::Unevaluated(uneval, ty) }
867870
};
868871

869872
let blocks = self.source.basic_blocks.as_mut();
@@ -896,22 +899,26 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
896899
let promoted_ref = local_decls.push(promoted_ref);
897900
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
898901

902+
let promoted_operand = promoted_operand(ref_ty, span);
899903
let promoted_ref_statement = Statement {
900904
source_info: statement.source_info,
901905
kind: StatementKind::Assign(Box::new((
902906
Place::from(promoted_ref),
903-
Rvalue::Use(promoted_operand(ref_ty, span)),
907+
Rvalue::Use(Operand::Constant(Box::new(promoted_operand))),
904908
))),
905909
};
906910
self.extra_statements.push((loc, promoted_ref_statement));
907911

908-
Rvalue::Ref(
909-
tcx.lifetimes.re_erased,
910-
*borrow_kind,
911-
Place {
912-
local: mem::replace(&mut place.local, promoted_ref),
913-
projection: List::empty(),
914-
},
912+
(
913+
Rvalue::Ref(
914+
tcx.lifetimes.re_erased,
915+
*borrow_kind,
916+
Place {
917+
local: mem::replace(&mut place.local, promoted_ref),
918+
projection: List::empty(),
919+
},
920+
),
921+
promoted_operand,
915922
)
916923
};
917924

@@ -923,6 +930,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
923930

924931
let span = self.promoted.span;
925932
self.assign(RETURN_PLACE, rvalue, span);
933+
934+
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
935+
if self.add_to_required {
936+
self.source.required_consts.push(promoted_op);
937+
}
938+
926939
self.promoted
927940
}
928941
}
@@ -982,6 +995,11 @@ fn promote_candidates<'tcx>(
982995
None,
983996
body.tainted_by_errors,
984997
);
998+
// We keep `required_consts` of the new MIR body empty. All consts mentioned here have
999+
// already been added to the parent MIR's `required_consts` (that is computed before
1000+
// promotion), and no matter where this promoted const ends up, our parent MIR must be
1001+
// somewhere in the reachable dependency chain so we can rely on its required consts being
1002+
// evaluated.
9851003
promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
9861004

9871005
let promoter = Promoter {
@@ -991,6 +1009,7 @@ fn promote_candidates<'tcx>(
9911009
temps: &mut temps,
9921010
extra_statements: &mut extra_statements,
9931011
keep_original: false,
1012+
add_to_required: false,
9941013
};
9951014

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

tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs

-4
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ static SOME_STRUCT: &SomeStruct = &SomeStruct {
2828
f: &id,
2929
//~^ ERROR mismatched types
3030
//~| ERROR mismatched types
31-
//~| ERROR mismatched types
32-
//~| ERROR mismatched types
33-
//~| ERROR implementation of `Fn` is not general enough
34-
//~| ERROR implementation of `Fn` is not general enough
3531
//~| ERROR implementation of `Fn` is not general enough
3632
//~| ERROR implementation of `Fn` is not general enough
3733
};

tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr

+1-41
Original file line numberDiff line numberDiff line change
@@ -34,45 +34,5 @@ LL | f: &id,
3434
= note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
3535
= note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
3636

37-
error[E0308]: mismatched types
38-
--> $DIR/rfc1623-2.rs:28:8
39-
|
40-
LL | f: &id,
41-
| ^^^ one type is more general than the other
42-
|
43-
= note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)`
44-
found trait `Fn(&Foo<'_>)`
45-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
46-
47-
error[E0308]: mismatched types
48-
--> $DIR/rfc1623-2.rs:28:8
49-
|
50-
LL | f: &id,
51-
| ^^^ one type is more general than the other
52-
|
53-
= note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)`
54-
found trait `Fn(&Foo<'_>)`
55-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
56-
57-
error: implementation of `FnOnce` is not general enough
58-
--> $DIR/rfc1623-2.rs:28:8
59-
|
60-
LL | f: &id,
61-
| ^^^ implementation of `FnOnce` is not general enough
62-
|
63-
= note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `FnOnce<(&'1 Foo<'b>,)>`, for any lifetime `'1`...
64-
= note: ...but it actually implements `FnOnce<(&'2 Foo<'_>,)>`, for some specific lifetime `'2`
65-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
66-
67-
error: implementation of `FnOnce` is not general enough
68-
--> $DIR/rfc1623-2.rs:28:8
69-
|
70-
LL | f: &id,
71-
| ^^^ implementation of `FnOnce` is not general enough
72-
|
73-
= note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `FnOnce<(&'a Foo<'1>,)>`, for any lifetime `'1`...
74-
= note: ...but it actually implements `FnOnce<(&Foo<'2>,)>`, for some specific lifetime `'2`
75-
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
76-
77-
error: aborting due to 8 previous errors
37+
error: aborting due to 4 previous errors
7838

0 commit comments

Comments
 (0)