Skip to content

Commit bc1fb62

Browse files
committed
auto merge of #4745 : jld/rust/constenum-bis, r=graydon
There were a bunch of problems with consts where an enum was contained within some other type (vector, tuple, struct, etc.); some of these would cause LLVM assertion failures, and some would silently read from the wrong address. These changes should fix all of that. It would be good if someone with access to a win32 host could do the equivalent of `make check-stageN-rpass TESTNAME=enum` on that platform before merging this.
2 parents 6647a34 + e89d985 commit bc1fb62

12 files changed

+247
-22
lines changed

src/librustc/lib/llvm.rs

+1
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ pub extern mod llvm {
382382
pub unsafe fn LLVMGetUsedValue(U: UseRef) -> ValueRef;
383383

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

src/librustc/middle/trans/consts.rs

+44-22
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,15 @@ pub fn const_vec(cx: @crate_ctxt, e: @ast::expr, es: &[@ast::expr])
7373
let vec_ty = ty::expr_ty(cx.tcx, e);
7474
let unit_ty = ty::sequence_element_type(cx.tcx, vec_ty);
7575
let llunitty = type_of::type_of(cx, unit_ty);
76-
let v = C_array(llunitty, es.map(|e| const_expr(cx, *e)));
7776
let unit_sz = machine::llsize_of(cx, llunitty);
7877
let sz = llvm::LLVMConstMul(C_uint(cx, es.len()), unit_sz);
78+
let vs = es.map(|e| const_expr(cx, *e));
79+
// If the vector contains enums, an LLVM array won't work.
80+
let v = if vs.any(|vi| val_ty(*vi) != llunitty) {
81+
C_struct(vs)
82+
} else {
83+
C_array(llunitty, vs)
84+
};
7985
return (v, sz, llunitty);
8086
}
8187
}
@@ -279,15 +285,17 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
279285
// call. Despite that being "a const", it's not the kind of
280286
// const you can ask for the integer-value of, evidently. This
281287
// might be an LLVM bug, not sure. In any case, to work around
282-
// this we drop down to the array-type level here and just ask
283-
// how long the array-type itself is, ignoring the length we
284-
// pulled out of the slice. This in turn only works because we
285-
// picked out the original globalvar via const_deref and so can
286-
// recover the array-size of the underlying array, and all this
287-
// will hold together exactly as long as we _don't_ support
288-
// const sub-slices (that is, slices that represent something
289-
// other than a whole array). At that point we'll have more and
290-
// uglier work to do here, but for now this should work.
288+
// this we obtain the initializer and count how many elements it
289+
// has, ignoring the length we pulled out of the slice. (Note
290+
// that the initializer might be a struct rather than an array,
291+
// if enums are involved.) This only works because we picked out
292+
// the original globalvar via const_deref and so can recover the
293+
// array-size of the underlying array (or the element count of
294+
// the underlying struct), and all this will hold together
295+
// exactly as long as we _don't_ support const sub-slices (that
296+
// is, slices that represent something other than a whole
297+
// array). At that point we'll have more and uglier work to do
298+
// here, but for now this should work.
291299
//
292300
// In the future, what we should be doing here is the
293301
// moral equivalent of:
@@ -299,7 +307,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
299307
// not want to consider sizeof() a constant expression
300308
// we can get the value (as a number) out of.
301309

