Merge pull request #4459 from jld/constenum

Allow consts to be initialized by non-nullary enum constructors
This commit is contained in:
Tim Chevalier 2013-01-13 14:42:57 -08:00
commit fbc33e0247
10 changed files with 185 additions and 50 deletions

View File

@ -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");
}
}
}

View File

@ -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");
@ -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 {

View File

@ -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);
@ -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")
}
}
@ -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);
}

View File

@ -801,7 +801,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)
};

View File

@ -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);

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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);

View File

@ -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.
//
@ -21,5 +21,10 @@ fn main() {
Bar => {}
Baz | Boo => fail
}
match Y {
Baz => {}
Bar | Boo => fail
}
}
const Y: Foo = Baz;

View File

@ -16,4 +16,7 @@ const X: Foo = Bar;
fn main() {
assert((X as uint) == 0xDEADBEE);
assert((Y as uint) == 0xDEADBEE);
}
const Y: Foo = Bar;