move more checks out of librustc

This commit is contained in:
Oliver Schneider 2016-01-21 10:52:37 +01:00
parent d5ec3ab685
commit c124deca7b
16 changed files with 645 additions and 635 deletions

View File

@ -102,7 +102,7 @@ DEPS_rustc_front := std syntax log serialize
DEPS_rustc_lint := rustc log syntax
DEPS_rustc_llvm := native:rustllvm libc std rustc_bitflags
DEPS_rustc_metadata := rustc rustc_front syntax rbml
DEPS_rustc_passes := syntax rustc core
DEPS_rustc_passes := syntax rustc core rustc_front
DEPS_rustc_mir := rustc rustc_front syntax
DEPS_rustc_resolve := arena rustc rustc_front log syntax
DEPS_rustc_platform_intrinsics := rustc rustc_llvm

View File

@ -215,187 +215,6 @@ match x {
```
"##,
E0010: r##"
The value of statics and constants must be known at compile time, and they live
for the entire lifetime of a program. Creating a boxed value allocates memory on
the heap at runtime, and therefore cannot be done at compile time. Erroneous
code example:
```
#![feature(box_syntax)]
const CON : Box<i32> = box 0;
```
"##,
E0011: r##"
Initializers for constants and statics are evaluated at compile time.
User-defined operators rely on user-defined functions, which cannot be evaluated
at compile time.
Bad example:
```
use std::ops::Index;
struct Foo { a: u8 }
impl Index<u8> for Foo {
type Output = u8;
fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
}
const a: Foo = Foo { a: 0u8 };
const b: u8 = a[0]; // Index trait is defined by the user, bad!
```
Only operators on builtin types are allowed.
Example:
```
const a: &'static [i32] = &[1, 2, 3];
const b: i32 = a[0]; // Good!
```
"##,
E0013: r##"
Static and const variables can refer to other const variables. But a const
variable cannot refer to a static variable. For example, `Y` cannot refer to `X`
here:
```
static X: i32 = 42;
const Y: i32 = X;
```
To fix this, the value can be extracted as a const and then used:
```
const A: i32 = 42;
static X: i32 = A;
const Y: i32 = A;
```
"##,
E0014: r##"
Constants can only be initialized by a constant value or, in a future
version of Rust, a call to a const function. This error indicates the use
of a path (like a::b, or x) denoting something other than one of these
allowed items. Example:
```
const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
```
To avoid it, you have to replace the non-constant value:
```
const FOO: i32 = { const X : i32 = 0; X };
// or even:
const FOO: i32 = { 0 }; // but brackets are useless here
```
"##,
// FIXME(#24111) Change the language here when const fn stabilizes
E0015: r##"
The only functions that can be called in static or constant expressions are
`const` functions, and struct/enum constructors. `const` functions are only
available on a nightly compiler. Rust currently does not support more general
compile-time function execution.
```
const FOO: Option<u8> = Some(1); // enum constructor
struct Bar {x: u8}
const BAR: Bar = Bar {x: 1}; // struct constructor
```
See [RFC 911] for more details on the design of `const fn`s.
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0017: r##"
References in statics and constants may only refer to immutable values. Example:
```
static X: i32 = 1;
const C: i32 = 2;
// these three are not allowed:
const CR: &'static mut i32 = &mut C;
static STATIC_REF: &'static mut i32 = &mut X;
static CONST_REF: &'static mut i32 = &mut C;
```
Statics are shared everywhere, and if they refer to mutable data one might
violate memory safety since holding multiple mutable references to shared data
is not allowed.
If you really want global mutable state, try using `static mut` or a global
`UnsafeCell`.
"##,
E0018: r##"
The value of static and const variables must be known at compile time. You
can't cast a pointer as an integer because we can't know what value the
address will take.
However, pointers to other constants' addresses are allowed in constants,
example:
```
const X: u32 = 50;
const Y: *const u32 = &X;
```
Therefore, casting one of these non-constant pointers to an integer results
in a non-constant integer which lead to this error. Example:
```
const X: u32 = 1;
const Y: usize = &X as *const u32 as usize;
println!("{}", Y);
```
"##,
E0019: r##"
A function call isn't allowed in the const's initialization expression
because the expression's value must be known at compile-time. Example of
erroneous code:
```
enum Test {
V1
}
impl Test {
fn test(&self) -> i32 {
12
}
}
fn main() {
const FOO: Test = Test::V1;
const A: i32 = FOO.test(); // You can't call Test::func() here !
}
```
Remember: you can't use a function call inside a const's initialization
expression! However, you can totally use it anywhere else:
```
fn main() {
const FOO: Test = Test::V1;
FOO.func(); // here is good
let x = FOO.func(); // or even here!
}
```
"##,
E0020: r##"
This error indicates that an attempt was made to divide by zero (or take the
remainder of a zero divisor) in a static or constant expression. Erroneous
@ -407,24 +226,6 @@ const X: i32 = 42 / 0;
```
"##,
E0030: r##"
When matching against a range, the compiler verifies that the range is
non-empty. Range patterns include both end-points, so this is equivalent to
requiring the start of the range to be less than or equal to the end of the
range.
For example:
```
match 5u32 {
// This range is ok, albeit pointless.
1 ... 1 => ...
// This range is empty, and the compiler can tell.
1000 ... 5 => ...
}
```
"##,
E0038: r####"
Trait objects like `Box<Trait>` can only be constructed when certain
requirements are satisfied by the trait in question.
@ -902,14 +703,6 @@ match Some(42) {
```
"##,
E0161: r##"
In Rust, you can only move a value when its size is known at compile time.
To work around this restriction, consider "hiding" the value behind a reference:
either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
it around as usual.
"##,
E0162: r##"
An if-let pattern attempts to match the pattern, and enters the body if the
match was successful. If the match is irrefutable (when it cannot fail to
@ -1101,67 +894,6 @@ extern "C" {
```
"##,
E0265: r##"
This error indicates that a static or constant references itself.
All statics and constants need to resolve to a value in an acyclic manner.
For example, neither of the following can be sensibly compiled:
```
const X: u32 = X;
```
```
const X: u32 = Y;
const Y: u32 = X;
```
"##,
E0267: r##"
This error indicates the use of a loop keyword (`break` or `continue`) inside a
closure but outside of any loop. Erroneous code example:
```
let w = || { break; }; // error: `break` inside of a closure
```
`break` and `continue` keywords can be used as normal inside closures as long as
they are also contained within a loop. To halt the execution of a closure you
should instead use a return statement. Example:
```
let w = || {
for _ in 0..10 {
break;
}
};
w();
```
"##,
E0268: r##"
This error indicates the use of a loop keyword (`break` or `continue`) outside
of a loop. Without a loop to break out of or continue in, no sensible action can
be taken. Erroneous code example:
```
fn some_func() {
break; // error: `break` outside of loop
}
```
Please verify that you are using `break` and `continue` only in loops. Example:
```
fn some_func() {
for _ in 0..10 {
break; // ok!
}
}
```
"##,
E0269: r##"
Functions must eventually return a value of their return type. For example, in
the following function
@ -1892,104 +1624,6 @@ struct Foo<T: 'static> {
```
"##,
E0378: r##"
Method calls that aren't calls to inherent `const` methods are disallowed
in statics, constants, and constant functions.
For example:
```
const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
struct Foo(i32);
impl Foo {
const fn foo(&self) -> i32 {
self.bar() // error, `bar` isn't `const`
}
fn bar(&self) -> i32 { self.0 }
}
```
For more information about `const fn`'s, see [RFC 911].
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0394: r##"
From [RFC 246]:
> It is invalid for a static to reference another static by value. It is
> required that all references be borrowed.
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
"##,
E0395: r##"
The value assigned to a constant expression must be known at compile time,
which is not the case when comparing raw pointers. Erroneous code example:
```
static foo: i32 = 42;
static bar: i32 = 43;
static baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
// error: raw pointers cannot be compared in statics!
```
Please check that the result of the comparison can be determined at compile time
or isn't assigned to a constant expression. Example:
```
static foo: i32 = 42;
static bar: i32 = 43;
let baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
// baz isn't a constant expression so it's ok
```
"##,
E0396: r##"
The value assigned to a constant expression must be known at compile time,
which is not the case when dereferencing raw pointers. Erroneous code
example:
```
const foo: i32 = 42;
const baz: *const i32 = (&foo as *const i32);
const deref: i32 = *baz;
// error: raw pointers cannot be dereferenced in constants
```
To fix this error, please do not assign this value to a constant expression.
Example:
```
const foo: i32 = 42;
const baz: *const i32 = (&foo as *const i32);
unsafe { let deref: i32 = *baz; }
// baz isn't a constant expression so it's ok
```
You'll also note that this assignment must be done in an unsafe block!
"##,
E0397: r##"
It is not allowed for a mutable static to allocate or have destructors. For
example:
```
// error: mutable statics are not allowed to have boxes
static mut FOO: Option<Box<usize>> = None;
// error: mutable statics are not allowed to have destructors
static mut BAR: Option<Vec<i32>> = None;
```
"##,
E0398: r##"
In Rust 1.3, the default object lifetime bounds are expected to
change, as described in RFC #1156 [1]. You are getting a warning
@ -2026,50 +1660,6 @@ contain references (with a maximum lifetime of `'a`).
[1]: https://github.com/rust-lang/rfcs/pull/1156
"##,
E0400: r##"
A user-defined dereference was attempted in an invalid context. Erroneous
code example:
```
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
const S: &'static str = &A;
// error: user-defined dereference operators are not allowed in constants
fn main() {
let foo = S;
}
```
You cannot directly use a dereference operation whilst initializing a constant
or a static. To fix this error, restructure your code to avoid this dereference,
perhaps moving it inline:
```
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
fn main() {
let foo : &str = &A;
}
```
"##,
E0452: r##"
An invalid lint attribute has been given. Erroneous code example:
@ -2087,136 +1677,6 @@ lint name). Ensure the attribute is of this form:
```
"##,
E0492: r##"
A borrow of a constant containing interior mutability was attempted. Erroneous
code example:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
const A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A;
// error: cannot borrow a constant which contains interior mutability, create a
// static instead
```
A `const` represents a constant value that should never change. If one takes
a `&` reference to the constant, then one is taking a pointer to some memory
location containing the value. Normally this is perfectly fine: most values
can't be changed via a shared `&` pointer, but interior mutability would allow
it. That is, a constant value could be mutated. On the other hand, a `static` is
explicitly a single memory location, which can be mutated at will.
So, in order to solve this error, either use statics which are `Sync`:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
static A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A; // ok!
```
You can also have this error while using a cell type:
```
#![feature(const_fn)]
use std::cell::Cell;
const A: Cell<usize> = Cell::new(1);
const B: &'static Cell<usize> = &A;
// error: cannot borrow a constant which contains interior mutability, create
// a static instead
// or:
struct C { a: Cell<usize> }
const D: C = C { a: Cell::new(1) };
const E: &'static Cell<usize> = &D.a; // error
// or:
const F: &'static C = &D; // error
```
This is because cell types do operations that are not thread-safe. Due to this,
they don't implement Sync and thus can't be placed in statics. In this
case, `StaticMutex` would work just fine, but it isn't stable yet:
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
However, if you still wish to use these types, you can achieve this by an unsafe
wrapper:
```
#![feature(const_fn)]
use std::cell::Cell;
use std::marker::Sync;
struct NotThreadSafe<T> {
value: Cell<T>,
}
unsafe impl<T> Sync for NotThreadSafe<T> {}
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
static B: &'static NotThreadSafe<usize> = &A; // ok!
```
Remember this solution is unsafe! You will have to ensure that accesses to the
cell are synchronized.
"##,
E0493: r##"
A type with a destructor was assigned to an invalid type of variable. Erroneous
code example:
```
struct Foo {
a: u32
}
impl Drop for Foo {
fn drop(&mut self) {}
}
const F : Foo = Foo { a : 0 };
// error: constants are not allowed to have destructors
static S : Foo = Foo { a : 0 };
// error: statics are not allowed to have destructors
```
To solve this issue, please use a type which does allow the usage of type with
destructors.
"##,
E0494: r##"
A reference of an interior static was assigned to another const/static.
Erroneous code example:
```
struct Foo {
a: u32
}
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a;
// error: cannot refer to the interior of another static, use a
// constant instead
```
The "base" variable has to be a const if you want another static/const variable
to refer to one of its fields. Example:
```
struct Foo {
a: u32
}
const S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; // ok!
```
"##,
E0496: r##"
A lifetime name is shadowing another lifetime name. Erroneous code example:

View File

@ -94,12 +94,9 @@ pub mod middle {
pub mod astconv_util;
pub mod expr_use_visitor; // STAGE0: increase glitch immunity
pub mod cfg;
pub mod check_const;
pub mod check_static_recursion;
pub mod check_loop;
pub mod check_match;
pub mod check_rvalues;
pub mod const_eval;
pub mod const_qualif;
pub mod cstore;
pub mod dataflow;
pub mod dead;

View File

@ -0,0 +1,44 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Const qualification, from partial to completely promotable.
bitflags! {
#[derive(RustcEncodable, RustcDecodable)]
flags ConstQualif: u8 {
// Inner mutability (can not be placed behind a reference) or behind
// &mut in a non-global expression. Can be copied from static memory.
const MUTABLE_MEM = 1 << 0,
// Constant value with a type that implements Drop. Can be copied
// from static memory, similar to MUTABLE_MEM.
const NEEDS_DROP = 1 << 1,
// Even if the value can be placed in static memory, copying it from
// there is more expensive than in-place instantiation, and/or it may
// be too large. This applies to [T; N] and everything containing it.
// N.B.: references need to clear this flag to not end up on the stack.
const PREFER_IN_PLACE = 1 << 2,
// May use more than 0 bytes of memory, doesn't impact the constness
// directly, but is not allowed to be borrowed mutably in a constant.
const NON_ZERO_SIZED = 1 << 3,
// Actually borrowed, has to always be in static memory. Does not
// propagate, and requires the expression to behave like a 'static
// lvalue. The set of expressions with this flag is the minimum
// that have to be promoted.
const HAS_STATIC_BORROWS = 1 << 4,
// Invalid const for miscellaneous reasons (e.g. not implemented).
const NOT_CONST = 1 << 5,
// Borrowing the expression won't produce &'static T if any of these
// bits are set, though the value could be copied from static memory
// if `NOT_CONST` isn't set.
const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
ConstQualif::NEEDS_DROP.bits |
ConstQualif::NOT_CONST.bits
}
}

View File

@ -74,7 +74,7 @@ use self::Aliasability::*;
use middle::def_id::DefId;
use front::map as ast_map;
use middle::infer;
use middle::check_const;
use middle::const_qualif::ConstQualif;
use middle::def::Def;
use middle::ty::adjustment;
use middle::ty::{self, Ty};
@ -795,19 +795,19 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
expr_ty: Ty<'tcx>)
-> cmt<'tcx> {
let qualif = self.tcx().const_qualif_map.borrow().get(&id).cloned()
.unwrap_or(check_const::ConstQualif::NOT_CONST);
.unwrap_or(ConstQualif::NOT_CONST);
// Only promote `[T; 0]` before an RFC for rvalue promotions
// is accepted.
let qualif = match expr_ty.sty {
ty::TyArray(_, 0) => qualif,
_ => check_const::ConstQualif::NOT_CONST
_ => ConstQualif::NOT_CONST
};
// Compute maximum lifetime of this rvalue. This is 'static if
// we can promote to a constant, otherwise equal to enclosing temp
// lifetime.
let re = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
let re = if qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
self.temporary_scope(id)
} else {
ty::ReStatic

View File

@ -373,7 +373,7 @@ pub struct ctxt<'tcx> {
repr_hint_cache: RefCell<DepTrackingMap<maps::ReprHints<'tcx>>>,
/// Maps Expr NodeId's to their constant qualification.
pub const_qualif_map: RefCell<NodeMap<middle::check_const::ConstQualif>>,
pub const_qualif_map: RefCell<NodeMap<middle::const_qualif::ConstQualif>>,
/// Caches CoerceUnsized kinds for impls on custom types.
pub custom_coerce_unsized_kinds: RefCell<DefIdMap<ty::adjustment::CustomCoerceUnsized>>,

View File

@ -34,6 +34,7 @@ use rustc_plugin::registry::Registry;
use rustc_plugin as plugin;
use rustc_front::hir;
use rustc_front::lowering::{lower_crate, LoweringContext};
use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
use super::Compilation;
use serialize::json;
@ -632,7 +633,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
time(time_passes,
"checking for inline asm in case the target doesn't support it",
|| ::rustc_passes::no_asm::check_crate(sess, &krate));
|| no_asm::check_crate(sess, &krate));
// One final feature gating of the true AST that gets compiled
// later, to make sure we've got everything (e.g. configuration
@ -649,7 +650,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
time(time_passes,
"const fn bodies and arguments",
|| ::rustc_passes::const_fn::check_crate(sess, &krate));
|| const_fn::check_crate(sess, &krate));
if sess.opts.debugging_opts.input_stats {
println!("Post-expansion node count: {}", count_nodes(&krate));
@ -743,11 +744,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
time(time_passes,
"loop checking",
|| middle::check_loop::check_crate(sess, krate));
|| loops::check_crate(sess, krate));
time(time_passes,
"static item recursion checking",
|| middle::check_static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map));
|| static_recursion::check_crate(sess, krate, &def_map.borrow(), &hir_map));
ty::ctxt::create_and_enter(sess,
arenas,
@ -764,7 +765,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
time(time_passes,
"const checking",
|| middle::check_const::check_crate(tcx));
|| consts::check_crate(tcx));
let access_levels =
time(time_passes, "privacy checking", || {
@ -805,7 +806,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
time(time_passes,
"rvalue checking",
|| middle::check_rvalues::check_crate(tcx));
|| rvalues::check_crate(tcx));
// Avoid overwhelming user with errors if type checking failed.
// I'm not sure how helpful this is, to be honest, but it avoids

View File

@ -29,7 +29,7 @@ use tyencode;
use middle::cstore::{InlinedItem, InlinedItemRef};
use middle::ty::adjustment;
use middle::ty::cast;
use middle::check_const::ConstQualif;
use middle::const_qualif::ConstQualif;
use middle::def::{self, Def};
use middle::def_id::DefId;
use middle::privacy::{AllPublic, LastMod};

View File

@ -24,20 +24,22 @@
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
// by borrowck::gather_loans
use dep_graph::DepNode;
use middle::ty::cast::{CastKind};
use middle::const_eval::{self, ConstEvalErr};
use middle::const_eval::ErrKind::IndexOpFeatureGated;
use middle::const_eval::EvalHint::ExprTypeChecked;
use middle::def::Def;
use middle::def_id::DefId;
use middle::expr_use_visitor as euv;
use middle::infer;
use middle::mem_categorization as mc;
use middle::mem_categorization::Categorization;
use middle::traits;
use middle::ty::{self, Ty};
use util::nodemap::NodeMap;
use rustc::dep_graph::DepNode;
use rustc::middle::ty::cast::{CastKind};
use rustc::middle::const_eval::{self, ConstEvalErr};
use rustc::middle::const_eval::ErrKind::IndexOpFeatureGated;
use rustc::middle::const_eval::EvalHint::ExprTypeChecked;
use rustc::middle::def::Def;
use rustc::middle::def_id::DefId;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::mem_categorization::Categorization;
use rustc::middle::traits;
use rustc::middle::ty::{self, Ty};
use rustc::util::nodemap::NodeMap;
use rustc::middle::const_qualif::ConstQualif;
use rustc::lint::builtin::CONST_ERR;
use rustc_front::hir;
use syntax::ast;
@ -48,41 +50,6 @@ use rustc_front::intravisit::{self, FnKind, Visitor};
use std::collections::hash_map::Entry;
use std::cmp::Ordering;
// Const qualification, from partial to completely promotable.
bitflags! {
#[derive(RustcEncodable, RustcDecodable)]
flags ConstQualif: u8 {
// Inner mutability (can not be placed behind a reference) or behind
// &mut in a non-global expression. Can be copied from static memory.
const MUTABLE_MEM = 1 << 0,
// Constant value with a type that implements Drop. Can be copied
// from static memory, similar to MUTABLE_MEM.
const NEEDS_DROP = 1 << 1,
// Even if the value can be placed in static memory, copying it from
// there is more expensive than in-place instantiation, and/or it may
// be too large. This applies to [T; N] and everything containing it.
// N.B.: references need to clear this flag to not end up on the stack.
const PREFER_IN_PLACE = 1 << 2,
// May use more than 0 bytes of memory, doesn't impact the constness
// directly, but is not allowed to be borrowed mutably in a constant.
const NON_ZERO_SIZED = 1 << 3,
// Actually borrowed, has to always be in static memory. Does not
// propagate, and requires the expression to behave like a 'static
// lvalue. The set of expressions with this flag is the minimum
// that have to be promoted.
const HAS_STATIC_BORROWS = 1 << 4,
// Invalid const for miscellaneous reasons (e.g. not implemented).
const NOT_CONST = 1 << 5,
// Borrowing the expression won't produce &'static T if any of these
// bits are set, though the value could be copied from static memory
// if `NOT_CONST` isn't set.
const NON_STATIC_BORROWS = ConstQualif::MUTABLE_MEM.bits |
ConstQualif::NEEDS_DROP.bits |
ConstQualif::NOT_CONST.bits
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Mode {
Const,
@ -463,7 +430,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
Ok(_) => {}
Err(ConstEvalErr { kind: IndexOpFeatureGated, ..}) => {},
Err(msg) => {
self.tcx.sess.add_lint(::lint::builtin::CONST_ERR, ex.id,
self.tcx.sess.add_lint(CONST_ERR, ex.id,
msg.span,
msg.description().into_owned())
}

View File

@ -11,6 +11,108 @@
#![allow(non_snake_case)]
register_long_diagnostics! {
E0010: r##"
The value of statics and constants must be known at compile time, and they live
for the entire lifetime of a program. Creating a boxed value allocates memory on
the heap at runtime, and therefore cannot be done at compile time. Erroneous
code example:
```
#![feature(box_syntax)]
const CON : Box<i32> = box 0;
```
"##,
E0011: r##"
Initializers for constants and statics are evaluated at compile time.
User-defined operators rely on user-defined functions, which cannot be evaluated
at compile time.
Bad example:
```
use std::ops::Index;
struct Foo { a: u8 }
impl Index<u8> for Foo {
type Output = u8;
fn index<'a>(&'a self, idx: u8) -> &'a u8 { &self.a }
}
const a: Foo = Foo { a: 0u8 };
const b: u8 = a[0]; // Index trait is defined by the user, bad!
```
Only operators on builtin types are allowed.
Example:
```
const a: &'static [i32] = &[1, 2, 3];
const b: i32 = a[0]; // Good!
```
"##,
E0013: r##"
Static and const variables can refer to other const variables. But a const
variable cannot refer to a static variable. For example, `Y` cannot refer to `X`
here:
```
static X: i32 = 42;
const Y: i32 = X;
```
To fix this, the value can be extracted as a const and then used:
```
const A: i32 = 42;
static X: i32 = A;
const Y: i32 = A;
```
"##,
E0014: r##"
Constants can only be initialized by a constant value or, in a future
version of Rust, a call to a const function. This error indicates the use
of a path (like a::b, or x) denoting something other than one of these
allowed items. Example:
```
const FOO: i32 = { let x = 0; x }; // 'x' isn't a constant nor a function!
```
To avoid it, you have to replace the non-constant value:
```
const FOO: i32 = { const X : i32 = 0; X };
// or even:
const FOO: i32 = { 0 }; // but brackets are useless here
```
"##,
// FIXME(#24111) Change the language here when const fn stabilizes
E0015: r##"
The only functions that can be called in static or constant expressions are
`const` functions, and struct/enum constructors. `const` functions are only
available on a nightly compiler. Rust currently does not support more general
compile-time function execution.
```
const FOO: Option<u8> = Some(1); // enum constructor
struct Bar {x: u8}
const BAR: Bar = Bar {x: 1}; // struct constructor
```
See [RFC 911] for more details on the design of `const fn`s.
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0016: r##"
Blocks in constants may only contain items (such as constant, function
definition, etc...) and a tail expression. Example:
@ -26,6 +128,86 @@ const FOO: i32 = { const X : i32 = 0; X };
```
"##,
E0017: r##"
References in statics and constants may only refer to immutable values. Example:
```
static X: i32 = 1;
const C: i32 = 2;
// these three are not allowed:
const CR: &'static mut i32 = &mut C;
static STATIC_REF: &'static mut i32 = &mut X;
static CONST_REF: &'static mut i32 = &mut C;
```
Statics are shared everywhere, and if they refer to mutable data one might
violate memory safety since holding multiple mutable references to shared data
is not allowed.
If you really want global mutable state, try using `static mut` or a global
`UnsafeCell`.
"##,
E0018: r##"
The value of static and const variables must be known at compile time. You
can't cast a pointer as an integer because we can't know what value the
address will take.
However, pointers to other constants' addresses are allowed in constants,
example:
```
const X: u32 = 50;
const Y: *const u32 = &X;
```
Therefore, casting one of these non-constant pointers to an integer results
in a non-constant integer which lead to this error. Example:
```
const X: u32 = 1;
const Y: usize = &X as *const u32 as usize;
println!("{}", Y);
```
"##,
E0019: r##"
A function call isn't allowed in the const's initialization expression
because the expression's value must be known at compile-time. Example of
erroneous code:
```
enum Test {
V1
}
impl Test {
fn test(&self) -> i32 {
12
}
}
fn main() {
const FOO: Test = Test::V1;
const A: i32 = FOO.test(); // You can't call Test::func() here !
}
```
Remember: you can't use a function call inside a const's initialization
expression! However, you can totally use it anywhere else:
```
fn main() {
const FOO: Test = Test::V1;
FOO.func(); // here is good
let x = FOO.func(); // or even here!
}
```
"##,
E0022: r##"
Constant functions are not allowed to mutate anything. Thus, binding to an
argument with a mutable pattern is not allowed. For example,
@ -43,6 +225,366 @@ you need to mutate the argument, try lazily initializing a global variable
instead of using a `const fn`, or refactoring the code to a functional style to
avoid mutation if possible.
"##,
E0030: r##"
When matching against a range, the compiler verifies that the range is
non-empty. Range patterns include both end-points, so this is equivalent to
requiring the start of the range to be less than or equal to the end of the
range.
For example:
```
match 5u32 {
// This range is ok, albeit pointless.
1 ... 1 => ...
// This range is empty, and the compiler can tell.
1000 ... 5 => ...
}
```
"##,
E0161: r##"
In Rust, you can only move a value when its size is known at compile time.
To work around this restriction, consider "hiding" the value behind a reference:
either `&x` or `&mut x`. Since a reference has a fixed size, this lets you move
it around as usual.
"##,
E0265: r##"
This error indicates that a static or constant references itself.
All statics and constants need to resolve to a value in an acyclic manner.
For example, neither of the following can be sensibly compiled:
```
const X: u32 = X;
```
```
const X: u32 = Y;
const Y: u32 = X;
```
"##,
E0267: r##"
This error indicates the use of a loop keyword (`break` or `continue`) inside a
closure but outside of any loop. Erroneous code example:
```
let w = || { break; }; // error: `break` inside of a closure
```
`break` and `continue` keywords can be used as normal inside closures as long as
they are also contained within a loop. To halt the execution of a closure you
should instead use a return statement. Example:
```
let w = || {
for _ in 0..10 {
break;
}
};
w();
```
"##,
E0268: r##"
This error indicates the use of a loop keyword (`break` or `continue`) outside
of a loop. Without a loop to break out of or continue in, no sensible action can
be taken. Erroneous code example:
```
fn some_func() {
break; // error: `break` outside of loop
}
```
Please verify that you are using `break` and `continue` only in loops. Example:
```
fn some_func() {
for _ in 0..10 {
break; // ok!
}
}
```
"##,
E0378: r##"
Method calls that aren't calls to inherent `const` methods are disallowed
in statics, constants, and constant functions.
For example:
```
const BAZ: i32 = Foo(25).bar(); // error, `bar` isn't `const`
struct Foo(i32);
impl Foo {
const fn foo(&self) -> i32 {
self.bar() // error, `bar` isn't `const`
}
fn bar(&self) -> i32 { self.0 }
}
```
For more information about `const fn`'s, see [RFC 911].
[RFC 911]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
"##,
E0394: r##"
From [RFC 246]:
> It is invalid for a static to reference another static by value. It is
> required that all references be borrowed.
[RFC 246]: https://github.com/rust-lang/rfcs/pull/246
"##,
E0395: r##"
The value assigned to a constant expression must be known at compile time,
which is not the case when comparing raw pointers. Erroneous code example:
```
static foo: i32 = 42;
static bar: i32 = 43;
static baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
// error: raw pointers cannot be compared in statics!
```
Please check that the result of the comparison can be determined at compile time
or isn't assigned to a constant expression. Example:
```
static foo: i32 = 42;
static bar: i32 = 43;
let baz: bool = { (&foo as *const i32) == (&bar as *const i32) };
// baz isn't a constant expression so it's ok
```
"##,
E0396: r##"
The value assigned to a constant expression must be known at compile time,
which is not the case when dereferencing raw pointers. Erroneous code
example:
```
const foo: i32 = 42;
const baz: *const i32 = (&foo as *const i32);
const deref: i32 = *baz;
// error: raw pointers cannot be dereferenced in constants
```
To fix this error, please do not assign this value to a constant expression.
Example:
```
const foo: i32 = 42;
const baz: *const i32 = (&foo as *const i32);
unsafe { let deref: i32 = *baz; }
// baz isn't a constant expression so it's ok
```
You'll also note that this assignment must be done in an unsafe block!
"##,
E0397: r##"
It is not allowed for a mutable static to allocate or have destructors. For
example:
```
// error: mutable statics are not allowed to have boxes
static mut FOO: Option<Box<usize>> = None;
// error: mutable statics are not allowed to have destructors
static mut BAR: Option<Vec<i32>> = None;
```
"##,
E0400: r##"
A user-defined dereference was attempted in an invalid context. Erroneous
code example:
```
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
const S: &'static str = &A;
// error: user-defined dereference operators are not allowed in constants
fn main() {
let foo = S;
}
```
You cannot directly use a dereference operation whilst initializing a constant
or a static. To fix this error, restructure your code to avoid this dereference,
perhaps moving it inline:
```
use std::ops::Deref;
struct A;
impl Deref for A {
type Target = str;
fn deref(&self)-> &str { "foo" }
}
fn main() {
let foo : &str = &A;
}
```
"##,
E0492: r##"
A borrow of a constant containing interior mutability was attempted. Erroneous
code example:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
const A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A;
// error: cannot borrow a constant which contains interior mutability, create a
// static instead
```
A `const` represents a constant value that should never change. If one takes
a `&` reference to the constant, then one is taking a pointer to some memory
location containing the value. Normally this is perfectly fine: most values
can't be changed via a shared `&` pointer, but interior mutability would allow
it. That is, a constant value could be mutated. On the other hand, a `static` is
explicitly a single memory location, which can be mutated at will.
So, in order to solve this error, either use statics which are `Sync`:
```
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
static A: AtomicUsize = ATOMIC_USIZE_INIT;
static B: &'static AtomicUsize = &A; // ok!
```
You can also have this error while using a cell type:
```
#![feature(const_fn)]
use std::cell::Cell;
const A: Cell<usize> = Cell::new(1);
const B: &'static Cell<usize> = &A;
// error: cannot borrow a constant which contains interior mutability, create
// a static instead
// or:
struct C { a: Cell<usize> }
const D: C = C { a: Cell::new(1) };
const E: &'static Cell<usize> = &D.a; // error
// or:
const F: &'static C = &D; // error
```
This is because cell types do operations that are not thread-safe. Due to this,
they don't implement Sync and thus can't be placed in statics. In this
case, `StaticMutex` would work just fine, but it isn't stable yet:
https://doc.rust-lang.org/nightly/std/sync/struct.StaticMutex.html
However, if you still wish to use these types, you can achieve this by an unsafe
wrapper:
```
#![feature(const_fn)]
use std::cell::Cell;
use std::marker::Sync;
struct NotThreadSafe<T> {
value: Cell<T>,
}
unsafe impl<T> Sync for NotThreadSafe<T> {}
static A: NotThreadSafe<usize> = NotThreadSafe { value : Cell::new(1) };
static B: &'static NotThreadSafe<usize> = &A; // ok!
```
Remember this solution is unsafe! You will have to ensure that accesses to the
cell are synchronized.
"##,
E0493: r##"
A type with a destructor was assigned to an invalid type of variable. Erroneous
code example:
```
struct Foo {
a: u32
}
impl Drop for Foo {
fn drop(&mut self) {}
}
const F : Foo = Foo { a : 0 };
// error: constants are not allowed to have destructors
static S : Foo = Foo { a : 0 };
// error: statics are not allowed to have destructors
```
To solve this issue, please use a type which does allow the usage of type with
destructors.
"##,
E0494: r##"
A reference of an interior static was assigned to another const/static.
Erroneous code example:
```
struct Foo {
a: u32
}
static S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a;
// error: cannot refer to the interior of another static, use a
// constant instead
```
The "base" variable has to be a const if you want another static/const variable
to refer to one of its fields. Example:
```
struct Foo {
a: u32
}
const S : Foo = Foo { a : 0 };
static A : &'static u32 = &S.a; // ok!
```
"##,
}
register_diagnostics! {

View File

@ -28,9 +28,16 @@
extern crate core;
extern crate rustc;
extern crate rustc_front;
#[macro_use] extern crate log;
#[macro_use] extern crate syntax;
pub mod diagnostics;
pub mod const_fn;
pub mod consts;
pub mod loops;
pub mod no_asm;
pub mod rvalues;
pub mod static_recursion;

View File

@ -9,7 +9,7 @@
// except according to those terms.
use self::Context::*;
use session::Session;
use rustc::session::Session;
use syntax::codemap::Span;
use rustc_front::intravisit::{self, Visitor};

View File

@ -11,12 +11,11 @@
// Checks that all rvalues in a crate have statically known size. check_crate
// is the public starting point.
use dep_graph::DepNode;
use middle::expr_use_visitor as euv;
use middle::infer;
use middle::mem_categorization as mc;
use middle::ty::ParameterEnvironment;
use middle::ty;
use rustc::dep_graph::DepNode;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::infer;
use rustc::middle::mem_categorization as mc;
use rustc::middle::ty::{self, ParameterEnvironment};
use rustc_front::hir;
use rustc_front::intravisit;

View File

@ -11,10 +11,10 @@
// This compiler pass detects constants that refer to themselves
// recursively.
use front::map as ast_map;
use session::Session;
use middle::def::{Def, DefMap};
use util::nodemap::NodeMap;
use rustc::front::map as ast_map;
use rustc::session::Session;
use rustc::middle::def::{Def, DefMap};
use rustc::util::nodemap::NodeMap;
use syntax::{ast};
use syntax::codemap::Span;

View File

@ -13,7 +13,7 @@ use back::abi;
use llvm;
use llvm::{ConstFCmp, ConstICmp, SetLinkage, SetUnnamedAddr};
use llvm::{InternalLinkage, ValueRef, Bool, True};
use middle::check_const;
use middle::const_qualif::ConstQualif;
use middle::cstore::LOCAL_CRATE;
use middle::const_eval::{self, ConstVal, ConstEvalErr};
use middle::const_eval::{const_int_checked_neg, const_uint_checked_neg};
@ -274,8 +274,7 @@ fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
-> Result<ValueRef, ConstEvalFailure> {
let expr = get_const_expr(ccx, def_id, ref_expr, param_substs);
let empty_substs = ccx.tcx().mk_substs(Substs::trans_empty());
match get_const_expr_as_global(ccx, expr, check_const::ConstQualif::empty(),
empty_substs, TrueConst::Yes) {
match get_const_expr_as_global(ccx, expr, ConstQualif::empty(), empty_substs, TrueConst::Yes) {
Err(Runtime(err)) => {
ccx.tcx().sess.span_err(expr.span, &err.description());
Err(Compiletime(err))
@ -286,7 +285,7 @@ fn get_const_val<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
expr: &hir::Expr,
qualif: check_const::ConstQualif,
qualif: ConstQualif,
param_substs: &'tcx Substs<'tcx>,
trueconst: TrueConst)
-> Result<ValueRef, ConstEvalFailure> {
@ -315,7 +314,7 @@ pub fn get_const_expr_as_global<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
}
let ty = monomorphize::apply_param_substs(ccx.tcx(), param_substs,
&ccx.tcx().expr_ty(expr));
let val = if qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS) {
let val = if qualif.intersects(ConstQualif::NON_STATIC_BORROWS) {
// Avoid autorefs as they would create global instead of stack
// references, even when only the latter are correct.
try!(const_expr_unadjusted(ccx, expr, ty, param_substs, None, trueconst))

View File

@ -53,7 +53,7 @@ use self::lazy_binop_ty::*;
use back::abi;
use llvm::{self, ValueRef, TypeKind};
use middle::check_const;
use middle::const_qualif::ConstQualif;
use middle::def::Def;
use middle::lang_items::CoerceUnsizedTraitLangItem;
use middle::subst::{Substs, VecPerParamSpace};
@ -128,11 +128,8 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
if !qualif.intersects(
check_const::ConstQualif::NOT_CONST |
check_const::ConstQualif::NEEDS_DROP
) {
if !qualif.intersects(check_const::ConstQualif::PREFER_IN_PLACE) {
if !qualif.intersects(ConstQualif::NOT_CONST | ConstQualif::NEEDS_DROP) {
if !qualif.intersects(ConstQualif::PREFER_IN_PLACE) {
if let SaveIn(lldest) = dest {
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
bcx.fcx.param_substs,
@ -231,16 +228,13 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let mut bcx = bcx;
let fcx = bcx.fcx;
let qualif = *bcx.tcx().const_qualif_map.borrow().get(&expr.id).unwrap();
let adjusted_global = !qualif.intersects(check_const::ConstQualif::NON_STATIC_BORROWS);
let global = if !qualif.intersects(
check_const::ConstQualif::NOT_CONST |
check_const::ConstQualif::NEEDS_DROP
) {
let adjusted_global = !qualif.intersects(ConstQualif::NON_STATIC_BORROWS);
let global = if !qualif.intersects(ConstQualif::NOT_CONST | ConstQualif::NEEDS_DROP) {
match consts::get_const_expr_as_global(bcx.ccx(), expr, qualif,
bcx.fcx.param_substs,
consts::TrueConst::No) {
Ok(global) => {
if qualif.intersects(check_const::ConstQualif::HAS_STATIC_BORROWS) {
if qualif.intersects(ConstQualif::HAS_STATIC_BORROWS) {
// Is borrowed as 'static, must return lvalue.
// Cast pointer to global, because constants have different types.