302-
let len = llvm::LLVMGetArrayLength(val_ty(arr)) as u64;
310+
let len = llvm::LLVMGetNumOperands(arr) as u64;
303311
let len = match ty::get(bt).sty {
304312
ty::ty_estr(*) => {assert len > 0; len - 1},
305313
_ => len
@@ -346,10 +354,8 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
346354
}
347355
ast::expr_addr_of(ast::m_imm, sub) => {
348356
let cv = const_expr(cx, sub);
349-
let subty = ty::expr_ty(cx.tcx, sub),
350-
llty = type_of::type_of(cx, subty);
351357
let gv = do str::as_c_str("const") |name| {
352-
llvm::LLVMAddGlobal(cx.llmod, llty, name)
358+
llvm::LLVMAddGlobal(cx.llmod, val_ty(cv), name)
353359
};
354360
llvm::LLVMSetInitializer(gv, cv);
355361
llvm::LLVMSetGlobalConstant(gv, True);
@@ -377,8 +383,7 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
377383
}
378384
})
379385
};
380-
let llty = type_of::type_of(cx, ety);
381-
C_named_struct(llty, [C_struct(cs)])
386+
C_struct([C_struct(cs)])
382387
}
383388
ast::expr_vec(es, ast::m_imm) => {
384389
let (v, _, _) = const_vec(cx, e, es);
@@ -434,7 +439,13 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
434439
let lldiscrim = base::get_discrim_val(cx, e.span,
435440
enum_did,
436441
variant_did);
437-
C_struct(~[lldiscrim])
442+
// However, we still have to pad it out to the
443+
// size of the full enum; see the expr_call case,
444+
// below.
445+
let ety = ty::expr_ty(cx.tcx, e);
446+
let size = machine::static_size_of_enum(cx, ety);
447+
let padding = C_null(T_array(T_i8(), size));
448+
C_struct(~[lldiscrim, padding])
438449
}
439450
Some(ast::def_struct(_)) => {
440451
let ety = ty::expr_ty(cx.tcx, e);
@@ -450,14 +461,12 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
450461
ast::expr_call(callee, args, _) => {
451462
match cx.tcx.def_map.find(&callee.id) {
452463
Some(ast::def_struct(def_id)) => {
453-
let ety = ty::expr_ty(cx.tcx, e);
454-
let llty = type_of::type_of(cx, ety);
455464
let llstructbody =
456465
C_struct(args.map(|a| const_expr(cx, *a)));
457466
if ty::ty_dtor(cx.tcx, def_id).is_present() {
458-
C_named_struct(llty, ~[ llstructbody, C_u8(0) ])
467+
C_struct(~[ llstructbody, C_u8(0) ])
459468
} else {
460-
C_named_struct(llty, ~[ llstructbody ])
469+
C_struct(~[ llstructbody ])
461470
}
462471
}
463472
Some(ast::def_variant(tid, vid)) => {
@@ -470,7 +479,20 @@ pub fn const_expr(cx: @crate_ctxt, e: @ast::expr) -> ValueRef {
470479

471480
// FIXME (#1645): enum body alignment is generaly wrong.
472481
if !degen {
473-
C_packed_struct(~[discrim, c_args])
482+
// Pad out the data to the size of its type_of;
483+
// this is necessary if the enum is contained
484+
// within an aggregate (tuple, struct, vector) so
485+
// that the next element is at the right offset.
486+
let actual_size =
487+
machine::llsize_of_real(cx, llvm::LLVMTypeOf(c_args));
488+
let padding =
489+
C_null(T_array(T_i8(), size - actual_size));
490+
// A packed_struct has an alignment of 1; thus,
491+
// wrapping one around c_args will misalign it the
492+
// same way we normally misalign enum bodies
493+
// without affecting its internal alignment or
494+
// changing the alignment of the enum.
495+
C_struct(~[discrim, C_packed_struct(~[c_args]), padding])
474496
} else if size == 0 {
475497
C_struct(~[discrim])
476498
} else {

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

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 { V0, V1(int) }
12+
const C: &static/E = &V0;
13+
14+
fn main() {
15+
match *C {
16+
V0 => (),
17+
_ => die!()
18+
}
19+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 { V16(u16), V32(u32) }
12+
struct S { a: E, b: u16, c: u16 }
13+
const C: S = S { a: V16(0xDEAD), b: 0x600D, c: 0xBAD };
14+
15+
fn main() {
16+
let n = C.b;
17+
assert n != 0xBAD;
18+
assert n == 0x600D;
19+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 { V0, V16(u16) }
12+
struct S { a: E, b: u16, c: u16 }
13+
const C: S = S { a: V0, b: 0x600D, c: 0xBAD };
14+
15+
fn main() {
16+
let n = C.b;
17+
assert n != 0xBAD;
18+
assert n == 0x600D;
19+
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 { V16(u16), V32(u32) }
12+
const C: (E, u16, u16) = (V16(0xDEAD), 0x600D, 0xBAD);
13+
14+
fn main() {
15+
let (_, n, _) = C;
16+
assert n != 0xBAD;
17+
assert n == 0x600D;
18+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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 { V0, V16(u16) }
12+
const C: (E, u16, u16) = (V0, 0x600D, 0xBAD);
13+
14+
fn main() {
15+
let (_, n, _) = C;
16+
assert n != 0xBAD;
17+
assert n == 0x600D;
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 { V16(u16), V32(u32) }
12+
struct S(E, u16, u16);
13+
const C: S = S(V16(0xDEAD), 0x600D, 0xBAD);
14+
15+
fn main() {
16+
let S(_, n, _) = C;
17+
assert n != 0xBAD;
18+
assert n == 0x600D;
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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 { V0, V16(u16) }
12+
struct S(E, u16, u16);
13+
const C: S = S(V0, 0x600D, 0xBAD);
14+
15+
fn main() {
16+
let S(_, n, _) = C;
17+
assert n != 0xBAD;
18+
assert n == 0x600D;
19+
}
+25
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 { V1(int), V0 }
12+
const C: &[E] = &[V0, V1(0xDEADBEE)];
13+
const C0: E = C[0];
14+
const C1: E = C[1];
15+
16+
fn main() {
17+
match C0 {
18+
V0 => (),
19+
_ => die!()
20+
}
21+
match C1 {
22+
V1(n) => assert(n == 0xDEADBEE),
23+
_ => die!()
24+
}
25+
}
+23
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 { V1(int), V0 }
12+
const C: &static/[E] = &[V0, V1(0xDEADBEE), V0];
13+
14+
fn main() {
15+
match C[1] {
16+
V1(n) => assert(n == 0xDEADBEE),
17+
_ => die!()
18+
}
19+
match C[2] {
20+
V0 => (),
21+
_ => die!()
22+
}
23+
}
+23
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 { V1(int), V0 }
12+
const C: [E * 3] = [V0, V1(0xDEADBEE), V0];
13+
14+
fn main() {
15+
match C[1] {
16+
V1(n) => assert(n == 0xDEADBEE),
17+
_ => die!()
18+
}
19+
match C[2] {
20+
V0 => (),
21+
_ => die!()
22+
}
23+
}

0 commit comments

Comments
 (0)