diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index b6124049579fd..37202276161c7 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -297,6 +297,17 @@ impl std::ops::Deref for Validator<'a, 'tcx> { struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { + /// Determines if this code could be executed at runtime and thus is subject to codegen. + /// That means even unused constants need to be evaluated. + /// + /// `const_kind` should not be used in this file other than through this method! + fn maybe_runtime(&self) -> bool { + match self.const_kind { + None | Some(hir::ConstContext::ConstFn) => true, + Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false, + } + } + fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { match candidate { Candidate::Ref(loc) => { @@ -363,12 +374,10 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] - // is allowed right now, and only in functions. + // is allowed right now. if let ty::Array(_, len) = ty.kind() { - // FIXME(eddyb) the `self.is_non_const_fn` condition - // seems unnecessary, given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) if self.const_kind.is_none() => {} + Some(0) => {} _ => return Err(Unpromotable), } } else { @@ -495,9 +504,10 @@ impl<'tcx> Validator<'_, 'tcx> { match place { PlaceRef { local, projection: [] } => self.validate_local(local), PlaceRef { local, projection: [proj_base @ .., elem] } => { + // Validate topmost projection, then recurse. match *elem { ProjectionElem::Deref => { - let mut not_promotable = true; + let mut promotable = false; // This is a special treatment for cases like *&STATIC where STATIC is a // global static variable. // This pattern is generated only when global static variables are directly @@ -512,6 +522,9 @@ impl<'tcx> Validator<'_, 'tcx> { }) = def_stmt { if let Some(did) = c.check_static_ptr(self.tcx) { + // Evaluating a promoted may not read statics except if it got + // promoted from a static (this is a CTFE check). So we + // can only promote static accesses inside statics. if let Some(hir::ConstContext::Static(..)) = self.const_kind { // The `is_empty` predicate is introduced to exclude the case // where the projection operations are [ .field, * ]. @@ -524,13 +537,13 @@ impl<'tcx> Validator<'_, 'tcx> { if proj_base.is_empty() && !self.tcx.is_thread_local_static(did) { - not_promotable = false; + promotable = true; } } } } } - if not_promotable { + if !promotable { return Err(Unpromotable); } } @@ -545,7 +558,7 @@ impl<'tcx> Validator<'_, 'tcx> { } ProjectionElem::Field(..) => { - if self.const_kind.is_none() { + if self.maybe_runtime() { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let Some(def) = base_ty.ty_adt_def() { @@ -573,6 +586,10 @@ impl<'tcx> Validator<'_, 'tcx> { if let Some(def_id) = c.check_static_ptr(self.tcx) { // Only allow statics (not consts) to refer to other statics. // FIXME(eddyb) does this matter at all for promotion? + // FIXME(RalfJung) it makes little sense to not promote this in `fn`/`const fn`, + // and in `const` this cannot occur anyway. The only concern is that we might + // promote even `let x = &STATIC` which would be useless, but this applies to + // promotion inside statics as well. let is_static = matches!(self.const_kind, Some(hir::ConstContext::Static(_))); if !is_static { return Err(Unpromotable); @@ -591,20 +608,20 @@ impl<'tcx> Validator<'_, 'tcx> { fn validate_rvalue(&self, rvalue: &Rvalue<'tcx>) -> Result<(), Unpromotable> { match *rvalue { - Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) if self.const_kind.is_none() => { + Rvalue::Cast(CastKind::Misc, ref operand, cast_ty) => { let operand_ty = operand.ty(self.body, self.tcx); let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast"); let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast"); match (cast_in, cast_out) { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { - // in normal functions, mark such casts as not promotable + // ptr-to-int casts are not possible in consts and thus not promotable return Err(Unpromotable); } _ => {} } } - Rvalue::BinaryOp(op, ref lhs, _) if self.const_kind.is_none() => { + Rvalue::BinaryOp(op, ref lhs, _) => { if let ty::RawPtr(_) | ty::FnPtr(..) = lhs.ty(self.body, self.tcx).kind() { assert!( op == BinOp::Eq @@ -616,13 +633,14 @@ impl<'tcx> Validator<'_, 'tcx> { || op == BinOp::Offset ); - // raw pointer operations are not allowed inside promoteds + // raw pointer operations are not allowed inside consts and thus not promotable return Err(Unpromotable); } } Rvalue::NullaryOp(NullOp::Box, _) => return Err(Unpromotable), + // FIXME(RalfJung): the rest is *implicitly considered promotable*... that seems dangerous. _ => {} } @@ -644,8 +662,8 @@ impl<'tcx> Validator<'_, 'tcx> { } Rvalue::AddressOf(_, place) => { - // Raw reborrows can come from reference to pointer coercions, - // so are allowed. + // We accept `&raw *`, i.e., raw reborrows -- creating a raw pointer is + // no problem, only using it is. if let [proj_base @ .., ProjectionElem::Deref] = place.projection.as_ref() { let base_ty = Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; if let ty::Ref(..) = base_ty.kind() { @@ -664,12 +682,10 @@ impl<'tcx> Validator<'_, 'tcx> { // In theory, any zero-sized value could be borrowed // mutably without consequences. However, only &mut [] - // is allowed right now, and only in functions. + // is allowed right now. if let ty::Array(_, len) = ty.kind() { - // FIXME(eddyb): We only return `Unpromotable` for `&mut []` inside a - // const context which seems unnecessary given that this is merely a ZST. match len.try_eval_usize(self.tcx, self.param_env) { - Some(0) if self.const_kind.is_none() => {} + Some(0) => {} _ => return Err(Unpromotable), } } else { @@ -734,14 +750,7 @@ impl<'tcx> Validator<'_, 'tcx> { ) -> Result<(), Unpromotable> { let fn_ty = callee.ty(self.body, self.tcx); - // `const` and `static` use the explicit rules for promotion regardless of the `Candidate`, - // meaning calls to `const fn` can be promoted. - let context_uses_explicit_promotion_rules = matches!( - self.const_kind, - Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) - ); - - if !self.explicit && !context_uses_explicit_promotion_rules { + if !self.explicit && self.maybe_runtime() { if let ty::FnDef(def_id, _) = *fn_ty.kind() { // Never promote runtime `const fn` calls of // functions without `#[rustc_promotable]`. diff --git a/src/test/ui/consts/promote-no-mut.rs b/src/test/ui/consts/promote-no-mut.rs deleted file mode 100644 index fb57c8bb93458..0000000000000 --- a/src/test/ui/consts/promote-no-mut.rs +++ /dev/null @@ -1,10 +0,0 @@ -// ignore-tidy-linelength -// We do not promote mutable references. -static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed - -static mut TEST2: &'static mut [i32] = { - let x = &mut [1,2,3]; //~ ERROR temporary value dropped while borrowed - x -}; - -fn main() {} diff --git a/src/test/ui/consts/promote-not.rs b/src/test/ui/consts/promote-not.rs new file mode 100644 index 0000000000000..8daac75837734 --- /dev/null +++ b/src/test/ui/consts/promote-not.rs @@ -0,0 +1,30 @@ +// ignore-tidy-linelength +// Test various things that we do not want to promote. +#![allow(unconditional_panic, const_err)] +#![feature(const_fn, const_fn_union)] + +// We do not promote mutable references. +static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed + +static mut TEST2: &'static mut [i32] = { + let x = &mut [1,2,3]; //~ ERROR temporary value dropped while borrowed + x +}; + +// We do not promote fn calls in `fn`, including `const fn`. +pub const fn promote_cal(b: bool) -> i32 { + const fn foo() { [()][42] } + + if b { + let _x: &'static () = &foo(); //~ ERROR temporary value dropped while borrowed + } + 13 +} + +// We do not promote union field accesses in `fn. +union U { x: i32, y: i32 } +pub const fn promote_union() { + let _x: &'static i32 = &unsafe { U { x: 0 }.x }; //~ ERROR temporary value dropped while borrowed +} + +fn main() {} diff --git a/src/test/ui/consts/promote-no-mut.stderr b/src/test/ui/consts/promote-not.stderr similarity index 51% rename from src/test/ui/consts/promote-no-mut.stderr rename to src/test/ui/consts/promote-not.stderr index 49d96546ada3f..efe921b601104 100644 --- a/src/test/ui/consts/promote-no-mut.stderr +++ b/src/test/ui/consts/promote-not.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/promote-no-mut.rs:3:50 + --> $DIR/promote-not.rs:7:50 | LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); | ----------^^^^^^^^^- @@ -9,7 +9,7 @@ LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); | using this value as a static requires that borrow lasts for `'static` error[E0716]: temporary value dropped while borrowed - --> $DIR/promote-no-mut.rs:6:18 + --> $DIR/promote-not.rs:10:18 | LL | let x = &mut [1,2,3]; | ^^^^^^^ creates a temporary which is freed while still in use @@ -18,6 +18,26 @@ LL | x LL | }; | - temporary value is freed at the end of this statement -error: aborting due to 2 previous errors +error[E0716]: temporary value dropped while borrowed + --> $DIR/promote-not.rs:19:32 + | +LL | let _x: &'static () = &foo(); + | ----------- ^^^^^ creates a temporary which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +LL | } + | - temporary value is freed at the end of this statement + +error[E0716]: temporary value dropped while borrowed + --> $DIR/promote-not.rs:27:29 + | +LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x }; + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use + | | + | type annotation requires that borrow lasts for `'static` +LL | } + | - temporary value is freed at the end of this statement + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0716`. diff --git a/src/test/ui/consts/promotion.rs b/src/test/ui/consts/promotion.rs index 3c5401e421216..5f84030a9e96b 100644 --- a/src/test/ui/consts/promotion.rs +++ b/src/test/ui/consts/promotion.rs @@ -1,4 +1,4 @@ -// run-pass +// check-pass // compile-flags: -O diff --git a/src/test/ui/statics/static-promotion.rs b/src/test/ui/statics/static-promotion.rs index bd8910bdb3f3f..b9eff469177e6 100644 --- a/src/test/ui/statics/static-promotion.rs +++ b/src/test/ui/statics/static-promotion.rs @@ -1,4 +1,4 @@ -// check-pass +// run-pass // Use of global static variables in literal values should be allowed for // promotion.