From 452642422dda045b89cbcb2b7d011c85c5202d5d Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 30 Dec 2012 20:29:17 -0800 Subject: [PATCH 1/6] Factor out base::get_discrim_val from const translation --- src/librustc/middle/trans/base.rs | 24 ++++++++++++++++++++++++ src/librustc/middle/trans/consts.rs | 27 +++------------------------ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 0134e1a7285a1..a02b8314ff3a1 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"); diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 588a64229f955..421d1981c415e 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -416,31 +416,10 @@ fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef { // variants. let ety = ty::expr_ty(cx.tcx, e); let llty = type_of::type_of(cx, ety); + let lldiscrim = base::get_discrim_val(cx, e.span, + enum_did, + variant_did); - // 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 { From f76e28aa1c093c59671749006e1a7ae203d66b7c Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 30 Dec 2012 20:30:23 -0800 Subject: [PATCH 2/6] Allow consts' LLVM types to depend on their initializers. Loosening the connection between the LLVM type and the Rust type is necessary to use non-nullary enum constructors as const initializers, because the const needs to be initialized with data of the actual type of the variant in question, which is (generally) not the same as the u8 array in the `type_of` type. Thus, referring to a const now requires casting the LLVM global to the expected pointer type instead of using it as-is. --- src/librustc/middle/trans/base.rs | 21 +++++++++++++-------- src/librustc/middle/trans/consts.rs | 7 ++++--- src/librustc/middle/trans/expr.rs | 6 +++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index a02b8314ff3a1..a8b0da47e9ac2 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2308,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 421d1981c415e..baa774724047f 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -464,12 +464,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 a13aa73f330d1..6b6db71726988 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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) }; From abae61257c4d866bb321bfb80ad16b7531736f7e Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 30 Dec 2012 20:39:15 -0800 Subject: [PATCH 3/6] Allow consts to be non-nullary enum constructors --- src/librustc/middle/check_const.rs | 3 ++- src/librustc/middle/trans/consts.rs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 7986352cac28b..df937c92d95db 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/consts.rs b/src/librustc/middle/trans/consts.rs index baa774724047f..23b4bb32eeaac 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -454,6 +454,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") } } From 349fa1e550905577fd9545aada328fb0806cf641 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Sun, 30 Dec 2012 20:51:53 -0800 Subject: [PATCH 4/6] Omit needless zeroes for C-like variants of non-C-like enums --- src/librustc/middle/trans/consts.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index 23b4bb32eeaac..74aaec0d7e73f 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -414,21 +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); let lldiscrim = base::get_discrim_val(cx, e.span, enum_did, variant_did); - - 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) + C_struct(~[lldiscrim]) } Some(ast::def_struct(_)) => { let ety = ty::expr_ty(cx.tcx, e); From 79f0d67d2801f17c143b38926f05a40d0c22f5c0 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Fri, 4 Jan 2013 01:22:56 -0800 Subject: [PATCH 5/6] Add more tests for enum constants. The tests have consts defined both before and after their uses in order to prevent bugs that depend on the order in which they are translated. --- src/test/run-pass/const-big-enum.rs | 38 +++++++++++++++++++ src/test/run-pass/const-newtype-enum.rs | 20 ++++++++++ src/test/run-pass/const-nullary-enum.rs | 7 +++- .../run-pass/const-nullary-univariant-enum.rs | 3 ++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/const-big-enum.rs create mode 100644 src/test/run-pass/const-newtype-enum.rs 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 0000000000000..aa977f17e691e --- /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-newtype-enum.rs b/src/test/run-pass/const-newtype-enum.rs new file mode 100644 index 0000000000000..069565aa4f858 --- /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 abdddfe1c62c2..098305bbe35e0 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 e1db50d566b43..2fa5a7760f6d1 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; From 3aca4a166331e26a12f14e619c78ddd5ef590f70 Mon Sep 17 00:00:00 2001 From: Jed Davis Date: Mon, 7 Jan 2013 02:07:56 -0800 Subject: [PATCH 6/6] Regression tests for passing enum-typed consts by reference. If the PointerCast in trans_def_lvalue is removed, these cases cause LLVM assertion failures. --- src/test/run-pass/const-enum-byref-self.rs | 25 ++++++++++++++++++++++ src/test/run-pass/const-enum-byref.rs | 23 ++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/test/run-pass/const-enum-byref-self.rs create mode 100644 src/test/run-pass/const-enum-byref.rs 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 0000000000000..cd939bc14d46e --- /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 0000000000000..8ee9e79670b4c --- /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) +}