mir: qualify and promote constants.

This commit is contained in:
Eduard Burtescu 2016-05-07 19:14:28 +03:00
parent 14efbf1481
commit 78884b7659
18 changed files with 1895 additions and 784 deletions

View File

@ -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

View File

@ -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);

View File

@ -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" }

View File

@ -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,

View 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
}

View File

@ -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 {

View File

@ -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,
} }
} }
} }

View File

@ -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;

View File

@ -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

View File

@ -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;

View 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
};
}
}
_ => {}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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");
}
}

View File

@ -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;
} }

View File

@ -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! {

View File

@ -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
View File

@ -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",
] ]

View File

@ -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
} }