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_metadata := rustc syntax rbml rustc_const_math
|
||||
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_platform_intrinsics := std
|
||||
DEPS_rustc_plugin := rustc rustc_metadata syntax rustc_mir
|
||||
|
@ -37,7 +37,7 @@ use rustc_privacy;
|
||||
use rustc_plugin::registry::Registry;
|
||||
use rustc_plugin as plugin;
|
||||
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 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 {
|
||||
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();
|
||||
// Push all the built-in passes.
|
||||
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::simplify_cfg::SimplifyCfg);
|
||||
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_math = { path = "../librustc_const_math" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
rustc_bitflags = { path = "../librustc_bitflags" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
|
@ -84,7 +84,7 @@ pub struct ScopeAuxiliary {
|
||||
pub postdoms: Vec<Location>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct Location {
|
||||
/// the location is within this block
|
||||
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])
|
||||
} else {
|
||||
// FIXME overflow
|
||||
match op.node {
|
||||
hir::BinOp_::BiAnd => {
|
||||
match (op.node, cx.constness) {
|
||||
// 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 {
|
||||
op: LogicalOp::And,
|
||||
lhs: lhs.to_ref(),
|
||||
rhs: rhs.to_ref(),
|
||||
}
|
||||
}
|
||||
hir::BinOp_::BiOr => {
|
||||
(hir::BinOp_::BiOr, hir::Constness::NotConst) => {
|
||||
ExprKind::LogicalOp {
|
||||
op: LogicalOp::Or,
|
||||
lhs: lhs.to_ref(),
|
||||
rhs: rhs.to_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
let op = bin_op(op.node);
|
||||
ExprKind::Binary {
|
||||
|
@ -32,13 +32,17 @@ use rustc_const_math::{ConstInt, ConstUsize};
|
||||
pub struct Cx<'a, 'tcx: 'a> {
|
||||
tcx: &'a TyCtxt<'tcx>,
|
||||
infcx: &'a InferCtxt<'a, 'tcx>,
|
||||
constness: hir::Constness
|
||||
}
|
||||
|
||||
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 {
|
||||
tcx: infcx.tcx,
|
||||
infcx: infcx,
|
||||
constness: constness,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,9 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
||||
#![cfg_attr(not(stage0), deny(warnings))]
|
||||
#![unstable(feature = "rustc_private", issue = "27812")]
|
||||
|
||||
#![feature(associated_consts)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(question_mark)]
|
||||
@ -31,10 +33,16 @@ extern crate graphviz as dot;
|
||||
extern crate rustc;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_back;
|
||||
#[macro_use]
|
||||
#[no_link]
|
||||
extern crate rustc_bitflags;
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
extern crate rustc_const_math;
|
||||
extern crate rustc_const_eval;
|
||||
|
||||
pub mod diagnostics;
|
||||
|
||||
pub mod build;
|
||||
pub mod graphviz;
|
||||
mod hair;
|
||||
|
@ -29,7 +29,8 @@ use rustc::traits::ProjectionMode;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::util::nodemap::NodeMap;
|
||||
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::codemap::Span;
|
||||
|
||||
@ -59,13 +60,27 @@ impl<'a, 'tcx> BuildMir<'a, 'tcx> {
|
||||
fn build<F>(&mut self, src: MirSource, f: F)
|
||||
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 infcx = infer::new_infer_ctxt(self.tcx,
|
||||
&self.tcx.tables,
|
||||
Some(param_env),
|
||||
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));
|
||||
|
||||
@ -151,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BuildMir<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self,
|
||||
fk: intravisit::FnKind<'tcx>,
|
||||
fk: FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Block,
|
||||
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))
|
||||
} else {
|
||||
None
|
||||
|
@ -14,3 +14,5 @@ pub mod erase_regions;
|
||||
pub mod no_landing_pads;
|
||||
pub mod type_check;
|
||||
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::Categorization;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::traits::{self, ProjectionMode};
|
||||
use rustc::traits::ProjectionMode;
|
||||
use rustc::util::nodemap::NodeMap;
|
||||
use rustc::middle::const_qualif::ConstQualif;
|
||||
use rustc::lint::builtin::CONST_ERR;
|
||||
@ -48,7 +48,6 @@ use rustc::lint::builtin::CONST_ERR;
|
||||
use rustc::hir::{self, PatKind};
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::feature_gate::UnstableFeatures;
|
||||
use rustc::hir::intravisit::{self, FnKind, Visitor};
|
||||
|
||||
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.
|
||||
fn handle_const_fn_call(&mut self,
|
||||
expr: &hir::Expr,
|
||||
_expr: &hir::Expr,
|
||||
def_id: DefId,
|
||||
ret_ty: Ty<'tcx>)
|
||||
-> bool {
|
||||
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(),
|
||||
fn_like.decl(),
|
||||
fn_like.body(),
|
||||
@ -245,42 +224,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
||||
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> {
|
||||
@ -289,11 +232,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
assert_eq!(self.mode, Mode::Var);
|
||||
match i.node {
|
||||
hir::ItemStatic(_, hir::MutImmutable, ref expr) => {
|
||||
self.check_static_type(&expr);
|
||||
self.global_expr(Mode::Static, &expr);
|
||||
}
|
||||
hir::ItemStatic(_, hir::MutMutable, ref expr) => {
|
||||
self.check_static_mut_type(&expr);
|
||||
self.global_expr(Mode::StaticMut, &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");
|
||||
}
|
||||
None => {
|
||||
self.tcx.sess.delay_span_bug(start.span,
|
||||
"non-constant path in constant expr");
|
||||
span_err!(self.tcx.sess, p.span, E0014,
|
||||
"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(_, _) => {},
|
||||
}
|
||||
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);
|
||||
}
|
||||
@ -455,11 +395,6 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
let tc = node_ty.type_contents(self.tcx);
|
||||
if self.qualif.intersects(ConstQualif::MUTABLE_MEM) && tc.interior_unsafe() {
|
||||
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
|
||||
// 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 {
|
||||
outer = outer | ConstQualif::NOT_CONST;
|
||||
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) {
|
||||
@ -525,11 +456,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
||||
ty::TyStruct(def, _) |
|
||||
ty::TyEnum(def, _) if def.has_dtor() => {
|
||||
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::ExprIndex(..) if v.tcx.tables.borrow().method_map.contains_key(&method_call) => {
|
||||
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(_) => {
|
||||
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) => {
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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"),
|
||||
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FnPtrAddrCast) => {
|
||||
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(..)) => {
|
||||
match v.mode {
|
||||
Mode::Static | Mode::StaticMut => {}
|
||||
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::Const | Mode::ConstFn => {}
|
||||
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.
|
||||
v.add_qualif(ConstQualif::NOT_CONST | ConstQualif::NON_ZERO_SIZED);
|
||||
}
|
||||
def => {
|
||||
_ => {
|
||||
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 {
|
||||
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(..) => {
|
||||
@ -714,11 +587,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
||||
};
|
||||
if !is_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(..) => {
|
||||
@ -773,10 +641,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
|
||||
hir::ExprAssignOp(..) |
|
||||
hir::ExprInlineAsm(..) => {
|
||||
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.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> {
|
||||
fn consume(&mut self,
|
||||
_consume_id: ast::NodeId,
|
||||
consume_span: Span,
|
||||
_consume_span: Span,
|
||||
cmt: mc::cmt,
|
||||
_mode: euv::ConsumeMode) {
|
||||
let mut cur = &cmt;
|
||||
loop {
|
||||
match cur.cat {
|
||||
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;
|
||||
}
|
||||
Categorization::Deref(ref cmt, _, _) |
|
||||
@ -848,7 +699,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
||||
}
|
||||
fn borrow(&mut self,
|
||||
borrow_id: ast::NodeId,
|
||||
borrow_span: Span,
|
||||
_borrow_span: Span,
|
||||
cmt: mc::cmt<'tcx>,
|
||||
_loan_region: ty::Region,
|
||||
bk: ty::BorrowKind,
|
||||
@ -866,7 +717,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
let mut cur = &cmt;
|
||||
let mut is_interior = false;
|
||||
loop {
|
||||
match cur.cat {
|
||||
Categorization::Rvalue(..) => {
|
||||
@ -891,20 +741,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for CheckCrateVisitor<'a, 'tcx> {
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
Categorization::Deref(ref cmt, _, _) |
|
||||
Categorization::Downcast(ref cmt, _) |
|
||||
Categorization::Interior(ref cmt, _) => {
|
||||
is_interior = true;
|
||||
cur = cmt;
|
||||
}
|
||||
|
||||
|
@ -12,70 +12,6 @@
|
||||
|
||||
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##"
|
||||
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
|
||||
@ -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##"
|
||||
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
|
||||
@ -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! {
|
||||
|
@ -37,7 +37,6 @@ extern crate rustc_const_math;
|
||||
|
||||
pub mod diagnostics;
|
||||
|
||||
pub mod const_fn;
|
||||
pub mod consts;
|
||||
pub mod loops;
|
||||
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",
|
||||
"rustc 0.0.0",
|
||||
"rustc_back 0.0.0",
|
||||
"rustc_bitflags 0.0.0",
|
||||
"rustc_const_eval 0.0.0",
|
||||
"rustc_const_math 0.0.0",
|
||||
"rustc_data_structures 0.0.0",
|
||||
@ -233,6 +234,7 @@ dependencies = [
|
||||
"log 0.0.0",
|
||||
"rustc 0.0.0",
|
||||
"rustc_const_eval 0.0.0",
|
||||
"rustc_const_math 0.0.0",
|
||||
"syntax 0.0.0",
|
||||
]
|
||||
|
||||
@ -278,6 +280,7 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"log 0.0.0",
|
||||
"rustc 0.0.0",
|
||||
"serialize 0.0.0",
|
||||
"syntax 0.0.0",
|
||||
]
|
||||
|
||||
|
@ -21,6 +21,20 @@ const C: usize = { foo!(); 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
|
||||
|
||||
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() {
|
||||
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