mir: qualify and promote constants.
This commit is contained in:
parent
14efbf1481
commit
78884b7659
@ -111,7 +111,7 @@ DEPS_rustc_lint := rustc log syntax rustc_const_eval
|
|||||||
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
|
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
|
||||||
DEPS_rustc_metadata := rustc syntax rbml rustc_const_math
|
DEPS_rustc_metadata := rustc syntax rbml rustc_const_math
|
||||||
DEPS_rustc_passes := syntax rustc core rustc_const_eval
|
DEPS_rustc_passes := syntax rustc core rustc_const_eval
|
||||||
DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval
|
DEPS_rustc_mir := rustc syntax rustc_const_math rustc_const_eval rustc_bitflags
|
||||||
DEPS_rustc_resolve := arena rustc log syntax
|
DEPS_rustc_resolve := arena rustc log syntax
|
||||||
DEPS_rustc_platform_intrinsics := std
|
DEPS_rustc_platform_intrinsics := std
|
||||||
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
|
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
|
||||||
|
@ -37,7 +37,7 @@ use rustc_privacy;
|
|||||||
use rustc_plugin::registry::Registry;
|
use rustc_plugin::registry::Registry;
|
||||||
use rustc_plugin as plugin;
|
use rustc_plugin as plugin;
|
||||||
use rustc::hir::lowering::{lower_crate, LoweringContext};
|
use rustc::hir::lowering::{lower_crate, LoweringContext};
|
||||||
use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
|
use rustc_passes::{no_asm, loops, consts, rvalues, static_recursion};
|
||||||
use rustc_const_eval::check_match;
|
use rustc_const_eval::check_match;
|
||||||
use super::Compilation;
|
use super::Compilation;
|
||||||
|
|
||||||
@ -726,10 +726,6 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
time(time_passes,
|
|
||||||
"const fn bodies and arguments",
|
|
||||||
|| const_fn::check_crate(sess, &krate))?;
|
|
||||||
|
|
||||||
if sess.opts.debugging_opts.input_stats {
|
if sess.opts.debugging_opts.input_stats {
|
||||||
println!("Post-expansion node count: {}", count_nodes(&krate));
|
println!("Post-expansion node count: {}", count_nodes(&krate));
|
||||||
}
|
}
|
||||||
@ -903,6 +899,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|
|||||||
let mut passes = sess.mir_passes.borrow_mut();
|
let mut passes = sess.mir_passes.borrow_mut();
|
||||||
// Push all the built-in passes.
|
// Push all the built-in passes.
|
||||||
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
|
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
|
||||||
|
passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants);
|
||||||
passes.push_pass(box mir::transform::type_check::TypeckMir);
|
passes.push_pass(box mir::transform::type_check::TypeckMir);
|
||||||
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
|
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
|
||||||
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
|
passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
|
||||||
|
@ -16,4 +16,5 @@ rustc_back = { path = "../librustc_back" }
|
|||||||
rustc_const_eval = { path = "../librustc_const_eval" }
|
rustc_const_eval = { path = "../librustc_const_eval" }
|
||||||
rustc_const_math = { path = "../librustc_const_math" }
|
rustc_const_math = { path = "../librustc_const_math" }
|
||||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||||
|
rustc_bitflags = { path = "../librustc_bitflags" }
|
||||||
syntax = { path = "../libsyntax" }
|
syntax = { path = "../libsyntax" }
|
||||||
|
@ -84,7 +84,7 @@ pub struct ScopeAuxiliary {
|
|||||||
pub postdoms: Vec<Location>,
|
pub postdoms: Vec<Location>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
pub struct Location {
|
pub struct Location {
|
||||||
/// the location is within this block
|
/// the location is within this block
|
||||||
pub block: BasicBlock,
|
pub block: BasicBlock,
|
||||||
|
387
src/librustc_mir/diagnostics.rs
Normal file
387
src/librustc_mir/diagnostics.rs
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
#![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:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
#![feature(box_syntax)]
|
||||||
|
|
||||||
|
const CON : Box<i32> = box 0;
|
||||||
|
```
|
||||||
|
"##,
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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;
|
||||||
|
```
|
||||||
|
"##,
|
||||||
|
|
||||||
|
// 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. Erroneous code example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
|
||||||
|
```
|
||||||
|
|
||||||
|
To avoid it, you have to replace the non-item object:
|
||||||
|
|
||||||
|
```
|
||||||
|
const FOO: i32 = { const X : i32 = 0; X };
|
||||||
|
```
|
||||||
|
"##,
|
||||||
|
|
||||||
|
E0017: r##"
|
||||||
|
References in statics and constants may only refer to immutable values.
|
||||||
|
Erroneous code example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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 constant integers must be known at compile time. You
|
||||||
|
can't cast a pointer to an integer because the address of a pointer can
|
||||||
|
vary.
|
||||||
|
|
||||||
|
For example, if you write:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
static MY_STATIC: u32 = 42;
|
||||||
|
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
|
||||||
|
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
|
||||||
|
the address can change when the program is linked, as well as change
|
||||||
|
between different executions due to ASLR, and many linkers would
|
||||||
|
not be able to calculate the value of `WHAT`.
|
||||||
|
|
||||||
|
On the other hand, static and constant pointers can point either to
|
||||||
|
a known numeric address or to the address of a symbol.
|
||||||
|
|
||||||
|
```
|
||||||
|
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
|
||||||
|
// ... and also
|
||||||
|
static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
|
||||||
|
|
||||||
|
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
|
||||||
|
```
|
||||||
|
|
||||||
|
This does not pose a problem by itself because they can't be
|
||||||
|
accessed directly.
|
||||||
|
"##,
|
||||||
|
|
||||||
|
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. Erroneous code
|
||||||
|
example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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,
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
const fn foo(mut x: u8) {
|
||||||
|
// do stuff
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Is incorrect because the function body may not mutate `x`.
|
||||||
|
|
||||||
|
Remove any mutable bindings from the argument list to fix this error. In case
|
||||||
|
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.
|
||||||
|
"##,
|
||||||
|
|
||||||
|
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 scalar must be known at compile time,
|
||||||
|
which is not the case when comparing raw pointers.
|
||||||
|
|
||||||
|
Erroneous code example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
static FOO: i32 = 42;
|
||||||
|
static BAR: i32 = 42;
|
||||||
|
|
||||||
|
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
|
||||||
|
// error: raw pointers cannot be compared in statics!
|
||||||
|
```
|
||||||
|
|
||||||
|
The address assigned by the linker to `FOO` and `BAR` may or may not
|
||||||
|
be identical, so the value of `BAZ` can't be determined.
|
||||||
|
|
||||||
|
If you want to do the comparison, please do it at run-time.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
static FOO: i32 = 42;
|
||||||
|
static BAR: i32 = 42;
|
||||||
|
|
||||||
|
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 behind a raw pointer can't be determined at compile-time
|
||||||
|
(or even link-time), which means it can't be used in a constant
|
||||||
|
expression. Erroneous code example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
|
||||||
|
|
||||||
|
const VALUE: u8 = unsafe { *REG_ADDR };
|
||||||
|
// error: raw pointers cannot be dereferenced in constants
|
||||||
|
```
|
||||||
|
|
||||||
|
A possible fix is to dereference your pointer at some point in run-time.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
|
||||||
|
|
||||||
|
let reg_value = unsafe { *REG_ADDR };
|
||||||
|
```
|
||||||
|
"##,
|
||||||
|
|
||||||
|
E0492: r##"
|
||||||
|
A borrow of a constant containing interior mutability was attempted. Erroneous
|
||||||
|
code example:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
#![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:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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:
|
||||||
|
|
||||||
|
```compile_fail
|
||||||
|
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! {
|
||||||
|
E0526, // shuffle indices are not constant
|
||||||
|
}
|
@ -351,21 +351,39 @@ fn make_mirror_unadjusted<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr
|
|||||||
pass_args, lhs.to_ref(), vec![rhs])
|
pass_args, lhs.to_ref(), vec![rhs])
|
||||||
} else {
|
} else {
|
||||||
// FIXME overflow
|
// FIXME overflow
|
||||||
match op.node {
|
match (op.node, cx.constness) {
|
||||||
hir::BinOp_::BiAnd => {
|
// FIXME(eddyb) use logical ops in constants when
|
||||||
|
// they can handle that kind of control-flow.
|
||||||
|
(hir::BinOp_::BiAnd, hir::Constness::Const) => {
|
||||||
|
ExprKind::Binary {
|
||||||
|
op: BinOp::BitAnd,
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(hir::BinOp_::BiOr, hir::Constness::Const) => {
|
||||||
|
ExprKind::Binary {
|
||||||
|
op: BinOp::BitOr,
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(hir::BinOp_::BiAnd, hir::Constness::NotConst) => {
|
||||||
ExprKind::LogicalOp {
|
ExprKind::LogicalOp {
|
||||||
op: LogicalOp::And,
|
op: LogicalOp::And,
|
||||||
lhs: lhs.to_ref(),
|
lhs: lhs.to_ref(),
|
||||||
rhs: rhs.to_ref(),
|
rhs: rhs.to_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::BinOp_::BiOr => {
|
(hir::BinOp_::BiOr, hir::Constness::NotConst) => {
|
||||||
ExprKind::LogicalOp {
|
ExprKind::LogicalOp {
|
||||||
op: LogicalOp::Or,
|
op: LogicalOp::Or,
|
||||||
lhs: lhs.to_ref(),
|
lhs: lhs.to_ref(),
|
||||||
rhs: rhs.to_ref(),
|
rhs: rhs.to_ref(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let op = bin_op(op.node);
|
let op = bin_op(op.node);
|
||||||
ExprKind::Binary {
|
ExprKind::Binary {
|
||||||
|
@ -32,13 +32,17 @@ use rustc_const_math::{ConstInt, ConstUsize};
|
|||||||
pub struct Cx<'a, 'tcx: 'a> {
|
pub struct Cx<'a, 'tcx: 'a> {
|
||||||
tcx: &'a TyCtxt<'tcx>,
|
tcx: &'a TyCtxt<'tcx>,
|
||||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||||
|
constness: hir::Constness
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a,'tcx> Cx<'a,'tcx> {
|
impl<'a,'tcx> Cx<'a,'tcx> {
|
||||||
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Cx<'a, 'tcx> {
|
pub fn new(infcx: &'a InferCtxt<'a, 'tcx>,
|
||||||
|
constness: hir::Constness)
|
||||||
|
-> Cx<'a, 'tcx> {
|
||||||
Cx {
|
Cx {
|
||||||
tcx: infcx.tcx,
|
tcx: infcx.tcx,
|
||||||
infcx: infcx,
|
infcx: infcx,
|
||||||
|
constness: constness,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
|||||||
#![cfg_attr(not(stage0), deny(warnings))]
|
#![cfg_attr(not(stage0), deny(warnings))]
|
||||||
#![unstable(feature = "rustc_private", issue = "27812")]
|
#![unstable(feature = "rustc_private", issue = "27812")]
|
||||||
|
|
||||||
|
#![feature(associated_consts)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
|
#![feature(rustc_diagnostic_macros)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(staged_api)]
|
#![feature(staged_api)]
|
||||||
#![feature(question_mark)]
|
#![feature(question_mark)]
|
||||||
@ -31,10 +33,16 @@ extern crate graphviz as dot;
|
|||||||
extern crate rustc;
|
extern crate rustc;
|
||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
extern crate rustc_back;
|
extern crate rustc_back;
|
||||||
|
#[macro_use]
|
||||||
|
#[no_link]
|
||||||
|
extern crate rustc_bitflags;
|
||||||
|
#[macro_use]
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
extern crate rustc_const_math;
|
extern crate rustc_const_math;
|
||||||
extern crate rustc_const_eval;
|
extern crate rustc_const_eval;
|
||||||
|
|
||||||
|
pub mod diagnostics;
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod graphviz;
|
pub mod graphviz;
|
||||||
mod hair;
|
mod hair;
|
||||||
|
@ -29,7 +29,8 @@ use rustc::traits::ProjectionMode;
|
|||||||
use rustc::ty::{self, Ty, TyCtxt};
|
use rustc::ty::{self, Ty, TyCtxt};
|
||||||
use rustc::util::nodemap::NodeMap;
|
use rustc::util::nodemap::NodeMap;
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use rustc::hir::intravisit::{self, Visitor};
|
use rustc::hir::intravisit::{self, FnKind, Visitor};
|
||||||
|
use rustc::hir::map::blocks::FnLikeNode;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
|
|
||||||
@ -59,13 +60,27 @@ impl<'a, 'tcx> BuildMir<'a, 'tcx> {
|
|||||||
fn build<F>(&mut self, src: MirSource, f: F)
|
fn build<F>(&mut self, src: MirSource, f: F)
|
||||||
where F: for<'b> FnOnce(Cx<'b, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec)
|
where F: for<'b> FnOnce(Cx<'b, 'tcx>) -> (Mir<'tcx>, build::ScopeAuxiliaryVec)
|
||||||
{
|
{
|
||||||
|
let constness = match src {
|
||||||
|
MirSource::Const(_) |
|
||||||
|
MirSource::Static(..) => hir::Constness::Const,
|
||||||
|
MirSource::Fn(id) => {
|
||||||
|
let fn_like = FnLikeNode::from_node(self.tcx.map.get(id));
|
||||||
|
match fn_like.map(|f| f.kind()) {
|
||||||
|
Some(FnKind::ItemFn(_, _, _, c, _, _, _)) => c,
|
||||||
|
Some(FnKind::Method(_, m, _, _)) => m.constness,
|
||||||
|
_ => hir::Constness::NotConst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MirSource::Promoted(..) => bug!()
|
||||||
|
};
|
||||||
|
|
||||||
let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id());
|
let param_env = ty::ParameterEnvironment::for_item(self.tcx, src.item_id());
|
||||||
let infcx = infer::new_infer_ctxt(self.tcx,
|
let infcx = infer::new_infer_ctxt(self.tcx,
|
||||||
&self.tcx.tables,
|
&self.tcx.tables,
|
||||||
Some(param_env),
|
Some(param_env),
|
||||||
ProjectionMode::AnyFinal);
|
ProjectionMode::AnyFinal);
|
||||||
|
|
||||||
let (mir, scope_auxiliary) = f(Cx::new(&infcx));
|
let (mir, scope_auxiliary) = f(Cx::new(&infcx, constness));
|
||||||
|
|
||||||
pretty::dump_mir(self.tcx, "mir_map", &0, src, &mir, Some(&scope_auxiliary));
|
pretty::dump_mir(self.tcx, "mir_map", &0, src, &mir, Some(&scope_auxiliary));
|
||||||
|
|
||||||
@ -151,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn visit_fn(&mut self,
|
fn visit_fn(&mut self,
|
||||||
fk: intravisit::FnKind<'tcx>,
|
fk: FnKind<'tcx>,
|
||||||
decl: &'tcx hir::FnDecl,
|
decl: &'tcx hir::FnDecl,
|
||||||
body: &'tcx hir::Block,
|
body: &'tcx hir::Block,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -165,7 +180,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let implicit_argument = if let intravisit::FnKind::Closure(..) = fk {
|
let implicit_argument = if let FnKind::Closure(..) = fk {
|
||||||
Some((closure_self_ty(&self.tcx, id, body.id), None))
|
Some((closure_self_ty(&self.tcx, id, body.id), None))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -14,3 +14,5 @@ pub mod erase_regions;
|
|||||||
pub mod no_landing_pads;
|
pub mod no_landing_pads;
|
||||||
pub mod type_check;
|
pub mod type_check;
|
||||||
pub mod break_critical_edges;
|
pub mod break_critical_edges;
|
||||||
|
pub mod promote_consts;
|
||||||
|
pub mod qualify_consts;
|
||||||
|
412
src/librustc_mir/transform/promote_consts.rs
Normal file
412
src/librustc_mir/transform/promote_consts.rs
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! A pass that promotes borrows of constant rvalues.
|
||||||
|
//!
|
||||||
|
//! The rvalues considered constant are trees of temps,
|
||||||
|
//! each with exactly one initialization, and holding
|
||||||
|
//! a constant value with no interior mutability.
|
||||||
|
//! They are placed into a new MIR constant body in
|
||||||
|
//! `promoted` and the borrow rvalue is replaced with
|
||||||
|
//! a `Literal::Promoted` using the index into `promoted`
|
||||||
|
//! of that constant MIR.
|
||||||
|
//!
|
||||||
|
//! This pass assumes that every use is dominated by an
|
||||||
|
//! initialization and can otherwise silence errors, if
|
||||||
|
//! move analysis runs after promotion on broken MIR.
|
||||||
|
|
||||||
|
use rustc::mir::repr::*;
|
||||||
|
use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor};
|
||||||
|
use rustc::ty::{self, TyCtxt};
|
||||||
|
use syntax::codemap::Span;
|
||||||
|
|
||||||
|
use build::Location;
|
||||||
|
use traversal::ReversePostorder;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
/// State of a temporary during collection and promotion.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum TempState {
|
||||||
|
/// No references to this temp.
|
||||||
|
Undefined,
|
||||||
|
/// One direct assignment and any number of direct uses.
|
||||||
|
/// A borrow of this temp is promotable if the assigned
|
||||||
|
/// value is qualified as constant.
|
||||||
|
Defined {
|
||||||
|
location: Location,
|
||||||
|
uses: usize
|
||||||
|
},
|
||||||
|
/// Any other combination of assignments/uses.
|
||||||
|
Unpromotable,
|
||||||
|
/// This temp was part of an rvalue which got extracted
|
||||||
|
/// during promotion and needs cleanup.
|
||||||
|
PromotedOut
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TempState {
|
||||||
|
pub fn is_promotable(&self) -> bool {
|
||||||
|
if let TempState::Defined { uses, .. } = *self {
|
||||||
|
uses > 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A "root candidate" for promotion, which will become the
|
||||||
|
/// returned value in a promoted MIR, unless it's a subset
|
||||||
|
/// of a larger candidate.
|
||||||
|
pub enum Candidate {
|
||||||
|
/// Borrow of a constant temporary.
|
||||||
|
Ref(Location),
|
||||||
|
|
||||||
|
/// Array of indices found in the third argument of
|
||||||
|
/// a call to one of the simd_shuffleN intrinsics.
|
||||||
|
ShuffleIndices(BasicBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TempCollector {
|
||||||
|
temps: Vec<TempState>,
|
||||||
|
location: Location,
|
||||||
|
span: Span
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for TempCollector {
|
||||||
|
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) {
|
||||||
|
self.super_lvalue(lvalue, context);
|
||||||
|
if let Lvalue::Temp(index) = *lvalue {
|
||||||
|
// Ignore drops, if the temp gets promoted,
|
||||||
|
// then it's constant and thus drop is noop.
|
||||||
|
if let LvalueContext::Drop = context {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp = &mut self.temps[index as usize];
|
||||||
|
if *temp == TempState::Undefined {
|
||||||
|
match context {
|
||||||
|
LvalueContext::Store |
|
||||||
|
LvalueContext::Call => {
|
||||||
|
*temp = TempState::Defined {
|
||||||
|
location: self.location,
|
||||||
|
uses: 0
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => { /* mark as unpromotable below */ }
|
||||||
|
}
|
||||||
|
} else if let TempState::Defined { ref mut uses, .. } = *temp {
|
||||||
|
match context {
|
||||||
|
LvalueContext::Borrow {..} |
|
||||||
|
LvalueContext::Consume |
|
||||||
|
LvalueContext::Inspect => {
|
||||||
|
*uses += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => { /* mark as unpromotable below */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*temp = TempState::Unpromotable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_statement(&mut self, bb: BasicBlock, statement: &Statement<'tcx>) {
|
||||||
|
assert_eq!(self.location.block, bb);
|
||||||
|
self.span = statement.span;
|
||||||
|
self.super_statement(bb, statement);
|
||||||
|
self.location.statement_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_terminator(&mut self, bb: BasicBlock, terminator: &Terminator<'tcx>) {
|
||||||
|
self.span = terminator.span;
|
||||||
|
self.super_terminator(bb, terminator);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
|
||||||
|
self.location.statement_index = 0;
|
||||||
|
self.location.block = bb;
|
||||||
|
self.super_basic_block_data(bb, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
|
||||||
|
let mut collector = TempCollector {
|
||||||
|
temps: vec![TempState::Undefined; mir.temp_decls.len()],
|
||||||
|
location: Location {
|
||||||
|
block: START_BLOCK,
|
||||||
|
statement_index: 0
|
||||||
|
},
|
||||||
|
span: mir.span
|
||||||
|
};
|
||||||
|
for (bb, data) in rpo {
|
||||||
|
collector.visit_basic_block_data(bb, data);
|
||||||
|
}
|
||||||
|
collector.temps
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Promoter<'a, 'tcx: 'a> {
|
||||||
|
source: &'a mut Mir<'tcx>,
|
||||||
|
promoted: Mir<'tcx>,
|
||||||
|
temps: &'a mut Vec<TempState>,
|
||||||
|
|
||||||
|
/// If true, all nested temps are also kept in the
|
||||||
|
/// source MIR, not moved to the promoted MIR.
|
||||||
|
keep_original: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Promoter<'a, 'tcx> {
|
||||||
|
fn new_block(&mut self) -> BasicBlock {
|
||||||
|
let index = self.promoted.basic_blocks.len();
|
||||||
|
self.promoted.basic_blocks.push(BasicBlockData {
|
||||||
|
statements: vec![],
|
||||||
|
terminator: Some(Terminator {
|
||||||
|
span: self.promoted.span,
|
||||||
|
scope: ScopeId::new(0),
|
||||||
|
kind: TerminatorKind::Return
|
||||||
|
}),
|
||||||
|
is_cleanup: false
|
||||||
|
});
|
||||||
|
BasicBlock::new(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
|
||||||
|
let data = self.promoted.basic_blocks.last_mut().unwrap();
|
||||||
|
data.statements.push(Statement {
|
||||||
|
span: span,
|
||||||
|
scope: ScopeId::new(0),
|
||||||
|
kind: StatementKind::Assign(dest, rvalue)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy the initialization of this temp to the
|
||||||
|
/// promoted MIR, recursing through temps.
|
||||||
|
fn promote_temp(&mut self, index: u32) -> u32 {
|
||||||
|
let index = index as usize;
|
||||||
|
let old_keep_original = self.keep_original;
|
||||||
|
let (bb, stmt_idx) = match self.temps[index] {
|
||||||
|
TempState::Defined {
|
||||||
|
location: Location { block, statement_index },
|
||||||
|
uses
|
||||||
|
} if uses > 0 => {
|
||||||
|
if uses > 1 {
|
||||||
|
self.keep_original = true;
|
||||||
|
}
|
||||||
|
(block, statement_index)
|
||||||
|
}
|
||||||
|
temp => {
|
||||||
|
span_bug!(self.promoted.span, "tmp{} not promotable: {:?}",
|
||||||
|
index, temp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !self.keep_original {
|
||||||
|
self.temps[index] = TempState::PromotedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
let no_stmts = self.source[bb].statements.len();
|
||||||
|
|
||||||
|
// First, take the Rvalue or Call out of the source MIR,
|
||||||
|
// or duplicate it, depending on keep_original.
|
||||||
|
let (mut rvalue, mut call) = (None, None);
|
||||||
|
let span = if stmt_idx < no_stmts {
|
||||||
|
let statement = &mut self.source[bb].statements[stmt_idx];
|
||||||
|
let StatementKind::Assign(_, ref mut rhs) = statement.kind;
|
||||||
|
if self.keep_original {
|
||||||
|
rvalue = Some(rhs.clone());
|
||||||
|
} else {
|
||||||
|
let unit = Rvalue::Aggregate(AggregateKind::Tuple, vec![]);
|
||||||
|
rvalue = Some(mem::replace(rhs, unit));
|
||||||
|
}
|
||||||
|
statement.span
|
||||||
|
} else if self.keep_original {
|
||||||
|
let terminator = self.source[bb].terminator().clone();
|
||||||
|
call = Some(terminator.kind);
|
||||||
|
terminator.span
|
||||||
|
} else {
|
||||||
|
let terminator = self.source[bb].terminator_mut();
|
||||||
|
let target = match terminator.kind {
|
||||||
|
TerminatorKind::Call {
|
||||||
|
destination: ref mut dest @ Some(_),
|
||||||
|
ref mut cleanup, ..
|
||||||
|
} => {
|
||||||
|
// No cleanup necessary.
|
||||||
|
cleanup.take();
|
||||||
|
|
||||||
|
// We'll put a new destination in later.
|
||||||
|
dest.take().unwrap().1
|
||||||
|
}
|
||||||
|
ref kind => {
|
||||||
|
span_bug!(terminator.span, "{:?} not promotable", kind);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
call = Some(mem::replace(&mut terminator.kind, TerminatorKind::Goto {
|
||||||
|
target: target
|
||||||
|
}));
|
||||||
|
terminator.span
|
||||||
|
};
|
||||||
|
|
||||||
|
// Then, recurse for components in the Rvalue or Call.
|
||||||
|
if stmt_idx < no_stmts {
|
||||||
|
self.visit_rvalue(rvalue.as_mut().unwrap());
|
||||||
|
} else {
|
||||||
|
self.visit_terminator_kind(bb, call.as_mut().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_index = self.promoted.temp_decls.len() as u32;
|
||||||
|
let new_temp = Lvalue::Temp(new_index);
|
||||||
|
self.promoted.temp_decls.push(TempDecl {
|
||||||
|
ty: self.source.temp_decls[index].ty
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject the Rvalue or Call into the promoted MIR.
|
||||||
|
if stmt_idx < no_stmts {
|
||||||
|
self.assign(new_temp, rvalue.unwrap(), span);
|
||||||
|
} else {
|
||||||
|
let last = self.promoted.basic_blocks.len() - 1;
|
||||||
|
let new_target = self.new_block();
|
||||||
|
let mut call = call.unwrap();
|
||||||
|
match call {
|
||||||
|
TerminatorKind::Call { ref mut destination, ..} => {
|
||||||
|
*destination = Some((new_temp, new_target));
|
||||||
|
}
|
||||||
|
_ => bug!()
|
||||||
|
}
|
||||||
|
let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
|
||||||
|
terminator.span = span;
|
||||||
|
terminator.kind = call;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the old duplication state.
|
||||||
|
self.keep_original = old_keep_original;
|
||||||
|
|
||||||
|
new_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn promote_candidate(mut self, candidate: Candidate) {
|
||||||
|
let span = self.promoted.span;
|
||||||
|
let new_operand = Operand::Constant(Constant {
|
||||||
|
span: span,
|
||||||
|
ty: self.promoted.return_ty.unwrap(),
|
||||||
|
literal: Literal::Promoted {
|
||||||
|
index: self.source.promoted.len()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut rvalue = match candidate {
|
||||||
|
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
||||||
|
match self.source[bb].statements[stmt_idx].kind {
|
||||||
|
StatementKind::Assign(_, ref mut rvalue) => {
|
||||||
|
mem::replace(rvalue, Rvalue::Use(new_operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Candidate::ShuffleIndices(bb) => {
|
||||||
|
match self.source[bb].terminator_mut().kind {
|
||||||
|
TerminatorKind::Call { ref mut args, .. } => {
|
||||||
|
Rvalue::Use(mem::replace(&mut args[2], new_operand))
|
||||||
|
}
|
||||||
|
_ => bug!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.visit_rvalue(&mut rvalue);
|
||||||
|
self.assign(Lvalue::ReturnPointer, rvalue, span);
|
||||||
|
self.source.promoted.push(self.promoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces all temporaries with their promoted counterparts.
|
||||||
|
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
|
||||||
|
fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) {
|
||||||
|
if let Lvalue::Temp(ref mut index) = *lvalue {
|
||||||
|
*index = self.promote_temp(*index);
|
||||||
|
}
|
||||||
|
self.super_lvalue(lvalue, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn promote_candidates<'tcx>(mir: &mut Mir<'tcx>,
|
||||||
|
tcx: &TyCtxt<'tcx>,
|
||||||
|
mut temps: Vec<TempState>,
|
||||||
|
candidates: Vec<Candidate>) {
|
||||||
|
// Visit candidates in reverse, in case they're nested.
|
||||||
|
for candidate in candidates.into_iter().rev() {
|
||||||
|
let (span, ty) = match candidate {
|
||||||
|
Candidate::Ref(Location { block: bb, statement_index: stmt_idx }) => {
|
||||||
|
let statement = &mir[bb].statements[stmt_idx];
|
||||||
|
let StatementKind::Assign(ref dest, _) = statement.kind;
|
||||||
|
if let Lvalue::Temp(index) = *dest {
|
||||||
|
if temps[index as usize] == TempState::PromotedOut {
|
||||||
|
// Already promoted.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(statement.span, mir.lvalue_ty(tcx, dest).to_ty(tcx))
|
||||||
|
}
|
||||||
|
Candidate::ShuffleIndices(bb) => {
|
||||||
|
let terminator = mir[bb].terminator();
|
||||||
|
let ty = match terminator.kind {
|
||||||
|
TerminatorKind::Call { ref args, .. } => {
|
||||||
|
mir.operand_ty(tcx, &args[2])
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
span_bug!(terminator.span,
|
||||||
|
"expected simd_shuffleN call to promote");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(terminator.span, ty)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut promoter = Promoter {
|
||||||
|
source: mir,
|
||||||
|
promoted: Mir {
|
||||||
|
basic_blocks: vec![],
|
||||||
|
scopes: vec![ScopeData {
|
||||||
|
span: span,
|
||||||
|
parent_scope: None
|
||||||
|
}],
|
||||||
|
promoted: vec![],
|
||||||
|
return_ty: ty::FnConverging(ty),
|
||||||
|
var_decls: vec![],
|
||||||
|
arg_decls: vec![],
|
||||||
|
temp_decls: vec![],
|
||||||
|
upvar_decls: vec![],
|
||||||
|
span: span
|
||||||
|
},
|
||||||
|
temps: &mut temps,
|
||||||
|
keep_original: false
|
||||||
|
};
|
||||||
|
assert_eq!(promoter.new_block(), START_BLOCK);
|
||||||
|
promoter.promote_candidate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminate assignments to, and drops of promoted temps.
|
||||||
|
let promoted = |index: u32| temps[index as usize] == TempState::PromotedOut;
|
||||||
|
for block in &mut mir.basic_blocks {
|
||||||
|
block.statements.retain(|statement| {
|
||||||
|
match statement.kind {
|
||||||
|
StatementKind::Assign(Lvalue::Temp(index), _) => {
|
||||||
|
!promoted(index)
|
||||||
|
}
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let terminator = block.terminator_mut();
|
||||||
|
match terminator.kind {
|
||||||
|
TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => {
|
||||||
|
if promoted(index) {
|
||||||
|
terminator.kind = TerminatorKind::Goto {
|
||||||
|
target: target
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1010
src/librustc_mir/transform/qualify_consts.rs
Normal file
1010
src/librustc_mir/transform/qualify_consts.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,118 +0,0 @@
|
|||||||
// Copyright 2012-2014 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.
|
|
||||||
|
|
||||||
//! Verifies that const fn arguments are immutable by value bindings
|
|
||||||
//! and the const fn body doesn't contain any statements
|
|
||||||
|
|
||||||
use rustc::session::{Session, CompileResult};
|
|
||||||
|
|
||||||
use syntax::ast::{self, PatKind};
|
|
||||||
use syntax::visit::{self, Visitor, FnKind};
|
|
||||||
use syntax::codemap::Span;
|
|
||||||
|
|
||||||
pub fn check_crate(sess: &Session, krate: &ast::Crate) -> CompileResult {
|
|
||||||
sess.track_errors(|| {
|
|
||||||
visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CheckConstFn<'a> {
|
|
||||||
sess: &'a Session,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CheckBlock<'a> {
|
|
||||||
sess: &'a Session,
|
|
||||||
kind: &'static str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'v> Visitor<'v> for CheckBlock<'a> {
|
|
||||||
fn visit_block(&mut self, block: &'v ast::Block) {
|
|
||||||
check_block(&self.sess, block, self.kind);
|
|
||||||
CheckConstFn{ sess: self.sess}.visit_block(block);
|
|
||||||
}
|
|
||||||
fn visit_expr(&mut self, e: &'v ast::Expr) {
|
|
||||||
if let ast::ExprKind::Closure(..) = e.node {
|
|
||||||
CheckConstFn{ sess: self.sess}.visit_expr(e);
|
|
||||||
} else {
|
|
||||||
visit::walk_expr(self, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_item(&mut self, _i: &'v ast::Item) { bug!("should be handled in CheckConstFn") }
|
|
||||||
fn visit_fn(&mut self,
|
|
||||||
_fk: FnKind<'v>,
|
|
||||||
_fd: &'v ast::FnDecl,
|
|
||||||
_b: &'v ast::Block,
|
|
||||||
_s: Span,
|
|
||||||
_fn_id: ast::NodeId) { bug!("should be handled in CheckConstFn") }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_block(sess: &Session, b: &ast::Block, kind: &'static str) {
|
|
||||||
// Check all statements in the block
|
|
||||||
for stmt in &b.stmts {
|
|
||||||
let span = match stmt.node {
|
|
||||||
ast::StmtKind::Decl(ref decl, _) => {
|
|
||||||
match decl.node {
|
|
||||||
ast::DeclKind::Local(_) => decl.span,
|
|
||||||
|
|
||||||
// Item statements are allowed
|
|
||||||
ast::DeclKind::Item(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::StmtKind::Expr(ref expr, _) => expr.span,
|
|
||||||
ast::StmtKind::Semi(ref semi, _) => semi.span,
|
|
||||||
ast::StmtKind::Mac(..) => bug!(),
|
|
||||||
};
|
|
||||||
span_err!(sess, span, E0016,
|
|
||||||
"blocks in {}s are limited to items and tail expressions", kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'v> Visitor<'v> for CheckConstFn<'a> {
|
|
||||||
fn visit_item(&mut self, i: &'v ast::Item) {
|
|
||||||
visit::walk_item(self, i);
|
|
||||||
match i.node {
|
|
||||||
ast::ItemKind::Const(_, ref e) => {
|
|
||||||
CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
|
|
||||||
},
|
|
||||||
ast::ItemKind::Static(_, _, ref e) => {
|
|
||||||
CheckBlock{ sess: self.sess, kind: "static"}.visit_expr(e)
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_fn(&mut self,
|
|
||||||
fk: FnKind<'v>,
|
|
||||||
fd: &'v ast::FnDecl,
|
|
||||||
b: &'v ast::Block,
|
|
||||||
s: Span,
|
|
||||||
_fn_id: ast::NodeId) {
|
|
||||||
visit::walk_fn(self, fk, fd, b, s);
|
|
||||||
match fk {
|
|
||||||
FnKind::ItemFn(_, _, _, ast::Constness::Const, _, _) => {},
|
|
||||||
FnKind::Method(_, m, _) if m.constness == ast::Constness::Const => {},
|
|
||||||
_ => return,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the arguments are simple, not mutable/by-ref or patterns.
|
|
||||||
for arg in &fd.inputs {
|
|
||||||
match arg.pat.node {
|
|
||||||
PatKind::Wild => {}
|
|
||||||
PatKind::Ident(ast::BindingMode::ByValue(ast::Mutability::Immutable), _, None) => {}
|
|
||||||
_ => {
|
|
||||||
span_err!(self.sess, arg.pat.span, E0022,
|
|
||||||
"arguments of constant functions can only \
|
|
||||||
be immutable by-value bindings");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check_block(&self.sess, b, "const function");
|
|
||||||
}
|
|
||||||
}
|
|
@ -40,7 +40,7 @@ use rustc::infer;
|
|||||||
use rustc::middle::mem_categorization as mc;
|
use rustc::middle::mem_categorization as mc;
|
||||||
use rustc::middle::mem_categorization::Categorization;
|
use rustc::middle::mem_categorization::Categorization;
|
||||||
use rustc::ty::{self, Ty, TyCtxt};
|
use rustc::ty::{self, Ty, TyCtxt};
|
||||||
use rustc::traits::{self, ProjectionMode};
|
use rustc::traits::ProjectionMode;
|
||||||
use rustc::util::nodemap::NodeMap;
|
use rustc::util::nodemap::NodeMap;
|
||||||
use rustc::middle::const_qualif::ConstQualif;
|
use rustc::middle::const_qualif::ConstQualif;
|
||||||
use rustc::lint::builtin::CONST_ERR;
|
use rustc::lint::builtin::CONST_ERR;
|
||||||
@ -48,7 +48,6 @@ use rustc::lint::builtin::CONST_ERR;
|
|||||||
use rustc::hir::{self, PatKind};
|
use rustc::hir::{self, PatKind};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::feature_gate::UnstableFeatures;
|
|
||||||
use rustc::hir::intravisit::{self, FnKind, Visitor};
|
use rustc::hir::intravisit::{self, FnKind, Visitor};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
@ -180,31 +179,11 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
|||||||
|
|
||||||
/// Returns true if the call is to a const fn or method.
|
/// Returns true if the call is to a const fn or method.
|
||||||
fn handle_const_fn_call(&mut self,
|
fn handle_const_fn_call(&mut self,
|
||||||
expr: &hir::Expr,
|
_expr: &hir::Expr,
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
ret_ty: Ty<'tcx>)
|
ret_ty: Ty<'tcx>)
|
||||||
-> bool {
|
-> bool {
|
||||||
if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
|
if let Some(fn_like) = lookup_const_fn_by_id(self.tcx, def_id) {
|
||||||
if
|
|
||||||
// we are in a static/const initializer
|
|
||||||
self.mode != Mode::Var &&
|
|
||||||
|
|
||||||
// feature-gate is not enabled
|
|
||||||
!self.tcx.sess.features.borrow().const_fn &&
|
|
||||||
|
|
||||||
// this doesn't come from a macro that has #[allow_internal_unstable]
|
|
||||||
!self.tcx.sess.codemap().span_allows_unstable(expr.span)
|
|
||||||
{
|
|
||||||
let mut err = self.tcx.sess.struct_span_err(
|
|
||||||
expr.span,
|
|
||||||
"const fns are an unstable feature");
|
|
||||||
help!(
|
|
||||||
&mut err,
|
|
||||||
"in Nightly builds, add `#![feature(const_fn)]` to the crate \
|
|
||||||
attributes to enable");
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
let qualif = self.fn_like(fn_like.kind(),
|
let qualif = self.fn_like(fn_like.kind(),
|
||||||
fn_like.decl(),
|
fn_like.decl(),
|
||||||
fn_like.body(),
|
fn_like.body(),
|
||||||
@ -245,42 +224,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
|||||||
Mode::Var => bug!(),
|
Mode::Var => bug!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_static_mut_type(&self, e: &hir::Expr) {
|
|
||||||
let node_ty = self.tcx.node_id_to_type(e.id);
|
|
||||||
let tcontents = node_ty.type_contents(self.tcx);
|
|
||||||
|
|
||||||
let suffix = if tcontents.has_dtor() {
|
|
||||||
"destructors"
|
|
||||||
} else if tcontents.owns_owned() {
|
|
||||||
"boxes"
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
span_err!(self.tcx.sess, e.span, E0397,
|
|
||||||
"mutable statics are not allowed to have {}", suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_static_type(&self, e: &hir::Expr) {
|
|
||||||
let ty = self.tcx.node_id_to_type(e.id);
|
|
||||||
let infcx = infer::new_infer_ctxt(self.tcx,
|
|
||||||
&self.tcx.tables,
|
|
||||||
None,
|
|
||||||
ProjectionMode::AnyFinal);
|
|
||||||
let cause = traits::ObligationCause::new(e.span, e.id, traits::SharedStatic);
|
|
||||||
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
|
||||||
fulfillment_cx.register_builtin_bound(&infcx, ty, ty::BoundSync, cause);
|
|
||||||
match fulfillment_cx.select_all_or_error(&infcx) {
|
|
||||||
Ok(()) => { },
|
|
||||||
Err(ref errors) => {
|
|
||||||
traits::report_fulfillment_errors(&infcx, errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Err(ref errors) = fulfillment_cx.select_rfc1592_obligations(&infcx) {
|
|
||||||
traits::report_fulfillment_errors_as_warnings(&infcx, errors, e.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||||
@ -289,11 +232,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
assert_eq!(self.mode, Mode::Var);
|
assert_eq!(self.mode, Mode::Var);
|
||||||
match i.node {
|
match i.node {
|
||||||
hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
|
hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
|
||||||
self.check_static_type(&expr);
|
|
||||||
self.global_expr(Mode::Static, &expr);
|
self.global_expr(Mode::Static, &expr);
|
||||||
}
|
}
|
||||||
hir::ItemStatic(_, hir::MutMutable, ref expr) => {
|
hir::ItemStatic(_, hir::MutMutable, ref expr) => {
|
||||||
self.check_static_mut_type(&expr);
|
|
||||||
self.global_expr(Mode::StaticMut, &expr);
|
self.global_expr(Mode::StaticMut, &expr);
|
||||||
}
|
}
|
||||||
hir::ItemConst(_, ref expr) => {
|
hir::ItemConst(_, ref expr) => {
|
||||||
@ -360,8 +301,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
"lower range bound must be less than or equal to upper");
|
"lower range bound must be less than or equal to upper");
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.tcx.sess.delay_span_bug(start.span,
|
span_err!(self.tcx.sess, p.span, E0014,
|
||||||
"non-constant path in constant expr");
|
"paths in {}s may only refer to constants",
|
||||||
|
self.msg());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,8 +326,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
hir::StmtSemi(_, _) => {},
|
hir::StmtSemi(_, _) => {},
|
||||||
}
|
}
|
||||||
self.add_qualif(ConstQualif::NOT_CONST);
|
self.add_qualif(ConstQualif::NOT_CONST);
|
||||||
// anything else should have been caught by check_const_fn
|
|
||||||
assert_eq!(self.mode, Mode::Var);
|
|
||||||
}
|
}
|
||||||
intravisit::walk_block(self, block);
|
intravisit::walk_block(self, block);
|
||||||
}
|
}
|
||||||
@ -455,11 +395,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
let tc = node_ty.type_contents(self.tcx);
|
let tc = node_ty.type_contents(self.tcx);
|
||||||
if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
|
if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
|
||||||
outer = outer | ConstQualif::NOT_CONST;
|
outer = outer | ConstQualif::NOT_CONST;
|
||||||
if self.mode != Mode::Var {
|
|
||||||
span_err!(self.tcx.sess, ex.span, E0492,
|
|
||||||
"cannot borrow a constant which contains \
|
|
||||||
interior mutability, create a static instead");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If the reference has to be 'static, avoid in-place initialization
|
// If the reference has to be 'static, avoid in-place initialization
|
||||||
// as that will end up pointing to the stack instead.
|
// as that will end up pointing to the stack instead.
|
||||||
@ -474,10 +409,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
if self.mode == Mode::Var {
|
if self.mode == Mode::Var {
|
||||||
outer = outer | ConstQualif::NOT_CONST;
|
outer = outer | ConstQualif::NOT_CONST;
|
||||||
self.add_qualif(ConstQualif::MUTABLE_MEM);
|
self.add_qualif(ConstQualif::MUTABLE_MEM);
|
||||||
} else {
|
|
||||||
span_err!(self.tcx.sess, ex.span, E0017,
|
|
||||||
"references in {}s may only refer \
|
|
||||||
to immutable values", self.msg())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
if !self.qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
|
||||||
@ -525,11 +456,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
ty::TyStruct(def, _) |
|
ty::TyStruct(def, _) |
|
||||||
ty::TyEnum(def, _) if def.has_dtor() => {
|
ty::TyEnum(def, _) if def.has_dtor() => {
|
||||||
v.add_qualif(ConstQualif::NEEDS_DROP);
|
v.add_qualif(ConstQualif::NEEDS_DROP);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0493,
|
|
||||||
"{}s are not allowed to have destructors",
|
|
||||||
v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -540,17 +466,9 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
hir::ExprBinary(..) |
|
hir::ExprBinary(..) |
|
||||||
hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
|
hir::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0011,
|
|
||||||
"user-defined operators are not allowed in {}s", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hir::ExprBox(_) => {
|
hir::ExprBox(_) => {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0010,
|
|
||||||
"allocations are not allowed in {}s", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hir::ExprUnary(op, ref inner) => {
|
hir::ExprUnary(op, ref inner) => {
|
||||||
match v.tcx.node_id_to_type(inner.id).sty {
|
match v.tcx.node_id_to_type(inner.id).sty {
|
||||||
@ -558,10 +476,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
assert!(op == hir::UnDeref);
|
assert!(op == hir::UnDeref);
|
||||||
|
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0396,
|
|
||||||
"raw pointers cannot be dereferenced in {}s", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -574,10 +488,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
op.node == hir::BiGe || op.node == hir::BiGt);
|
op.node == hir::BiGe || op.node == hir::BiGt);
|
||||||
|
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0395,
|
|
||||||
"raw pointers cannot be compared in {}s", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -588,10 +498,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
None => span_bug!(e.span, "no kind for cast"),
|
None => span_bug!(e.span, "no kind for cast"),
|
||||||
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
|
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0018,
|
|
||||||
"raw pointers cannot be cast to integers in {}s", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -616,11 +522,7 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
Some(Def::Static(..)) => {
|
Some(Def::Static(..)) => {
|
||||||
match v.mode {
|
match v.mode {
|
||||||
Mode::Static | Mode::StaticMut => {}
|
Mode::Static | Mode::StaticMut => {}
|
||||||
Mode::Const | Mode::ConstFn => {
|
Mode::Const | Mode::ConstFn => {}
|
||||||
span_err!(v.tcx.sess, e.span, E0013,
|
|
||||||
"{}s cannot refer to other statics, insert \
|
|
||||||
an intermediate constant instead", v.msg());
|
|
||||||
}
|
|
||||||
Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
|
Mode::Var => v.add_qualif(ConstQualif::NOT_CONST)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -636,14 +538,8 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
// Sadly, we can't determine whether the types are zero-sized.
|
// Sadly, we can't determine whether the types are zero-sized.
|
||||||
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
|
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
|
||||||
}
|
}
|
||||||
def => {
|
_ => {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
debug!("(checking const) found bad def: {:?}", def);
|
|
||||||
span_err!(v.tcx.sess, e.span, E0014,
|
|
||||||
"paths in {}s may only refer to constants \
|
|
||||||
or functions", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,29 +577,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
};
|
};
|
||||||
if !is_const {
|
if !is_const {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
// FIXME(#24111) Remove this check when const fn stabilizes
|
|
||||||
let (msg, note) =
|
|
||||||
if let UnstableFeatures::Disallow = v.tcx.sess.opts.unstable_features {
|
|
||||||
(format!("function calls in {}s are limited to \
|
|
||||||
struct and enum constructors",
|
|
||||||
v.msg()),
|
|
||||||
Some("a limited form of compile-time function \
|
|
||||||
evaluation is available on a nightly \
|
|
||||||
compiler via `const fn`"))
|
|
||||||
} else {
|
|
||||||
(format!("function calls in {}s are limited \
|
|
||||||
to constant functions, \
|
|
||||||
struct and enum constructors",
|
|
||||||
v.msg()),
|
|
||||||
None)
|
|
||||||
};
|
|
||||||
let mut err = struct_span_err!(v.tcx.sess, e.span, E0015, "{}", msg);
|
|
||||||
if let Some(note) = note {
|
|
||||||
err.span_note(e.span, note);
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::ExprMethodCall(..) => {
|
hir::ExprMethodCall(..) => {
|
||||||
@ -714,11 +587,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
};
|
};
|
||||||
if !is_const {
|
if !is_const {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0378,
|
|
||||||
"method calls in {}s are limited to \
|
|
||||||
constant inherent methods", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hir::ExprStruct(..) => {
|
hir::ExprStruct(..) => {
|
||||||
@ -773,10 +641,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
|||||||
hir::ExprAssignOp(..) |
|
hir::ExprAssignOp(..) |
|
||||||
hir::ExprInlineAsm(..) => {
|
hir::ExprInlineAsm(..) => {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0019,
|
|
||||||
"{} contains unimplemented expression type", v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -796,11 +660,6 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
|
|||||||
v.tcx.is_overloaded_autoderef(e.id, autoderef)
|
v.tcx.is_overloaded_autoderef(e.id, autoderef)
|
||||||
}) {
|
}) {
|
||||||
v.add_qualif(ConstQualif::NOT_CONST);
|
v.add_qualif(ConstQualif::NOT_CONST);
|
||||||
if v.mode != Mode::Var {
|
|
||||||
span_err!(v.tcx.sess, e.span, E0400,
|
|
||||||
"user-defined dereference operators are not allowed in {}s",
|
|
||||||
v.msg());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -819,21 +678,13 @@ pub fn check_crate(tcx: &TyCtxt) {
|
|||||||
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
||||||
fn consume(&mut self,
|
fn consume(&mut self,
|
||||||
_consume_id: ast::NodeId,
|
_consume_id: ast::NodeId,
|
||||||
consume_span: Span,
|
_consume_span: Span,
|
||||||
cmt: mc::cmt,
|
cmt: mc::cmt,
|
||||||
_mode: euv::ConsumeMode) {
|
_mode: euv::ConsumeMode) {
|
||||||
let mut cur = &cmt;
|
let mut cur = &cmt;
|
||||||
loop {
|
loop {
|
||||||
match cur.cat {
|
match cur.cat {
|
||||||
Categorization::StaticItem => {
|
Categorization::StaticItem => {
|
||||||
if self.mode != Mode::Var {
|
|
||||||
// statics cannot be consumed by value at any time, that would imply
|
|
||||||
// that they're an initializer (what a const is for) or kept in sync
|
|
||||||
// over time (not feasible), so deny it outright.
|
|
||||||
span_err!(self.tcx.sess, consume_span, E0394,
|
|
||||||
"cannot refer to other statics by value, use the \
|
|
||||||
address-of operator or a constant instead");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Categorization::Deref(ref cmt, _, _) |
|
Categorization::Deref(ref cmt, _, _) |
|
||||||
@ -848,7 +699,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
fn borrow(&mut self,
|
fn borrow(&mut self,
|
||||||
borrow_id: ast::NodeId,
|
borrow_id: ast::NodeId,
|
||||||
borrow_span: Span,
|
_borrow_span: Span,
|
||||||
cmt: mc::cmt<'tcx>,
|
cmt: mc::cmt<'tcx>,
|
||||||
_loan_region: ty::Region,
|
_loan_region: ty::Region,
|
||||||
bk: ty::BorrowKind,
|
bk: ty::BorrowKind,
|
||||||
@ -866,7 +717,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut cur = &cmt;
|
let mut cur = &cmt;
|
||||||
let mut is_interior = false;
|
|
||||||
loop {
|
loop {
|
||||||
match cur.cat {
|
match cur.cat {
|
||||||
Categorization::Rvalue(..) => {
|
Categorization::Rvalue(..) => {
|
||||||
@ -891,20 +741,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Categorization::StaticItem => {
|
Categorization::StaticItem => {
|
||||||
if is_interior && self.mode != Mode::Var {
|
|
||||||
// Borrowed statics can specifically *only* have their address taken,
|
|
||||||
// not any number of other borrows such as borrowing fields, reading
|
|
||||||
// elements of an array, etc.
|
|
||||||
span_err!(self.tcx.sess, borrow_span, E0494,
|
|
||||||
"cannot refer to the interior of another \
|
|
||||||
static, use a constant instead");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Categorization::Deref(ref cmt, _, _) |
|
Categorization::Deref(ref cmt, _, _) |
|
||||||
Categorization::Downcast(ref cmt, _) |
|
Categorization::Downcast(ref cmt, _) |
|
||||||
Categorization::Interior(ref cmt, _) => {
|
Categorization::Interior(ref cmt, _) => {
|
||||||
is_interior = true;
|
|
||||||
cur = cmt;
|
cur = cmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,70 +12,6 @@
|
|||||||
|
|
||||||
register_long_diagnostics! {
|
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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
#![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.
|
|
||||||
|
|
||||||
Erroneous code example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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]; // Ok!
|
|
||||||
```
|
|
||||||
"##,
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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##"
|
E0014: r##"
|
||||||
Constants can only be initialized by a constant value or, in a future
|
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
|
version of Rust, a call to a const function. This error indicates the use
|
||||||
@ -95,149 +31,6 @@ const FOO2: 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. Erroneous code example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
const FOO: i32 = { let x = 0; x }; // 'x' isn't an item!
|
|
||||||
```
|
|
||||||
|
|
||||||
To avoid it, you have to replace the non-item object:
|
|
||||||
|
|
||||||
```
|
|
||||||
const FOO: i32 = { const X : i32 = 0; X };
|
|
||||||
```
|
|
||||||
"##,
|
|
||||||
|
|
||||||
E0017: r##"
|
|
||||||
References in statics and constants may only refer to immutable values.
|
|
||||||
Erroneous code example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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 constant integers must be known at compile time. You
|
|
||||||
can't cast a pointer to an integer because the address of a pointer can
|
|
||||||
vary.
|
|
||||||
|
|
||||||
For example, if you write:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
static MY_STATIC: u32 = 42;
|
|
||||||
static MY_STATIC_ADDR: usize = &MY_STATIC as *const _ as usize;
|
|
||||||
static WHAT: usize = (MY_STATIC_ADDR^17) + MY_STATIC_ADDR;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then `MY_STATIC_ADDR` would contain the address of `MY_STATIC`. However,
|
|
||||||
the address can change when the program is linked, as well as change
|
|
||||||
between different executions due to ASLR, and many linkers would
|
|
||||||
not be able to calculate the value of `WHAT`.
|
|
||||||
|
|
||||||
On the other hand, static and constant pointers can point either to
|
|
||||||
a known numeric address or to the address of a symbol.
|
|
||||||
|
|
||||||
```
|
|
||||||
static MY_STATIC_ADDR: &'static u32 = &MY_STATIC;
|
|
||||||
// ... and also
|
|
||||||
static MY_STATIC_ADDR2: *const u32 = &MY_STATIC;
|
|
||||||
|
|
||||||
const CONST_ADDR: *const u8 = 0x5f3759df as *const u8;
|
|
||||||
```
|
|
||||||
|
|
||||||
This does not pose a problem by itself because they can't be
|
|
||||||
accessed directly.
|
|
||||||
"##,
|
|
||||||
|
|
||||||
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. Erroneous code
|
|
||||||
example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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,
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
const fn foo(mut x: u8) {
|
|
||||||
// do stuff
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Is incorrect because the function body may not mutate `x`.
|
|
||||||
|
|
||||||
Remove any mutable bindings from the argument list to fix this error. In case
|
|
||||||
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##"
|
E0030: r##"
|
||||||
When matching against a range, the compiler verifies that the range is
|
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
|
non-empty. Range patterns include both end-points, so this is equivalent to
|
||||||
@ -325,281 +118,6 @@ fn some_func() {
|
|||||||
```
|
```
|
||||||
"##,
|
"##,
|
||||||
|
|
||||||
E0378: r##"
|
|
||||||
Method calls that aren't calls to inherent `const` methods are disallowed
|
|
||||||
in statics, constants, and constant functions.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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 scalar must be known at compile time,
|
|
||||||
which is not the case when comparing raw pointers.
|
|
||||||
|
|
||||||
Erroneous code example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
static FOO: i32 = 42;
|
|
||||||
static BAR: i32 = 42;
|
|
||||||
|
|
||||||
static BAZ: bool = { (&FOO as *const i32) == (&BAR as *const i32) };
|
|
||||||
// error: raw pointers cannot be compared in statics!
|
|
||||||
```
|
|
||||||
|
|
||||||
The address assigned by the linker to `FOO` and `BAR` may or may not
|
|
||||||
be identical, so the value of `BAZ` can't be determined.
|
|
||||||
|
|
||||||
If you want to do the comparison, please do it at run-time.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
static FOO: i32 = 42;
|
|
||||||
static BAR: i32 = 42;
|
|
||||||
|
|
||||||
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 behind a raw pointer can't be determined at compile-time
|
|
||||||
(or even link-time), which means it can't be used in a constant
|
|
||||||
expression. Erroneous code example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
|
|
||||||
|
|
||||||
const VALUE: u8 = unsafe { *REG_ADDR };
|
|
||||||
// error: raw pointers cannot be dereferenced in constants
|
|
||||||
```
|
|
||||||
|
|
||||||
A possible fix is to dereference your pointer at some point in run-time.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
const REG_ADDR: *const u8 = 0x5f3759df as *const u8;
|
|
||||||
|
|
||||||
let reg_value = unsafe { *REG_ADDR };
|
|
||||||
```
|
|
||||||
"##,
|
|
||||||
|
|
||||||
E0397: r##"
|
|
||||||
It is not allowed for a mutable static to allocate or have destructors. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
// 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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
#![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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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:
|
|
||||||
|
|
||||||
```compile_fail
|
|
||||||
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! {
|
register_diagnostics! {
|
||||||
|
@ -37,7 +37,6 @@ extern crate rustc_const_math;
|
|||||||
|
|
||||||
pub mod diagnostics;
|
pub mod diagnostics;
|
||||||
|
|
||||||
pub mod const_fn;
|
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod loops;
|
pub mod loops;
|
||||||
pub mod no_asm;
|
pub mod no_asm;
|
||||||
|
3
src/rustc/Cargo.lock
generated
3
src/rustc/Cargo.lock
generated
@ -220,6 +220,7 @@ dependencies = [
|
|||||||
"log 0.0.0",
|
"log 0.0.0",
|
||||||
"rustc 0.0.0",
|
"rustc 0.0.0",
|
||||||
"rustc_back 0.0.0",
|
"rustc_back 0.0.0",
|
||||||
|
"rustc_bitflags 0.0.0",
|
||||||
"rustc_const_eval 0.0.0",
|
"rustc_const_eval 0.0.0",
|
||||||
"rustc_const_math 0.0.0",
|
"rustc_const_math 0.0.0",
|
||||||
"rustc_data_structures 0.0.0",
|
"rustc_data_structures 0.0.0",
|
||||||
@ -233,6 +234,7 @@ dependencies = [
|
|||||||
"log 0.0.0",
|
"log 0.0.0",
|
||||||
"rustc 0.0.0",
|
"rustc 0.0.0",
|
||||||
"rustc_const_eval 0.0.0",
|
"rustc_const_eval 0.0.0",
|
||||||
|
"rustc_const_math 0.0.0",
|
||||||
"syntax 0.0.0",
|
"syntax 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -278,6 +280,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log 0.0.0",
|
"log 0.0.0",
|
||||||
"rustc 0.0.0",
|
"rustc 0.0.0",
|
||||||
|
"serialize 0.0.0",
|
||||||
"syntax 0.0.0",
|
"syntax 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -21,6 +21,20 @@ const C: usize = { foo!(); 2 };
|
|||||||
|
|
||||||
const D: usize = { let x = 4; 2 };
|
const D: usize = { let x = 4; 2 };
|
||||||
//~^ ERROR: blocks in constants are limited to items and tail expressions
|
//~^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
//~^^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
|
||||||
|
enum Foo {
|
||||||
|
Bar = { let x = 1; 3 }
|
||||||
|
//~^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
//~^^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
}
|
||||||
|
|
||||||
|
type Array = [u32; { let x = 2; 5 }];
|
||||||
|
//~^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
//~^^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
let _: Array = [0; { let x = 3; 5 }];
|
||||||
|
//~^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
|
//~^^ ERROR: blocks in constants are limited to items and tail expressions
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user