Auto merge of #30930 - oli-obk:fix/30887, r=arielb1
this makes sure the checks run before typeck (which might use the constant or const function to calculate an array length) and gives prettier error messages in case of for loops and such (since they aren't expanded yet). fixes #30887 r? @pnkfelix
This commit is contained in:
commit
83c3b7f5a4
@ -57,7 +57,7 @@ TARGET_CRATES := libc std flate arena term \
|
||||
RUSTC_CRATES := rustc rustc_typeck rustc_mir rustc_borrowck rustc_resolve rustc_driver \
|
||||
rustc_trans rustc_back rustc_llvm rustc_privacy rustc_lint \
|
||||
rustc_data_structures rustc_front rustc_platform_intrinsics \
|
||||
rustc_plugin rustc_metadata
|
||||
rustc_plugin rustc_metadata rustc_passes
|
||||
HOST_CRATES := syntax syntax_ext $(RUSTC_CRATES) rustdoc fmt_macros
|
||||
TOOLS := compiletest rustdoc rustc rustbook error-index-generator
|
||||
|
||||
@ -97,11 +97,12 @@ DEPS_rustc_data_structures := std log serialize
|
||||
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
|
||||
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
|
||||
rustc_trans rustc_privacy rustc_lint rustc_front rustc_plugin \
|
||||
rustc_metadata syntax_ext
|
||||
rustc_metadata syntax_ext rustc_passes
|
||||
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_mir := rustc rustc_front syntax
|
||||
DEPS_rustc_resolve := arena rustc rustc_front log syntax
|
||||
DEPS_rustc_platform_intrinsics := rustc rustc_llvm
|
||||
|
@ -316,21 +316,6 @@ 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:
|
||||
|
||||
```
|
||||
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. Example:
|
||||
|
||||
@ -422,24 +407,6 @@ const X: i32 = 42 / 0;
|
||||
```
|
||||
"##,
|
||||
|
||||
E0022: r##"
|
||||
Constant functions are not allowed to mutate anything. Thus, binding to an
|
||||
argument with a mutable pattern is not allowed. For example,
|
||||
|
||||
```
|
||||
const fn foo(mut x: u8) {
|
||||
// do stuff
|
||||
}
|
||||
```
|
||||
|
||||
is bad because the function body may not mutate `x`.
|
||||
|
||||
Remove any mutable bindings from the argument list to fix this error. In case
|
||||
you need to mutate the argument, try lazily initializing a global variable
|
||||
instead of using a `const fn`, or refactoring the code to a functional style to
|
||||
avoid mutation if possible.
|
||||
"##,
|
||||
|
||||
E0030: r##"
|
||||
When matching against a range, the compiler verifies that the range is
|
||||
non-empty. Range patterns include both end-points, so this is equivalent to
|
||||
@ -2358,7 +2325,6 @@ register_diagnostics! {
|
||||
E0316, // nested quantification of lifetimes
|
||||
E0453, // overruled by outer forbid
|
||||
E0471, // constant evaluation error: ..
|
||||
E0472, // asm! is unsupported on this target
|
||||
E0473, // dereference of reference outside its lifetime
|
||||
E0474, // captured variable `..` does not outlive the enclosing closure
|
||||
E0475, // index of slice outside its lifetime
|
||||
|
@ -102,7 +102,6 @@ pub mod middle {
|
||||
pub mod check_static_recursion;
|
||||
pub mod check_loop;
|
||||
pub mod check_match;
|
||||
pub mod check_no_asm;
|
||||
pub mod check_rvalues;
|
||||
pub mod const_eval;
|
||||
pub mod cstore;
|
||||
|
@ -175,21 +175,6 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
|
||||
_ => Mode::Var
|
||||
};
|
||||
|
||||
// Ensure the arguments are simple, not mutable/by-ref or patterns.
|
||||
if mode == Mode::ConstFn {
|
||||
for arg in &fd.inputs {
|
||||
match arg.pat.node {
|
||||
hir::PatWild => {}
|
||||
hir::PatIdent(hir::BindByValue(hir::MutImmutable), _, None) => {}
|
||||
_ => {
|
||||
span_err!(self.tcx.sess, arg.pat.span, E0022,
|
||||
"arguments of constant functions can only \
|
||||
be immutable by-value bindings");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let qualif = self.with_mode(mode, |this| {
|
||||
this.with_euv(Some(fn_id), |euv| euv.walk_fn(fd, b));
|
||||
intravisit::walk_fn(this, fk, fd, b, s);
|
||||
@ -397,24 +382,20 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
||||
fn visit_block(&mut self, block: &hir::Block) {
|
||||
// Check all statements in the block
|
||||
for stmt in &block.stmts {
|
||||
let span = match stmt.node {
|
||||
match stmt.node {
|
||||
hir::StmtDecl(ref decl, _) => {
|
||||
match decl.node {
|
||||
hir::DeclLocal(_) => decl.span,
|
||||
|
||||
hir::DeclLocal(_) => {},
|
||||
// Item statements are allowed
|
||||
hir::DeclItem(_) => continue
|
||||
}
|
||||
}
|
||||
hir::StmtExpr(ref expr, _) => expr.span,
|
||||
hir::StmtSemi(ref semi, _) => semi.span,
|
||||
};
|
||||
self.add_qualif(ConstQualif::NOT_CONST);
|
||||
if self.mode != Mode::Var {
|
||||
span_err!(self.tcx.sess, span, E0016,
|
||||
"blocks in {}s are limited to items and \
|
||||
tail expressions", self.msg());
|
||||
hir::StmtExpr(_, _) => {},
|
||||
hir::StmtSemi(_, _) => {},
|
||||
}
|
||||
self.add_qualif(ConstQualif::NOT_CONST);
|
||||
// anything else should have been caught by check_const_fn
|
||||
assert_eq!(self.mode, Mode::Var);
|
||||
}
|
||||
intravisit::walk_block(self, block);
|
||||
}
|
||||
|
@ -632,7 +632,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",
|
||||
|| middle::check_no_asm::check_crate(sess, &krate));
|
||||
|| ::rustc_passes::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
|
||||
@ -647,6 +647,10 @@ pub fn phase_2_configure_and_expand(sess: &Session,
|
||||
sess.abort_if_errors();
|
||||
});
|
||||
|
||||
time(time_passes,
|
||||
"const fn bodies and arguments",
|
||||
|| ::rustc_passes::const_fn::check_crate(sess, &krate));
|
||||
|
||||
if sess.opts.debugging_opts.input_stats {
|
||||
println!("Post-expansion node count: {}", count_nodes(&krate));
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ extern crate libc;
|
||||
extern crate rustc;
|
||||
extern crate rustc_back;
|
||||
extern crate rustc_borrowck;
|
||||
extern crate rustc_passes;
|
||||
extern crate rustc_front;
|
||||
extern crate rustc_lint;
|
||||
extern crate rustc_plugin;
|
||||
|
117
src/librustc_passes/const_fn.rs
Normal file
117
src/librustc_passes/const_fn.rs
Normal file
@ -0,0 +1,117 @@
|
||||
// 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;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::visit::{self, Visitor, FnKind};
|
||||
use syntax::codemap::Span;
|
||||
|
||||
pub fn check_crate(sess: &Session, krate: &ast::Crate) {
|
||||
visit::walk_crate(&mut CheckConstFn{ sess: sess }, krate);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
|
||||
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::ExprClosure(..) = e.node {
|
||||
CheckConstFn{ sess: self.sess}.visit_expr(e);
|
||||
} else {
|
||||
visit::walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
fn visit_item(&mut self, _i: &'v ast::Item) { panic!("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) { panic!("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::StmtDecl(ref decl, _) => {
|
||||
match decl.node {
|
||||
ast::DeclLocal(_) => decl.span,
|
||||
|
||||
// Item statements are allowed
|
||||
ast::DeclItem(_) => continue,
|
||||
}
|
||||
}
|
||||
ast::StmtExpr(ref expr, _) => expr.span,
|
||||
ast::StmtSemi(ref semi, _) => semi.span,
|
||||
ast::StmtMac(..) => unreachable!(),
|
||||
};
|
||||
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::ItemConst(_, ref e) => {
|
||||
CheckBlock{ sess: self.sess, kind: "constant"}.visit_expr(e)
|
||||
},
|
||||
ast::ItemStatic(_, _, 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 {
|
||||
ast::PatWild => {}
|
||||
ast::PatIdent(ast::BindingMode::ByValue(ast::MutImmutable), _, 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");
|
||||
}
|
||||
}
|
50
src/librustc_passes/diagnostics.rs
Normal file
50
src/librustc_passes/diagnostics.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// 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! {
|
||||
E0016: r##"
|
||||
Blocks in constants may only contain items (such as constant, function
|
||||
definition, etc...) and a tail expression. Example:
|
||||
|
||||
```
|
||||
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 };
|
||||
```
|
||||
"##,
|
||||
|
||||
E0022: r##"
|
||||
Constant functions are not allowed to mutate anything. Thus, binding to an
|
||||
argument with a mutable pattern is not allowed. For example,
|
||||
|
||||
```
|
||||
const fn foo(mut x: u8) {
|
||||
// do stuff
|
||||
}
|
||||
```
|
||||
|
||||
is bad 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.
|
||||
"##,
|
||||
}
|
||||
|
||||
register_diagnostics! {
|
||||
E0472, // asm! is unsupported on this target
|
||||
}
|
36
src/librustc_passes/lib.rs
Normal file
36
src/librustc_passes/lib.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2012-2013 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.
|
||||
|
||||
//! Various checks
|
||||
//!
|
||||
//! # Note
|
||||
//!
|
||||
//! This API is completely unstable and subject to change.
|
||||
|
||||
#![crate_name = "rustc_passes"]
|
||||
#![unstable(feature = "rustc_private", issue = "27812")]
|
||||
#![crate_type = "dylib"]
|
||||
#![crate_type = "rlib"]
|
||||
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
|
||||
html_root_url = "https://doc.rust-lang.org/nightly/")]
|
||||
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate core;
|
||||
extern crate rustc;
|
||||
|
||||
#[macro_use] extern crate syntax;
|
||||
|
||||
pub mod diagnostics;
|
||||
pub mod const_fn;
|
||||
pub mod no_asm;
|
@ -12,7 +12,7 @@
|
||||
/// Inline asm isn't allowed on virtual ISA based targets, so we reject it
|
||||
/// here.
|
||||
|
||||
use session::Session;
|
||||
use rustc::session::Session;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::visit::Visitor;
|
29
src/test/compile-fail/const-fn-error.rs
Normal file
29
src/test/compile-fail/const-fn-error.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
// test that const fn signature and body errors are checked
|
||||
// even in array lengths, which are evaluated before check_const
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
const X : usize = 2;
|
||||
|
||||
const fn f(x: usize) -> usize {
|
||||
let mut sum = 0; //~ ERROR: E0016
|
||||
for i in 0..x { //~ ERROR: E0016
|
||||
sum += i;
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn main() {
|
||||
let a : [i32; f(X)];
|
||||
}
|
@ -37,11 +37,5 @@ const fn get_Y_addr() -> &'static u32 {
|
||||
//~^ ERROR E0013
|
||||
}
|
||||
|
||||
const fn get() -> u32 {
|
||||
let x = 22; //~ ERROR E0016
|
||||
let y = 44; //~ ERROR E0016
|
||||
x + y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
44
src/test/compile-fail/const-fn-not-safe-for-const2.rs
Normal file
44
src/test/compile-fail/const-fn-not-safe-for-const2.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
// Test that we can't call random fns in a const fn or do other bad things.
|
||||
|
||||
#![feature(const_fn)]
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
fn random() -> u32 { 0 }
|
||||
|
||||
const fn sub(x: &u32) -> usize {
|
||||
unsafe { transmute(x) }
|
||||
}
|
||||
|
||||
const fn sub1() -> u32 {
|
||||
random()
|
||||
}
|
||||
|
||||
static Y: u32 = 0;
|
||||
|
||||
const fn get_Y() -> u32 {
|
||||
Y
|
||||
}
|
||||
|
||||
const fn get_Y_addr() -> &'static u32 {
|
||||
&Y
|
||||
}
|
||||
|
||||
const fn get() -> u32 {
|
||||
let x = 22; //~ ERROR E0016
|
||||
let y = 44; //~ ERROR E0016
|
||||
x + y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
17
src/test/compile-fail/issue-18118-2.rs
Normal file
17
src/test/compile-fail/issue-18118-2.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 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.
|
||||
|
||||
pub fn main() {
|
||||
const z: &'static isize = {
|
||||
static p: isize = 3;
|
||||
&p
|
||||
//~^ ERROR constants cannot refer to other statics, insert an intermediate constant instead
|
||||
};
|
||||
}
|
@ -13,6 +13,5 @@ pub fn main() {
|
||||
let p = 3;
|
||||
//~^ ERROR blocks in constants are limited to items and tail expressions
|
||||
&p
|
||||
//~^ ERROR paths in constants may only refer to constants or functions
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user