Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 97b4461

Browse files
committedMar 23, 2024
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 c3b05c6 + 85433c4 commit 97b4461

20 files changed

+343
-295
lines changed
 

‎compiler/rustc_const_eval/src/const_eval/dummy_machine.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ impl<'mir, 'tcx: 'mir> interpret::Machine<'mir, 'tcx> for DummyMachine {
4646
type MemoryKind = !;
4747
const PANIC_ON_ALLOC_FAIL: bool = true;
4848

49+
// We want to just eval random consts in the program, so `eval_mir_const` can fail.
50+
const ALL_CONSTS_ARE_PRECHECKED: bool = false;
51+
4952
#[inline(always)]
5053
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
5154
false // no reason to enforce alignment

‎compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 11 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,10 @@ 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_CONSTS_ARE_PRECHECKED && !matches!(err, ErrorHandled::TooGeneric(..)) {
1181+
// Looks like the const is not captued by `required_consts`, that's bad.
1182+
bug!("interpret const eval failure of {val:?} which is not in required_consts");
1183+
}
11841184
err.emit_note(*ecx.tcx);
11851185
err
11861186
})?;

‎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+
const ALL_CONSTS_ARE_PRECHECKED: bool = true;
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: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,20 @@ 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+
_ => true,
249+
},
250+
Const::Val(..) => false, // already a value, cannot error
251+
Const::Unevaluated(..) => true,
252+
}
253+
}
254+
241255
#[inline]
242256
pub fn try_to_scalar(self) -> Option<Scalar> {
243257
match self {

‎compiler/rustc_mir_transform/src/inline.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -706,18 +706,12 @@ impl<'tcx> Inliner<'tcx> {
706706
kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) },
707707
});
708708

709-
// Copy only unevaluated constants from the callee_body into the caller_body.
710-
// Although we are only pushing `ConstKind::Unevaluated` consts to
711-
// `required_consts`, here we may not only have `ConstKind::Unevaluated`
712-
// because we are calling `instantiate_and_normalize_erasing_regions`.
713-
caller_body.required_consts.extend(callee_body.required_consts.iter().copied().filter(
714-
|&ct| match ct.const_ {
715-
Const::Ty(_) => {
716-
bug!("should never encounter ty::UnevaluatedConst in `required_consts`")
717-
}
718-
Const::Val(..) | Const::Unevaluated(..) => true,
719-
},
720-
));
709+
// Copy required constants from the callee_body into the caller_body. Although we are only
710+
// pushing unevaluated consts to `required_consts`, here they may have been evaluated
711+
// because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again.
712+
caller_body.required_consts.extend(
713+
callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()),
714+
);
721715
// Now that we incorporated the callee's `required_consts`, we can remove the callee from
722716
// `mentioned_items` -- but we have to take their `mentioned_items` in return. This does
723717
// some extra work here to save the monomorphization collector work later. It helps a lot,
@@ -733,8 +727,9 @@ impl<'tcx> Inliner<'tcx> {
733727
caller_body.mentioned_items.remove(idx);
734728
caller_body.mentioned_items.extend(callee_body.mentioned_items);
735729
} else {
736-
// If we can't find the callee, there's no point in adding its items.
737-
// Probably it already got removed by being inlined elsewhere in the same function.
730+
// If we can't find the callee, there's no point in adding its items. Probably it
731+
// already got removed by being inlined elsewhere in the same function, so we already
732+
// took its items.
738733
}
739734
}
740735

‎compiler/rustc_mir_transform/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ fn mir_promoted(
344344
body.tainted_by_errors = Some(error_reported);
345345
}
346346

