Skip to content

Commit fbc33e0

Browse files
committed
Merge pull request #4459 from jld/constenum
Allow consts to be initialized by non-nullary enum constructors
2 parents 1b01629 + 3aca4a1 commit fbc33e0

File tree

10 files changed

+185
-50
lines changed

10 files changed

+185
-50
lines changed

src/librustc/middle/check_const.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,12 @@ fn check_expr(sess: Session, def_map: resolve::DefMap,
138138
expr_call(callee, _, false) => {
139139
match def_map.find(callee.id) {
140140
Some(def_struct(*)) => {} // OK.
141+
Some(def_variant(*)) => {} // OK.
141142
_ => {
142143
sess.span_err(
143144
e.span,
144145
~"function calls in constants are limited to \
145-
structure constructors");
146+
struct and enum constructors");
146147
}
147148
}
148149
}

src/librustc/middle/trans/base.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,30 @@ fn trans_external_path(ccx: @crate_ctxt, did: ast::def_id, t: ty::t)
779779
};
780780
}
781781
782+
fn get_discrim_val(cx: @crate_ctxt, span: span, enum_did: ast::def_id,
783+
variant_did: ast::def_id) -> ValueRef {
784+
// Can't use `discrims` from the crate context here because
785+
// those discriminants have an extra level of indirection,
786+
// and there's no LLVM constant load instruction.
787+
let mut lldiscrim_opt = None;
788+
for ty::enum_variants(cx.tcx, enum_did).each |variant_info| {
789+
if variant_info.id == variant_did {
790+
lldiscrim_opt = Some(C_int(cx,
791+
variant_info.disr_val));
792+
break;
793+
}
794+
}
795+
796+
match lldiscrim_opt {
797+
None => {
798+
cx.tcx.sess.span_bug(span, ~"didn't find discriminant?!");
799+
}
800+
Some(found_lldiscrim) => {
801+
found_lldiscrim
802+
}
803+
}
804+
}
805+
782806
fn lookup_discriminant(ccx: @crate_ctxt, vid: ast::def_id) -> ValueRef {
783807
unsafe {
784808
let _icx = ccx.insn_ctxt("lookup_discriminant");
@@ -2284,16 +2308,21 @@ fn get_item_val(ccx: @crate_ctxt, id: ast::node_id) -> ValueRef {
22842308
let my_path = vec::append(/*bad*/copy *pth,
22852309
~[path_name(i.ident)]);
22862310
match i.node {
2287-
ast::item_const(_, _) => {
2311+
ast::item_const(_, expr) => {
22882312
let typ = ty::node_id_to_type(ccx.tcx, i.id);
22892313
let s = mangle_exported_name(ccx, my_path, typ);
2290-
let g = str::as_c_str(s, |buf| {
2291-
unsafe {
2292-
llvm::LLVMAddGlobal(ccx.llmod, type_of(ccx, typ), buf)
2293-
}
2294-
});
2295-
ccx.item_symbols.insert(i.id, s);
2296-
g
2314+
// We need the translated value here, because for enums the
2315+
// LLVM type is not fully determined by the Rust type.
2316+
let v = consts::const_expr(ccx, expr);
2317+
ccx.const_values.insert(id, v);
2318+
unsafe {
2319+
let llty = llvm::LLVMTypeOf(v);
2320+
let g = str::as_c_str(s, |buf| {
2321+
llvm::LLVMAddGlobal(ccx.llmod, llty, buf)
2322+
});
2323+
ccx.item_symbols.insert(i.id, s);
2324+
g
2325+
}
22972326
}
22982327
ast::item_fn(_, purity, _, _) => {
22992328
let llfn = if purity != ast::extern_fn {

src/librustc/middle/trans/consts.rs

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -414,42 +414,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
414414
// variant or we wouldn't have gotten here -- the constant
415415
// checker forbids paths that don't map to C-like enum
416416
// variants.
417-
let ety = ty::expr_ty(cx.tcx, e);
418-
let llty = type_of::type_of(cx, ety);
419-
420-
// Can't use `discrims` from the crate context here
421-
// because those discriminants have an extra level of
422-
// indirection, and there's no LLVM constant load
423-
// instruction.
424-
let mut lldiscrim_opt = None;
425-
for ty::enum_variants(cx.tcx, enum_did).each
426-
|variant_info| {
427-
if variant_info.id == variant_did {
428-
lldiscrim_opt = Some(C_int(cx,
429-
variant_info.disr_val));
430-
break;
431-
}
432-
}
433-
434-
let lldiscrim;
435-
match lldiscrim_opt {
436-
None => {
437-
cx.tcx.sess.span_bug(e.span,
438-
~"didn't find discriminant?!");
439-
}
440-
Some(found_lldiscrim) => {
441-
lldiscrim = found_lldiscrim;
442-
}
443-
}
444-
let fields = if ty::enum_is_univariant(cx.tcx, enum_did) {
445-
~[lldiscrim]
446-
} else {
447-
let llstructtys =
448-
lib::llvm::struct_element_types(llty);
449-
~[lldiscrim, C_null(llstructtys[1])]
450-
};
451-
452-
C_named_struct(llty, fields)
417+
let lldiscrim = base::get_discrim_val(cx, e.span,
418+
enum_did,
419+
variant_did);
420+
C_struct(~[lldiscrim])
453421
}
454422
Some(ast::def_struct(_)) => {
455423
let ety = ty::expr_ty(cx.tcx, e);
@@ -475,6 +443,24 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
475443
C_named_struct(llty, ~[ llstructbody ])
476444
}
477445
}
446+
Some(ast::def_variant(tid, vid)) => {
447+
let ety = ty::expr_ty(cx.tcx, e);
448+
let degen = ty::enum_is_univariant(cx.tcx, tid);
449+
let size = shape::static_size_of_enum(cx, ety);
450+
451+
let discrim = base::get_discrim_val(cx, e.span, tid, vid);
452+
let c_args = C_struct(args.map(|a| const_expr(cx, *a)));
453+
454+
let fields = if !degen {
455+
~[discrim, c_args]
456+
} else if size == 0 {
457+
~[discrim]
458+
} else {
459+
~[c_args]
460+
};
461+
462+
C_struct(fields)
463+
}
478464
_ => cx.sess.span_bug(e.span, ~"expected a struct def")
479465
}
480466
}
@@ -485,12 +471,13 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
485471
}
486472
}
487473

488-
fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) {
474+
fn trans_const(ccx: @crate_ctxt, _e: @ast::expr, id: ast::node_id) {
489475
unsafe {
490476
let _icx = ccx.insn_ctxt("trans_const");
491477
let g = base::get_item_val(ccx, id);
492-
let v = const_expr(ccx, e);
493-
ccx.const_values.insert(id, v);
478+
// At this point, get_item_val has already translated the
479+
// constant's initializer to determine its LLVM type.
480+
let v = ccx.const_values.get(id);
494481
llvm::LLVMSetInitializer(g, v);
495482
llvm::LLVMSetGlobalConstant(g, True);
496483
}

src/librustc/middle/trans/expr.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,11 @@ fn trans_def_lvalue(bcx: block,
801801
ast::def_const(did) => {
802802
let const_ty = expr_ty(bcx, ref_expr);
803803
let val = if did.crate == ast::local_crate {
804-
base::get_item_val(ccx, did.node)
804+
// The LLVM global has the type of its initializer,
805+
// which may not be equal to the enum's type for
806+
// non-C-like enums.
807+
PointerCast(bcx, base::get_item_val(ccx, did.node),
808+
T_ptr(type_of(bcx.ccx(), const_ty)))
805809
} else {
806810
base::trans_external_path(ccx, did, const_ty)
807811
};

src/test/run-pass/const-big-enum.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Foo {
12+
Bar(u32),
13+
Baz,
14+
Quux(u64, u16)
15+
}
16+
17+
const X: Foo = Baz;
18+
19+
fn main() {
20+
match X {
21+
Baz => {}
22+
_ => fail
23+
}
24+
match Y {
25+
Bar(s) => assert(s == 2654435769),
26+
_ => fail
27+
}
28+
match Z {
29+
Quux(d,h) => {
30+
assert(d == 0x123456789abcdef0);
31+
assert(h == 0x1234);
32+
}
33+
_ => fail
34+
}
35+
}
36+
37+
const Y: Foo = Bar(2654435769);
38+
const Z: Foo = Quux(0x123456789abcdef0, 0x1234);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum E { V, VV(int) }
12+
const C: E = V;
13+
14+
impl E {
15+
fn method(&self) {
16+
match *self {
17+
V => {}
18+
VV(*) => fail
19+
}
20+
}
21+
}
22+
23+
fn main() {
24+
C.method()
25+
}

src/test/run-pass/const-enum-byref.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum E { V, VV(int) }
12+
const C: E = V;
13+
14+
fn f(a: &E) {
15+
match *a {
16+
V => {}
17+
VV(*) => fail
18+
}
19+
}
20+
21+
fn main() {
22+
f(&C)
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Foo = u32;
12+
13+
const X: Foo = Foo(17);
14+
15+
fn main() {
16+
assert(*X == 17);
17+
assert(*Y == 23);
18+
}
19+
20+
const Y: Foo = Foo(23);

src/test/run-pass/const-nullary-enum.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
22
// file at the top-level directory of this distribution and at
33
// http://rust-lang.org/COPYRIGHT.
44
//
@@ -21,5 +21,10 @@ fn main() {
2121
Bar => {}
2222
Baz | Boo => fail
2323
}
24+
match Y {
25+
Baz => {}
26+
Bar | Boo => fail
27+
}
2428
}
2529

30+
const Y: Foo = Baz;

src/test/run-pass/const-nullary-univariant-enum.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ const X: Foo = Bar;
1616

1717
fn main() {
1818
assert((X as uint) == 0xDEADBEE);
19+
assert((Y as uint) == 0xDEADBEE);
1920
}
21+
22+
const Y: Foo = Bar;

0 commit comments

Comments
 (0)