Skip to content

Commit 8989689

Browse files
committed
Auto merge of rust-lang#80418 - oli-obk:this_could_have_been_so_simple, r=RalfJung
Allow references to interior mutable data behind a feature gate supercedes rust-lang#80373 by simply not checking for interior mutability on borrows of locals that have `StorageDead` and thus can never be leaked to the final value of the constant tracking issue: rust-lang#80384 r? `@RalfJung`
2 parents 8018418 + 90b56b9 commit 8989689

24 files changed

+230
-62
lines changed

Diff for: compiler/rustc_error_codes/src/error_codes/E0492.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Erroneous code example:
66
use std::sync::atomic::AtomicUsize;
77
88
const A: AtomicUsize = AtomicUsize::new(0);
9-
static B: &'static AtomicUsize = &A;
9+
const B: &'static AtomicUsize = &A;
1010
// error: cannot borrow a constant which may contain interior mutability,
1111
// create a static instead
1212
```
@@ -18,7 +18,7 @@ can't be changed via a shared `&` pointer, but interior mutability would allow
1818
it. That is, a constant value could be mutated. On the other hand, a `static` is
1919
explicitly a single memory location, which can be mutated at will.
2020

21-
So, in order to solve this error, either use statics which are `Sync`:
21+
So, in order to solve this error, use statics which are `Sync`:
2222

2323
```
2424
use std::sync::atomic::AtomicUsize;

Diff for: compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,9 @@ declare_features! (
626626
/// Allows const generics to have default values (e.g. `struct Foo<const N: usize = 3>(...);`).
627627
(active, const_generics_defaults, "1.51.0", Some(44580), None),
628628

629+
/// Allows references to types with interior mutability within constants
630+
(active, const_refs_to_cell, "1.51.0", Some(80384), None),
631+
629632
// -------------------------------------------------------------------------
630633
// feature-group-end: actual feature gates
631634
// -------------------------------------------------------------------------

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

+49-4
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,61 @@ impl NonConstOp for LiveDrop {
209209
}
210210

211211
#[derive(Debug)]
212+
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
213+
/// the final value of the constant.
214+
pub struct TransientCellBorrow;
215+
impl NonConstOp for TransientCellBorrow {
216+
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
217+
Status::Unstable(sym::const_refs_to_cell)
218+
}
219+
fn importance(&self) -> DiagnosticImportance {
220+
// The cases that cannot possibly work will already emit a `CellBorrow`, so we should
221+
// not additionally emit a feature gate error if activating the feature gate won't work.
222+
DiagnosticImportance::Secondary
223+
}
224+
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
225+
feature_err(
226+
&ccx.tcx.sess.parse_sess,
227+
sym::const_refs_to_cell,
228+
span,
229+
"cannot borrow here, since the borrowed element may contain interior mutability",
230+
)
231+
}
232+
}
233+
234+
#[derive(Debug)]
235+
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow might escape to
236+
/// the final value of the constant, and thus we cannot allow this (for now). We may allow
237+
/// it in the future for static items.
212238
pub struct CellBorrow;
213239
impl NonConstOp for CellBorrow {
214240
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
215-
struct_span_err!(
241+
let mut err = struct_span_err!(
216242
ccx.tcx.sess,
217243
span,
218244
E0492,
219-
"cannot borrow a constant which may contain \
220-
interior mutability, create a static instead"
221-
)
245+
"{}s cannot refer to interior mutable data",
246+
ccx.const_kind(),
247+
);
248+
err.span_label(
249+
span,
250+
format!("this borrow of an interior mutable value may end up in the final value"),
251+
);
252+
if let hir::ConstContext::Static(_) = ccx.const_kind() {
253+
err.help(
254+
"to fix this, the value can be extracted to a separate \
255+
`static` item and then referenced",
256+
);
257+
}
258+
if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
259+
err.note(
260+
"A constant containing interior mutable data behind a reference can allow you
261+
to modify that data. This would make multiple uses of a constant to be able to
262+
see different values and allow circumventing the `Send` and `Sync` requirements
263+
for shared mutable data, which is unsound.",
264+
);
265+
}
266+
err
222267
}
223268
}
224269

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

+49-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
44
use rustc_hir::def_id::DefId;
55
use rustc_hir::{self as hir, HirId, LangItem};
6+
use rustc_index::bit_set::BitSet;
67
use rustc_infer::infer::TyCtxtInferExt;
78
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
89
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
@@ -188,6 +189,9 @@ pub struct Validator<'mir, 'tcx> {
188189
/// The span of the current statement.
189190
span: Span,
190191

192+
/// A set that stores for each local whether it has a `StorageDead` for it somewhere.
193+
local_has_storage_dead: Option<BitSet<Local>>,
194+
191195
error_emitted: Option<ErrorReported>,
192196
secondary_errors: Vec<Diagnostic>,
193197
}
@@ -206,6 +210,7 @@ impl Validator<'mir, 'tcx> {
206210
span: ccx.body.span,
207211
ccx,
208212
qualifs: Default::default(),
213+
local_has_storage_dead: None,
209214
error_emitted: None,
210215
secondary_errors: Vec::new(),
211216
}
@@ -282,6 +287,27 @@ impl Validator<'mir, 'tcx> {
282287
}
283288
}
284289

290+
fn local_has_storage_dead(&mut self, local: Local) -> bool {
291+
let ccx = self.ccx;
292+
self.local_has_storage_dead
293+
.get_or_insert_with(|| {
294+
struct StorageDeads {
295+
locals: BitSet<Local>,
296+
}
297+
impl Visitor<'tcx> for StorageDeads {
298+
fn visit_statement(&mut self, stmt: &Statement<'tcx>, _: Location) {
299+
if let StatementKind::StorageDead(l) = stmt.kind {
300+
self.locals.insert(l);
301+
}
302+
}
303+
}
304+
let mut v = StorageDeads { locals: BitSet::new_empty(ccx.body.local_decls.len()) };
305+
v.visit_body(ccx.body);
306+
v.locals
307+
})
308+
.contains(local)
309+
}
310+
285311
pub fn qualifs_in_return_place(&mut self) -> ConstQualifs {
286312
self.qualifs.in_return_place(self.ccx, self.error_emitted)
287313
}
@@ -556,7 +582,29 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> {
556582
);
557583