347+
// Collect `required_consts` *before* promotion, so if there are any consts being promoted
348+
// we still add them to the list in the outer MIR body.
347349
let mut required_consts = Vec::new();
348350
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
349351
for (bb, bb_data) in traversal::reverse_postorder(&body) {

‎compiler/rustc_mir_transform/src/promote_consts.rs

Lines changed: 117 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//! move analysis runs after promotion on broken MIR.
1414
1515
use either::{Left, Right};
16+
use rustc_data_structures::fx::FxHashSet;
1617
use rustc_hir as hir;
1718
use rustc_middle::mir;
1819
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -175,6 +176,12 @@ fn collect_temps_and_candidates<'tcx>(
175176
struct Validator<'a, 'tcx> {
176177
ccx: &'a ConstCx<'a, 'tcx>,
177178
temps: &'a mut IndexSlice<Local, TempState>,
179+
/// For backwards compatibility, we are promoting function calls in `const`/`static`
180+
/// initializers. But we want to avoid evaluating code that might panic and that otherwise would
181+
/// not have been evaluated, so we only promote such calls in basic blocks that are guaranteed
182+
/// to execute. In other words, we only promote such calls in basic blocks that are definitely
183+
/// not dead code. Here we cache the result of computing that set of basic blocks.
184+
promotion_safe_blocks: Option<FxHashSet<BasicBlock>>,
178185
}
179186

180187
impl<'a, 'tcx> std::ops::Deref for Validator<'a, 'tcx> {
@@ -260,7 +267,9 @@ impl<'tcx> Validator<'_, 'tcx> {
260267
self.validate_rvalue(rhs)
261268
}
262269
Right(terminator) => match &terminator.kind {
263-
TerminatorKind::Call { func, args, .. } => self.validate_call(func, args),
270+
TerminatorKind::Call { func, args, .. } => {
271+
self.validate_call(func, args, loc.block)
272+
}
264273
TerminatorKind::Yield { .. } => Err(Unpromotable),
265274
kind => {
266275
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
@@ -587,53 +596,103 @@ impl<'tcx> Validator<'_, 'tcx> {
587596
Ok(())
588597
}
589598

599+
/// Computes the sets of blocks of this MIR that are definitely going to be executed
600+
/// if the function returns successfully. That makes it safe to promote calls in them
601+
/// that might fail.
602+
fn promotion_safe_blocks(body: &mir::Body<'tcx>) -> FxHashSet<BasicBlock> {
603+
let mut safe_blocks = FxHashSet::default();
604+
let mut safe_block = START_BLOCK;
605+
loop {
606+
safe_blocks.insert(safe_block);
607+
// Let's see if we can find another safe block.
608+
safe_block = match body.basic_blocks[safe_block].terminator().kind {
609+
TerminatorKind::Goto { target } => target,
610+
TerminatorKind::Call { target: Some(target), .. }
611+
| TerminatorKind::Drop { target, .. } => {
612+
// This calls a function or the destructor. `target` does not get executed if
613+
// the callee loops or panics. But in both cases the const already fails to
614+
// evaluate, so we are fine considering `target` a safe block for promotion.
615+
target
616+
}
617+
TerminatorKind::Assert { target, .. } => {
618+
// Similar to above, we only consider successful execution.
619+
target
620+
}
621+
_ => {
622+
// No next safe block.
623+
break;
624+
}
625+
};
626+
}
627+
safe_blocks
628+
}
629+
630+
/// Returns whether the block is "safe" for promotion, which means it cannot be dead code.
631+
/// We use this to avoid promoting operations that can fail in dead code.
632+
fn is_promotion_safe_block(&mut self, block: BasicBlock) -> bool {
633+
let body = self.body;
634+
let safe_blocks =
635+
self.promotion_safe_blocks.get_or_insert_with(|| Self::promotion_safe_blocks(body));
636+
safe_blocks.contains(&block)
637+
}
638+
590639
fn validate_call(
591640
&mut self,
592641
callee: &Operand<'tcx>,
593642
args: &[Spanned<Operand<'tcx>>],
643+
block: BasicBlock,
594644
) -> Result<(), Unpromotable> {
645+
// Validate the operands. If they fail, there's no question -- we cannot promote.
646+
self.validate_operand(callee)?;
647+
for arg in args {
648+
self.validate_operand(&arg.node)?;
649+
}
650+
651+
// Functions marked `#[rustc_promotable]` are explicitly allowed to be promoted, so we can
652+
// accept them at this point.
595653
let fn_ty = callee.ty(self.body, self.tcx);
654+
if let ty::FnDef(def_id, _) = *fn_ty.kind() {
655+
if self.tcx.is_promotable_const_fn(def_id) {
656+
return Ok(());
657+
}
658+
}
596659

597-
// Inside const/static items, we promote all (eligible) function calls.
598-
// Everywhere else, we require `#[rustc_promotable]` on the callee.
599-
let promote_all_const_fn = matches!(
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!(
600665
self.const_kind,
601666
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const { inline: false })
602667
);
603-
if !promote_all_const_fn {
604-
if let ty::FnDef(def_id, _) = *fn_ty.kind() {
605-
// Never promote runtime `const fn` calls of
606-
// functions without `#[rustc_promotable]`.
607-
if !self.tcx.is_promotable_const_fn(def_id) {
608-
return Err(Unpromotable);
609-
}
610-
}
668+
if !promote_all_fn {
669+
return Err(Unpromotable);
611670
}
612-
671+
// Make sure the callee is a `const fn`.
613672
let is_const_fn = match *fn_ty.kind() {
614673
ty::FnDef(def_id, _) => self.tcx.is_const_fn_raw(def_id),
615674
_ => false,
616675
};
617676
if !is_const_fn {
618677
return Err(Unpromotable);
619678
}
620-
621-
self.validate_operand(callee)?;
622-
for arg in args {
623-
self.validate_operand(&arg.node)?;
679+
// The problem is, this may promote calls to functions that panic.
680+
// We don't want to introduce compilation errors if there's a panic in a call in dead code.
681+
// So we ensure that this is not dead code.
682+
if !self.is_promotion_safe_block(block) {
683+
return Err(Unpromotable);
624684
}
625-
685+
// This passed all checks, so let's accept.
626686
Ok(())
627687
}
628688
}
629689

630-
// FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
631690
fn validate_candidates(
632691
ccx: &ConstCx<'_, '_>,
633692
temps: &mut IndexSlice<Local, TempState>,
634693
candidates: &[Candidate],
635694
) -> Vec<Candidate> {
636-
let mut validator = Validator { ccx, temps };
695+
let mut validator = Validator { ccx, temps, promotion_safe_blocks: None };
637696

638697
candidates
639698
.iter()
@@ -652,6 +711,10 @@ struct Promoter<'a, 'tcx> {
652711
/// If true, all nested temps are also kept in the
653712
/// source MIR, not moved to the promoted MIR.
654713
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,
655718
}
656719

657720
impl<'a, 'tcx> Promoter<'a, 'tcx> {
@@ -754,6 +817,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
754817
TerminatorKind::Call {
755818
mut func, mut args, call_source: desugar, fn_span, ..
756819
} => {
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+
757824
self.visit_operand(&mut func, loc);
758825
for arg in &mut args {
759826
self.visit_operand(&mut arg.node, loc);
@@ -788,7 +855,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
788855

789856
fn promote_candidate(mut self, candidate: Candidate, next_promoted_id: usize) -> Body<'tcx> {
790857
let def = self.source.source.def_id();
791-
let mut rvalue = {
858+
let (mut rvalue, promoted_op) = {
792859
let promoted = &mut self.promoted;
793860
let promoted_id = Promoted::new(next_promoted_id);
794861
let tcx = self.tcx;
@@ -798,11 +865,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
798865
let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def));
799866
let uneval = mir::UnevaluatedConst { def, args, promoted: Some(promoted_id) };
800867

801-
Operand::Constant(Box::new(ConstOperand {
802-
span,
803-
user_ty: None,
804-
const_: Const::Unevaluated(uneval, ty),
805-
}))
868+
ConstOperand { span, user_ty: None, const_: Const::Unevaluated(uneval, ty) }
806869
};
807870

808871
let blocks = self.source.basic_blocks.as_mut();
@@ -835,22 +898,26 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
835898
let promoted_ref = local_decls.push(promoted_ref);
836899
assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
837900

901+
let promoted_operand = promoted_operand(ref_ty, span);
838902
let promoted_ref_statement = Statement {
839903
source_info: statement.source_info,
840904
kind: StatementKind::Assign(Box::new((
841905
Place::from(promoted_ref),
842-
Rvalue::Use(promoted_operand(ref_ty, span)),
906+
Rvalue::Use(Operand::Constant(Box::new(promoted_operand))),
843907
))),
844908
};
845909
self.extra_statements.push((loc, promoted_ref_statement));
846910

847-
Rvalue::Ref(
848-
tcx.lifetimes.re_erased,
849-
*borrow_kind,
850-
Place {
851-
local: mem::replace(&mut place.local, promoted_ref),
852-
projection: List::empty(),
853-
},
911+
(
912+
Rvalue::Ref(
913+
tcx.lifetimes.re_erased,
914+
*borrow_kind,
915+
Place {
916+
local: mem::replace(&mut place.local, promoted_ref),
917+
projection: List::empty(),
918+
},
919+
),
920+
promoted_operand,
854921
)
855922
};
856923

@@ -862,6 +929,12 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
862929

863930
let span = self.promoted.span;
864931
self.assign(RETURN_PLACE, rvalue, span);
932+
933+
// Now that we did promotion, we know whether we'll want to add this to `required_consts`.
934+
if self.add_to_required {
935+
self.source.required_consts.push(promoted_op);
936+
}
937+
865938
self.promoted
866939
}
867940
}
@@ -877,6 +950,14 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
877950
*local = self.promote_temp(*local);
878951
}
879952
}
953+
954+
fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
955+
if constant.const_.is_required_const() {
956+
self.promoted.required_consts.push(*constant);
957+
}
958+
959+
// Skipping `super_constant` as the visitor is otherwise only looking for locals.
960+
}
880961
}
881962

882963
fn promote_candidates<'tcx>(
@@ -930,8 +1011,10 @@ fn promote_candidates<'tcx>(
9301011
temps: &mut temps,
9311012
extra_statements: &mut extra_statements,
9321013
keep_original: false,
1014+
add_to_required: false,
9331015
};
9341016

