Skip to content

Allow consts to be initialized by non-nullary enum constructors #4459

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

Merged
merged 6 commits into from
Jan 13, 2013
Merged
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
3 changes: 2 additions & 1 deletion src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,12 @@ fn check_expr(sess: Session, def_map: resolve::DefMap,
expr_call(callee, _, false) => {
match def_map.find(callee.id) {
Some(def_struct(*)) => {} // OK.
Some(def_variant(*)) => {} // OK.
_ => {
sess.span_err(
e.span,
~"function calls in constants are limited to \
structure constructors");
struct and enum constructors");
}
}
}
Expand Down
45 changes: 37 additions & 8 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,30 @@ fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t)
};
}

fn get_discrim_val(cx: @crate_ctxt, span: span, enum_did: ast::def_id,
variant_did: ast::def_id) -> ValueRef {
// Can't use `discrims` from the crate context here because
// those discriminants have an extra level of indirection,
// and there's no LLVM constant load instruction.
let mut lldiscrim_opt = None;
for ty::enum_variants(cx.tcx, enum_did).each |variant_info| {
if variant_info.id == variant_did {
lldiscrim_opt = Some(C_int(cx,
variant_info.disr_val));
break;
}
}

match lldiscrim_opt {
None => {
cx.tcx.sess.span_bug(span, ~"didn't find discriminant?!");
}
Some(found_lldiscrim) => {
found_lldiscrim
}
}
}

fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef {
unsafe {
let _icx = ccx.insn_ctxt("lookup_discriminant");
Expand Down Expand Up @@ -2284,16 +2308,21 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef {
let my_path = vec::append(/*bad*/copy *pth,
~[path_name(i.ident)]);
match i.node {
ast::item_const(_, _) => {
ast::item_const(_, expr) => {
let typ = ty::node_id_to_type(ccx.tcx, i.id);
let s = mangle_exported_name(ccx, my_path, typ);
let g = str::as_c_str(s, |buf| {
unsafe {
llvm::LLVMAddGlobal(ccx.llmod, type_of(ccx, typ), buf)
}
});
ccx.item_symbols.insert(i.id, s);
g
// We need the translated value here, because for enums the
// LLVM type is not fully determined by the Rust type.
let v = consts::const_expr(ccx, expr);
ccx.const_values.insert(id, v);
unsafe {
let llty = llvm::LLVMTypeOf(v);
let g = str::as_c_str(s, |buf| {
llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
});
ccx.item_symbols.insert(i.id, s);
g
}
}
ast::item_fn(_, purity, _, _) => {
let llfn = if purity != ast::extern_fn {
Expand Down
65 changes: 26 additions & 39 deletions src/librustc/middle/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,42 +414,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
// variant or we wouldn't have gotten here -- the constant
// checker forbids paths that don't map to C-like enum
// variants.
let ety = ty::expr_ty(cx.tcx, e);
let llty = type_of::type_of(cx, ety);

// Can't use `discrims` from the crate context here
// because those discriminants have an extra level of
// indirection, and there's no LLVM constant load
// instruction.
let mut lldiscrim_opt = None;
for ty::enum_variants(cx.tcx, enum_did).each
|variant_info| {
if variant_info.id == variant_did {
lldiscrim_opt = Some(C_int(cx,
variant_info.disr_val));
break;
}
}

let lldiscrim;
match lldiscrim_opt {
None => {
cx.tcx.sess.span_bug(e.span,
~"didn't find discriminant?!");
}
Some(found_lldiscrim) => {
lldiscrim = found_lldiscrim;
}
}
let fields = if ty::enum_is_univariant(cx.tcx, enum_did) {
~[lldiscrim]
} else {
let llstructtys =
lib::llvm::struct_element_types(llty);
~[lldiscrim, C_null(llstructtys[1])]
};

C_named_struct(llty, fields)
let lldiscrim = base::get_discrim_val(cx, e.span,
enum_did,
variant_did);
C_struct(~[lldiscrim])
}
Some(ast::def_struct(_)) => {
let ety = ty::expr_ty(cx.tcx, e);
Expand All @@ -475,6 +443,24 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
C_named_struct(llty, ~[ llstructbody ])
}
}
Some(ast::def_variant(tid, vid)) => {
let ety = ty::expr_ty(cx.tcx, e);
let degen = ty::enum_is_univariant(cx.tcx, tid);
let size = shape::static_size_of_enum(cx, ety);

let discrim = base::get_discrim_val(cx, e.span, tid, vid);
let c_args = C_struct(args.map(|a| const_expr(cx, *a)));

let fields = if !degen {
~[discrim, c_args]
} else if size == 0 {
~[discrim]
} else {
~[c_args]
};

C_struct(fields)
}
_ => cx.sess.span_bug(e.span, ~"expected a struct def")
}
}
Expand All @@ -485,12 +471,13 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
}
}

fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) {
fn trans_const(ccx: @crate_ctxt, _e: @ast::expr, id: ast::node_id) {
unsafe {
let _icx = ccx.insn_ctxt("trans_const");
let g = base::get_item_val(ccx, id);
let v = const_expr(ccx, e);
ccx.const_values.insert(id, v);
// At this point, get_item_val has already translated the
// constant's initializer to determine its LLVM type.
let v = ccx.const_values.get(id);
llvm::LLVMSetInitializer(g, v);
llvm::LLVMSetGlobalConstant(g, True);
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/middle/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,11 @@ fn trans_def_lvalue(bcx: block,
ast::def_const(did) => {
let const_ty = expr_ty(bcx, ref_expr);
let val = if did.crate == ast::local_crate {
base::get_item_val(ccx, did.node)
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
// non-C-like enums.
PointerCast(bcx, base::get_item_val(ccx, did.node),
T_ptr(type_of(bcx.ccx(), const_ty)))
} else {
base::trans_external_path(ccx, did, const_ty)
};
Expand Down
38 changes: 38 additions & 0 deletions src/test/run-pass/const-big-enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 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 <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.

enum Foo {
Bar(u32),
Baz,
Quux(u64, u16)
}

const X: Foo = Baz;

fn main() {
match X {
Baz => {}
_ => fail
}
match Y {
Bar(s) => assert(s == 2654435769),
_ => fail
}
match Z {
Quux(d,h) => {
assert(d == 0x123456789abcdef0);
assert(h == 0x1234);
}
_ => fail
}
}

const Y: Foo = Bar(2654435769);
const Z: Foo = Quux(0x123456789abcdef0, 0x1234);
25 changes: 25 additions & 0 deletions src/test/run-pass/const-enum-byref-self.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 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 <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.

enum E { V, VV(int) }
const C: E = V;

impl E {
fn method(&self) {
match *self {
V => {}
VV(*) => fail
}
}
}

fn main() {
C.method()
}
23 changes: 23 additions & 0 deletions src/test/run-pass/const-enum-byref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 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 <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.

enum E { V, VV(int) }
const C: E = V;

fn f(a: &E) {
match *a {
V => {}
VV(*) => fail
}
}

fn main() {
f(&C)
}
20 changes: 20 additions & 0 deletions src/test/run-pass/const-newtype-enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 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 <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.

enum Foo = u32;

const X: Foo = Foo(17);

fn main() {
assert(*X == 17);
assert(*Y == 23);
}

const Y: Foo = Foo(23);
7 changes: 6 additions & 1 deletion src/test/run-pass/const-nullary-enum.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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.
//
Expand All @@ -21,5 +21,10 @@ fn main() {
Bar => {}
Baz | Boo => fail
}
match Y {
Baz => {}
Bar | Boo => fail
}
}

const Y: Foo = Baz;
3 changes: 3 additions & 0 deletions src/test/run-pass/const-nullary-univariant-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ const X: Foo = Bar;

fn main() {
assert((X as uint) == 0xDEADBEE);
assert((Y as uint) == 0xDEADBEE);
}

const Y: Foo = Bar;