Skip to content

Commit 4d1ef03

Browse files
committed
cleanup promotion const_kind checks
in particular allow a few more promotions for consistency when they were already allowed in other contexts
1 parent 59fb88d commit 4d1ef03

File tree

6 files changed

+83
-45
lines changed

6 files changed

+83
-45
lines changed

compiler/rustc_mir/src/transform/promote_consts.rs

+28-30
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
297297
struct Unpromotable;
298298

299299
impl<'tcx> Validator<'_, 'tcx> {
300+
//! Determines if this code could be executed at runtime and thus is subject to codegen.
301+
//! That means even unused constants need to be evaluated.
302+
//!
303+
//! `const_kind` should not be used in this file other than through this method!
304+
fn maybe_runtime(&self) -> bool {
305+
match self.const_kind {
306+
None | Some(hir::ConstContext::ConstFn) => true,
307+
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false,
308+
}
309+
}
310+
300311
fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
301312
match candidate {
302313
Candidate::Ref(loc) => {
@@ -365,10 +376,8 @@ impl<'tcx> Validator<'_, 'tcx> {
365376
// mutably without consequences. However, only &mut []
366377
// is allowed right now, and only in functions.
367378
if let ty::Array(_, len) = ty.kind() {
368-
// FIXME(eddyb) the `self.is_non_const_fn` condition
369-
// seems unnecessary, given that this is merely a ZST.
370379
match len.try_eval_usize(self.tcx, self.param_env) {
371-
Some(0) if self.const_kind.is_none() => {}
380+
Some(0) => {}
372381
_ => return Err(Unpromotable),
373382
}
374383
} else {
@@ -495,9 +504,10 @@ impl<'tcx> Validator<'_, 'tcx> {
495504
match place {
496505
PlaceRef { local, projection: [] } => self.validate_local(local),
497506
PlaceRef { local, projection: [proj_base @ .., elem] } => {
507+
// Validate topmost projection, then recurse.
498508
match *elem {
499509
ProjectionElem::Deref => {
500-
let mut not_promotable = true;
510+
let mut promotable = false;
501511
// This is a special treatment for cases like *&STATIC where STATIC is a
502512
// global static variable.
503513
// This pattern is generated only when global static variables are directly
@@ -512,6 +522,9 @@ impl<'tcx> Validator<'_, 'tcx> {
512522
}) = def_stmt
513523
{
514524
if let Some(did) = c.check_static_ptr(self.tcx) {
525+
// Evaluating a promoted may not read statics except if it got
526+
// promoted from a static (this is a CTFE check). So we
527+
// can only promoted static accesses inside statics.
515528
if let Some(hir::ConstContext::Static(..)) = self.const_kind {
516529
// The `is_empty` predicate is introduced to exclude the case
517530
// where the projection operations are [ .field, * ].
@@ -524,13 +537,13 @@ impl<'tcx> Validator<'_, 'tcx> {
524537
if proj_base.is_empty()
525538
&& !self.tcx.is_thread_local_static(did)
526539
{
527-
not_promotable = false;
540+
promotable = true;
528541
}
529542
}
530543
}
531544
}
532545
}
533-
if not_promotable {
546+
if !promotable {
534547
return Err(Unpromotable);
535548
}
536549
}
@@ -545,7 +558,7 @@ impl<'tcx> Validator<'_, 'tcx> {
545558
}
546559

547560
ProjectionElem::Field(..) => {
548-
if self.const_kind.is_none() {
561+
if self.maybe_runtime() {
549562
let base_ty =
550563
Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
551564
if let Some(def) = base_ty.ty_adt_def() {
@@ -571,13 +584,6 @@ impl<'tcx> Validator<'_, 'tcx> {
571584
// `validate_rvalue` upon access.
572585
Operand::Constant(c) => {
573586
if let Some(def_id) = c.check_static_ptr(self.tcx) {
574-
// Only allow statics (not consts) to refer to other statics.
575-
// FIXME(eddyb) does this matter at all for promotion?
576-
let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_)));
577-
if !is_static {
578-
return Err(Unpromotable);
579-
}
580-
581587
let is_thread_local = self.tcx.is_thread_local_static(def_id);
582588
if is_thread_local {
583589
return Err(Unpromotable);
@@ -591,20 +597,20 @@ impl<'tcx> Validator<'_, 'tcx> {
591597

592598
fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> {
593599
match *rvalue {
594-
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => {
600+
Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.maybe_runtime() => {
595601
let operand_ty = operand.ty(self.body, self.tcx);
596602
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
597603
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
598604
match (cast_in, cast_out) {
599605
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
600-
// in normal functions, mark such casts as not promotable
606+
// ptr-to-int casts are not promotable
601607
return Err(Unpromotable);
602608
}
603609
_ => {}
604610
}
605611
}
606612

607-
Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => {
613+
Rvalue::BinaryOp(op, ref lhs, _) if self.maybe_runtime() => {
608614
if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() {
609615
assert!(
610616
op == BinOp::Eq
@@ -623,6 +629,7 @@ impl<'tcx> Validator<'_, 'tcx> {
623629

624630
Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable),
625631

632+
// FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous.
626633
_ => {}
627634
}
628635

@@ -644,8 +651,8 @@ impl<'tcx> Validator<'_, 'tcx> {
644651
}
645652

646653
Rvalue::AddressOf(_, place) => {
647-
// Raw reborrows can come from reference to pointer coercions,
648-
// so are allowed.
654+
// We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is
655+
// no problem, only using it is.
649656
if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() {
650657
let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
651658
if let ty::Ref(..) = base_ty.kind() {
@@ -666,10 +673,8 @@ impl<'tcx> Validator<'_, 'tcx> {
666673
// mutably without consequences. However, only &mut []
667674
// is allowed right now, and only in functions.
668675
if let ty::Array(_, len) = ty.kind() {
669-
// FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a
670-
// const context which seems unnecessary given that this is merely a ZST.
671676
match len.try_eval_usize(self.tcx, self.param_env) {
672-
Some(0) if self.const_kind.is_none() => {}
677+
Some(0) => {}
673678
_ => return Err(Unpromotable),
674679
}
675680
} else {
@@ -734,14 +739,7 @@ impl<'tcx> Validator<'_, 'tcx> {
734739
) -> Result<(), Unpromotable> {
735740
let fn_ty = callee.ty(self.body, self.tcx);
736741

737-
// `const` and `static` use the explicit rules for promotion regardless of the `Candidate`,
738-
// meaning calls to `const fn` can be promoted.
739-
let context_uses_explicit_promotion_rules = matches!(
740-
self.const_kind,
741-
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const)
742-
);
743-
744-
if !self.explicit && !context_uses_explicit_promotion_rules {
742+
if !self.explicit && self.maybe_runtime() {
745743
if let ty::FnDef(def_id, _) = *fn_ty.kind() {
746744
// Never promote runtime `const fn` calls of
747745
// functions without `#[rustc_promotable]`.

src/test/ui/consts/promote-no-mut.rs

-10
This file was deleted.

src/test/ui/consts/promote-not.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// ignore-tidy-linelength
2+
// Test various things that we do not want to promote.
3+
#![allow(unconditional_panic, const_err)]
4+
#![feature(const_fn, const_fn_union)]
5+
6+
// We do not promote mutable references.
7+
static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed
8+
9+
static mut TEST2: &'static mut [i32] = {
10+
let x = &mut [1,2,3]; //~ ERROR temporary value dropped while borrowed
11+
x
12+
};
13+
14+
// We do not promote fn calls in `fn`, including `const fn`.
15+
pub const fn promote_cal(b: bool) -> i32 {
16+
const fn foo() { [()][42] }
17+
18+
if b {
19+
let _x: &'static () = &foo(); //~ ERROR temporary value dropped while borrowed
20+
}
21+
13
22+
}
23+
24+
// We do not promote union field accesses in `fn.
25+
union U { x: i32, y: i32 }
26+
pub const fn promote_union() {
27+
let _x: &'static i32 = &unsafe { U { x: 0 }.x }; //~ ERROR temporary value dropped while borrowed
28+
}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0716]: temporary value dropped while borrowed
2-
--> $DIR/promote-no-mut.rs:3:50
2+
--> $DIR/promote-not.rs:7:50
33
|
44
LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]);
55
| ----------^^^^^^^^^-
@@ -9,7 +9,7 @@ LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]);
99
| using this value as a static requires that borrow lasts for `'static`
1010

1111
error[E0716]: temporary value dropped while borrowed
12-
--> $DIR/promote-no-mut.rs:6:18
12+
--> $DIR/promote-not.rs:10:18
1313
|
1414
LL | let x = &mut [1,2,3];
1515
| ^^^^^^^ creates a temporary which is freed while still in use
@@ -18,6 +18,26 @@ LL | x
1818
LL | };
1919
| - temporary value is freed at the end of this statement
2020

21-
error: aborting due to 2 previous errors
21+
error[E0716]: temporary value dropped while borrowed
22+
--> $DIR/promote-not.rs:19:32
23+
|
24+
LL | let _x: &'static () = &foo();
25+
| ----------- ^^^^^ creates a temporary which is freed while still in use
26+
| |
27+
| type annotation requires that borrow lasts for `'static`
28+
LL | }
29+
| - temporary value is freed at the end of this statement
30+
31+
error[E0716]: temporary value dropped while borrowed
32+
--> $DIR/promote-not.rs:27:29
33+
|
34+
LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
35+
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
36+
| |
37+
| type annotation requires that borrow lasts for `'static`
38+
LL | }
39+
| - temporary value is freed at the end of this statement
40+
41+
error: aborting due to 4 previous errors
2242

2343
For more information about this error, try `rustc --explain E0716`.

src/test/ui/consts/promotion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// run-pass
1+
// check-pass
22

33
// compile-flags: -O
44

src/test/ui/statics/static-promotion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// check-pass
1+
// run-pass
22

33
// Use of global static variables in literal values should be allowed for
44
// promotion.

0 commit comments

Comments
 (0)