Skip to content

Commit

Permalink
Allow blocks in const expressions
Browse files Browse the repository at this point in the history
Only blocks with tail expressions that are const expressions
and items are allowed.
  • Loading branch information
Kimundi authored and alexcrichton committed May 14, 2014
1 parent cbc31df commit 24ece07
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 1 deletion.
27 changes: 27 additions & 0 deletions src/librustc/middle/check_const.rs
Expand Up @@ -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(_) |
Expand Down
14 changes: 13 additions & 1 deletion src/librustc/middle/const_eval.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -479,6 +485,12 @@ pub fn eval_const_expr_partial<T: ty::ExprTyProvider>(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())
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/trans/base.rs
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/middle/trans/consts.rs
Expand Up @@ -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")
};
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/middle/typeck/check/mod.rs
Expand Up @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions 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 <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 static BLOCK_FN_DEF: fn(uint) -> uint = {
fn foo(a: uint) -> uint {
a + 10
}
foo
};
28 changes: 28 additions & 0 deletions 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 <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.

#![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() {
}
17 changes: 17 additions & 0 deletions 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 <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.

// aux-build:cci_const_block.rs

extern crate cci_const_block;

pub fn main() {
assert_eq!(cci_const_block::BLOCK_FN_DEF(390), 400);
}
49 changes: 49 additions & 0 deletions 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 <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.

// 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);
}
56 changes: 56 additions & 0 deletions 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 <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.

#![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);
}
69 changes: 69 additions & 0 deletions 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 <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(dead_code)]
#![allow(unused_unsafe)]

struct Foo {
a: uint,
b: *()
}

fn foo<T>(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<uint> = { 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::<uint> };

// FIXME: #13972
// static BLOCK_ENUM_CONSTRUCTOR: fn(uint) -> Option<uint> = { 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);
}

0 comments on commit 24ece07

Please sign in to comment.