Skip to content
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

Allow blocks in const expressions #14183

Closed
wants to merge 1 commit into from
Closed
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
27 changes: 27 additions & 0 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
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?!")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think macro_rules! macros may stick around un-expanded. Can you add a test case too make sure we don't ICE here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean macro_rules! itself, or macros generated by it? For the latter there is a testcase already.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, macro_rules! itself

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a testcase, doesn't seem to ICE. :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent, thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or actually, can you add another test with the macro tagged with #[macro_export]? Those seem to be the ones that stick around.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static BLOCK_MACRO_RULES_EXPORTED: uint = {
    #[macro_export]
    macro_rules! baz {
        () => (612)
    }
    baz!()
};

gives me the error

    /home/marvin/dev/rust/fork/rust/src/test/run-pass/const-block-item.rs:51:19: 51:20 error: expected item after attributes
    /home/marvin/dev/rust/fork/rust/src/test/run-pass/const-block-item.rs:51     #[macro_export]
                                                                                               ^

Am I doing something wrong?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh cool, looks like the parser doesn't even accept that currently (it wouldn't make much sense anyway I think). Thanks for checking!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the same error in a regular function as well:

pub fn main() {
    #[macro_export]
    macro_rules! baz {
        () => (612)
    }
}

}
}
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
Original file line number Diff line number Diff line change
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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems odd, but it may just be that I'm unfamiliar with trans. Is this how we translate to ()?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I didn't understand it myself, but other code treated unit that way, so I just copied it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, nevermind then!

}
}
_ => Err("unsupported constant expr".to_strbuf())
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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);
}