From 1471d932a9d191216c0da318c085b508f9bdfc1f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 15 Jan 2016 13:16:54 +0100 Subject: [PATCH] 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). --- mk/crates.mk | 5 +- src/librustc/diagnostics.rs | 34 ----- src/librustc/lib.rs | 1 - src/librustc/middle/check_const.rs | 33 ++--- src/librustc_driver/driver.rs | 6 +- src/librustc_driver/lib.rs | 1 + src/librustc_passes/const_fn.rs | 117 ++++++++++++++++++ src/librustc_passes/diagnostics.rs | 50 ++++++++ src/librustc_passes/lib.rs | 36 ++++++ .../no_asm.rs} | 2 +- src/test/compile-fail/const-fn-error.rs | 29 +++++ .../const-fn-not-safe-for-const.rs | 6 - .../const-fn-not-safe-for-const2.rs | 44 +++++++ src/test/compile-fail/issue-18118-2.rs | 17 +++ src/test/compile-fail/issue-18118.rs | 1 - 15 files changed, 310 insertions(+), 72 deletions(-) create mode 100644 src/librustc_passes/const_fn.rs create mode 100644 src/librustc_passes/diagnostics.rs create mode 100644 src/librustc_passes/lib.rs rename src/{librustc/middle/check_no_asm.rs => librustc_passes/no_asm.rs} (97%) create mode 100644 src/test/compile-fail/const-fn-error.rs create mode 100644 src/test/compile-fail/const-fn-not-safe-for-const2.rs create mode 100644 src/test/compile-fail/issue-18118-2.rs 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 @@ 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 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 a9b3043e090..ab04d98e5da 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -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); } 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 cdac3de3682..df545048340 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -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; 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 }; }