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

@ -521,6 +521,7 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
InvalidNullPointerUsage |
ReadPointerAsBytes |
ReadBytesAsPointer |
ReadForeignStatic |
InvalidPointerMath |
ReadUndefBytes |
DeadLocal |

View File

@ -188,6 +188,7 @@ pub enum EvalErrorKind<'tcx, O> {
InvalidNullPointerUsage,
ReadPointerAsBytes,
ReadBytesAsPointer,
ReadForeignStatic,
InvalidPointerMath,
ReadUndefBytes,
DeadLocal,
@ -304,6 +305,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
"a raw memory access tried to access part of a pointer value as raw bytes",
ReadBytesAsPointer =>
"a memory access tried to interpret some bytes as a pointer",
ReadForeignStatic =>
"tried to read from foreign (extern) static",
InvalidPointerMath =>
"attempted to do invalid arithmetic on pointers that would leak base addresses, e.g. comparing pointers into different allocations",
ReadUndefBytes =>

View File

@ -506,6 +506,7 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
InvalidNullPointerUsage => InvalidNullPointerUsage,
ReadPointerAsBytes => ReadPointerAsBytes,
ReadBytesAsPointer => ReadBytesAsPointer,
ReadForeignStatic => ReadForeignStatic,
InvalidPointerMath => InvalidPointerMath,
ReadUndefBytes => ReadUndefBytes,
DeadLocal => DeadLocal,

View File

