Auto merge of #31024 - oli-obk:move_checks_out_of_librustc, r=arielb1
- check_const - check_static_recursion - check_loop - check_rvalues r? @arielb1
This commit is contained in:
commit
340e7eb2a7
@ -102,7 +102,7 @@ DEPS_rustc_front := std syntax log serialize
|
||||
DEPS_rustc_lint := rustc log syntax
|
||||
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
|
||||
DEPS_rustc_metadata := rustc rustc_front syntax rbml
|
||||
DEPS_rustc_passes := syntax rustc core
|
||||
DEPS_rustc_passes := syntax rustc core rustc_front
|
||||
DEPS_rustc_mir := rustc rustc_front syntax
|
||||
DEPS_rustc_resolve := arena rustc rustc_front log syntax
|
||||
DEPS_rustc_platform_intrinsics := rustc rustc_llvm
|
||||
|
@ -215,187 +215,6 @@ match x {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0010: r##"
|
||||
The value of statics and constants must be known at compile time, and they live
|
||||
for the entire lifetime of a program. Creating a boxed value allocates memory on
|
||||
the heap at runtime, and therefore cannot be done at compile time. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
#![feature(box_syntax)]
|
||||
|
||||
const CON : Box<i32> = box 0;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0011: r##"
|
||||
Initializers for constants and statics are evaluated at compile time.
|
||||
User-defined operators rely on user-defined functions, which cannot be evaluated
|
||||
at compile time.
|
||||
|
||||
Bad example:
|
||||
|
||||
```
|
||||
use std::ops::Index;
|
||||
|
||||
struct Foo { a: u8 }
|
||||
|
||||
impl Index<u8> for Foo {
|
||||
type Output = u8;
|
||||
|
||||
fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
|
||||
}
|
||||
|
||||
const a: Foo = Foo { a: 0u8 };
|
||||
const b: u8 = a[0]; // Index trait is defined by the user, bad!
|
||||
```
|
||||
|
||||
Only operators on builtin types are allowed.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
const a: &'static [i32] = &[1, 2, 3];
|
||||
const b: i32 = a[0]; // Good!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0013: r##"
|
||||
Static and const variables can refer to other const variables. But a const
|
||||
variable cannot refer to a static variable. For example, `Y` cannot refer to `X`
|
||||
here:
|
||||
|
||||
```
|
||||
static X: i32 = 42;
|
||||
const Y: i32 = X;
|
||||
```
|
||||
|
||||
To fix this, the value can be extracted as a const and then used:
|
||||
|
||||
```
|
||||
const A: i32 = 42;
|
||||
static X: i32 = A;
|
||||
const Y: i32 = A;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0014: r##"
|
||||
Constants can only be initialized by a constant value or, in a future
|
||||
version of Rust, a call to a const function. This error indicates the use
|
||||
of a path (like a::b, or x) denoting something other than one of these
|
||||
allowed items. Example:
|
||||
|
||||
```
|
||||
const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
|
||||
```
|
||||
|
||||
To avoid it, you have to replace the non-constant value:
|
||||
|
||||
```
|
||||
const FOO: i32 = { const X : i32 = 0; X };
|
||||
// or even:
|
||||
const FOO: i32 = { 0 }; // but brackets are useless here
|
||||
```
|
||||
"##,
|
||||
|
||||
// FIXME(#24111) Change the language here when const fn stabilizes
|
||||
E0015: r##"
|
||||
The only functions that can be called in static or constant expressions are
|
||||
`const` functions, and struct/enum constructors. `const` functions are only
|
||||
available on a nightly compiler. Rust currently does not support more general
|
||||
compile-time function execution.
|
||||
|
||||
```
|
||||
const FOO: Option<u8> = Some(1); // enum constructor
|
||||
struct Bar {x: u8}
|
||||
const BAR: Bar = Bar {x: 1}; // struct constructor
|
||||
```
|
||||
|
||||
See [RFC 911] for more details on the design of `const fn`s.
|
||||
|
||||
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
|
||||
"##,
|
||||
|
||||
E0017: r##"
|
||||
References in statics and constants may only refer to immutable values. Example:
|
||||
|
||||
```
|
||||
static X: i32 = 1;
|
||||
const C: i32 = 2;
|
||||
|
||||
// these three are not allowed:
|
||||
const CR: &'static mut i32 = &mut C;
|
||||
static STATIC_REF: &'static mut i32 = &mut X;
|
||||
static CONST_REF: &'static mut i32 = &mut C;
|
||||
```
|
||||
|
||||
Statics are shared everywhere, and if they refer to mutable data one might
|
||||
violate memory safety since holding multiple mutable references to shared data
|
||||
is not allowed.
|
||||
|
||||
If you really want global mutable state, try using `static mut` or a global
|
||||
`UnsafeCell`.
|
||||
"##,
|
||||
|
||||
E0018: r##"
|
||||
The value of static and const variables must be known at compile time. You
|
||||
can't cast a pointer as an integer because we can't know what value the
|
||||
address will take.
|
||||
|
||||
However, pointers to other constants' addresses are allowed in constants,
|
||||
example:
|
||||
|
||||
```
|
||||
const X: u32 = 50;
|
||||
const Y: *const u32 = &X;
|
||||
```
|
||||
|
||||
Therefore, casting one of these non-constant pointers to an integer results
|
||||
in a non-constant integer which lead to this error. Example:
|
||||
|
||||
```
|
||||
const X: u32 = 1;
|
||||
const Y: usize = &X as *const u32 as usize;
|
||||
println!("{}", Y);
|
||||
```
|
||||
"##,
|
||||
|
||||
E0019: r##"
|
||||
A function call isn't allowed in the const's initialization expression
|
||||
because the expression's value must be known at compile-time. Example of
|
||||
erroneous code:
|
||||
|
||||
```
|
||||
enum Test {
|
||||
V1
|
||||
}
|
||||
|
||||
impl Test {
|
||||
fn test(&self) -> i32 {
|
||||
12
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const FOO: Test = Test::V1;
|
||||
|
||||
const A: i32 = FOO.test(); // You can't call Test::func() here !
|
||||
}
|
||||
```
|
||||
|
||||
Remember: you can't use a function call inside a const's initialization
|
||||
expression! However, you can totally use it anywhere else:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
const FOO: Test = Test::V1;
|
||||
|
||||
FOO.func(); // here is good
|
||||
let x = FOO.func(); // or even here!
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0020: r##"
|
||||
This error indicates that an attempt was made to divide by zero (or take the
|
||||
remainder of a zero divisor) in a static or constant expression. Erroneous
|
||||
@ -407,24 +226,6 @@ const X: i32 = 42 / 0;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0030: r##"
|
||||
When matching against a range, the compiler verifies that the range is
|
||||
non-empty. Range patterns include both end-points, so this is equivalent to
|
||||
requiring the start of the range to be less than or equal to the end of the
|
||||
range.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
match 5u32 {
|
||||
// This range is ok, albeit pointless.
|
||||
1 ... 1 => ...
|
||||
// This range is empty, and the compiler can tell.
|
||||
1000 ... 5 => ...
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0038: r####"
|
||||
Trait objects like `Box<Trait>` can only be constructed when certain
|
||||
requirements are satisfied by the trait in question.
|
||||
@ -902,14 +703,6 @@ match Some(42) {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0161: r##"
|
||||
In Rust, you can only move a value when its size is known at compile time.
|
||||
|
||||
To work around this restriction, consider "hiding" the value behind a reference:
|
||||
either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
|
||||
it around as usual.
|
||||
"##,
|
||||
|
||||
E0162: r##"
|
||||
An if-let pattern attempts to match the pattern, and enters the body if the
|
||||
match was successful. If the match is irrefutable (when it cannot fail to
|
||||
@ -1101,67 +894,6 @@ extern "C" {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0265: r##"
|
||||
This error indicates that a static or constant references itself.
|
||||
All statics and constants need to resolve to a value in an acyclic manner.
|
||||
|
||||
For example, neither of the following can be sensibly compiled:
|
||||
|
||||
```
|
||||
const X: u32 = X;
|
||||
```
|
||||
|
||||
```
|
||||
const X: u32 = Y;
|
||||
const Y: u32 = X;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0267: r##"
|
||||
This error indicates the use of a loop keyword (`break` or `continue`) inside a
|
||||
closure but outside of any loop. Erroneous code example:
|
||||
|
||||
```
|
||||
let w = || { break; }; // error: `break` inside of a closure
|
||||
```
|
||||
|
||||
`break` and `continue` keywords can be used as normal inside closures as long as
|
||||
they are also contained within a loop. To halt the execution of a closure you
|
||||
should instead use a return statement. Example:
|
||||
|
||||
```
|
||||
let w = || {
|
||||
for _ in 0..10 {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
w();
|
||||
```
|
||||
"##,
|
||||
|
||||
E0268: r##"
|
||||
This error indicates the use of a loop keyword (`break` or `continue`) outside
|
||||
of a loop. Without a loop to break out of or continue in, no sensible action can
|
||||
be taken. Erroneous code example:
|
||||
|
||||
```
|
||||
fn some_func() {
|
||||
break; // error: `break` outside of loop
|
||||
}
|
||||
```
|
||||
|
||||
Please verify that you are using `break` and `continue` only in loops. Example:
|
||||
|
||||
```
|
||||
fn some_func() {
|
||||
for _ in 0..10 {
|
||||
break; // ok!
|
||||
}
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0269: r##"
|
||||
Functions must eventually return a value of their return type. For example, in
|
||||
the following function
|
||||
@ -1892,104 +1624,6 @@ struct Foo<T: 'static> {
|
||||
```
|
||||
"##,
|
||||
|
||||
E0378: r##"
|
||||
Method calls that aren't calls to inherent `const` methods are disallowed
|
||||
in statics, constants, and constant functions.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
|
||||
|
||||
struct Foo(i32);
|
||||
|
||||
impl Foo {
|
||||
const fn foo(&self) -> i32 {
|
||||
self.bar() // error, `bar` isn't `const`
|
||||
}
|
||||
|
||||
fn bar(&self) -> i32 { self.0 }
|
||||
}
|
||||
```
|
||||
|
||||
For more information about `const fn`'s, see [RFC 911].
|
||||
|
||||
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
|
||||
"##,
|
||||
|
||||
E0394: r##"
|
||||
From [RFC 246]:
|
||||
|
||||
> It is invalid for a static to reference another static by value. It is
|
||||
> required that all references be borrowed.
|
||||
|
||||
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
|
||||
"##,
|
||||
|
||||
E0395: r##"
|
||||
The value assigned to a constant expression must be known at compile time,
|
||||
which is not the case when comparing raw pointers. Erroneous code example:
|
||||
|
||||
```
|
||||
static foo: i32 = 42;
|
||||
static bar: i32 = 43;
|
||||
|
||||
static baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
|
||||
// error: raw pointers cannot be compared in statics!
|
||||
```
|
||||
|
||||
Please check that the result of the comparison can be determined at compile time
|
||||
or isn't assigned to a constant expression. Example:
|
||||
|
||||
```
|
||||
static foo: i32 = 42;
|
||||
static bar: i32 = 43;
|
||||
|
||||
let baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
|
||||
// baz isn't a constant expression so it's ok
|
||||
```
|
||||
"##,
|
||||
|
||||
E0396: r##"
|
||||
The value assigned to a constant expression must be known at compile time,
|
||||
which is not the case when dereferencing raw pointers. Erroneous code
|
||||
example:
|
||||
|
||||
```
|
||||
const foo: i32 = 42;
|
||||
const baz: *const i32 = (&foo as *const i32);
|
||||
|
||||
const deref: i32 = *baz;
|
||||
// error: raw pointers cannot be dereferenced in constants
|
||||
```
|
||||
|
||||
To fix this error, please do not assign this value to a constant expression.
|
||||
Example:
|
||||
|
||||
```
|
||||
const foo: i32 = 42;
|
||||
const baz: *const i32 = (&foo as *const i32);
|
||||
|
||||
unsafe { let deref: i32 = *baz; }
|
||||
// baz isn't a constant expression so it's ok
|
||||
```
|
||||
|
||||
You'll also note that this assignment must be done in an unsafe block!
|
||||
"##,
|
||||
|
||||
E0397: r##"
|
||||
It is not allowed for a mutable static to allocate or have destructors. For
|
||||
example:
|
||||
|
||||
```
|
||||
// error: mutable statics are not allowed to have boxes
|
||||
static mut FOO: Option<Box<usize>> = None;
|
||||
|
||||
// error: mutable statics are not allowed to have destructors
|
||||
static mut BAR: Option<Vec<i32>> = None;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0398: r##"
|
||||
In Rust 1.3, the default object lifetime bounds are expected to
|
||||
change, as described in RFC #1156 [1]. You are getting a warning
|
||||
@ -2026,50 +1660,6 @@ contain references (with a maximum lifetime of `'a`).
|
||||
[1]: https://github.com/rust-lang/rfcs/pull/1156
|
||||
"##,
|
||||
|
||||
E0400: r##"
|
||||
A user-defined dereference was attempted in an invalid context. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
use std::ops::Deref;
|
||||
|
||||
struct A;
|
||||
|
||||
impl Deref for A {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self)-> &str { "foo" }
|
||||
}
|
||||
|
||||
const S: &'static str = &A;
|
||||
// error: user-defined dereference operators are not allowed in constants
|
||||
|
||||
fn main() {
|
||||
let foo = S;
|
||||
}
|
||||
```
|
||||
|
||||
You cannot directly use a dereference operation whilst initializing a constant
|
||||
or a static. To fix this error, restructure your code to avoid this dereference,
|
||||
perhaps moving it inline:
|
||||
|
||||
```
|
||||
use std::ops::Deref;
|
||||
|
||||
struct A;
|
||||
|
||||
impl Deref for A {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self)-> &str { "foo" }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo : &str = &A;
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0452: r##"
|
||||
An invalid lint attribute has been given. Erroneous code example:
|
||||
|
||||
@ -2087,136 +1677,6 @@ lint name). Ensure the attribute is of this form:
|
||||
```
|
||||
"##,
|
||||
|
||||
E0492: r##"
|
||||
A borrow of a constant containing interior mutability was attempted. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
|
||||
|
||||
const A: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
static B: &'static AtomicUsize = &A;
|
||||
// error: cannot borrow a constant which contains interior mutability, create a
|
||||
// static instead
|
||||
```
|
||||
|
||||
A `const` represents a constant value that should never change. If one takes
|
||||
a `&` reference to the constant, then one is taking a pointer to some memory
|
||||
location containing the value. Normally this is perfectly fine: most values
|
||||
can't be changed via a shared `&` pointer, but interior mutability would allow
|
||||
it. That is, a constant value could be mutated. On the other hand, a `static` is
|
||||
explicitly a single memory location, which can be mutated at will.
|
||||
|
||||
So, in order to solve this error, either use statics which are `Sync`:
|
||||
|
||||
```
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
|
||||
|
||||
static A: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
static B: &'static AtomicUsize = &A; // ok!
|
||||
```
|
||||
|
||||
You can also have this error while using a cell type:
|
||||
|
||||
```
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
const A: Cell<usize> = Cell::new(1);
|
||||
const B: &'static Cell<usize> = &A;
|
||||
// error: cannot borrow a constant which contains interior mutability, create
|
||||
// a static instead
|
||||
|
||||
// or:
|
||||
struct C { a: Cell<usize> }
|
||||
|
||||
const D: C = C { a: Cell::new(1) };
|
||||
const E: &'static Cell<usize> = &D.a; // error
|
||||
|
||||
// or:
|
||||
const F: &'static C = &D; // error
|
||||
```
|
||||
|
||||
This is because cell types do operations that are not thread-safe. Due to this,
|
||||
they don't implement Sync and thus can't be placed in statics. In this
|
||||
case, `StaticMutex` would work just fine, but it isn't stable yet:
|
||||
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
|
||||
|
||||
However, if you still wish to use these types, you can achieve this by an unsafe
|
||||
wrapper:
|
||||
|
||||
```
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker::Sync;
|
||||
|
||||
struct NotThreadSafe<T> {
|
||||
value: Cell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for NotThreadSafe<T> {}
|
||||
|
||||
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
|
||||
static B: &'static NotThreadSafe<usize> = &A; // ok!
|
||||
```
|
||||
|
||||
Remember this solution is unsafe! You will have to ensure that accesses to the
|
||||
cell are synchronized.
|
||||
"##,
|
||||
|
||||
E0493: r##"
|
||||
A type with a destructor was assigned to an invalid type of variable. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
struct Foo {
|
||||
a: u32
|
||||
}
|
||||
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
const F : Foo = Foo { a : 0 };
|
||||
// error: constants are not allowed to have destructors
|
||||
static S : Foo = Foo { a : 0 };
|
||||
// error: statics are not allowed to have destructors
|
||||
```
|
||||
|
||||
To solve this issue, please use a type which does allow the usage of type with
|
||||
destructors.
|
||||
"##,
|
||||
|
||||
E0494: r##"
|
||||
A reference of an interior static was assigned to another const/static.
|
||||
Erroneous code example:
|
||||
|
||||
```
|
||||
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!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0496: r##"
|
||||
A lifetime name is shadowing another lifetime name. Erroneous code example:
|
||||
|
||||
|
@ -94,12 +94,9 @@ pub mod middle {
|
||||
pub mod astconv_util;
|
||||
pub mod expr_use_visitor; // STAGE0: increase glitch immunity
|
||||
pub mod cfg;
|
||||
pub mod check_const;
|
||||
pub mod check_static_recursion;
|
||||
pub mod check_loop;
|
||||
pub mod check_match;
|
||||
pub mod check_rvalues;
|
||||
pub mod const_eval;
|
||||
pub mod const_qualif;
|
||||
pub mod cstore;
|
||||
pub mod dataflow;
|
||||
pub mod dead;
|
||||
|
44
src/librustc/middle/const_qualif.rs
Normal file
44
src/librustc/middle/const_qualif.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Const qualification, from partial to completely promotable.
|
||||
bitflags! {
|
||||
#[derive(RustcEncodable, RustcDecodable)]
|
||||
flags ConstQualif: u8 {
|
||||
// Inner mutability (can not be placed behind a reference) or behind
|
||||
// &mut in a non-global expression. Can be copied from static memory.
|
||||
const MUTABLE_MEM = 1 << 0,
|
||||
// Constant value with a type that implements Drop. Can be copied
|
||||
// from static memory, similar to MUTABLE_MEM.
|
||||
const NEEDS_DROP = 1 << 1,
|
||||
// Even if the value can be placed in static memory, copying it from
|
||||
// there is more expensive than in-place instantiation, and/or it may
|
||||
// be too large. This applies to [T; N] and everything containing it.
|
||||
// N.B.: references need to clear this flag to not end up on the stack.
|
||||
const PREFER_IN_PLACE = 1 << 2,
|
||||
// May use more than 0 bytes of memory, doesn't impact the constness
|
||||
// directly, but is not allowed to be borrowed mutably in a constant.
|
||||
const NON_ZERO_SIZED = 1 << 3,
|
||||
// Actually borrowed, has to always be in static memory. Does not
|
||||
// propagate, and requires the expression to behave like a 'static
|
||||
// lvalue. The set of expressions with this flag is the minimum
|
||||
// that have to be promoted.
|
||||
const HAS_STATIC_BORROWS = 1 << 4,
|
||||
// Invalid const for miscellaneous reasons (e.g. not implemented).
|
||||
const NOT_CONST = 1 << 5,
|
||||
|
||||
// Borrowing the expression won't produce &'static T if any of these
|
||||
// bits are set, though the value could be copied from static memory
|
||||
// if `NOT_CONST` isn't set.
|
||||
const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
|
||||
ConstQualif::NEEDS_DROP.bits |
|
||||
ConstQualif::NOT_CONST.bits
|
||||
}
|
||||
}
|
@ -74,7 +74,7 @@ use self::Aliasability::*;
|
||||
use middle::def_id::DefId;
|
||||
use front::map as ast_map;
|
||||
use middle::infer;
|
||||
use middle::check_const;
|
||||
use middle::const_qualif::ConstQualif;
|
||||
use middle::def::Def;
|
||||
use middle::ty::adjustment;
|
||||
use middle::ty::{self, Ty};
|
||||
@ -795,19 +795,19 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
|
||||
expr_ty: Ty<'tcx>)
|
||||
-> cmt<'tcx> {
|
||||
let qualif = self.tcx().const_qualif_map.borrow().get(&id).cloned()
|
||||
.unwrap_or(check_const::ConstQualif::NOT_CONST);
|
||||
.unwrap_or(ConstQualif::NOT_CONST);
|
||||
|
||||
// Only promote `[T; 0]` before an RFC for rvalue promotions
|
||||
// is accepted.
|
||||
let qualif = match expr_ty.sty {
|
||||
ty::TyArray(_, 0) => qualif,
|
||||
_ => check_const::ConstQualif::NOT_CONST
|
||||
_ => ConstQualif::NOT_CONST
|
||||
};
|
||||
|
||||
// Compute maximum lifetime of this rvalue. This is 'static if
|
||||
// we can promote to a constant, otherwise equal to enclosing temp
|
||||
// lifetime.
|
||||
let re = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
|
||||
let re = if qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
||||
self.temporary_scope(id)
|
||||
} else {
|
||||
ty::ReStatic
|
||||
|
@ -373,7 +373,7 @@ pub struct ctxt<'tcx> {
|
||||
repr_hint_cache: RefCell<DepTrackingMap<maps::ReprHints<'tcx>>>,
|
||||
|
||||
/// Maps Expr NodeId's to their constant qualification.
|
||||
pub const_qualif_map: RefCell<NodeMap<middle::check_const::ConstQualif>>,
|
||||
pub const_qualif_map: RefCell<NodeMap<middle::const_qualif::ConstQualif>>,
|
||||
|
||||
/// Caches CoerceUnsized kinds for impls on custom types.
|
||||
pub custom_coerce_unsized_kinds: RefCell<DefIdMap<ty::adjustment::CustomCoerceUnsized>>,
|
||||
|
@ -34,6 +34,7 @@ use rustc_plugin::registry::Registry;
|
||||
use rustc_plugin as plugin;
|
||||
use rustc_front::hir;
|
||||
use rustc_front::lowering::{lower_crate, LoweringContext};
|
||||
use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
|
||||
use super::Compilation;
|
||||
|
||||
use serialize::json;
|
||||
@ -632,7 +633,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
||||
|
||||
time(time_passes,
|
||||
"checking for inline asm in case the target doesn't support it",
|
||||
|| ::rustc_passes::no_asm::check_crate(sess, &krate));
|
||||
|| no_asm::check_crate(sess, &krate));
|
||||
|
||||
// One final feature gating of the true AST that gets compiled
|
||||
// later, to make sure we've got everything (e.g. configuration
|
||||
@ -649,7 +650,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
||||
|
||||
time(time_passes,
|
||||
"const fn bodies and arguments",
|
||||
|| ::rustc_passes::const_fn::check_crate(sess, &krate));
|
||||
|| const_fn::check_crate(sess, &krate));
|
||||
|
||||
if sess.opts.debugging_opts.input_stats {
|
||||
println!("Post-expansion node count: {}", count_nodes(&krate));
|
||||
@ -743,11 +744,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
||||
|
||||
time(time_passes,
|
||||
"loop checking",
|
||||
|| middle::check_loop::check_crate(sess, krate));
|
||||
|| loops::check_crate(sess, krate));
|
||||
|
||||
time(time_passes,
|
||||
"static item recursion checking",
|
||||
|| middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map));
|
||||
|| static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map));
|
||||
|
||||
ty::ctxt::create_and_enter(sess,
|
||||
arenas,
|
||||
@ -764,7 +765,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
||||
|
||||
time(time_passes,
|
||||
"const checking",
|
||||
|| middle::check_const::check_crate(tcx));
|
||||
|| consts::check_crate(tcx));
|
||||
|
||||
let access_levels =
|
||||
time(time_passes, "privacy checking", || {
|
||||
@ -805,7 +806,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
||||
|
||||
time(time_passes,
|
||||
"rvalue checking",
|
||||
|| middle::check_rvalues::check_crate(tcx));
|
||||
|| rvalues::check_crate(tcx));
|
||||
|
||||
// Avoid overwhelming user with errors if type checking failed.
|
||||
// I'm not sure how helpful this is, to be honest, but it avoids
|
||||
|
@ -29,7 +29,7 @@ use tyencode;
|
||||
use middle::cstore::{InlinedItem, InlinedItemRef};
|
||||
use middle::ty::adjustment;
|
||||
use middle::ty::cast;
|
||||
use middle::check_const::ConstQualif;
|
||||
use middle::const_qualif::ConstQualif;
|
||||
use middle::def::{self, Def};
|
||||
use middle::def_id::DefId;
|
||||
use middle::privacy::{AllPublic, LastMod};
|
||||
|
@ -24,20 +24,22 @@
|
||||
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
|
||||
// by borrowck::gather_loans
|
||||
|
||||
use dep_graph::DepNode;
|
||||
use middle::ty::cast::{CastKind};
|
||||
use middle::const_eval::{self, ConstEvalErr};
|
||||
use middle::const_eval::ErrKind::IndexOpFeatureGated;
|
||||
use middle::const_eval::EvalHint::ExprTypeChecked;
|
||||
use middle::def::Def;
|
||||
use middle::def_id::DefId;
|
||||
use middle::expr_use_visitor as euv;
|
||||
use middle::infer;
|
||||
use middle::mem_categorization as mc;
|
||||
use middle::mem_categorization::Categorization;
|
||||
use middle::traits;
|
||||
use middle::ty::{self, Ty};
|
||||
use util::nodemap::NodeMap;
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::middle::ty::cast::{CastKind};
|
||||
use rustc::middle::const_eval::{self, ConstEvalErr};
|
||||
use rustc::middle::const_eval::ErrKind::IndexOpFeatureGated;
|
||||
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc::middle::def::Def;
|
||||
use rustc::middle::def_id::DefId;
|
||||
use rustc::middle::expr_use_visitor as euv;
|
||||
use rustc::middle::infer;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::mem_categorization::Categorization;
|
||||
use rustc::middle::traits;
|
||||
use rustc::middle::ty::{self, Ty};
|
||||
use rustc::util::nodemap::NodeMap;
|
||||
use rustc::middle::const_qualif::ConstQualif;
|
||||
use rustc::lint::builtin::CONST_ERR;
|
||||
|
||||
use rustc_front::hir;
|
||||
use syntax::ast;
|
||||
@ -48,41 +50,6 @@ use rustc_front::intravisit::{self, FnKind, Visitor};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// Const qualification, from partial to completely promotable.
|
||||
bitflags! {
|
||||
#[derive(RustcEncodable, RustcDecodable)]
|
||||
flags ConstQualif: u8 {
|
||||
// Inner mutability (can not be placed behind a reference) or behind
|
||||
// &mut in a non-global expression. Can be copied from static memory.
|
||||
const MUTABLE_MEM = 1 << 0,
|
||||
// Constant value with a type that implements Drop. Can be copied
|
||||
// from static memory, similar to MUTABLE_MEM.
|
||||
const NEEDS_DROP = 1 << 1,
|
||||
// Even if the value can be placed in static memory, copying it from
|
||||
// there is more expensive than in-place instantiation, and/or it may
|
||||
// be too large. This applies to [T; N] and everything containing it.
|
||||
// N.B.: references need to clear this flag to not end up on the stack.
|
||||
const PREFER_IN_PLACE = 1 << 2,
|
||||
// May use more than 0 bytes of memory, doesn't impact the constness
|
||||
// directly, but is not allowed to be borrowed mutably in a constant.
|
||||
const NON_ZERO_SIZED = 1 << 3,
|
||||
// Actually borrowed, has to always be in static memory. Does not
|
||||
// propagate, and requires the expression to behave like a 'static
|
||||
// lvalue. The set of expressions with this flag is the minimum
|
||||
// that have to be promoted.
|
||||
const HAS_STATIC_BORROWS = 1 << 4,
|
||||
// Invalid const for miscellaneous reasons (e.g. not implemented).
|
||||
const NOT_CONST = 1 << 5,
|
||||
|
||||
// Borrowing the expression won't produce &'static T if any of these
|
||||
// bits are set, though the value could be copied from static memory
|
||||
// if `NOT_CONST` isn't set.
|
||||
const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
|
||||
ConstQualif::NEEDS_DROP.bits |
|
||||
ConstQualif::NOT_CONST.bits
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
enum Mode {
|
||||
Const,
|
||||
@ -463,7 +430,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
Ok(_) => {}
|
||||
Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
|
||||
Err(msg) => {
|
||||
self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,
|
||||
self.tcx.sess.add_lint(CONST_ERR, ex.id,
|
||||
msg.span,
|
||||
msg.description().into_owned())
|
||||
}
|
@ -11,6 +11,108 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
register_long_diagnostics! {
|
||||
|
||||
E0010: r##"
|
||||
The value of statics and constants must be known at compile time, and they live
|
||||
for the entire lifetime of a program. Creating a boxed value allocates memory on
|
||||
the heap at runtime, and therefore cannot be done at compile time. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
#![feature(box_syntax)]
|
||||
|
||||
const CON : Box<i32> = box 0;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0011: r##"
|
||||
Initializers for constants and statics are evaluated at compile time.
|
||||
User-defined operators rely on user-defined functions, which cannot be evaluated
|
||||
at compile time.
|
||||
|
||||
Bad example:
|
||||
|
||||
```
|
||||
use std::ops::Index;
|
||||
|
||||
struct Foo { a: u8 }
|
||||
|
||||
impl Index<u8> for Foo {
|
||||
type Output = u8;
|
||||
|
||||
fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
|
||||
}
|
||||
|
||||
const a: Foo = Foo { a: 0u8 };
|
||||
const b: u8 = a[0]; // Index trait is defined by the user, bad!
|
||||
```
|
||||
|
||||
Only operators on builtin types are allowed.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
const a: &'static [i32] = &[1, 2, 3];
|
||||
const b: i32 = a[0]; // Good!
|
||||
```
|
||||
"##,
|
||||
|
||||
E0013: r##"
|
||||
Static and const variables can refer to other const variables. But a const
|
||||
variable cannot refer to a static variable. For example, `Y` cannot refer to `X`
|
||||
here:
|
||||
|
||||
```
|
||||
static X: i32 = 42;
|
||||
const Y: i32 = X;
|
||||
```
|
||||
|
||||
To fix this, the value can be extracted as a const and then used:
|
||||
|
||||
```
|
||||
const A: i32 = 42;
|
||||
static X: i32 = A;
|
||||
const Y: i32 = A;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0014: r##"
|
||||
Constants can only be initialized by a constant value or, in a future
|
||||
version of Rust, a call to a const function. This error indicates the use
|
||||
of a path (like a::b, or x) denoting something other than one of these
|
||||
allowed items. Example:
|
||||
|
||||
```
|
||||
const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
|
||||
```
|
||||
|
||||
To avoid it, you have to replace the non-constant value:
|
||||
|
||||
```
|
||||
const FOO: i32 = { const X : i32 = 0; X };
|
||||
// or even:
|
||||
const FOO: i32 = { 0 }; // but brackets are useless here
|
||||
```
|
||||
"##,
|
||||
|
||||
// FIXME(#24111) Change the language here when const fn stabilizes
|
||||
E0015: r##"
|
||||
The only functions that can be called in static or constant expressions are
|
||||
`const` functions, and struct/enum constructors. `const` functions are only
|
||||
available on a nightly compiler. Rust currently does not support more general
|
||||
compile-time function execution.
|
||||
|
||||
```
|
||||
const FOO: Option<u8> = Some(1); // enum constructor
|
||||
struct Bar {x: u8}
|
||||
const BAR: Bar = Bar {x: 1}; // struct constructor
|
||||
```
|
||||
|
||||
See [RFC 911] for more details on the design of `const fn`s.
|
||||
|
||||
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
|
||||
"##,
|
||||
|
||||
E0016: r##"
|
||||
Blocks in constants may only contain items (such as constant, function
|
||||
definition, etc...) and a tail expression. Example:
|
||||
@ -26,6 +128,86 @@ const FOO: i32 = { const X : i32 = 0; X };
|
||||
```
|
||||
"##,
|
||||
|
||||
E0017: r##"
|
||||
References in statics and constants may only refer to immutable values. Example:
|
||||
|
||||
```
|
||||
static X: i32 = 1;
|
||||
const C: i32 = 2;
|
||||
|
||||
// these three are not allowed:
|
||||
const CR: &'static mut i32 = &mut C;
|
||||
static STATIC_REF: &'static mut i32 = &mut X;
|
||||
static CONST_REF: &'static mut i32 = &mut C;
|
||||
```
|
||||
|
||||
Statics are shared everywhere, and if they refer to mutable data one might
|
||||
violate memory safety since holding multiple mutable references to shared data
|
||||
is not allowed.
|
||||
|
||||
If you really want global mutable state, try using `static mut` or a global
|
||||
`UnsafeCell`.
|
||||
"##,
|
||||
|
||||
E0018: r##"
|
||||
The value of static and const variables must be known at compile time. You
|
||||
can't cast a pointer as an integer because we can't know what value the
|
||||
address will take.
|
||||
|
||||
However, pointers to other constants' addresses are allowed in constants,
|
||||
example:
|
||||
|
||||
```
|
||||
const X: u32 = 50;
|
||||
const Y: *const u32 = &X;
|
||||
```
|
||||
|
||||
Therefore, casting one of these non-constant pointers to an integer results
|
||||
in a non-constant integer which lead to this error. Example:
|
||||
|
||||
```
|
||||
const X: u32 = 1;
|
||||
const Y: usize = &X as *const u32 as usize;
|
||||
println!("{}", Y);
|
||||
```
|
||||
"##,
|
||||
|
||||
E0019: r##"
|
||||
A function call isn't allowed in the const's initialization expression
|
||||
because the expression's value must be known at compile-time. Example of
|
||||
erroneous code:
|
||||
|
||||
```
|
||||
enum Test {
|
||||
V1
|
||||
}
|
||||
|
||||
impl Test {
|
||||
fn test(&self) -> i32 {
|
||||
12
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const FOO: Test = Test::V1;
|
||||
|
||||
const A: i32 = FOO.test(); // You can't call Test::func() here !
|
||||
}
|
||||
```
|
||||
|
||||
Remember: you can't use a function call inside a const's initialization
|
||||
expression! However, you can totally use it anywhere else:
|
||||
|
||||
```
|
||||
fn main() {
|
||||
const FOO: Test = Test::V1;
|
||||
|
||||
FOO.func(); // here is good
|
||||
let x = FOO.func(); // or even here!
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0022: r##"
|
||||
Constant functions are not allowed to mutate anything. Thus, binding to an
|
||||
argument with a mutable pattern is not allowed. For example,
|
||||
@ -43,6 +225,366 @@ you need to mutate the argument, try lazily initializing a global variable
|
||||
instead of using a `const fn`, or refactoring the code to a functional style to
|
||||
avoid mutation if possible.
|
||||
"##,
|
||||
|
||||
E0030: r##"
|
||||
When matching against a range, the compiler verifies that the range is
|
||||
non-empty. Range patterns include both end-points, so this is equivalent to
|
||||
requiring the start of the range to be less than or equal to the end of the
|
||||
range.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
match 5u32 {
|
||||
// This range is ok, albeit pointless.
|
||||
1 ... 1 => ...
|
||||
// This range is empty, and the compiler can tell.
|
||||
1000 ... 5 => ...
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0161: r##"
|
||||
In Rust, you can only move a value when its size is known at compile time.
|
||||
|
||||
To work around this restriction, consider "hiding" the value behind a reference:
|
||||
either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
|
||||
it around as usual.
|
||||
"##,
|
||||
|
||||
E0265: r##"
|
||||
This error indicates that a static or constant references itself.
|
||||
All statics and constants need to resolve to a value in an acyclic manner.
|
||||
|
||||
For example, neither of the following can be sensibly compiled:
|
||||
|
||||
```
|
||||
const X: u32 = X;
|
||||
```
|
||||
|
||||
```
|
||||
const X: u32 = Y;
|
||||
const Y: u32 = X;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0267: r##"
|
||||
This error indicates the use of a loop keyword (`break` or `continue`) inside a
|
||||
closure but outside of any loop. Erroneous code example:
|
||||
|
||||
```
|
||||
let w = || { break; }; // error: `break` inside of a closure
|
||||
```
|
||||
|
||||
`break` and `continue` keywords can be used as normal inside closures as long as
|
||||
they are also contained within a loop. To halt the execution of a closure you
|
||||
should instead use a return statement. Example:
|
||||
|
||||
```
|
||||
let w = || {
|
||||
for _ in 0..10 {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
w();
|
||||
```
|
||||
"##,
|
||||
|
||||
E0268: r##"
|
||||
This error indicates the use of a loop keyword (`break` or `continue`) outside
|
||||
of a loop. Without a loop to break out of or continue in, no sensible action can
|
||||
be taken. Erroneous code example:
|
||||
|
||||
```
|
||||
fn some_func() {
|
||||
break; // error: `break` outside of loop
|
||||
}
|
||||
```
|
||||
|
||||
Please verify that you are using `break` and `continue` only in loops. Example:
|
||||
|
||||
```
|
||||
fn some_func() {
|
||||
for _ in 0..10 {
|
||||
break; // ok!
|
||||
}
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0378: r##"
|
||||
Method calls that aren't calls to inherent `const` methods are disallowed
|
||||
in statics, constants, and constant functions.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
|
||||
|
||||
struct Foo(i32);
|
||||
|
||||
impl Foo {
|
||||
const fn foo(&self) -> i32 {
|
||||
self.bar() // error, `bar` isn't `const`
|
||||
}
|
||||
|
||||
fn bar(&self) -> i32 { self.0 }
|
||||
}
|
||||
```
|
||||
|
||||
For more information about `const fn`'s, see [RFC 911].
|
||||
|
||||
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
|
||||
"##,
|
||||
|
||||
E0394: r##"
|
||||
From [RFC 246]:
|
||||
|
||||
> It is invalid for a static to reference another static by value. It is
|
||||
> required that all references be borrowed.
|
||||
|
||||
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
|
||||
"##,
|
||||
|
||||
E0395: r##"
|
||||
The value assigned to a constant expression must be known at compile time,
|
||||
which is not the case when comparing raw pointers. Erroneous code example:
|
||||
|
||||
```
|
||||
static foo: i32 = 42;
|
||||
static bar: i32 = 43;
|
||||
|
||||
static baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
|
||||
// error: raw pointers cannot be compared in statics!
|
||||
```
|
||||
|
||||
Please check that the result of the comparison can be determined at compile time
|
||||
or isn't assigned to a constant expression. Example:
|
||||
|
||||
```
|
||||
static foo: i32 = 42;
|
||||
static bar: i32 = 43;
|
||||
|
||||
let baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
|
||||
// baz isn't a constant expression so it's ok
|
||||
```
|
||||
"##,
|
||||
|
||||
E0396: r##"
|
||||
The value assigned to a constant expression must be known at compile time,
|
||||
which is not the case when dereferencing raw pointers. Erroneous code
|
||||
example:
|
||||
|
||||
```
|
||||
const foo: i32 = 42;
|
||||
const baz: *const i32 = (&foo as *const i32);
|
||||
|
||||
const deref: i32 = *baz;
|
||||
// error: raw pointers cannot be dereferenced in constants
|
||||
```
|
||||
|
||||
To fix this error, please do not assign this value to a constant expression.
|
||||
Example:
|
||||
|
||||
```
|
||||
const foo: i32 = 42;
|
||||
const baz: *const i32 = (&foo as *const i32);
|
||||
|
||||
unsafe { let deref: i32 = *baz; }
|
||||
// baz isn't a constant expression so it's ok
|
||||
```
|
||||
|
||||
You'll also note that this assignment must be done in an unsafe block!
|
||||
"##,
|
||||
|
||||
E0397: r##"
|
||||
It is not allowed for a mutable static to allocate or have destructors. For
|
||||
example:
|
||||
|
||||
```
|
||||
// error: mutable statics are not allowed to have boxes
|
||||
static mut FOO: Option<Box<usize>> = None;
|
||||
|
||||
// error: mutable statics are not allowed to have destructors
|
||||
static mut BAR: Option<Vec<i32>> = None;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0400: r##"
|
||||
A user-defined dereference was attempted in an invalid context. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
use std::ops::Deref;
|
||||
|
||||
struct A;
|
||||
|
||||
impl Deref for A {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self)-> &str { "foo" }
|
||||
}
|
||||
|
||||
const S: &'static str = &A;
|
||||
// error: user-defined dereference operators are not allowed in constants
|
||||
|
||||
fn main() {
|
||||
let foo = S;
|
||||
}
|
||||
```
|
||||
|
||||
You cannot directly use a dereference operation whilst initializing a constant
|
||||
or a static. To fix this error, restructure your code to avoid this dereference,
|
||||
perhaps moving it inline:
|
||||
|
||||
```
|
||||
use std::ops::Deref;
|
||||
|
||||
struct A;
|
||||
|
||||
impl Deref for A {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self)-> &str { "foo" }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo : &str = &A;
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0492: r##"
|
||||
A borrow of a constant containing interior mutability was attempted. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
|
||||
|
||||
const A: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
static B: &'static AtomicUsize = &A;
|
||||
// error: cannot borrow a constant which contains interior mutability, create a
|
||||
// static instead
|
||||
```
|
||||
|
||||
A `const` represents a constant value that should never change. If one takes
|
||||
a `&` reference to the constant, then one is taking a pointer to some memory
|
||||
location containing the value. Normally this is perfectly fine: most values
|
||||
can't be changed via a shared `&` pointer, but interior mutability would allow
|
||||
it. That is, a constant value could be mutated. On the other hand, a `static` is
|
||||
explicitly a single memory location, which can be mutated at will.
|
||||
|
||||
So, in order to solve this error, either use statics which are `Sync`:
|
||||
|
||||
```
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
|
||||
|
||||
static A: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
static B: &'static AtomicUsize = &A; // ok!
|
||||
```
|
||||
|
||||
You can also have this error while using a cell type:
|
||||
|
||||
```
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
const A: Cell<usize> = Cell::new(1);
|
||||
const B: &'static Cell<usize> = &A;
|
||||
// error: cannot borrow a constant which contains interior mutability, create
|
||||
// a static instead
|
||||
|
||||
// or:
|
||||
struct C { a: Cell<usize> }
|
||||
|
||||
const D: C = C { a: Cell::new(1) };
|
||||
const E: &'static Cell<usize> = &D.a; // error
|
||||
|
||||
// or:
|
||||
const F: &'static C = &D; // error
|
||||
```
|
||||
|
||||
This is because cell types do operations that are not thread-safe. Due to this,
|
||||
they don't implement Sync and thus can't be placed in statics. In this
|
||||
case, `StaticMutex` would work just fine, but it isn't stable yet:
|
||||
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
|
||||
|
||||
However, if you still wish to use these types, you can achieve this by an unsafe
|
||||
wrapper:
|
||||
|
||||
```
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::marker::Sync;
|
||||
|
||||
struct NotThreadSafe<T> {
|
||||
value: Cell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for NotThreadSafe<T> {}
|
||||
|
||||
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
|
||||
static B: &'static NotThreadSafe<usize> = &A; // ok!
|
||||
```
|
||||
|
||||
Remember this solution is unsafe! You will have to ensure that accesses to the
|
||||
cell are synchronized.
|
||||
"##,
|
||||
|
||||
E0493: r##"
|
||||
A type with a destructor was assigned to an invalid type of variable. Erroneous
|
||||
code example:
|
||||
|
||||
```
|
||||
struct Foo {
|
||||
a: u32
|
||||
}
|
||||
|
||||
impl Drop for Foo {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
const F : Foo = Foo { a : 0 };
|
||||
// error: constants are not allowed to have destructors
|
||||
static S : Foo = Foo { a : 0 };
|
||||
// error: statics are not allowed to have destructors
|
||||
```
|
||||
|
||||
To solve this issue, please use a type which does allow the usage of type with
|
||||
destructors.
|
||||
"##,
|
||||
|
||||
E0494: r##"
|
||||
A reference of an interior static was assigned to another const/static.
|
||||
Erroneous code example:
|
||||
|
||||
```
|
||||
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!
|
||||
```
|
||||
"##,
|
||||
|
||||
}
|
||||
|
||||
register_diagnostics! {
|
||||
|
@ -28,9 +28,16 @@
|
||||
|
||||
extern crate core;
|
||||
extern crate rustc;
|
||||
extern crate rustc_front;
|
||||
|
||||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate syntax;
|
||||
|
||||
pub mod diagnostics;
|
||||
|
||||
pub mod const_fn;
|
||||
pub mod consts;
|
||||
pub mod loops;
|
||||
pub mod no_asm;
|
||||
pub mod rvalues;
|
||||
pub mod static_recursion;
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
use self::Context::*;
|
||||
|
||||
use session::Session;
|
||||
use rustc::session::Session;
|
||||
|
||||
use syntax::codemap::Span;
|
||||
use rustc_front::intravisit::{self, Visitor};
|
@ -11,12 +11,11 @@
|
||||
// Checks that all rvalues in a crate have statically known size. check_crate
|
||||
// is the public starting point.
|
||||
|
||||
use dep_graph::DepNode;
|
||||
use middle::expr_use_visitor as euv;
|
||||
use middle::infer;
|
||||
use middle::mem_categorization as mc;
|
||||
use middle::ty::ParameterEnvironment;
|
||||
use middle::ty;
|
||||
use rustc::dep_graph::DepNode;
|
||||
use rustc::middle::expr_use_visitor as euv;
|
||||
use rustc::middle::infer;
|
||||
use rustc::middle::mem_categorization as mc;
|
||||
use rustc::middle::ty::{self, ParameterEnvironment};
|
||||
|
||||
use rustc_front::hir;
|
||||
use rustc_front::intravisit;
|
@ -11,10 +11,10 @@
|
||||
// This compiler pass detects constants that refer to themselves
|
||||
// recursively.
|
||||
|
||||
use front::map as ast_map;
|
||||
use session::Session;
|
||||
use middle::def::{Def, DefMap};
|
||||
use util::nodemap::NodeMap;
|
||||
use rustc::front::map as ast_map;
|
||||
use rustc::session::Session;
|
||||
use rustc::middle::def::{Def, DefMap};
|
||||
use rustc::util::nodemap::NodeMap;
|
||||
|
||||
use syntax::{ast};
|
||||
use syntax::codemap::Span;
|
@ -13,7 +13,7 @@ use back::abi;
|
||||
use llvm;
|
||||
use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
|
||||
use llvm::{InternalLinkage, ValueRef, Bool, True};
|
||||
use middle::check_const;
|
||||
use middle::const_qualif::ConstQualif;
|
||||
use middle::cstore::LOCAL_CRATE;
|
||||
use middle::const_eval::{self, ConstVal, ConstEvalErr};
|
||||
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
|
||||
@ -274,8 +274,7 @@ fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
-> Result<ValueRef, ConstEvalFailure> {
|
||||
let expr = get_const_expr(ccx, def_id, ref_expr, param_substs);
|
||||
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
|
||||
match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(),
|
||||
empty_substs, TrueConst::Yes) {
|
||||
match get_const_expr_as_global(ccx, expr, ConstQualif::empty(), empty_substs, TrueConst::Yes) {
|
||||
Err(Runtime(err)) => {
|
||||
ccx.tcx().sess.span_err(expr.span, &err.description());
|
||||
Err(Compiletime(err))
|
||||
@ -286,7 +285,7 @@ fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
|
||||
pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
expr: &hir::Expr,
|
||||
qualif: check_const::ConstQualif,
|
||||
qualif: ConstQualif,
|
||||
param_substs: &'tcx Substs<'tcx>,
|
||||
trueconst: TrueConst)
|
||||
-> Result<ValueRef, ConstEvalFailure> {
|
||||
@ -315,7 +314,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
}
|
||||
let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
|
||||
&ccx.tcx().expr_ty(expr));
|
||||
let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
|
||||
let val = if qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
||||
// Avoid autorefs as they would create global instead of stack
|
||||
// references, even when only the latter are correct.
|
||||
try!(const_expr_unadjusted(ccx, expr, ty, param_substs, None, trueconst))
|
||||
|
@ -53,7 +53,7 @@ use self::lazy_binop_ty::*;
|
||||
|
||||
use back::abi;
|
||||
use llvm::{self, ValueRef, TypeKind};
|
||||
use middle::check_const;
|
||||
use middle::const_qualif::ConstQualif;
|
||||
use middle::def::Def;
|
||||
use middle::lang_items::CoerceUnsizedTraitLangItem;
|
||||
use middle::subst::{Substs, VecPerParamSpace};
|
||||
@ -128,11 +128,8 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
}
|
||||
|
||||
let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
|
||||
if !qualif.intersects(
|
||||
check_const::ConstQualif::NOT_CONST |
|
||||
check_const::ConstQualif::NEEDS_DROP
|
||||
) {
|
||||
if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) {
|
||||
if !qualif.intersects(ConstQualif::NOT_CONST | ConstQualif::NEEDS_DROP) {
|
||||
if !qualif.intersects(ConstQualif::PREFER_IN_PLACE) {
|
||||
if let SaveIn(lldest) = dest {
|
||||
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
||||
bcx.fcx.param_substs,
|
||||
@ -231,16 +228,13 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
let mut bcx = bcx;
|
||||
let fcx = bcx.fcx;
|
||||
let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
|
||||
let adjusted_global = !qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS);
|
||||
let global = if !qualif.intersects(
|
||||
check_const::ConstQualif::NOT_CONST |
|
||||
check_const::ConstQualif::NEEDS_DROP
|
||||
) {
|
||||
let adjusted_global = !qualif.intersects(ConstQualif::NON_STATIC_BORROWS);
|
||||
let global = if !qualif.intersects(ConstQualif::NOT_CONST | ConstQualif::NEEDS_DROP) {
|
||||
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
|
||||
bcx.fcx.param_substs,
|
||||
consts::TrueConst::No) {
|
||||
Ok(global) => {
|
||||
if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) {
|
||||
if qualif.intersects(ConstQualif::HAS_STATIC_BORROWS) {
|
||||
// Is borrowed as 'static, must return lvalue.
|
||||
|
||||
// Cast pointer to global, because constants have different types.
|
||||
|
Loading…
x
Reference in New Issue
Block a user