Skip to content
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

auto: Fix assorted issues with enums in constants. #4745

Closed
wants to merge 9 commits into from
1 change: 1 addition & 0 deletions src/librustc/lib/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ pub extern mod llvm {
pub unsafe fn LLVMGetUsedValue(U: UseRef) -> ValueRef;

/* Operations on Users */
pub unsafe fn LLVMGetNumOperands(Val: ValueRef) -> c_int;
pub unsafe fn LLVMGetOperand(Val: ValueRef, Index: c_uint) -> ValueRef;
pub unsafe fn LLVMSetOperand(Val: ValueRef, Index: c_uint, Op: ValueRef);

Expand Down
66 changes: 44 additions & 22 deletions src/librustc/middle/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,15 @@ pub fn const_vec(cx: @crate_ctxt, e: @ast::expr, es: &[@ast::expr])
let vec_ty = ty::expr_ty(cx.tcx, e);
let unit_ty = ty::sequence_element_type(cx.tcx, vec_ty);
let llunitty = type_of::type_of(cx, unit_ty);
let v = C_array(llunitty, es.map(|e| const_expr(cx, *e)));
let unit_sz = machine::llsize_of(cx, llunitty);
let sz = llvm::LLVMConstMul(C_uint(cx, es.len()), unit_sz);
let vs = es.map(|e| const_expr(cx, *e));
// If the vector contains enums, an LLVM array won't work.
let v = if vs.any(|vi| val_ty(*vi) != llunitty) {
C_struct(vs)
} else {
C_array(llunitty, vs)
};
return (v, sz, llunitty);
}
}
Expand Down Expand Up @@ -279,15 +285,17 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
// call. Despite that being "a const", it's not the kind of
// const you can ask for the integer-value of, evidently. This
// might be an LLVM bug, not sure. In any case, to work around
// this we drop down to the array-type level here and just ask
// how long the array-type itself is, ignoring the length we
// pulled out of the slice. This in turn only works because we
// picked out the original globalvar via const_deref and so can
// recover the array-size of the underlying array, and all this
// will hold together exactly as long as we _don't_ support
// const sub-slices (that is, slices that represent something
// other than a whole array). At that point we'll have more and
// uglier work to do here, but for now this should work.
// this we obtain the initializer and count how many elements it
// has, ignoring the length we pulled out of the slice. (Note
// that the initializer might be a struct rather than an array,
// if enums are involved.) This only works because we picked out
// the original globalvar via const_deref and so can recover the
// array-size of the underlying array (or the element count of
// the underlying struct), and all this will hold together
// exactly as long as we _don't_ support const sub-slices (that
// is, slices that represent something other than a whole
// array). At that point we'll have more and uglier work to do
// here, but for now this should work.
//
// In the future, what we should be doing here is the
// moral equivalent of:
Expand All @@ -299,7 +307,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
// not want to consider sizeof() a constant expression
// we can get the value (as a number) out of.

