Skip to content

Commit d9c177f

Browse files
Rollup merge of #78578 - oli-obk:const_mut_refs, r=RalfJung
Permit mutable references in all const contexts fixes #71212 cc `@rust-lang/wg-const-eval` `@christianpoveda`
2 parents 5a1f2ec + 819b008 commit d9c177f

39 files changed

+431
-208
lines changed

Diff for: compiler/rustc_mir/src/transform/check_consts/ops.rs

+44-27
Original file line numberDiff line numberDiff line change
@@ -268,16 +268,20 @@ impl NonConstOp for CellBorrow {
268268
}
269269

270270
#[derive(Debug)]
271+
/// This op is for `&mut` borrows in the trailing expression of a constant
272+
/// which uses the "enclosing scopes rule" to leak its locals into anonymous
273+
/// static or const items.
271274
pub struct MutBorrow(pub hir::BorrowKind);
272275

273276
impl NonConstOp for MutBorrow {
274-
fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
275-
// Forbid everywhere except in const fn with a feature gate
276-
if ccx.const_kind() == hir::ConstContext::ConstFn {
277-
Status::Unstable(sym::const_mut_refs)
278-
} else {
279-
Status::Forbidden
280-
}
277+
fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
278+
Status::Forbidden
279+
}
280+
281+
fn importance(&self) -> DiagnosticImportance {
282+
// If there were primary errors (like non-const function calls), do not emit further
283+
// errors about mutable references.
284+
DiagnosticImportance::Secondary
281285
}
282286

283287
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
@@ -286,25 +290,15 @@ impl NonConstOp for MutBorrow {
286290
hir::BorrowKind::Ref => "",
287291
};
288292

289-
let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn {
290-
feature_err(
291-
&ccx.tcx.sess.parse_sess,
292-
sym::const_mut_refs,
293-
span,
294-
&format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
295-
)
296-
} else {
297-
let mut err = struct_span_err!(
298-
ccx.tcx.sess,
299-
span,
300-
E0764,
301-
"{}mutable references are not allowed in {}s",
302-
raw,
303-
ccx.const_kind(),
304-
);
305-
err.span_label(span, format!("`&{}mut` is only allowed in `const fn`", raw));
306-
err
307-
};
293+
let mut err = struct_span_err!(
294+
ccx.tcx.sess,
295+
span,
296+
E0764,
297+
"{}mutable references are not allowed in the final value of {}s",
298+
raw,
299+
ccx.const_kind(),
300+
);
301+
308302
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
309303
err.note(
310304
"References in statics and constants may only refer \
@@ -321,6 +315,29 @@ impl NonConstOp for MutBorrow {
321315
}
322316
}
323317

318+
#[derive(Debug)]
319+
pub struct TransientMutBorrow(pub hir::BorrowKind);
320+
321+
impl NonConstOp for TransientMutBorrow {
322+
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
323+
Status::Unstable(sym::const_mut_refs)
324+
}
325+
326+
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
327+
let raw = match self.0 {
328+
hir::BorrowKind::Raw => "raw ",
329+
hir::BorrowKind::Ref => "",
330+
};
331+
332+
feature_err(
333+
&ccx.tcx.sess.parse_sess,
334+
sym::const_mut_refs,
335+
span,
336+
&format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()),
337+
)
338+
}
339+
}
340+
324341
#[derive(Debug)]
325342
pub struct MutDeref;
326343
impl NonConstOp for MutDeref {
@@ -329,7 +346,7 @@ impl NonConstOp for MutDeref {
329346
}
330347

331348
fn importance(&self) -> DiagnosticImportance {
332-
// Usually a side-effect of a `MutBorrow` somewhere.
349+
// Usually a side-effect of a `TransientMutBorrow` somewhere.
333350
DiagnosticImportance::Secondary
334351
}
335352

Diff for: compiler/rustc_mir/src/transform/check_consts/validation.rs

+26-3
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,29 @@ impl Validator<'mir, 'tcx> {
466466
}
467467
}
468468
}
469+
470+
fn check_mut_borrow(&mut self, local: Local, kind: hir::BorrowKind) {
471+
match self.const_kind() {
472+
// In a const fn all borrows are transient or point to the places given via
473+
// references in the arguments (so we already checked them with
474+
// TransientMutBorrow/MutBorrow as appropriate).
475+
// The borrow checker guarantees that no new non-transient borrows are created.
476+
// NOTE: Once we have heap allocations during CTFE we need to figure out
477+
// how to prevent `const fn` to create long-lived allocations that point
478+
// to mutable memory.
479+
hir::ConstContext::ConstFn => self.check_op(ops::TransientMutBorrow(kind)),
480+
_ => {
481+
// Locals with StorageDead do not live beyond the evaluation and can
482+
// thus safely be borrowed without being able to be leaked to the final
483+
// value of the constant.
484+
if self.local_has_storage_dead(local) {
485+
self.check_op(ops::TransientMutBorrow(kind));
486+
} else {
487+
self.check_op(ops::MutBorrow(kind));
488+
}
489+
}
490+
}
491+
}
469492
}
470493