1017+
// `required_consts` of the promoted itself gets filled while building the MIR body.
9351018
let mut promoted = promoter.promote_candidate(candidate, promotions.len());
9361019
promoted.source.promoted = Some(promotions.next_index());
9371020
promotions.push(promoted);
Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use rustc_middle::mir::visit::Visitor;
2-
use rustc_middle::mir::{Const, ConstOperand, Location};
3-
use rustc_middle::ty::ConstKind;
2+
use rustc_middle::mir::{ConstOperand, Location};
43

54
pub struct RequiredConstsVisitor<'a, 'tcx> {
65
required_consts: &'a mut Vec<ConstOperand<'tcx>>,
@@ -14,14 +13,8 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
1413

1514
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
1615
fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
17-
let const_ = constant.const_;
18-
match const_ {
19-
Const::Ty(c) => match c.kind() {
20-
ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {}
21-
_ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
22-
},
23-
Const::Unevaluated(..) => self.required_consts.push(*constant),
24-
Const::Val(..) => {}
16+
if constant.const_.is_required_const() {
17+
self.required_consts.push(*constant);
2518
}
2619
}
2720
}

‎tests/ui/consts/const-eval/promoted_errors.noopt.stderr

Lines changed: 0 additions & 44 deletions
This file was deleted.

‎tests/ui/consts/const-eval/promoted_errors.opt.stderr

Lines changed: 0 additions & 44 deletions
This file was deleted.

‎tests/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr

Lines changed: 0 additions & 44 deletions
This file was deleted.

‎tests/ui/consts/const-eval/promoted_errors.rs

Lines changed: 0 additions & 52 deletions
This file was deleted.

‎tests/ui/consts/promote-not.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ const TEST_DROP_NOT_PROMOTE: &String = {
5151
};
5252

5353

54+
// We do not promote function calls in `const` initializers in dead code.
55+
const fn mk_panic() -> u32 { panic!() }
56+
const fn mk_false() -> bool { false }
57+
const Y: () = {
58+
if mk_false() {
59+
let _x: &'static u32 = &mk_panic(); //~ ERROR temporary value dropped while borrowed
60+
}
61+
};
62+
5463
fn main() {
5564
// We must not promote things with interior mutability. Not even if we "project it away".
5665
let _val: &'static _ = &(Cell::new(1), 2).0; //~ ERROR temporary value dropped while borrowed

‎tests/ui/consts/promote-not.stderr

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ LL | let x = &String::new();
4747
LL | };
4848
| - value is dropped here
4949

50+
error[E0716]: temporary value dropped while borrowed
51+
--> $DIR/promote-not.rs:59:33
52+
|
53+
LL | let _x: &'static u32 = &mk_panic();
54+
| ------------ ^^^^^^^^^^ creates a temporary value which is freed while still in use
55+
| |
56+
| type annotation requires that borrow lasts for `'static`
57+
LL | }
58+
| - temporary value is freed at the end of this statement
59+
5060
error[E0716]: temporary value dropped while borrowed
5161
--> $DIR/promote-not.rs:21:32
5262
|
@@ -68,7 +78,7 @@ LL | }
6878
| - temporary value is freed at the end of this statement
6979

