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
This commit is contained in:
bors 2018-07-01 23:00:27 +00:00
commit c697a56d01
25 changed files with 127 additions and 236 deletions

View file

@ -29,7 +29,6 @@ static Y: u32 = 0;
const fn get_Y() -> u32 {
Y
//~^ ERROR E0013
//~| ERROR cannot refer to statics by value
}
const fn get_Y_addr() -> &'static u32 {
@ -49,5 +48,4 @@ const fn get() -> u32 {
//~| ERROR let bindings in constant functions are unstable
}
fn main() {
}
fn main() {}

View file

@ -13,6 +13,10 @@
extern {
pub static symbol: ();
}
static CRASH: () = symbol; //~ cannot refer to other statics by value
static CRASH: () = symbol;
//~^ ERROR could not evaluate static initializer
//~| tried to read from foreign (extern) static
//~^^^ ERROR could not evaluate static initializer
//~| tried to read from foreign (extern) static
fn main() {}

View file

@ -22,7 +22,6 @@ mod Y {
static foo: *const Y::X = Y::foo(Y::x as *const Y::X);
//~^ ERROR `*const usize` cannot be shared between threads safely [E0277]
//~| ERROR cannot refer to other statics by value, use the address-of operator or a constant instead
//~| ERROR E0015
fn main() {}

View file

@ -22,14 +22,13 @@ static T4: &'static usize = &S;
const T5: usize = C;
const T6: usize = S; //~ ERROR: constants cannot refer to statics
//~^ cannot refer to statics
static T7: usize = C;
static T8: usize = S; //~ ERROR: cannot refer to other statics by value
static T8: usize = S;
const T9: Struct = Struct { a: C };
const T10: Struct = Struct { a: S }; //~ ERROR: cannot refer to statics by value
const T10: Struct = Struct { a: S };
//~^ ERROR: constants cannot refer to statics
static T11: Struct = Struct { a: C };
static T12: Struct = Struct { a: S }; //~ ERROR: cannot refer to other statics by value
static T12: Struct = Struct { a: S };
fn main() {}

View file

@ -15,6 +15,9 @@ extern {
}
pub static BAZ: u32 = *&error_message_count;
//~^ ERROR cannot refer to other statics by value
//~^ ERROR could not evaluate static initializer
//~| tried to read from foreign (extern) static
//~^^^ ERROR could not evaluate static initializer
//~| tried to read from foreign (extern) static
fn main() {}

View file

@ -1,4 +1,4 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,12 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo {
a: u32
}
pub static FOO: u32 = FOO;
//~^ ERROR cycle detected when const-evaluating `FOO`
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; //~ ERROR E0494
fn main() {
}
fn main() {}

View file

@ -15,14 +15,12 @@ static A: u32 = 1;
static B: u32 = A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
//~| ERROR cannot refer to other statics by value
static C: &u32 = &A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
const D: u32 = A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
//~| ERROR cannot refer to statics by value
const E: &u32 = &A;
//~^ ERROR thread-local statics cannot be accessed at compile-time
@ -30,7 +28,6 @@ const E: &u32 = &A;
const fn f() -> u32 {
A
//~^ ERROR thread-local statics cannot be accessed at compile-time
//~| ERROR cannot refer to statics by value
}
fn main() {}

View file

@ -0,0 +1,22 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(const_let)]
pub static mut A: u32 = 0;
pub static mut B: () = unsafe { A = 1; };
//~^ ERROR statements in statics are unstable
pub static mut C: u32 = unsafe { C = 1; 0 };
//~^ ERROR statements in statics are unstable
pub static D: u32 = D;
fn main() {}

View file

@ -0,0 +1,25 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// revisions:rpass1 rpass2
#[cfg(rpass1)]
pub static A: u8 = 42;
#[cfg(rpass2)]
pub static A: u8 = 43;
static B: &u8 = &C.1;
static C: (&&u8, u8) = (&B, A);
fn main() {
assert_eq!(*B, A);
assert_eq!(**C.0, A);
assert_eq!(C.1, A);
}

View file

@ -11,9 +11,6 @@
#![allow(dead_code, warnings)]
static mut x: isize = 3;
static mut y: isize = unsafe {
x
//~^ ERROR cannot refer to other statics by value, use the address-of operator or a constant instea
};
static mut y: isize = unsafe { x };
fn main() {}

View file

@ -9,17 +9,19 @@
// except according to those terms.
struct S { a: usize }
static A: S = S { a: 3 };
static A: S = S { a: 3 };
static B: &'static usize = &A.a;
//~^ ERROR: cannot refer to the interior of another static
static C: &'static usize = &(A.a);
//~^ ERROR: cannot refer to the interior of another static
static D: [usize; 1] = [1];
static E: usize = D[0];
//~^ ERROR: cannot refer to the interior of another static
//~^^ ERROR: cannot refer to other statics by value
static F: &'static usize = &D[0];
//~^ ERROR: cannot refer to the interior of another static
fn main() {}
fn main() {
assert_eq!(*B, A.a);
assert_eq!(*B, A.a);
assert_eq!(E, D[0]);
assert_eq!(*F, D[0]);
}

View file

@ -16,6 +16,5 @@ struct A {
static B: &'static A = &A { a: &() };
static C: &'static A = &B;
//~^ ERROR cannot refer to other statics by value
fn main() {}

View file

@ -10,6 +10,5 @@
static x: &'static usize = &1;
static y: usize = *x;
//~^ ERROR cannot refer to other statics by value,
// use the address-of operator or a constant instead
fn main() {}

View file

@ -1,4 +1,4 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
@ -8,11 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(warnings)]
static A: u32 = 0;
static B: u32 = A;
//~^ ERROR E0394
static A: usize = 42;
static B: usize = A;
fn main() {
assert_eq!(B, 42);
}

View file

@ -15,13 +15,7 @@ extern crate pub_static_array as array;
use array::ARRAY;
static X: &'static u8 = &ARRAY[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
static Y: &'static u8 = &(&ARRAY)[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
static Z: u8 = (&ARRAY)[0];
//~^ ERROR: cannot refer to the interior of another static, use a constant
//~^^ ERROR: cannot refer to other statics by value
pub fn main() {}

View file

@ -1,11 +0,0 @@
error[E0394]: cannot refer to other statics by value, use the address-of operator or a constant instead
--> $DIR/E0394.rs:14:17
|
LL | static B: u32 = A;
| ^ referring to another static by value
|
= note: use the address-of operator or a constant instead
error: aborting due to previous error
For more information about this error, try `rustc --explain E0394`.

View file

@ -1,9 +0,0 @@
error[E0494]: cannot refer to the interior of another static, use a constant instead
--> $DIR/E0494.rs:16:27
|
LL | static A : &'static u32 = &S.a; //~ ERROR E0494
| ^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0494`.