let len = llvm::LLVMGetArrayLength(val_ty(arr)) as u64;
let len = llvm::LLVMGetNumOperands(arr) as u64;
let len = match ty::get(bt).sty {
ty::ty_estr(*) => {assert len > 0; len - 1},
_ => len
Expand Down Expand Up @@ -346,10 +354,8 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
}
ast::expr_addr_of(ast::m_imm, sub) => {
let cv = const_expr(cx, sub);
let subty = ty::expr_ty(cx.tcx, sub),
llty = type_of::type_of(cx, subty);
let gv = do str::as_c_str("const") |name| {
llvm::LLVMAddGlobal(cx.llmod, llty, name)
llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name)
};
llvm::LLVMSetInitializer(gv, cv);
llvm::LLVMSetGlobalConstant(gv, True);
Expand Down Expand Up @@ -377,8 +383,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
}
})
};
let llty = type_of::type_of(cx, ety);
C_named_struct(llty, [C_struct(cs)])
C_struct([C_struct(cs)])
}
ast::expr_vec(es, ast::m_imm) => {
let (v, _, _) = const_vec(cx, e, es);
Expand Down Expand Up @@ -434,7 +439,13 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
let lldiscrim = base::get_discrim_val(cx, e.span,
enum_did,
variant_did);
C_struct(~[lldiscrim])
// However, we still have to pad it out to the
// size of the full enum; see the expr_call case,
// below.
let ety = ty::expr_ty(cx.tcx, e);
let size = machine::static_size_of_enum(cx, ety);
let padding = C_null(T_array(T_i8(), size));
C_struct(~[lldiscrim, padding])
}
Some(ast::def_struct(_)) => {
let ety = ty::expr_ty(cx.tcx, e);
Expand All @@ -450,14 +461,12 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
ast::expr_call(callee, args, _) => {
match cx.tcx.def_map.find(&callee.id) {
Some(ast::def_struct(def_id)) => {
let ety = ty::expr_ty(cx.tcx, e);
let llty = type_of::type_of(cx, ety);
let llstructbody =
C_struct(args.map(|a| const_expr(cx, *a)));
if ty::ty_dtor(cx.tcx, def_id).is_present() {
C_named_struct(llty, ~[ llstructbody, C_u8(0) ])
C_struct(~[ llstructbody, C_u8(0) ])
} else {
C_named_struct(llty, ~[ llstructbody ])
C_struct(~[ llstructbody ])
}
}
Some(ast::def_variant(tid, vid)) => {
Expand All @@ -470,7 +479,20 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {

// FIXME (#1645): enum body alignment is generaly wrong.
if !degen {
C_packed_struct(~[discrim, c_args])
// Pad out the data to the size of its type_of;
// this is necessary if the enum is contained
// within an aggregate (tuple, struct, vector) so
// that the next element is at the right offset.
let actual_size =
machine::llsize_of_real(cx, llvm::LLVMTypeOf(c_args));
let padding =
C_null(T_array(T_i8(), size - actual_size));
// A packed_struct has an alignment of 1; thus,
// wrapping one around c_args will misalign it the
// same way we normally misalign enum bodies
// without affecting its internal alignment or
// changing the alignment of the enum.
C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
} else if size == 0 {
C_struct(~[discrim])
} else {
Expand Down
19 changes: 19 additions & 0 deletions src/test/run-pass/const-enum-ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 { V0, V1(int) }
const C: &static/E = &V0;

fn main() {
match *C {
V0 => (),
_ => die!()
}
}
19 changes: 19 additions & 0 deletions src/test/run-pass/const-enum-struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 { V16(u16), V32(u32) }
struct S { a: E, b: u16, c: u16 }
const C: S = S { a: V16(0xDEAD), b: 0x600D, c: 0xBAD };

fn main() {
let n = C.b;
assert n != 0xBAD;
assert n == 0x600D;
}
19 changes: 19 additions & 0 deletions src/test/run-pass/const-enum-struct2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 { V0, V16(u16) }
struct S { a: E, b: u16, c: u16 }
const C: S = S { a: V0, b: 0x600D, c: 0xBAD };

fn main() {
let n = C.b;
assert n != 0xBAD;
assert n == 0x600D;
}
18 changes: 18 additions & 0 deletions src/test/run-pass/const-enum-tuple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 { V16(u16), V32(u32) }
const C: (E, u16, u16) = (V16(0xDEAD), 0x600D, 0xBAD);

fn main() {
let (_, n, _) = C;
assert n != 0xBAD;
assert n == 0x600D;
}
18 changes: 18 additions & 0 deletions src/test/run-pass/const-enum-tuple2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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 { V0, V16(u16) }
const C: (E, u16, u16) = (V0, 0x600D, 0xBAD);

fn main() {
let (_, n, _) = C;
assert n != 0xBAD;
assert n == 0x600D;
}
19 changes: 19 additions & 0 deletions src/test/run-pass/const-enum-tuplestruct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 { V16(u16), V32(u32) }
struct S(E, u16, u16);
const C: S = S(V16(0xDEAD), 0x600D, 0xBAD);

fn main() {
let S(_, n, _) = C;
assert n != 0xBAD;
assert n == 0x600D;
}
19 changes: 19 additions & 0 deletions src/test/run-pass/const-enum-tuplestruct2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// 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 { V0, V16(u16) }
struct S(E, u16, u16);
const C: S = S(V0, 0x600D, 0xBAD);

fn main() {
let S(_, n, _) = C;
assert n != 0xBAD;
assert n == 0x600D;
}
25 changes: 25 additions & 0 deletions src/test/run-pass/const-enum-vec-index.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 { V1(int), V0 }
const C: &[E] = &[V0, V1(0xDEADBEE)];
const C0: E = C[0];
const C1: E = C[1];

fn main() {
match C0 {
V0 => (),
_ => die!()
}
match C1 {
V1(n) => assert(n == 0xDEADBEE),
_ => die!()
}
}
23 changes: 23 additions & 0 deletions src/test/run-pass/const-enum-vec-ptr.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 { V1(int), V0 }
const C: &static/[E] = &[V0, V1(0xDEADBEE), V0];

fn main() {
match C[1] {
V1(n) => assert(n == 0xDEADBEE),
_ => die!()
}
match C[2] {
V0 => (),
_ => die!()
}
}
23 changes: 23 additions & 0 deletions src/test/run-pass/const-enum-vector.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 { V1(int), V0 }
const C: [E * 3] = [V0, V1(0xDEADBEE), V0];

fn main() {
match C[1] {
V1(n) => assert(n == 0xDEADBEE),
_ => die!()
}
match C[2] {
V0 => (),
_ => die!()
}
}