Skip to content

Commit f923b84

Browse files
committed
Auto merge of rust-lang#121557 - RalfJung:const-fn-call-promotion, r=<try>
restrict promotion of `const fn` calls We only promote them in `const`/`static` initializers, but even that is still unfortunate -- we still cannot add promoteds to required_consts. But we should add them there to make sure it's always okay to evaluate every const we encounter in a MIR body. That effort of not promoting things that can fail to evaluate is tracked in rust-lang#80619. These `const fn` calls are the last missing piece. So I propose that we do not promote const-fn calls in const when that may fail without the entire const failing, thereby completing rust-lang#80619. Unfortunately we can't just reject promoting these functions outright due to backwards compatibility. So let's see if we can find a hack that makes crater happy... For the record, this is the [crater analysis](rust-lang#80243 (comment)) from when I tried to entirely forbid this kind of promotion. It's a tiny amount of breakage and if we had a nice alternative for code like that, we could conceivably push it through... but sadly, inline const expressions are still blocked on t-lang concerns about post-monomorphization errors and we haven't yet figured out an implementation that can resolve those concerns. So we're forced to make progress via other means, such as terrible hacks like this. Attempt one: only promote calls on the "safe path" at the beginning of a MIR block. This is the path that starts at the start block and continues via gotos and calls, but stops at the first branch. If we had imposed this restriction before stabilizing `if` and `match` in `const`, this would have definitely been sufficient... EDIT: Turns out that works. :) **Here's the t-lang [nomination comment](rust-lang#121557 (comment) And here's the [FCP comment](rust-lang#121557 (comment)). r? `@oli-obk`
2 parents 6e1f7b5 + 6d23af4 commit f923b84

22 files changed

+355
-292
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,11 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
375375

376376
const PANIC_ON_ALLOC_FAIL: bool = false; // will be raised as a proper error
377377

378+
#[inline]
379+
fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
380+
true
381+
}
382+
378383
#[inline(always)]
379384
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
380385
matches!(ecx.machine.check_alignment, CheckAlignment::Error)

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -822,15 +822,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
822822
self.stack_mut().push(frame);
823823

824824
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
825-
if M::POST_MONO_CHECKS {
826-
for &const_ in &body.required_consts {
827-
let c = self
828-
.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
829-
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
830-
err.emit_note(*self.tcx);
831-
err
832-
})?;
833-
}
825+
for &const_ in &body.required_consts {
826+
let c =
827+
self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
828+
c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
829+
err.emit_note(*self.tcx);
830+
err
831+
})?;
834832
}
835833

836834
// done
@@ -1179,8 +1177,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11791177
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
11801178
M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
11811179
let const_val = val.eval(*ecx.tcx, ecx.param_env, span).map_err(|err| {
1182-
// FIXME: somehow this is reachable even when POST_MONO_CHECKS is on.
1183-
// Are we not always populating `required_consts`?
1180+
if M::all_required_consts_are_checked(self)
1181+
&& !matches!(err, ErrorHandled::TooGeneric(..))
1182+
{
1183+
// Looks like the const is not captued by `required_consts`, that's bad.
1184+
bug!("interpret const eval failure of {val:?} which is not in required_consts");
1185+
}
11841186
err.emit_note(*ecx.tcx);
11851187
err
11861188
})?;

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,9 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
140140
/// Should the machine panic on allocation failures?
141141
const PANIC_ON_ALLOC_FAIL: bool;
142142

143-
/// Should post-monomorphization checks be run when a stack frame is pushed?
144-
const POST_MONO_CHECKS: bool = true;
143+
/// Determines whether `eval_mir_constant` can never fail because all required consts have
144+
/// already been checked before.
145+
fn all_required_consts_are_checked(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
145146

146147
/// Whether memory accesses should be alignment-checked.
147148
fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;

compiler/rustc_middle/src/mir/consts.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,24 @@ impl<'tcx> Const<'tcx> {
238238
}
239239
}
240240

241+
/// Determines whether we need to add this const to `required_consts`. This is the case if and
242+
/// only if evaluating it may error.
243+
#[inline]
244+
pub fn is_required_const(&self) -> bool {
245+
match self {
246+
Const::Ty(c) => match c.kind() {
247+
ty::ConstKind::Value(_) => false, // already a value, cannot error
248+
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) => true, // these are errors or could be replaced by errors
249+
_ => bug!(
250+
"only ConstKind::Param/Value/Error should be encountered here, got {:#?}",
251+
c
252+
),
253+
},
254+
Const::Unevaluated(..) => true,
255+
Const::Val(..) => false, // already a value, cannot error
256+
}
257+
}
258+
241259
#[inline]
242260
pub fn try_to_scalar(self) -> Option<Scalar> {
243261
match self {

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -902,6 +902,12 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
902902
type MemoryKind = !;
903903
const PANIC_ON_ALLOC_FAIL: bool = true;
904904

905+
#[inline(always)]
906+
fn all_required_consts_are_checked(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
907+
// We want to just eval random consts in the program, so `eval_mir_const` can fail.
908+
false
909+
}
910+
905911
#[inline(always)]
906912
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
907913
false // no reason to enforce alignment

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -706,17 +706,7 @@ impl<'tcx> Inliner<'tcx> {
706706
});
707707

708708
// Copy only unevaluated constants from the callee_body into the caller_body.
709-
// Although we are only pushing `ConstKind::Unevaluated` consts to
710-
// `required_consts`, here we may not only have `ConstKind::Unevaluated`
711-
// because we are calling `instantiate_and_normalize_erasing_regions`.
712-
caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter(
713-
|&ct| match ct.const_ {
714-
Const::Ty(_) => {
715-
bug!("should never encounter ty::UnevaluatedConst in `required_consts`")
716-
}
717-
Const::Val(..) | Const::Unevaluated(..) => true,
718-
},
719-
));
709+
caller_body.required_consts.extend(callee_body.required_consts);
720710
}
721711

722712
fn make_call_args(

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ 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.
346348
let mut required_consts = Vec::new();
347349
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
348350
for (bb, bb_data) in traversal::reverse_postorder(&body) {

0 commit comments

Comments
 (0)