move const block checks before lowering step

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).
This commit is contained in:
Oliver Schneider 2016-01-15 13:16:54 +01:00
parent 5b3a75fe56
commit 1471d932a9
15 changed files with 310 additions and 72 deletions

View file

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

View file

@ -316,21 +316,6 @@ struct Bar {x: u8}
[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 @@ fn main() {
```
"##,
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 @@ impl Foo {
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

View file

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

View file

@ -175,21 +175,6 @@ fn fn_like(&mut self,
_ => 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 @@ fn visit_pat(&mut self, p: &hir::Pat) {
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);
}

View file

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

View file

@ -38,6 +38,7 @@
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;

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

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

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

View file

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

View 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)];
}

View file

@ -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() {
}

View 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() {
}

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

View file

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