@ -1145,33 +1145,6 @@ fn main() {
```
"##,
E0394: r##"
A static was referred to by value by another static.
Erroneous code examples:
```compile_fail,E0394
static A: u32 = 0;
static B: u32 = A; // error: cannot refer to other statics by value, use the
// address-of operator or a constant instead
```
A static cannot be referred by value. To fix this issue, either use a
constant:
```
const A: u32 = 0; // `A` is now a constant
static B: u32 = A; // ok!
```
Or refer to `A` by reference:
```
static A: u32 = 0;
static B: &'static u32 = &A; // ok!
```
"##,
E0395: r##"
The value assigned to a constant scalar must be known at compile time,
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
cell are synchronized.
"##,
E0494: r##"
A reference of an interior static was assigned to another const/static.
Erroneous code example:
```compile_fail,E0494
struct Foo {
a: u32
}
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a;
// error: cannot refer to the interior of another static, use a
// constant instead
```
The "base" variable has to be a const if you want another static/const variable
to refer to one of its fields. Example:
```
struct Foo {
a: u32
}
const S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; // ok!
```
"##,
E0499: r##"
A variable was borrowed as mutable more than once. Erroneous code example:

View File

@ -374,7 +374,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
Ok(None)
} else {
Err(
ConstEvalError::NeedsRfc("Pointer arithmetic or comparison".to_string()).into(),
ConstEvalError::NeedsRfc("pointer arithmetic or comparison".to_string()).into(),
)
}
}
@ -404,7 +404,7 @@ impl<'mir, 'tcx> super::Machine<'mir, 'tcx> for CompileTimeEvaluator {
_dest: Place,
) -> EvalResult<'tcx> {
Err(
ConstEvalError::NeedsRfc("Heap allocations via `box` keyword".to_string()).into(),
ConstEvalError::NeedsRfc("heap allocations via `box` keyword".to_string()).into(),
)
}

View File

@ -279,6 +279,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
/// Allocation accessors
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
fn const_eval_static(&self, def_id: DefId) -> EvalResult<'tcx, &'tcx Allocation> {
if self.tcx.is_foreign_item(def_id) {
return err!(ReadForeignStatic);
}
let instance = Instance::mono(self.tcx.tcx, def_id);
let gid = GlobalId {
instance,
@ -302,7 +305,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
Some(alloc) => Ok(alloc),
None => {
// static alloc?
match self.tcx.alloc_map.lock().get(id) {
let alloc = self.tcx.alloc_map.lock().get(id);
match alloc {
Some(AllocType::Memory(mem)) => Ok(mem),
Some(AllocType::Function(..)) => {
Err(EvalErrorKind::DerefFunctionPointer.into())

View File

@ -56,19 +56,13 @@ bitflags! {
// Function argument.
const FN_ARGUMENT = 1 << 2;
// Static place or move from a static.
const STATIC = 1 << 3;
// Reference to a static.
const STATIC_REF = 1 << 4;
// Not constant at all - non-`const fn` calls, asm!,
// pointer comparisons, ptr-to-int casts, etc.
const NOT_CONST = 1 << 5;
const NOT_CONST = 1 << 3;
// Refers to temporaries which cannot be promoted as
// promote_consts decided they weren't simple enough.
const NOT_PROMOTABLE = 1 << 6;
const NOT_PROMOTABLE = 1 << 4;
// Const items can only have MUTABLE_INTERIOR
// and NOT_PROMOTABLE without producing an error.
@ -226,42 +220,6 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
self.add(original);
}
/// Check if a Local with the current qualifications is promotable.
fn can_promote(&self, qualif: Qualif) -> bool {
// References to statics are allowed, but only in other statics.
if self.mode == Mode::Static || self.mode == Mode::StaticMut {
(qualif - Qualif::STATIC_REF).is_empty()
} else {
qualif.is_empty()
}
}
/// Check if a Place with the current qualifications could
/// be consumed, by either an operand or a Deref projection.
fn try_consume(&mut self) -> bool {
if self.qualif.intersects(Qualif::STATIC) && self.mode != Mode::Fn {
let msg = if self.mode == Mode::Static ||
self.mode == Mode::StaticMut {
"cannot refer to other statics by value, use the \
address-of operator or a constant instead"
} else {
"cannot refer to statics by value, use a constant instead"
};
struct_span_err!(self.tcx.sess, self.span, E0394, "{}", msg)
.span_label(self.span, "referring to another static by value")
.note("use the address-of operator or a constant instead")
.emit();
// Replace STATIC with NOT_CONST to avoid further errors.
self.qualif = self.qualif - Qualif::STATIC;
self.add(Qualif::NOT_CONST);
false
} else {
true
}
}
/// Assign the current qualification to the given destination.
fn assign(&mut self, dest: &Place<'tcx>, location: Location) {
trace!("assign: {:?}", dest);
@ -305,7 +263,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
}) if self.mir.local_kind(index) == LocalKind::Temp
&& self.mir.local_decls[index].ty.is_box()
&& self.local_qualif[index].map_or(false, |qualif| {
qualif.intersects(Qualif::NOT_CONST)
qualif.contains(Qualif::NOT_CONST)
}) => {
// Part of `box expr`, we should've errored
// already for the Box allocation Rvalue.
@ -492,21 +450,26 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
match *place {
Place::Local(ref local) => self.visit_local(local, context, location),
Place::Static(ref global) => {
self.add(Qualif::STATIC);
if self.mode != Mode::Fn {
for attr in &self.tcx.get_attrs(global.def_id)[..] {
if attr.check_name("thread_local") {
span_err!(self.tcx.sess, self.span, E0625,
"thread-local statics cannot be \
accessed at compile-time");
self.add(Qualif::NOT_CONST);
return;
}
if self.tcx
.get_attrs(global.def_id)
.iter()
.any(|attr| attr.check_name("thread_local")) {
if self.mode != Mode::Fn {
span_err!(self.tcx.sess, self.span, E0625,
"thread-local statics cannot be \
accessed at compile-time");
}
self.add(Qualif::NOT_CONST);
return;
}
if self.mode == Mode::Const || self.mode == Mode::ConstFn {
// Only allow statics (not consts) to refer to other statics.
if self.mode == Mode::Static || self.mode == Mode::StaticMut {
return;
}
self.add(Qualif::NOT_CONST);
if self.mode != Mode::Fn {
let mut err = struct_span_err!(self.tcx.sess, self.span, E0013,
"{}s cannot refer to statics, use \
a constant instead", self.mode);
@ -527,15 +490,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
this.super_place(place, context, location);
match proj.elem {
ProjectionElem::Deref => {
if !this.try_consume() {
return;
}
if this.qualif.intersects(Qualif::STATIC_REF) {
this.qualif = this.qualif - Qualif::STATIC_REF;
this.add(Qualif::STATIC);
}
this.add(Qualif::NOT_CONST);
let base_ty = proj.base.ty(this.mir, this.tcx).to_ty(this.tcx);
@ -573,11 +527,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
this.not_const();
}
}
} else if this.qualif.intersects(Qualif::STATIC) {
span_err!(this.tcx.sess, this.span, E0494,
"cannot refer to the interior of another \
static, use a constant instead");
}
let ty = place.ty(this.mir, this.tcx).to_ty(this.tcx);
this.qualif.restrict(ty, this.tcx, this.param_env);
}
@ -594,14 +545,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
self.super_operand(operand, location);
match *operand {
Operand::Copy(_) |
Operand::Move(_) => {
self.nest(|this| {
this.super_operand(operand, location);
this.try_consume();
});
// Mark the consumed locals to indicate later drops are noops.
if let Operand::Move(Place::Local(local)) = *operand {
self.local_qualif[local] = self.local_qualif[local].map(|q|
@ -646,20 +594,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
if is_reborrow {
self.nest(|this| {
this.super_place(place, PlaceContext::Borrow {
region,
kind
}, location);
if !this.try_consume() {
return;
}
if this.qualif.intersects(Qualif::STATIC_REF) {
this.qualif = this.qualif - Qualif::STATIC_REF;
this.add(Qualif::STATIC);
}
});
self.super_place(place, PlaceContext::Borrow {
region,
kind
}, location);
} else {
self.super_rvalue(rvalue, location);
}
@ -678,22 +616,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
Rvalue::Cast(CastKind::Unsize, ..) |
Rvalue::Discriminant(..) => {}
Rvalue::Len(_) => {
// Static places in consts would have errored already,
// don't treat length checks as reads from statics.
self.qualif = self.qualif - Qualif::STATIC;
}
Rvalue::Discriminant(..) |
Rvalue::Len(_) => {}
Rvalue::Ref(_, kind, ref place) => {
// Static places in consts would have errored already,
// only keep track of references to them here.
if self.qualif.intersects(Qualif::STATIC) {
self.qualif = self.qualif - Qualif::STATIC;
self.add(Qualif::STATIC_REF);
}
let ty = place.ty(self.mir, self.tcx).to_ty(self.tcx);
// Default to forbidding the borrow and/or its promotion,
@ -744,7 +670,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
// Constants cannot be borrowed if they contain interior mutability as
// it means that our "silent insertion of statics" could change
// initializer values (very bad).
if self.qualif.intersects(Qualif::MUTABLE_INTERIOR) {
if self.qualif.contains(Qualif::MUTABLE_INTERIOR) {
// A reference of a MUTABLE_INTERIOR place is instead
// NOT_CONST (see `if forbidden_mut` below), to avoid
// duplicate errors (from reborrowing, for example).
@ -781,7 +707,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
// This allows borrowing fields which don't have
// `MUTABLE_INTERIOR`, from a type that does, e.g.:
// `let _: &'static _ = &(Cell::new(1), 2).1;`
if self.can_promote(qualif - Qualif::MUTABLE_INTERIOR) {
if (qualif - Qualif::MUTABLE_INTERIOR).is_empty() {
self.promotion_candidates.push(candidate);
}
}
@ -889,7 +815,7 @@ This does not pose a problem by itself because they can't be accessed directly."
if Some(def.did) == self.tcx.lang_items().unsafe_cell_type() {
let ty = rvalue.ty(self.mir, self.tcx);
self.add_type(ty);
assert!(self.qualif.intersects(Qualif::MUTABLE_INTERIOR));
assert!(self.qualif.contains(Qualif::MUTABLE_INTERIOR));
}
}
}
@ -949,7 +875,7 @@ This does not pose a problem by itself because they can't be accessed directly."
}
let candidate = Candidate::Argument { bb, index: i };
if is_shuffle && i == 2 {
if this.can_promote(this.qualif) {
if this.qualif.is_empty() {
this.promotion_candidates.push(candidate);
} else {
span_err!(this.tcx.sess, this.span, E0526,
@ -965,7 +891,7 @@ This does not pose a problem by itself because they can't be accessed directly."
if !constant_arguments.contains(&i) {
return
}
if this.can_promote(this.qualif) {
if this.qualif.is_empty() {
this.promotion_candidates.push(candidate);
} else {
this.tcx.sess.span_err(this.span,
@ -1059,7 +985,7 @@ This does not pose a problem by itself because they can't be accessed directly."
// HACK(eddyb) Emulate a bit of dataflow analysis,
// conservatively, that drop elaboration will do.
let needs_drop = if let Place::Local(local) = *place {
if self.local_qualif[local].map_or(true, |q| q.intersects(Qualif::NEEDS_DROP)) {
if self.local_qualif[local].map_or(true, |q| q.contains(Qualif::NEEDS_DROP)) {
Some(self.mir.local_decls[local].source_info.span)
} else {
None
@ -1111,7 +1037,7 @@ This does not pose a problem by itself because they can't be accessed directly."
}
// Avoid a generic error for other uses of arguments.
if self.qualif.intersects(Qualif::FN_ARGUMENT) {
if self.qualif.contains(Qualif::FN_ARGUMENT) {
let decl = &self.mir.local_decls[index];
let mut err = feature_err(
&self.tcx.sess.parse_sess,

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`.