7080
error[E0716]: temporary value dropped while borrowed
71-
--> $DIR/promote-not.rs:56:29
81+
--> $DIR/promote-not.rs:65:29
7282
|
7383
LL | let _val: &'static _ = &(Cell::new(1), 2).0;
7484
| ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -79,7 +89,7 @@ LL | }
7989
| - temporary value is freed at the end of this statement
8090

8191
error[E0716]: temporary value dropped while borrowed
82-
--> $DIR/promote-not.rs:57:29
92+
--> $DIR/promote-not.rs:66:29
8393
|
8494
LL | let _val: &'static _ = &(Cell::new(1), 2).1;
8595
| ---------- ^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -90,7 +100,7 @@ LL | }
90100
| - temporary value is freed at the end of this statement
91101

92102
error[E0716]: temporary value dropped while borrowed
93-
--> $DIR/promote-not.rs:60:29
103+
--> $DIR/promote-not.rs:69:29
94104
|
95105
LL | let _val: &'static _ = &(1/0);
96106
| ---------- ^^^^^ creates a temporary value which is freed while still in use
@@ -101,7 +111,7 @@ LL | }
101111
| - temporary value is freed at the end of this statement
102112

103113
error[E0716]: temporary value dropped while borrowed
104-
--> $DIR/promote-not.rs:61:29
114+
--> $DIR/promote-not.rs:70:29
105115
|
106116
LL | let _val: &'static _ = &(1/(1-1));
107117
| ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -112,7 +122,7 @@ LL | }
112122
| - temporary value is freed at the end of this statement
113123

114124
error[E0716]: temporary value dropped while borrowed
115-
--> $DIR/promote-not.rs:62:29
125+
--> $DIR/promote-not.rs:71:29
116126
|
117127
LL | let _val: &'static _ = &((1+1)/(1-1));
118128
| ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -123,7 +133,7 @@ LL | }
123133
| - temporary value is freed at the end of this statement
124134

125135
error[E0716]: temporary value dropped while borrowed
126-
--> $DIR/promote-not.rs:63:29
136+
--> $DIR/promote-not.rs:72:29
127137
|
128138
LL | let _val: &'static _ = &(i32::MIN/-1);
129139
| ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -134,7 +144,7 @@ LL | }
134144
| - temporary value is freed at the end of this statement
135145

136146
error[E0716]: temporary value dropped while borrowed
137-
--> $DIR/promote-not.rs:64:29
147+
--> $DIR/promote-not.rs:73:29
138148
|
139149
LL | let _val: &'static _ = &(i32::MIN/(0-1));
140150
| ---------- ^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -145,7 +155,7 @@ LL | }
145155
| - temporary value is freed at the end of this statement
146156

147157
error[E0716]: temporary value dropped while borrowed
148-
--> $DIR/promote-not.rs:65:29
158+
--> $DIR/promote-not.rs:74:29
149159
|
150160
LL | let _val: &'static _ = &(-128i8/-1);
151161
| ---------- ^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -156,7 +166,7 @@ LL | }
156166
| - temporary value is freed at the end of this statement
157167

