mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Auto merge of #78363 - RalfJung:promotion, r=oli-obk
remove this weird special case from promotion Promotion has a special case to ignore interior mutability under some specific circumstances. The purpose of this PR is to figure out what changes if we remove that. Since `Cell::new` and friends only get promoted inside `const`/`static` initializers these days, it actually is not easy to exploit this case: you need something like ```rust const TEST_INTERIOR_MUT: () = { // The "0." case is already ruled out by not permitting any interior mutability in `const`. let _val: &'static _ = &(Cell::new(1), 2).1; }; ``` I assume something like `&Some(&(Cell::new(1), 2).1)` would hit the nested case inside `validate_rvalue`... though I am not sure why that would not just trigger nested promotion, first promoting the inner reference and then the outer one? Fixes https://github.com/rust-lang/rust/issues/67534 (by simply rejecting that code^^) r? `@oli-obk` (but for now this is not meant to be merged!) Cc `@rust-lang/wg-const-eval`
This commit is contained in:
commit
c0bfe3485f
@ -22,7 +22,7 @@ use rustc_middle::ty::cast::CastTy;
|
|||||||
use rustc_middle::ty::subst::InternalSubsts;
|
use rustc_middle::ty::subst::InternalSubsts;
|
||||||
use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable};
|
use rustc_middle::ty::{self, List, TyCtxt, TypeFoldable};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{Span, DUMMY_SP};
|
use rustc_span::Span;
|
||||||
|
|
||||||
use rustc_index::vec::{Idx, IndexVec};
|
use rustc_index::vec::{Idx, IndexVec};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
@ -326,41 +326,16 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||||||
if place.projection.contains(&ProjectionElem::Deref) {
|
if place.projection.contains(&ProjectionElem::Deref) {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
|
if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
|
||||||
let mut has_mut_interior =
|
return Err(Unpromotable);
|
||||||
self.qualif_local::<qualifs::HasMutInterior>(place.local);
|
|
||||||
// HACK(eddyb) this should compute the same thing as
|
|
||||||
// `<HasMutInterior as Qualif>::in_projection` from
|
|
||||||
// `check_consts::qualifs` but without recursion.
|
|
||||||
if has_mut_interior {
|
|
||||||
// This allows borrowing fields which don't have
|
|
||||||
// `HasMutInterior`, from a type that does, e.g.:
|
|
||||||
// `let _: &'static _ = &(Cell::new(1), 2).1;`
|
|
||||||
let mut place_projection = &place.projection[..];
|
|
||||||
// FIXME(eddyb) use a forward loop instead of a reverse one.
|
|
||||||
while let &[ref proj_base @ .., elem] = place_projection {
|
|
||||||
// FIXME(eddyb) this is probably excessive, with
|
|
||||||
// the exception of `union` member accesses.
|
|
||||||
let ty =
|
|
||||||
Place::ty_from(place.local, proj_base, self.body, self.tcx)
|
|
||||||
.projection_ty(self.tcx, elem)
|
|
||||||
.ty;
|
|
||||||
if ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) {
|
|
||||||
has_mut_interior = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
place_projection = proj_base;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(eddyb) this duplicates part of `validate_rvalue`.
|
// FIXME(eddyb) this duplicates part of `validate_rvalue`.
|
||||||
|
let has_mut_interior =
|
||||||
|
self.qualif_local::<qualifs::HasMutInterior>(place.local);
|
||||||
if has_mut_interior {
|
if has_mut_interior {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
if self.qualif_local::<qualifs::NeedsDrop>(place.local) {
|
|
||||||
return Err(Unpromotable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let BorrowKind::Mut { .. } = kind {
|
if let BorrowKind::Mut { .. } = kind {
|
||||||
let ty = place.ty(self.body, self.tcx).ty;
|
let ty = place.ty(self.body, self.tcx).ty;
|
||||||
@ -692,28 +667,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||||||
|
|
||||||
self.validate_place(place)?;
|
self.validate_place(place)?;
|
||||||
|
|
||||||
// HACK(eddyb) this should compute the same thing as
|
let has_mut_interior = self.qualif_local::<qualifs::HasMutInterior>(place.local);
|
||||||
// `<HasMutInterior as Qualif>::in_projection` from
|
|
||||||
// `check_consts::qualifs` but without recursion.
|
|
||||||
let mut has_mut_interior =
|
|
||||||
self.qualif_local::<qualifs::HasMutInterior>(place.local);
|
|
||||||
if has_mut_interior {
|
|
||||||
let mut place_projection = place.projection;
|
|
||||||
// FIXME(eddyb) use a forward loop instead of a reverse one.
|
|
||||||
while let &[ref proj_base @ .., elem] = place_projection {
|
|
||||||
// FIXME(eddyb) this is probably excessive, with
|
|
||||||
// the exception of `union` member accesses.
|
|
||||||
let ty = Place::ty_from(place.local, proj_base, self.body, self.tcx)
|
|
||||||
.projection_ty(self.tcx, elem)
|
|
||||||
.ty;
|
|
||||||
if ty.is_freeze(self.tcx.at(DUMMY_SP), self.param_env) {
|
|
||||||
has_mut_interior = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
place_projection = proj_base;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if has_mut_interior {
|
if has_mut_interior {
|
||||||
return Err(Unpromotable);
|
return Err(Unpromotable);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#![allow(unconditional_panic, const_err)]
|
#![allow(unconditional_panic, const_err)]
|
||||||
#![feature(const_fn, const_fn_union)]
|
#![feature(const_fn, const_fn_union)]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
// We do not promote mutable references.
|
// 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 TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]); //~ ERROR temporary value dropped while borrowed
|
||||||
|
|
||||||
@ -32,4 +34,14 @@ const TEST_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
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {}
|
// In a `const`, we do not promote things with interior mutability. Not even if we "project it away".
|
||||||
|
const TEST_INTERIOR_MUT: () = {
|
||||||
|
// The "0." case is already ruled out by not permitting any interior mutability in `const`.
|
||||||
|
let _val: &'static _ = &(Cell::new(1), 2).1; //~ ERROR temporary value dropped while borrowed
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// We must not promote things with interior mutability. Not even if we "project it away".
|
||||||
|
let _val: &'static _ = &(Cell::new(1), 2).0; //~ ERROR temporary value dropped while borrowed
|
||||||
|
let _val: &'static _ = &(Cell::new(1), 2).1; //~ ERROR temporary value dropped while borrowed
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0716]: temporary value dropped while borrowed
|
error[E0716]: temporary value dropped while borrowed
|
||||||
--> $DIR/promote-not.rs:7:50
|
--> $DIR/promote-not.rs:9:50
|
||||||
|
|
|
|
||||||
LL | static mut TEST1: Option<&mut [i32]> = Some(&mut [1, 2, 3]);
|
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`
|
| using this value as a static requires that borrow lasts for `'static`
|
||||||
|
|
||||||
error[E0716]: temporary value dropped while borrowed
|
error[E0716]: temporary value dropped while borrowed
|
||||||
--> $DIR/promote-not.rs:10:18
|
--> $DIR/promote-not.rs:12:18
|
||||||
|
|
|
|
||||||
LL | let x = &mut [1,2,3];
|
LL | let x = &mut [1,2,3];
|
||||||
| ^^^^^^^ creates a temporary which is freed while still in use
|
| ^^^^^^^ creates a temporary which is freed while still in use
|
||||||
@ -19,7 +19,7 @@ LL | };
|
|||||||
| - temporary value is freed at the end of this statement
|
| - temporary value is freed at the end of this statement
|
||||||
|
|
||||||
error[E0716]: temporary value dropped while borrowed
|
error[E0716]: temporary value dropped while borrowed
|
||||||
--> $DIR/promote-not.rs:19:32
|
--> $DIR/promote-not.rs:21:32
|
||||||
|
|
|
|
||||||
LL | let _x: &'static () = &foo();
|
LL | let _x: &'static () = &foo();
|
||||||
| ----------- ^^^^^ creates a temporary which is freed while still in use
|
| ----------- ^^^^^ creates a temporary which is freed while still in use
|
||||||
@ -29,7 +29,7 @@ LL | }
|
|||||||
| - temporary value is freed at the end of this statement
|
| - temporary value is freed at the end of this statement
|
||||||
|
|
||||||
error[E0716]: temporary value dropped while borrowed
|
error[E0716]: temporary value dropped while borrowed
|
||||||
--> $DIR/promote-not.rs:27:29
|
--> $DIR/promote-not.rs:29:29
|
||||||
|
|
|
|
||||||
LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
|
LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
|
||||||
@ -39,7 +39,7 @@ LL | }
|
|||||||
| - temporary value is freed at the end of this statement
|
| - temporary value is freed at the end of this statement
|
||||||
|
|
||||||
error[E0716]: temporary value dropped while borrowed
|
error[E0716]: temporary value dropped while borrowed
|
||||||
--> $DIR/promote-not.rs:32:29
|
--> $DIR/promote-not.rs:34:29
|
||||||
|
|
|
|
||||||
LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
|
LL | let _x: &'static i32 = &unsafe { U { x: 0 }.x };
|
||||||
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
|
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
|
||||||
@ -48,6 +48,37 @@ 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 5 previous errors
|
error[E0716]: temporary value dropped while borrowed
|
||||||
|
--> $DIR/promote-not.rs:40:29
|
||||||
|
|
|
||||||
|
LL | let _val: &'static _ = &(Cell::new(1), 2).1;
|
||||||
|
| ---------- ^^^^^^^^^^^^^^^^^ 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:45:29
|
||||||
|
|
|
||||||
|
LL | let _val: &'static _ = &(Cell::new(1), 2).0;
|
||||||
|
| ---------- ^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
|
||||||
|
| |
|
||||||
|
| type annotation requires that borrow lasts for `'static`
|
||||||
|
LL | let _val: &'static _ = &(Cell::new(1), 2).1;
|
||||||
|
LL | }
|
||||||
|
| - temporary value is freed at the end of this statement
|
||||||
|
|
||||||
|
error[E0716]: temporary value dropped while borrowed
|
||||||
|
--> $DIR/promote-not.rs:46:29
|
||||||
|
|
|
||||||
|
LL | let _val: &'static _ = &(Cell::new(1), 2).1;
|
||||||
|
| ---------- ^^^^^^^^^^^^^^^^^ 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 8 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0716`.
|
For more information about this error, try `rustc --explain E0716`.
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
// run-pass
|
|
||||||
// compile-flags: -Z borrowck=mir
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
|
||||||
|
|
||||||
const FIVE: Cell<i32> = Cell::new(5);
|
|
||||||
|
|
||||||
#[inline(never)]
|
|
||||||
fn tuple_field() -> &'static u32 {
|
|
||||||
// This test is MIR-borrowck-only because the old borrowck
|
|
||||||
// doesn't agree that borrows of "frozen" (i.e., without any
|
|
||||||
// interior mutability) fields of non-frozen temporaries,
|
|
||||||
// should be promoted, while MIR promotion does promote them.
|
|
||||||
&(FIVE, 42).1
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
assert_eq!(tuple_field().to_string(), "42");
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user