Auto merge of #77526 - RalfJung:dont-promote-unions, r=lcnr
stop promoting union field accesses in 'const' Turns out that promotion of union field accesses is the only difference between "promotion in `const`/`static` bodies" and "explicit promotion". So if we can remove this, we have finally achieved what I thought to already be the case -- that the bodies of `const`/`static` initializers behave the same as explicit promotion contexts. The reason we do not want to promote union field accesses is that they can introduce UB, i.e., they can go wrong. We want to [minimize the ways promoteds can fail to evaluate](https://github.com/rust-lang/const-eval/issues/53). Also this change makes things more consistent overall, removing a special case that was added without much consideration (as far as I can tell). Cc `@rust-lang/wg-const-eval`
This commit is contained in:
commit
36a74944cb
3 changed files with 30 additions and 21 deletions
|
@ -301,17 +301,6 @@ impl std::ops::Deref for Validator<'a, 'tcx> {
|
||||||
struct Unpromotable;
|
struct Unpromotable;
|
||||||
|
|
||||||
impl<'tcx> Validator<'_, 'tcx> {
|
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> {
|
fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> {
|
||||||
match candidate {
|
match candidate {
|
||||||
Candidate::Ref(loc) => {
|
Candidate::Ref(loc) => {
|
||||||
|
@ -562,14 +551,12 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionElem::Field(..) => {
|
ProjectionElem::Field(..) => {
|
||||||
if self.maybe_runtime() {
|
let base_ty =
|
||||||
let base_ty =
|
Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
|
||||||
Place::ty_from(place.local, proj_base, self.body, self.tcx).ty;
|
if let Some(def) = base_ty.ty_adt_def() {
|
||||||
if let Some(def) = base_ty.ty_adt_def() {
|
// No promotion of union field accesses.
|
||||||
// No promotion of union field accesses.
|
if def.is_union() {
|
||||||
if def.is_union() {
|
return Err(Unpromotable);
|
||||||
return Err(Unpromotable);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,7 +738,14 @@ impl<'tcx> Validator<'_, 'tcx> {
|
||||||
) -> Result<(), Unpromotable> {
|
) -> Result<(), Unpromotable> {
|
||||||
let fn_ty = callee.ty(self.body, self.tcx);
|
let fn_ty = callee.ty(self.body, self.tcx);
|
||||||
|
|
||||||
if !self.explicit && self.maybe_runtime() {
|
// When doing explicit promotion and inside const/static items, we promote all (eligible) function calls.
|
||||||
|
// Everywhere else, we require `#[rustc_promotable]` on the callee.
|
||||||
|
let promote_all_const_fn = self.explicit
|
||||||
|
|| matches!(
|
||||||
|
self.const_kind,
|
||||||
|
Some(hir::ConstContext::Static(_) | hir::ConstContext::Const)
|
||||||
|
);
|
||||||
|
if !promote_all_const_fn {
|
||||||
if let ty::FnDef(def_id, _) = *fn_ty.kind() {
|
if let ty::FnDef(def_id, _) = *fn_ty.kind() {
|
||||||
// Never promote runtime `const fn` calls of
|
// Never promote runtime `const fn` calls of
|
||||||
// functions without `#[rustc_promotable]`.
|
// functions without `#[rustc_promotable]`.
|
||||||
|
|
|
@ -27,4 +27,9 @@ pub const fn promote_union() {
|
||||||
let _x: &'static i32 = &unsafe { U { x: 0 }.x }; //~ ERROR temporary value dropped while borrowed
|
let _x: &'static i32 = &unsafe { U { x: 0 }.x }; //~ ERROR temporary value dropped while borrowed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We do not promote union field accesses in `const`, either.
|
||||||
|
const TEST_UNION: () = {
|
||||||
|
let _x: &'static i32 = &unsafe { U { x: 0 }.x }; //~ ERROR temporary value dropped while borrowed
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -38,6 +38,16 @@ LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
|
||||||
LL | }
|
LL | }
|
||||||
| - temporary value is freed at the end of this statement
|
| - temporary value is freed at the end of this statement
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error[E0716]: temporary value dropped while borrowed
|
||||||
|
--> $DIR/promote-not.rs:32: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 5 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0716`.
|
For more information about this error, try `rustc --explain E0716`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue