Skip to content

move const block checks before lowering step #30930

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
34 changes: 0 additions & 34 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
33 changes: 7 additions & 26 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_driver/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
117 changes: 117 additions & 0 deletions src/librustc_passes/const_fn.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 50 additions & 0 deletions src/librustc_passes/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions src/librustc_passes/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading