diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 7986352cac2..df937c92d95 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -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"); } } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 0134e1a7285..a8b0da47e9a 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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 { diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 588a64229f9..74aaec0d7e7 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -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); } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 5cbd7fa47c5..c26328d7d9c 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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) }; diff --git a/src/test/run-pass/const-big-enum.rs b/src/test/run-pass/const-big-enum.rs new file mode 100644 index 00000000000..aa977f17e69 --- /dev/null +++ b/src/test/run-pass/const-big-enum.rs @@ -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 or the MIT license +// , 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); diff --git a/src/test/run-pass/const-enum-byref-self.rs b/src/test/run-pass/const-enum-byref-self.rs new file mode 100644 index 00000000000..cd939bc14d4 --- /dev/null +++ b/src/test/run-pass/const-enum-byref-self.rs @@ -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 or the MIT license +// , 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() +} diff --git a/src/test/run-pass/const-enum-byref.rs b/src/test/run-pass/const-enum-byref.rs new file mode 100644 index 00000000000..8ee9e79670b --- /dev/null +++ b/src/test/run-pass/const-enum-byref.rs @@ -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 or the MIT license +// , 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) +} diff --git a/src/test/run-pass/const-newtype-enum.rs b/src/test/run-pass/const-newtype-enum.rs new file mode 100644 index 00000000000..069565aa4f8 --- /dev/null +++ b/src/test/run-pass/const-newtype-enum.rs @@ -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 or the MIT license +// , 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); diff --git a/src/test/run-pass/const-nullary-enum.rs b/src/test/run-pass/const-nullary-enum.rs index abdddfe1c62..098305bbe35 100644 --- a/src/test/run-pass/const-nullary-enum.rs +++ b/src/test/run-pass/const-nullary-enum.rs @@ -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; diff --git a/src/test/run-pass/const-nullary-univariant-enum.rs b/src/test/run-pass/const-nullary-univariant-enum.rs index e1db50d566b..2fa5a7760f6 100644 --- a/src/test/run-pass/const-nullary-univariant-enum.rs +++ b/src/test/run-pass/const-nullary-univariant-enum.rs @@ -16,4 +16,7 @@ const X: Foo = Bar; fn main() { assert((X as uint) == 0xDEADBEE); + assert((Y as uint) == 0xDEADBEE); } + +const Y: Foo = Bar;