471494
impl Visitor<'tcx> for Validator<'mir, 'tcx> {
@@ -562,15 +585,15 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
562585

563586
if !is_allowed {
564587
if let BorrowKind::Mut { .. } = kind {
565-
self.check_op(ops::MutBorrow(hir::BorrowKind::Ref));
588+
self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
566589
} else {
567590
self.check_op(ops::CellBorrow);
568591
}
569592
}
570593
}
571594

572-
Rvalue::AddressOf(Mutability::Mut, _) => {
573-
self.check_op(ops::MutBorrow(hir::BorrowKind::Raw))
595+
Rvalue::AddressOf(Mutability::Mut, ref place) => {
596+
self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
574597
}
575598

576599
Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)

Diff for: src/test/ui/check-static-immutable-mut-slices.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Checks that immutable static items can't have mutable slices
22

33
static TEST: &'static mut [isize] = &mut [];
4-
//~^ ERROR mutable references are not allowed in statics
4+
//~^ ERROR mutable references are not allowed
55

66
pub fn main() { }

Diff for: src/test/ui/check-static-immutable-mut-slices.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0764]: mutable references are not allowed in statics
1+
error[E0764]: mutable references are not allowed in the final value of statics
22
--> $DIR/check-static-immutable-mut-slices.rs:3:37
33
|
44
LL | static TEST: &'static mut [isize] = &mut [];
5-
| ^^^^^^^ `&mut` is only allowed in `const fn`
5+
| ^^^^^^^
66

77
error: aborting due to previous error
88

Diff for: src/test/ui/consts/const-address-of-mut.stderr

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
1-
error[E0764]: raw mutable references are not allowed in constants
1+
error[E0658]: raw mutable references are not allowed in constants
22
--> $DIR/const-address-of-mut.rs:3:32
33
|
44
LL | const A: () = { let mut x = 2; &raw mut x; };
5-
| ^^^^^^^^^^ `&raw mut` is only allowed in `const fn`
5+
| ^^^^^^^^^^
6+
|
7+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
8+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
69

7-
error[E0764]: raw mutable references are not allowed in statics
10+
error[E0658]: raw mutable references are not allowed in statics
811
--> $DIR/const-address-of-mut.rs:5:33
912
|
1013
LL | static B: () = { let mut x = 2; &raw mut x; };
11-
| ^^^^^^^^^^ `&raw mut` is only allowed in `const fn`
14+
| ^^^^^^^^^^
15+
|
16+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
17+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
1218

13-
error[E0764]: raw mutable references are not allowed in statics
19+
error[E0658]: raw mutable references are not allowed in statics
1420
--> $DIR/const-address-of-mut.rs:7:37
1521
|
1622
LL | static mut C: () = { let mut x = 2; &raw mut x; };
17-
| ^^^^^^^^^^ `&raw mut` is only allowed in `const fn`
23+
| ^^^^^^^^^^
24+
|
25+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
26+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
1827

1928
error[E0658]: raw mutable references are not allowed in constant functions
2029
--> $DIR/const-address-of-mut.rs:11:13
@@ -27,5 +36,4 @@ LL | let y = &raw mut x;
2736

2837
error: aborting due to 4 previous errors
2938

30-
Some errors have detailed explanations: E0658, E0764.
31-
For more information about an error, try `rustc --explain E0658`.
39+
For more information about this error, try `rustc --explain E0658`.

Diff for: src/test/ui/consts/const-eval/issue-65394.stderr

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error[E0764]: mutable references are not allowed in constants
1+
error[E0658]: mutable references are not allowed in constants
22
--> $DIR/issue-65394.rs:8:13
33
|
44
LL | let r = &mut x;
5-
| ^^^^^^ `&mut` is only allowed in `const fn`
5+
| ^^^^^^
6+
|
7+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
8+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
69

710
error[E0493]: destructors cannot be evaluated at compile-time
811
--> $DIR/issue-65394.rs:7:9
@@ -15,5 +18,5 @@ LL | };
1518

1619
error: aborting due to 2 previous errors
1720

