Skip to content

Commit aa51aec

Browse files
committed
compute required_consts before promotion, and add promoteds that may fail
1 parent 9b0ef4c commit aa51aec

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
@@ -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

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

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

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

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

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

868871
let blocks = self.source.basic_blocks.as_mut();
@@ -898,22 +901,26 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
898901
let promoted_ref = local_decls.push(promoted_ref);
899902
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
900903

904+
let promoted_operand = promoted_operand(ref_ty, span);
901905
let promoted_ref_statement = Statement {
902906
source_info: statement.source_info,
903907
kind: StatementKind::Assign(Box::new((
904908
Place::from(promoted_ref),
905-
Rvalue::Use(promoted_operand(ref_ty, span)),
909+
Rvalue::Use(Operand::Constant(Box::new(promoted_operand))),
906910
))),
907911
};
908912
self.extra_statements.push((loc, promoted_ref_statement));
909913

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

@@ -925,6 +932,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
925932

926933
let span = self.promoted.span;
927934
self.assign(RETURN_PLACE, rvalue, span);
935+
936+
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
937+
if self.add_to_required {
938+
self.source.required_consts.push(promoted_op);
939+
}
940+
928941
self.promoted
929942
}
930943
}
@@ -984,6 +997,11 @@ fn promote_candidates<'tcx>(
984997
None,
985998
body.tainted_by_errors,
986999
);
1000+
// We keep `required_consts` of the new MIR body empty. All consts mentioned here have
1001+
// already been added to the parent MIR's `required_consts` (that is computed before
1002+
// promotion), and no matter where this promoted const ends up, our parent MIR must be
1003+
// somewhere in the reachable dependency chain so we can rely on its required consts being
1004+
// evaluated.
9871005
promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
9881006

9891007
let promoter = Promoter {
@@ -993,6 +1011,7 @@ fn promote_candidates<'tcx>(
9931011
temps: &mut temps,
9941012
extra_statements: &mut extra_statements,
9951013
keep_original: false,
1014+
add_to_required: false,
9961015
};
9971016

9981017
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 `FnOnce` is not general enough
34-
//~| ERROR implementation of `FnOnce` is not general enough
3531
//~| ERROR implementation of `FnOnce` is not general enough
3632
//~| ERROR implementation of `FnOnce` is not general enough
3733
};

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

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

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

8040
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)