558584
if borrowed_place_has_mut_interior {
559-
self.check_op(ops::CellBorrow);
585+
match self.const_kind() {
586+
// In a const fn all borrows are transient or point to the places given via
587+
// references in the arguments (so we already checked them with
588+
// TransientCellBorrow/CellBorrow as appropriate).
589+
// The borrow checker guarantees that no new non-transient borrows are created.
590+
// NOTE: Once we have heap allocations during CTFE we need to figure out
591+
// how to prevent `const fn` to create long-lived allocations that point
592+
// to (interior) mutable memory.
593+
hir::ConstContext::ConstFn => self.check_op(ops::TransientCellBorrow),
594+
_ => {
595+
// Locals with StorageDead are definitely not part of the final constant value, and
596+
// it is thus inherently safe to permit such locals to have their
597+
// address taken as we can't end up with a reference to them in the
598+
// final value.
599+
// Note: This is only sound if every local that has a `StorageDead` has a
600+
// `StorageDead` in every control flow path leading to a `return` terminator.
601+
if self.local_has_storage_dead(place.local) {
602+
self.check_op(ops::TransientCellBorrow);
603+
} else {
604+
self.check_op(ops::CellBorrow);
605+
}
606+
}
607+
}
560608
}
561609
}
562610

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ symbols! {
381381
const_ptr,
382382
const_raw_ptr_deref,
383383
const_raw_ptr_to_usize_cast,
384+
const_refs_to_cell,
384385
const_slice_ptr,
385386
const_trait_bound_opt_out,
386387
const_trait_impl,
+17-5
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
1-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
1+
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
22
--> $DIR/const-address-of-interior-mut.rs:5:39
33
|
44
LL | const A: () = { let x = Cell::new(2); &raw const x; };
55
| ^^^^^^^^^^^^
6+
|
7+
= note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
8+
= help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
69

7-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
10+
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
811
--> $DIR/const-address-of-interior-mut.rs:7:40
912
|
1013
LL | static B: () = { let x = Cell::new(2); &raw const x; };
1114
| ^^^^^^^^^^^^
15+
|
16+
= note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
17+
= help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
1218

13-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
19+
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
1420
--> $DIR/const-address-of-interior-mut.rs:9:44
1521
|
1622
LL | static mut C: () = { let x = Cell::new(2); &raw const x; };
1723
| ^^^^^^^^^^^^
24+
|
25+
= note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
26+
= help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
1827

19-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
28+
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
2029
--> $DIR/const-address-of-interior-mut.rs:13:13
2130
|
2231
LL | let y = &raw const x;
2332
| ^^^^^^^^^^^^
33+
|
34+
= note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
35+
= help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
2436

2537
error: aborting due to 4 previous errors
2638

27-
For more information about this error, try `rustc --explain E0492`.
39+
For more information about this error, try `rustc --explain E0658`.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const _: i32 = {
1313

1414
const _: std::cell::Cell<i32> = {
1515
let mut a = std::cell::Cell::new(5);
16-
let p = &a; //~ ERROR cannot borrow a constant which may contain interior mutability
16+
let p = &a; //~ ERROR borrowed element may contain interior mutability
1717

1818
let reborrow = {p};
1919
let pp = &reborrow;

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ error[E0764]: mutable references are not allowed in constants
44
LL | let p = &mut a;
55
| ^^^^^^ `&mut` is only allowed in `const fn`
66

7-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
7+
error[E0658]: cannot borrow here, since the borrowed element may contain interior mutability
88
--> $DIR/const-multi-ref.rs:16:13
99
|
1010
LL | let p = &a;
1111
| ^^
12+
|
13+
= note: see issue #80384 <https://github.com/rust-lang/rust/issues/80384> for more information
14+
= help: add `#![feature(const_refs_to_cell)]` to the crate attributes to enable
1215

1316
error: aborting due to 2 previous errors
1417

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

Diff for: src/test/ui/consts/partial_qualif.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::cell::Cell;
33
const FOO: &(Cell<usize>, bool) = {
44
let mut a = (Cell::new(0), false);
55
a.1 = true; // sets `qualif(a)` to `qualif(a) | qualif(true)`
6-
&{a} //~ ERROR cannot borrow a constant which may contain interior mutability
6+
&{a} //~ ERROR cannot refer to interior mutable
77
};
88

99
fn main() {}

Diff for: src/test/ui/consts/partial_qualif.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
1+
error[E0492]: constants cannot refer to interior mutable data
22
--> $DIR/partial_qualif.rs:6:5
33
|
44
LL | &{a}
5-
| ^^^^
5+
| ^^^^ this borrow of an interior mutable value may end up in the final value
66

77
error: aborting due to previous error
88

Diff for: src/test/ui/consts/qualif_overwrite.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::cell::Cell;
77
const FOO: &Option<Cell<usize>> = {
88
let mut a = Some(Cell::new(0));
99
a = None; // sets `qualif(a)` to `qualif(a) | qualif(None)`
10-
&{a} //~ ERROR cannot borrow a constant which may contain interior mutability
10+
&{a} //~ ERROR cannot refer to interior mutable
1111
};
1212

1313
fn main() {}

Diff for: src/test/ui/consts/qualif_overwrite.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
1+
error[E0492]: constants cannot refer to interior mutable data
22
--> $DIR/qualif_overwrite.rs:10:5
33
|
44
LL | &{a}
5-
| ^^^^
5+
| ^^^^ this borrow of an interior mutable value may end up in the final value
66

77
error: aborting due to previous error
88

Diff for: src/test/ui/consts/qualif_overwrite_2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::cell::Cell;
55
const FOO: &Option<Cell<usize>> = {
66
let mut a = (Some(Cell::new(0)),);
77
a.0 = None; // sets `qualif(a)` to `qualif(a) | qualif(None)`
8-
&{a.0} //~ ERROR cannot borrow a constant which may contain interior mutability
8+
&{a.0} //~ ERROR cannot refer to interior mutable
99
};
1010

1111
fn main() {}

Diff for: src/test/ui/consts/qualif_overwrite_2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0492]: cannot borrow a constant which may contain interior mutability, create a static instead
1+
error[E0492]: constants cannot refer to interior mutable data
22
--> $DIR/qualif_overwrite_2.rs:8:5
33
|
44
LL | &{a.0}
5-
| ^^^^^^
5+
| ^^^^^^ this borrow of an interior mutable value may end up in the final value
66

77
error: aborting due to previous error
88

Diff for: src/test/ui/consts/std/cell.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1+
#![feature(const_refs_to_cell)]
2+
13
use std::cell::*;
24

3-
// not ok, because this would create a silent constant with interior mutability.
4-
// the rules could be relaxed in the future
5+
// not ok, because this creates a dangling pointer, just like `let x = Cell::new(42).as_ptr()` would
56
static FOO: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr());
6-
//~^ ERROR cannot borrow a constant which may contain interior mutability
7+
//~^ ERROR encountered dangling pointer
8+
const FOO_CONST: Wrap<*mut u32> = Wrap(Cell::new(42).as_ptr());
9+
//~^ ERROR encountered dangling pointer
710

11+
// Ok, these are just base values and it is the `Wrap` author's job to uphold `Send` and `Sync`
12+
// invariants, since they used `unsafe impl`.
813
static FOO3: Wrap<Cell<u32>> = Wrap(Cell::new(42));
9-
// ok
14+
const FOO3_CONST: Wrap<Cell<u32>> = Wrap(Cell::new(42));
15+
16+
// ok, we are referring to the memory of another static item.
1017
static FOO4: Wrap<*mut u32> = Wrap(FOO3.0.as_ptr());
1118

12-
// not ok, because the `as_ptr` call takes a reference to a type with interior mutability
13-
// which is not allowed in constants
19+
// not ok, the use of a constant here is equivalent to an inline declaration of the value, so
20+
// its memory will get freed before the constant is finished evaluating, thus creating a dangling
21+
// pointer. This would happen exactly the same at runtime.
22+
const FOO4_CONST: Wrap<*mut u32> = Wrap(FOO3_CONST.0.as_ptr());
23+
//~^ ERROR encountered dangling pointer
24+
25+
// not ok, because the `as_ptr` call takes a reference to a temporary that will get freed
26+
// before the constant is finished evaluating.
1427
const FOO2: *mut u32 = Cell::new(42).as_ptr();
15-
//~^ ERROR cannot borrow a constant which may contain interior mutability
28+
//~^ ERROR encountered dangling pointer
1629

1730
struct IMSafeTrustMe(UnsafeCell<u32>);
1831
unsafe impl Send for IMSafeTrustMe {}
@@ -21,10 +34,13 @@ unsafe impl Sync for IMSafeTrustMe {}
2134
static BAR: IMSafeTrustMe = IMSafeTrustMe(UnsafeCell::new(5));
2235

2336

37+
2438
struct Wrap<T>(T);
2539
unsafe impl<T> Send for Wrap<T> {}
2640
unsafe impl<T> Sync for Wrap<T> {}
2741

2842
static BAR_PTR: Wrap<*mut u32> = Wrap(BAR.0.get());
2943

44+
const fn fst_ref<T, U>(x: &(T, U)) -> &T { &x.0 }
45+
3046
fn main() {}

0 commit comments

Comments
 (0)