158168
error[E0716]: temporary value dropped while borrowed
159-
--> $DIR/promote-not.rs:66:29
169+
--> $DIR/promote-not.rs:75:29
160170
|
161171
LL | let _val: &'static _ = &(1%0);
162172
| ---------- ^^^^^ creates a temporary value which is freed while still in use
@@ -167,7 +177,7 @@ LL | }
167177
| - temporary value is freed at the end of this statement
168178

169179
error[E0716]: temporary value dropped while borrowed
170-
--> $DIR/promote-not.rs:67:29
180+
--> $DIR/promote-not.rs:76:29
171181
|
172182
LL | let _val: &'static _ = &(1%(1-1));
173183
| ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -178,7 +188,7 @@ LL | }
178188
| - temporary value is freed at the end of this statement
179189

180190
error[E0716]: temporary value dropped while borrowed
181-
--> $DIR/promote-not.rs:68:29
191+
--> $DIR/promote-not.rs:77:29
182192
|
183193
LL | let _val: &'static _ = &([1,2,3][4]+1);
184194
| ---------- ^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -189,7 +199,7 @@ LL | }
189199
| - temporary value is freed at the end of this statement
190200

191201
error[E0716]: temporary value dropped while borrowed
192-
--> $DIR/promote-not.rs:72:29
202+
--> $DIR/promote-not.rs:81:29
193203
|
194204
LL | let _val: &'static _ = &TEST_DROP;
195205
| ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -200,7 +210,7 @@ LL | }
200210
| - temporary value is freed at the end of this statement
201211

202212
error[E0716]: temporary value dropped while borrowed
203-
--> $DIR/promote-not.rs:74:29
213+
--> $DIR/promote-not.rs:83:29
204214
|
205215
LL | let _val: &'static _ = &&TEST_DROP;
206216
| ---------- ^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -211,7 +221,7 @@ LL | }
211221
| - temporary value is freed at the end of this statement
212222

213223
error[E0716]: temporary value dropped while borrowed
214-
--> $DIR/promote-not.rs:74:30
224+
--> $DIR/promote-not.rs:83:30
215225
|
216226
LL | let _val: &'static _ = &&TEST_DROP;
217227
| ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -222,7 +232,7 @@ LL | }
222232
| - temporary value is freed at the end of this statement
223233

224234
error[E0716]: temporary value dropped while borrowed
225-
--> $DIR/promote-not.rs:77:29
235+
--> $DIR/promote-not.rs:86:29
226236
|
227237
LL | let _val: &'static _ = &(&TEST_DROP,);
228238
| ---------- ^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -233,7 +243,7 @@ LL | }
233243
| - temporary value is freed at the end of this statement
234244

235245
error[E0716]: temporary value dropped while borrowed
236-
--> $DIR/promote-not.rs:77:31
246+
--> $DIR/promote-not.rs:86:31
237247
|
238248
LL | let _val: &'static _ = &(&TEST_DROP,);
239249
| ---------- ^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -244,7 +254,7 @@ LL | }
244254
| - temporary value is freed at the end of this statement
245255

246256
error[E0716]: temporary value dropped while borrowed
247-
--> $DIR/promote-not.rs:80:29
257+
--> $DIR/promote-not.rs:89:29
248258
|
249259
LL | let _val: &'static _ = &[&TEST_DROP; 1];
250260
| ---------- ^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -255,7 +265,7 @@ LL | }
255265
| - temporary value is freed at the end of this statement
256266

257267
error[E0716]: temporary value dropped while borrowed
258-
--> $DIR/promote-not.rs:80:31
268+
--> $DIR/promote-not.rs:89:31
259269
|
260270
LL | let _val: &'static _ = &[&TEST_DROP; 1];
261271
| ---------- ^^^^^^^^^ - temporary value is freed at the end of this statement
@@ -264,7 +274,7 @@ LL | let _val: &'static _ = &[&TEST_DROP; 1];
264274
| type annotation requires that borrow lasts for `'static`
265275

266276
error[E0716]: temporary value dropped while borrowed
267-
--> $DIR/promote-not.rs:89:26
277+
--> $DIR/promote-not.rs:98:26
268278
|
269279
LL | let x: &'static _ = &UnionWithCell { f1: 0 };
270280
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
@@ -274,7 +284,7 @@ LL |
274284
LL | }
275285
| - temporary value is freed at the end of this statement
276286

