diff --git a/mk/crates.mk b/mk/crates.mk index be53234cb02..5cc8a468784 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -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 diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 3aabe4b4931..9f323379b95 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -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 diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index f84d5fbaf81..7a047cde0db 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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; diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index a458c2e14be..6001b120587 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -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); } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 840260da330..01ffd0efbe3 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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)); } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 62bea612168..31151e10a5a 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -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; diff --git a/src/librustc_passes/const_fn.rs b/src/librustc_passes/const_fn.rs new file mode 100644 index 00000000000..cda5267f727 --- /dev/null +++ b/src/librustc_passes/const_fn.rs @@ -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 or the MIT license +// , 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"); + } +} diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs new file mode 100644 index 00000000000..380eada18a1 --- /dev/null +++ b/src/librustc_passes/diagnostics.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs new file mode 100644 index 00000000000..4adaa0cab7a --- /dev/null +++ b/src/librustc_passes/lib.rs @@ -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 or the MIT license +// , 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; diff --git a/src/librustc/middle/check_no_asm.rs b/src/librustc_passes/no_asm.rs similarity index 97% rename from src/librustc/middle/check_no_asm.rs rename to src/librustc_passes/no_asm.rs index b9f8f20536a..3022d9fb9e3 100644 --- a/src/librustc/middle/check_no_asm.rs +++ b/src/librustc_passes/no_asm.rs @@ -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; diff --git a/src/test/compile-fail/const-fn-error.rs b/src/test/compile-fail/const-fn-error.rs new file mode 100644 index 00000000000..cb6f2d0215f --- /dev/null +++ b/src/test/compile-fail/const-fn-error.rs @@ -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 or the MIT license +// , 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)]; +} diff --git a/src/test/compile-fail/const-fn-not-safe-for-const.rs b/src/test/compile-fail/const-fn-not-safe-for-const.rs index baa3eba0680..f8381978dc7 100644 --- a/src/test/compile-fail/const-fn-not-safe-for-const.rs +++ b/src/test/compile-fail/const-fn-not-safe-for-const.rs @@ -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() { } diff --git a/src/test/compile-fail/const-fn-not-safe-for-const2.rs b/src/test/compile-fail/const-fn-not-safe-for-const2.rs new file mode 100644 index 00000000000..a053847e882 --- /dev/null +++ b/src/test/compile-fail/const-fn-not-safe-for-const2.rs @@ -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 or the MIT license +// , 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() { +} diff --git a/src/test/compile-fail/issue-18118-2.rs b/src/test/compile-fail/issue-18118-2.rs new file mode 100644 index 00000000000..1fbf48f5b21 --- /dev/null +++ b/src/test/compile-fail/issue-18118-2.rs @@ -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 or the MIT license +// , 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 + }; +} diff --git a/src/test/compile-fail/issue-18118.rs b/src/test/compile-fail/issue-18118.rs index c5370879cc2..9c8ed314d22 100644 --- a/src/test/compile-fail/issue-18118.rs +++ b/src/test/compile-fail/issue-18118.rs @@ -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 }; }