Skip to content

Commit c697a56

Browse files
committed
Auto merge of #51110 - alexreg:new-static-eval-rules, r=eddyb
Loosened rules involving statics mentioning other statics Before this PR, trying to mention a static in any way other than taking a reference to it caused a compile-time error. So, while ```rust static A: u32 = 42; static B: &u32 = &A; ``` compiles successfully, ```rust static A: u32 = 42; static B: u32 = A; // error ``` and ```rust static A: u32 = 42; static B: u32 = *&A; // error ``` are not possible to express in Rust. On the other hand, introducing an intermediate `const fn` can presently allow one to do just that: ```rust static A: u32 = 42; static B: u32 = foo(&A); // success! const fn foo(a: &u32) -> u32 { *a } ``` Preventing `const fn` from allowing to work around the ban on reading from statics would cripple `const fn` almost into uselessness. Additionally, the limitation for reading from statics comes from the old const evaluator(s) and is not shared by `miri`. This PR loosens the rules around use of statics to allow statics to evaluate other statics by value, allowing all of the above examples to compile and run successfully. Reads from extern (foreign) statics are however still disallowed by miri, because there is no compile-time value to be read. ```rust extern static A: u32; static B: u32 = A; // error ``` This opens up a new avenue of potential issues, as a static can now not just refer to other statics or read from other statics, but even contain references that point into itself. While it might seem like this could cause subtle bugs like allowing a static to be initialized by its own value, this is inherently impossible in miri. Reading from a static causes the `const_eval` query for that static to be invoked. Calling the `const_eval` query for a static while already inside the `const_eval` query of said static will cause cycle errors. It is not possible to accidentally create a bug in miri that would enable initializing a static with itself, because the memory of the static *does not exist* while being initialized. The memory is not uninitialized, it is not there. Thus any change that would accidentally allow reading from a not yet initialized static would cause ICEs. Tests have been modified according to the new rules, and new tests have been added for writing to `static mut`s within definitions of statics (which needs to fail), and incremental compilation with complex/interlinking static definitions. Note that incremental compilation did not need to be adjusted, because all of this was already possible before with workarounds (like intermediate `const fn`s) and the encoding/decoding already supports all the possible cases. r? @eddyb
2 parents a7a60dc + 1c34227 commit c697a56

25 files changed

+127
-236
lines changed

src/librustc/ich/impls_ty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
521521
InvalidNullPointerUsage |
522522
ReadPointerAsBytes |
523523
ReadBytesAsPointer |
524+
ReadForeignStatic |
524525
InvalidPointerMath |
525526
ReadUndefBytes |
526527
DeadLocal |

src/librustc/mir/interpret/error.rs

+3
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ pub enum EvalErrorKind<'tcx, O> {
188188
InvalidNullPointerUsage,
189189
ReadPointerAsBytes,
190190
ReadBytesAsPointer,
191+
ReadForeignStatic,
191192
InvalidPointerMath,
192193
ReadUndefBytes,
193194
DeadLocal,
@@ -304,6 +305,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
304305
"a raw memory access tried to access part of a pointer value as raw bytes",
305306
ReadBytesAsPointer =>
306307
"a memory access tried to interpret some bytes as a pointer",
308+
ReadForeignStatic =>
309+
"tried to read from foreign (extern) static",
307310
InvalidPointerMath =>
308311
"attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations",
309312
ReadUndefBytes =>

src/librustc/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
506506
InvalidNullPointerUsage => InvalidNullPointerUsage,
507507
ReadPointerAsBytes => ReadPointerAsBytes,
508508
ReadBytesAsPointer => ReadBytesAsPointer,
509+
ReadForeignStatic => ReadForeignStatic,
509510
InvalidPointerMath => InvalidPointerMath,
510511
ReadUndefBytes => ReadUndefBytes,
511512
DeadLocal => DeadLocal,

src/librustc_mir/diagnostics.rs

-55
Original file line numberDiff line numberDiff line change
@@ -1145,33 +1145,6 @@ fn main() {
11451145
```
11461146
"##,
11471147

1148-
E0394: r##"
1149-
A static was referred to by value by another static.
1150-
1151-
Erroneous code examples:
1152-
1153-
```compile_fail,E0394
1154-
static A: u32 = 0;
1155-
static B: u32 = A; // error: cannot refer to other statics by value, use the
1156-
// address-of operator or a constant instead
1157-
```
1158-
1159-
A static cannot be referred by value. To fix this issue, either use a
1160-
constant:
1161-
1162-
```
1163-
const A: u32 = 0; // `A` is now a constant
1164-
static B: u32 = A; // ok!
1165-
```
1166-
1167-
Or refer to `A` by reference:
1168-
1169-
```
1170-
static A: u32 = 0;
1171-
static B: &'static u32 = &A; // ok!
1172-
```
1173-
"##,
1174-
11751148
E0395: r##"
11761149
The value assigned to a constant scalar must be known at compile time,
11771150
which is not the case when comparing raw pointers.
@@ -1333,34 +1306,6 @@ Remember this solution is unsafe! You will have to ensure that accesses to the
13331306
cell are synchronized.
13341307
"##,
13351308

1336-
E0494: r##"
1337-
A reference of an interior static was assigned to another const/static.
1338-
Erroneous code example:
1339-
1340-
```compile_fail,E0494
1341-
struct Foo {
1342-
a: u32
1343-
}
1344-
1345-
static S : Foo = Foo { a : 0 };
1346-
static A : &'static u32 = &S.a;
1347-
// error: cannot refer to the interior of another static, use a
1348-
// constant instead
1349-
```
1350-
1351-
The "base" variable has to be a const if you want another static/const variable
1352-
to refer to one of its fields. Example:
1353-
1354-
```
1355-
struct Foo {
1356-
a: u32
1357-
}
1358-
1359-
const S : Foo = Foo { a : 0 };
1360-
static A : &'static u32 = &S.a; // ok!
1361-
```
1362-
"##,
1363-
13641309
E0499: r##"
13651310
A variable was borrowed as mutable more than once. Erroneous code example:
13661311

src/librustc_mir/interpret/const_eval.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
374374
Ok(None)
375375
} else {
376376
Err(
377-
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
377+
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
378378
)
379379
}
380380
}
@@ -404,7 +404,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
404404
_dest: Place,
405405
) -> EvalResult<'tcx> {
406406
Err(
407-
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
407+
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
408408
)
409409
}
410410

src/librustc_mir/interpret/memory.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
279279
/// Allocation accessors
280280
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
281281
fn const_eval_static(&self, def_id: DefId) -> EvalResult<'tcx, &'tcx Allocation> {
282+
if self.tcx.is_foreign_item(def_id) {
283+
return err!(ReadForeignStatic);
284+
}
282285
let instance = Instance::mono(self.tcx.tcx, def_id);
283286
let gid = GlobalId {
284287
instance,
@@ -302,7 +305,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
302305
Some(alloc) => Ok(alloc),
303306
None => {
304307
// static alloc?
305-
match self.tcx.alloc_map.lock().get(id) {
308+
let alloc = self.tcx.alloc_map.lock().get(id);
309+
match alloc {
306310
Some(AllocType::Memory(mem)) => Ok(mem),
307311
Some(AllocType::Function(..)) => {
308312
Err(EvalErrorKind::DerefFunctionPointer.into())

0 commit comments

Comments
 (0)