From c124deca7b3d93e72b5d849b392a5bea83eacfb5 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 21 Jan 2016 10:52:37 +0100 Subject: [PATCH] move more checks out of librustc --- mk/crates.mk | 2 +- src/librustc/diagnostics.rs | 540 ----------------- src/librustc/lib.rs | 5 +- src/librustc/middle/const_qualif.rs | 44 ++ src/librustc/middle/mem_categorization.rs | 8 +- src/librustc/middle/ty/context.rs | 2 +- src/librustc_driver/driver.rs | 13 +- src/librustc_metadata/astencode.rs | 2 +- .../consts.rs} | 67 +-- src/librustc_passes/diagnostics.rs | 542 ++++++++++++++++++ src/librustc_passes/lib.rs | 7 + .../loops.rs} | 2 +- .../rvalues.rs} | 11 +- .../static_recursion.rs} | 8 +- src/librustc_trans/trans/consts.rs | 9 +- src/librustc_trans/trans/expr.rs | 18 +- 16 files changed, 645 insertions(+), 635 deletions(-) create mode 100644 src/librustc/middle/const_qualif.rs rename src/{librustc/middle/check_const.rs => librustc_passes/consts.rs} (93%) rename src/{librustc/middle/check_loop.rs => librustc_passes/loops.rs} (98%) rename src/{librustc/middle/check_rvalues.rs => librustc_passes/rvalues.rs} (94%) rename src/{librustc/middle/check_static_recursion.rs => librustc_passes/static_recursion.rs} (98%) diff --git a/mk/crates.mk b/mk/crates.mk index 5cc8a468784..f47c4857ef8 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index aa2f60f71f9..e5942e64a9e 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 = 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 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 = 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` 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 { ``` "##, -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> = None; - -// error: mutable statics are not allowed to have destructors -static mut BAR: Option> = 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 = Cell::new(1); -const B: &'static Cell = &A; -// error: cannot borrow a constant which contains interior mutability, create -// a static instead - -// or: -struct C { a: Cell } - -const D: C = C { a: Cell::new(1) }; -const E: &'static Cell = &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 { - value: Cell, -} - -unsafe impl Sync for NotThreadSafe {} - -static A: NotThreadSafe = NotThreadSafe { value : Cell::new(1) }; -static B: &'static NotThreadSafe = &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: diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 501a03f1286..4d772de7835 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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; diff --git a/src/librustc/middle/const_qualif.rs b/src/librustc/middle/const_qualif.rs new file mode 100644 index 00000000000..ec98637922e --- /dev/null +++ b/src/librustc/middle/const_qualif.rs @@ -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 or the MIT license +// , 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 + } +} diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 55bbde21514..affd963fb79 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -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 diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index d1504d25288..6e56105dd0d 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -373,7 +373,7 @@ pub struct ctxt<'tcx> { repr_hint_cache: RefCell>>, /// Maps Expr NodeId's to their constant qualification. - pub const_qualif_map: RefCell>, + pub const_qualif_map: RefCell>, /// Caches CoerceUnsized kinds for impls on custom types. pub custom_coerce_unsized_kinds: RefCell>, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 01ffd0efbe3..fd5f711c9d6 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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 diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index f0cd75ba958..e91c7e6ac45 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -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}; diff --git a/src/librustc/middle/check_const.rs b/src/librustc_passes/consts.rs similarity index 93% rename from src/librustc/middle/check_const.rs rename to src/librustc_passes/consts.rs index 68016a153b0..60cc658eeca 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc_passes/consts.rs @@ -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()) } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 380eada18a1..2c08cbd3233 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -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 = 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 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 = 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> = None; + +// error: mutable statics are not allowed to have destructors +static mut BAR: Option> = 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 = Cell::new(1); +const B: &'static Cell = &A; +// error: cannot borrow a constant which contains interior mutability, create +// a static instead + +// or: +struct C { a: Cell } + +const D: C = C { a: Cell::new(1) }; +const E: &'static Cell = &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 { + value: Cell, +} + +unsafe impl Sync for NotThreadSafe {} + +static A: NotThreadSafe = NotThreadSafe { value : Cell::new(1) }; +static B: &'static NotThreadSafe = &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! { diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 4adaa0cab7a..fcdbd6384d5 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -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; diff --git a/src/librustc/middle/check_loop.rs b/src/librustc_passes/loops.rs similarity index 98% rename from src/librustc/middle/check_loop.rs rename to src/librustc_passes/loops.rs index 22e9df63d01..eb2e445f9b0 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc_passes/loops.rs @@ -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}; diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc_passes/rvalues.rs similarity index 94% rename from src/librustc/middle/check_rvalues.rs rename to src/librustc_passes/rvalues.rs index 5ead8fb95f8..f5cc020932b 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc_passes/rvalues.rs @@ -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; diff --git a/src/librustc/middle/check_static_recursion.rs b/src/librustc_passes/static_recursion.rs similarity index 98% rename from src/librustc/middle/check_static_recursion.rs rename to src/librustc_passes/static_recursion.rs index abea48f0bd6..b49db16b4ce 100644 --- a/src/librustc/middle/check_static_recursion.rs +++ b/src/librustc_passes/static_recursion.rs @@ -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; diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 2011f1b5352..0f5d8dbd94d 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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 { 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 { @@ -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)) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 60afcaa0fbf..ada37c5d8df 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -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.