277-
error: aborting due to 26 previous errors
287+
error: aborting due to 27 previous errors
278288

279289
Some errors have detailed explanations: E0493, E0716.
280290
For more information about an error, try `rustc --explain E0493`.

‎tests/ui/consts/promotion.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,30 @@
55

66
//@ build-pass
77

8+
#![allow(arithmetic_overflow)]
9+
10+
use std::mem;
11+
812
const fn assert_static<T>(_: &'static T) {}
913

10-
#[allow(unconditional_panic)]
11-
const fn fail() -> i32 {
12-
1/0
13-
}
14-
const C: i32 = {
15-
// Promoted that fails to evaluate in dead code -- this must work
16-
// (for backwards compatibility reasons).
17-
if false {
18-
assert_static(&fail());
19-
}
14+
// Function calls in const on the "main path" (not inside conditionals)
15+
// do get promoted.
16+
const fn make_thing() -> i32 {
2017
42
18+
}
19+
const C: () = {
20+
assert_static(&make_thing());
21+
// Make sure this works even when there's other stuff (like function calls) above the relevant
22+
// call in the const initializer.
23+
assert_static(&make_thing());
2124
};
2225

2326
fn main() {
2427
assert_static(&["a", "b", "c"]);
2528
assert_static(&["d", "e", "f"]);
26-
assert_eq!(C, 42);
2729

2830
// make sure that this does not cause trouble despite overflowing
29-
assert_static(&(0-1));
31+
assert_static(&(0u32 - 1));
3032

3133
// div-by-non-0 (and also not MIN/-1) is okay
3234
assert_static(&(1/1));
@@ -36,12 +38,16 @@ fn main() {
3638
assert_static(&(1%1));
3739

3840
// in-bounds array access is okay
39-
assert_static(&([1,2,3][0] + 1));
40-
assert_static(&[[1,2][1]]);
41+
assert_static(&([1, 2, 3][0] + 1));
42+
assert_static(&[[1, 2][1]]);
4143

4244
// Top-level projections are not part of the promoted, so no error here.
4345
if false {
4446
#[allow(unconditional_panic)]
45-
assert_static(&[1,2,3][4]);
47+
assert_static(&[1, 2, 3][4]);
4648
}
49+
50+
// More complicated case involving control flow and a `#[rustc_promotable]` function
51+
let decision = std::hint::black_box(true);
52+
let x: &'static usize = if decision { &mem::size_of::<usize>() } else { &0 };
4753
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error[E0080]: evaluation of `Fail::<i32>::C` failed
2+
--> $DIR/collect-in-promoted-const.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-promoted-const.rs:9:19
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-in-promoted-const.rs:20:21
11+
|
12+
LL | let _val = &Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
note: erroneous constant encountered
16+
--> $DIR/collect-in-promoted-const.rs:20:20
17+
|
18+
LL | let _val = &Fail::<T>::C;
19+
| ^^^^^^^^^^^^^
20+
21+
note: erroneous constant encountered
22+
--> $DIR/collect-in-promoted-const.rs:20:21
23+
|
24+
LL | let _val = &Fail::<T>::C;
25+
| ^^^^^^^^^^^^
26+
|
27+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
28+
29+
note: the above error was encountered while instantiating `fn f::<i32>`
30+
--> $DIR/collect-in-promoted-const.rs:25:5
31+
|
32+
LL | f::<i32>();
33+
| ^^^^^^^^^^
34+
35+
error: aborting due to 1 previous error
36+
37+
For more information about this error, try `rustc --explain E0080`.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
error[E0080]: evaluation of `Fail::<T>::C` failed
2+
--> $DIR/collect-in-promoted-const.rs:9:19
3+
|
4+
LL | const C: () = panic!();
5+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-promoted-const.rs:9:19
6+
|
7+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
8+
9+
note: erroneous constant encountered
10+
--> $DIR/collect-in-promoted-const.rs:20:21
11+
|
12+
LL | let _val = &Fail::<T>::C;
13+
| ^^^^^^^^^^^^
14+
15+
error[E0080]: evaluation of `Fail::<i32>::C` failed
16+
--> $DIR/collect-in-promoted-const.rs:9:19
17+
|
18+
LL | const C: () = panic!();
19+
| ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-promoted-const.rs:9:19
20+
|
21+
= note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
22+
23+
note: erroneous constant encountered
24+
--> $DIR/collect-in-promoted-const.rs:20:21
25+
|
26+
LL | let _val = &Fail::<T>::C;
27+
| ^^^^^^^^^^^^
28+
|
29+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
30+
31+
note: erroneous constant encountered
32+
--> $DIR/collect-in-promoted-const.rs:20:20
33+
|
34+
LL | let _val = &Fail::<T>::C;
35+
| ^^^^^^^^^^^^^
36+
37+
note: erroneous constant encountered
38+
--> $DIR/collect-in-promoted-const.rs:20:21
39+
|
40+
LL | let _val = &Fail::<T>::C;
41+
| ^^^^^^^^^^^^
42+
|
43+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
44+
45+
note: the above error was encountered while instantiating `fn f::<i32>`
46+
--> $DIR/collect-in-promoted-const.rs:25:5
47+
|
48+
LL | f::<i32>();
49+
| ^^^^^^^^^^
50+
51+
error: aborting due to 2 previous errors
52+
53+
For more information about this error, try `rustc --explain E0080`.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@revisions: noopt opt
2+
//@ build-fail
3+
//@[noopt] compile-flags: -Copt-level=0
4+
//@[opt] compile-flags: -O
5+
//! Make sure we error on erroneous consts even if they get promoted.
6+
7+
struct Fail<T>(T);
8+
impl<T> Fail<T> {
9+
const C: () = panic!(); //~ERROR evaluation of `Fail::<i32>::C` failed
10+
//[opt]~^ ERROR evaluation of `Fail::<T>::C` failed
11+
// (Not sure why optimizations lead to this being emitted twice, but as long as compilation
12+
// fails either way it's fine.)
13+
}
14+
15+
#[inline(never)]
16+
fn f<T>() {
17+
if false {
18+
// If promotion moved `C` from our required_consts to its own, without adding itself to
19+
// our required_consts, then we'd miss the const-eval failure here.
20+
let _val = &Fail::<T>::C;
21+
}
22+
}
23+
24+
fn main() {
25+
f::<i32>();
26+
}

‎tests/ui/consts/required-consts/interpret-in-promoted.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//@revisions: noopt opt
22
//@[noopt] compile-flags: -Copt-level=0
33
//@[opt] compile-flags: -O
4-
//! Make sure we error on erroneous consts even if they are unused.
4+
//! Make sure we evaluate const fn calls even if they get promoted and their result ignored.
55
66
const unsafe fn ub() {
77
std::hint::unreachable_unchecked();

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fn non_elidable<'a, 'b>(a: &'a u8, b: &'b u8) -> &'a u8 {
44
a
55
}
66

7-
// The incorrect case without `for<'a>` is tested for in `rfc1623-2.rs`
7+
// The incorrect case without `for<'a>` is tested for in `rfc1623-3.rs`
88
static NON_ELIDABLE_FN: &for<'a> fn(&'a u8, &'a u8) -> &'a u8 =
99
&(non_elidable as for<'a> fn(&'a u8, &'a u8) -> &'a u8);
1010

@@ -26,10 +26,10 @@ static SOME_STRUCT: &SomeStruct = &SomeStruct {
2626
foo: &Foo { bools: &[false, true] },
2727
bar: &Bar { bools: &[true, true] },
2828
f: &id,
29-
//~^ ERROR implementation of `Fn` is not general enough
30-
//~| ERROR implementation of `Fn` is not general enough
31-
//~| ERROR implementation of `FnOnce` is not general enough
29+
//~^ ERROR implementation of `FnOnce` is not general enough
3230
//~| ERROR implementation of `FnOnce` is not general enough
31+
//~| ERROR implementation of `Fn` is not general enough
32+
//~| ERROR implementation of `Fn` is not general enough
3333
};
3434

3535
// very simple test for a 'static static with default lifetime

0 commit comments

Comments
 (0)
This repository has been archived.