18-
Some errors have detailed explanations: E0493, E0764.
21+
Some errors have detailed explanations: E0493, E0658.
1922
For more information about an error, try `rustc --explain E0493`.

Diff for: src/test/ui/consts/const-multi-ref.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
error[E0764]: mutable references are not allowed in constants
1+
error[E0658]: mutable references are not allowed in constants
22
--> $DIR/const-multi-ref.rs:6:13
33
|
44
LL | let p = &mut a;
5-
| ^^^^^^ `&mut` is only allowed in `const fn`
5+
| ^^^^^^
6+
|
7+
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
8+
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
69

710
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
811
--> $DIR/const-multi-ref.rs:16:13
@@ -15,5 +18,4 @@ LL | let p = &a;
1518

1619
error: aborting due to 2 previous errors
1720

18-
Some errors have detailed explanations: E0658, E0764.
19-
For more information about an error, try `rustc --explain E0658`.
21+
For more information about this error, try `rustc --explain E0658`.

Diff for: src/test/ui/consts/const-mut-refs/const_mut_address_of.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// check-pass
12
#![feature(const_mut_refs)]
23
#![feature(const_fn)]
34
#![feature(raw_ref_op)]
@@ -22,9 +23,7 @@ const fn baz(foo: &mut Foo)-> *mut usize {
2223

2324
const _: () = {
2425
foo().bar();
25-
//~^ ERROR mutable references are not allowed in constants
2626
baz(&mut foo());
27-
//~^ ERROR mutable references are not allowed in constants
2827
};
2928

3029
fn main() {}

Diff for: src/test/ui/consts/const-mut-refs/const_mut_address_of.stderr

-15
This file was deleted.

Diff for: src/test/ui/consts/const-mut-refs/const_mut_refs.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// check-pass
12
#![feature(const_mut_refs)]
23

34
struct Foo {
@@ -29,9 +30,6 @@ const fn bazz(foo: &mut Foo) -> usize {
2930

3031
fn main() {
3132
let _: [(); foo().bar()] = [(); 1];
32-
//~^ ERROR mutable references are not allowed in constants
3333
let _: [(); baz(&mut foo())] = [(); 2];
34-
//~^ ERROR mutable references are not allowed in constants
3534
let _: [(); bazz(&mut foo())] = [(); 3];
36-
//~^ ERROR mutable references are not allowed in constants
3735
}

Diff for: src/test/ui/consts/const-mut-refs/const_mut_refs.stderr

-21
This file was deleted.
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#![feature(const_mut_refs)]
2+
#![feature(const_fn)]
3+
#![feature(raw_ref_op)]
4+
#![feature(const_raw_ptr_deref)]
5+
6+
const NULL: *mut i32 = std::ptr::null_mut();
7+
const A: *const i32 = &4;
8+
9+
// It could be made sound to allow it to compile,
10+
// but we do not want to allow this to compile,
11+
// as that would be an enormous footgun in oli-obk's opinion.
12+
const B: *mut i32 = &mut 4; //~ ERROR mutable references are not allowed
13+
14+
// Ok, no actual mutable allocation exists
15+
const B2: Option<&mut i32> = None;
16+
17+
// Not ok, can't prove that no mutable allocation ends up in final value
18+
const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR temporary value dropped while borrowed
19+
20+
const fn helper(x: &mut i32) -> Option<&mut i32> { Some(x) }
21+
const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped while borrowed
22+
23+
// Ok, because no references to mutable data exist here, since the `{}` moves
24+
// its value and then takes a reference to that.
25+
const C: *const i32 = &{
26+
let mut x = 42;
27+
x += 3;
28+
x
29+
};
30+
31+
use std::cell::UnsafeCell;
32+
struct NotAMutex<T>(UnsafeCell<T>);
33+
34+
unsafe impl<T> Sync for NotAMutex<T> {}
35+
36+
const FOO: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
37+
//~^ ERROR temporary value dropped while borrowed
38+
39+
static FOO2: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
40+
//~^ ERROR temporary value dropped while borrowed
41+
42+
static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
43+
//~^ ERROR temporary value dropped while borrowed
44+
45+
// `BAR` works, because `&42` promotes immediately instead of relying on
46+
// the enclosing scope rule.
47+
const BAR: NotAMutex<&i32> = NotAMutex(UnsafeCell::new(&42));
48+
49+
fn main() {
50+
println!("{}", unsafe { *A });
51+
unsafe { *B = 4 } // Bad news
52+
53+
unsafe {
54+
**FOO.0.get() = 99;
55+
assert_eq!(**FOO.0.get(), 99);
56+
}
57+
}

0 commit comments

Comments
 (0)