diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index f5f551e36f7..7da56655378 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -148,6 +148,33 @@ fn check_expr(v: &mut CheckCrateVisitor, e: &Expr, is_const: bool) { } } } + ExprBlock(ref block) => { + // Check all statements in the block + for stmt in block.stmts.iter() { + let block_span_err = |span| + v.tcx.sess.span_err(span, + "blocks in constants are limited to \ + items and tail expressions"); + match stmt.node { + StmtDecl(ref span, _) => { + match span.node { + DeclLocal(_) => block_span_err(span.span), + + // Item statements are allowed + DeclItem(_) => {} + } + } + StmtExpr(ref expr, _) => block_span_err(expr.span), + StmtSemi(ref semi, _) => block_span_err(semi.span), + StmtMac(..) => v.tcx.sess.span_bug(e.span, + "unexpanded statement macro in const?!") + } + } + match block.expr { + Some(ref expr) => check_expr(v, &**expr, true), + None => {} + } + } ExprVstore(_, ExprVstoreMutSlice) | ExprVstore(_, ExprVstoreSlice) | ExprVec(_) | diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 34f36363b92..aa0b573eba8 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -47,7 +47,6 @@ use std::rc::Rc; // fixed-size vectors and strings: [] and ""/_ // vector and string slices: &[] and &"" // tuples: (,) -// records: {...} // enums: foo(...) // floating point literals and operators // & and * pointers @@ -241,6 +240,13 @@ impl<'a> ConstEvalVisitor<'a> { ast::ExprRepeat(..) => general_const, + ast::ExprBlock(ref block) => { + match block.expr { + Some(ref e) => self.classify(&**e), + None => integral_const + } + } + _ => non_const }; self.ccache.insert(did, cn); @@ -479,6 +485,12 @@ pub fn eval_const_expr_partial(tcx: &T, e: &Expr) // If we have a vstore, just keep going; it has to be a string ExprVstore(e, _) => eval_const_expr_partial(tcx, e), ExprParen(e) => eval_const_expr_partial(tcx, e), + ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => eval_const_expr_partial(tcx, &**expr), + None => Ok(const_int(0i64)) + } + } _ => Err("unsupported constant expr".to_strbuf()) } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 10f35255abb..3cfabf7f96b 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1611,6 +1611,9 @@ pub fn trans_item(ccx: &CrateContext, item: &ast::Item) { } } ast::ItemStatic(_, m, expr) => { + // Recurse on the expression to catch items in blocks + let mut v = TransItemVisitor{ ccx: ccx }; + v.visit_expr(expr, ()); consts::trans_const(ccx, m, item.id); // Do static_assert checking. It can't really be done much earlier // because we need to get the value of the bool out of LLVM diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 548746362cf..b5ab0a391f3 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -672,6 +672,12 @@ fn const_expr_unadjusted(cx: &CrateContext, e: &ast::Expr, } } ast::ExprParen(e) => { const_expr(cx, e, is_local) } + ast::ExprBlock(ref block) => { + match block.expr { + Some(ref expr) => const_expr(cx, &**expr, is_local), + None => (C_nil(cx), true) + } + } _ => cx.sess().span_bug(e.span, "bad constant expression type in consts::const_expr") }; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 6be96a408b8..68f4fd95626 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3554,6 +3554,12 @@ pub fn check_const_with_ty(fcx: &FnCtxt, _: Span, e: &ast::Expr, declty: ty::t) { + // Gather locals in statics (because of block expressions). + // This is technically uneccessary because locals in static items are forbidden, + // but prevents type checking from blowing up before const checking can properly + // emit a error. + GatherLocalsVisitor { fcx: fcx }.visit_expr(e, ()); + check_expr(fcx, e); let cty = fcx.expr_ty(e); demand::suptype(fcx, e.span, declty, cty); diff --git a/src/test/auxiliary/cci_const_block.rs b/src/test/auxiliary/cci_const_block.rs new file mode 100644 index 00000000000..a3bcbd201e1 --- /dev/null +++ b/src/test/auxiliary/cci_const_block.rs @@ -0,0 +1,16 @@ +// 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 static BLOCK_FN_DEF: fn(uint) -> uint = { + fn foo(a: uint) -> uint { + a + 10 + } + foo +}; diff --git a/src/test/compile-fail/const-block-non-item-statement.rs b/src/test/compile-fail/const-block-non-item-statement.rs new file mode 100644 index 00000000000..ace917c704a --- /dev/null +++ b/src/test/compile-fail/const-block-non-item-statement.rs @@ -0,0 +1,28 @@ +// 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. + +#![feature(macro_rules)] + +static A: uint = { 1; 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +static B: uint = { { } 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +macro_rules! foo { + () => (()) //~ ERROR: blocks in constants are limited to items and tail expressions +} +static C: uint = { foo!() 2 }; + +static D: uint = { let x = 4; 2 }; +//~^ ERROR: blocks in constants are limited to items and tail expressions + +pub fn main() { +} diff --git a/src/test/run-pass/const-block-cross-crate-fn.rs b/src/test/run-pass/const-block-cross-crate-fn.rs new file mode 100644 index 00000000000..16360ff08d0 --- /dev/null +++ b/src/test/run-pass/const-block-cross-crate-fn.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. + +// aux-build:cci_const_block.rs + +extern crate cci_const_block; + +pub fn main() { + assert_eq!(cci_const_block::BLOCK_FN_DEF(390), 400); +} diff --git a/src/test/run-pass/const-block-item-macro-codegen.rs b/src/test/run-pass/const-block-item-macro-codegen.rs new file mode 100644 index 00000000000..09f26b15734 --- /dev/null +++ b/src/test/run-pass/const-block-item-macro-codegen.rs @@ -0,0 +1,49 @@ +// 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. + +// General test that function items in static blocks +// can be generated with a macro. + +#![feature(macro_rules)] + +struct MyType { + desc: &'static str, + data: uint, + code: fn(uint, uint) -> uint +} + +impl MyType { + fn eval(&self, a: uint) -> uint { + (self.code)(self.data, a) + } +} + +macro_rules! codegen { + ($e:expr, $v:expr) => { + { + fn generated(a: uint, b: uint) -> uint { + a - ($e * b) + } + MyType { + desc: "test", + data: $v, + code: generated + } + } + } +} + +static GENERATED_CODE_1: MyType = codegen!(2, 100); +static GENERATED_CODE_2: MyType = codegen!(5, 1000); + +pub fn main() { + assert_eq!(GENERATED_CODE_1.eval(10), 80); + assert_eq!(GENERATED_CODE_2.eval(100), 500); +} diff --git a/src/test/run-pass/const-block-item.rs b/src/test/run-pass/const-block-item.rs new file mode 100644 index 00000000000..3365f09cd80 --- /dev/null +++ b/src/test/run-pass/const-block-item.rs @@ -0,0 +1,56 @@ +// 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. + +#![feature(macro_rules)] + +mod foo { + pub trait Value { + fn value(&self) -> uint; + } +} + +static BLOCK_USE: uint = { + use foo::Value; + 100 +}; + +static BLOCK_PUB_USE: uint = { + pub use foo::Value; + 200 +}; + +static BLOCK_STRUCT_DEF: uint = { + struct Foo { + a: uint + } + Foo{ a: 300 }.a +}; + +static BLOCK_FN_DEF: fn(uint) -> uint = { + fn foo(a: uint) -> uint { + a + 10 + } + foo +}; + +static BLOCK_MACRO_RULES: uint = { + macro_rules! baz { + () => (412) + } + baz!() +}; + +pub fn main() { + assert_eq!(BLOCK_USE, 100); + assert_eq!(BLOCK_PUB_USE, 200); + assert_eq!(BLOCK_STRUCT_DEF, 300); + assert_eq!(BLOCK_FN_DEF(390), 400); + assert_eq!(BLOCK_MACRO_RULES, 412); +} diff --git a/src/test/run-pass/const-block.rs b/src/test/run-pass/const-block.rs new file mode 100644 index 00000000000..feac6e68e48 --- /dev/null +++ b/src/test/run-pass/const-block.rs @@ -0,0 +1,69 @@ +// 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. + +#![allow(dead_code)] +#![allow(unused_unsafe)] + +struct Foo { + a: uint, + b: *() +} + +fn foo(a: T) -> T { + a +} + +static BLOCK_INTEGRAL: uint = { 1 }; +static BLOCK_EXPLICIT_UNIT: () = { () }; +static BLOCK_IMPLICIT_UNIT: () = { }; +static BLOCK_FLOAT: f64 = { 1.0 }; +static BLOCK_ENUM: Option = { Some(100) }; +static BLOCK_STRUCT: Foo = { Foo { a: 12, b: 0 as *() } }; +static BLOCK_UNSAFE: uint = unsafe { 1000 }; + +// FIXME: #13970 +// static BLOCK_FN_INFERRED: fn(uint) -> uint = { foo }; + +// FIXME: #13971 +// static BLOCK_FN: fn(uint) -> uint = { foo:: }; + +// FIXME: #13972 +// static BLOCK_ENUM_CONSTRUCTOR: fn(uint) -> Option = { Some }; + +// FIXME: #13973 +// static BLOCK_UNSAFE_SAFE_PTR: &'static int = unsafe { &*(0xdeadbeef as *int) }; +// static BLOCK_UNSAFE_SAFE_PTR_2: &'static int = unsafe { +// static X: *int = 0xdeadbeef as *int; +// &*X +// }; + +pub fn main() { + assert_eq!(BLOCK_INTEGRAL, 1); + assert_eq!(BLOCK_EXPLICIT_UNIT, ()); + assert_eq!(BLOCK_IMPLICIT_UNIT, ()); + assert_eq!(BLOCK_FLOAT, 1.0_f64); + assert_eq!(BLOCK_STRUCT.a, 12); + assert_eq!(BLOCK_STRUCT.b, 0 as *()); + assert_eq!(BLOCK_ENUM, Some(100)); + assert_eq!(BLOCK_UNSAFE, 1000); + + // FIXME: #13970 + // assert_eq!(BLOCK_FN_INFERRED(300), 300); + + // FIXME: #13971 + // assert_eq!(BLOCK_FN(300), 300); + + // FIXME: #13972 + // assert_eq!(BLOCK_ENUM_CONSTRUCTOR(200), Some(200)); + + // FIXME: #13973 + // assert_eq!(BLOCK_UNSAFE_SAFE_PTR as *int as uint, 0xdeadbeef_u); + // assert_eq!(BLOCK_UNSAFE_SAFE_PTR_2 as *int as uint, 0xdeadbeef_u); +}