Initial implementation of enum discrimnant sizing.

Allows an enum with a discriminant to use any of the primitive integer
types to store it.  By default the smallest usable type is chosen, but
this can be overridden with an attribute: `#[repr(int)]` etc., or
`#[repr(C)]` to match the target's C ABI for the equivalent C enum.

This commit breaks a few things, due to transmutes that now no longer
match in size, or u8 enums being passed to C that expects int, or
reflection; later commits on this branch fix them.
This commit is contained in:
Jed Davis 2013-06-30 22:42:30 -07:00
parent f1124a2f55
commit 01740acd5a
5 changed files with 253 additions and 78 deletions

View File

@ -14,7 +14,8 @@
* This module determines how to represent enums, structs, and tuples * This module determines how to represent enums, structs, and tuples
* based on their monomorphized types; it is responsible both for * based on their monomorphized types; it is responsible both for
* choosing a representation and translating basic operations on * choosing a representation and translating basic operations on
* values of those types. * values of those types. (Note: exporting the representations for
* debuggers is handled in debuginfo.rs, not here.)
* *
* Note that the interface treats everything as a general case of an * Note that the interface treats everything as a general case of an
* enum, so structs/tuples/etc. have one pseudo-variant with * enum, so structs/tuples/etc. have one pseudo-variant with
@ -29,8 +30,6 @@
* that might contain one and adjust GEP indices accordingly. See * that might contain one and adjust GEP indices accordingly. See
* issue #4578. * issue #4578.
* *
* - Using smaller integer types for discriminants.
*
* - Store nested enums' discriminants in the same word. Rather, if * - Store nested enums' discriminants in the same word. Rather, if
* some variants start with enums, and those enums representations * some variants start with enums, and those enums representations
* have unused alignment padding between discriminant and body, the * have unused alignment padding between discriminant and body, the
@ -56,16 +55,21 @@ use middle::trans::machine;
use middle::trans::type_of; use middle::trans::type_of;
use middle::ty; use middle::ty;
use middle::ty::Disr; use middle::ty::Disr;
use syntax::abi::{X86, X86_64, Arm, Mips};
use syntax::ast; use syntax::ast;
use syntax::attr;
use syntax::attr::IntType;
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
use middle::trans::type_::Type; use middle::trans::type_::Type;
type Hint = attr::ReprAttr;
/// Representations. /// Representations.
pub enum Repr { pub enum Repr {
/// C-like enums; basically an int. /// C-like enums; basically an int.
CEnum(Disr, Disr), // discriminant range CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
/** /**
* Single-case variants, and structs/tuples/records. * Single-case variants, and structs/tuples/records.
* *
@ -78,7 +82,7 @@ pub enum Repr {
* General-case enums: for each case there is a struct, and they * General-case enums: for each case there is a struct, and they
* all start with a field for the discriminant. * all start with a field for the discriminant.
*/ */
General(~[Struct]), General(IntType, ~[Struct]),
/** /**
* Two cases distinguished by a nullable pointer: the case with discriminant * Two cases distinguished by a nullable pointer: the case with discriminant
* `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th * `nndiscr` is represented by the struct `nonnull`, where the `ptrfield`th
@ -166,11 +170,19 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
if cases.iter().all(|c| c.tys.len() == 0) { if cases.iter().all(|c| c.tys.len() == 0) {
// All bodies empty -> intlike // All bodies empty -> intlike
let discrs = cases.map(|c| c.discr); let discrs = cases.map(|c| c.discr);
return CEnum(*discrs.iter().min().unwrap(), *discrs.iter().max().unwrap()); let hint = ty::lookup_repr_hint(cx.tcx, def_id);
let bounds = IntBounds {
ulo: *discrs.iter().min().unwrap(),
uhi: *discrs.iter().max().unwrap(),
slo: discrs.iter().map(|n| *n as i64).min().unwrap(),
shi: discrs.iter().map(|n| *n as i64).max().unwrap()
};
return mk_cenum(cx, hint, &bounds);
} }
if cases.len() == 1 { if cases.len() == 1 {
// Equivalent to a struct/tuple/newtype. // Equivalent to a struct/tuple/newtype.
// FIXME: should this conflict with a discriminant size hint?
assert_eq!(cases[0].discr, 0); assert_eq!(cases[0].discr, 0);
return Univariant(mk_struct(cx, cases[0].tys, false), false) return Univariant(mk_struct(cx, cases[0].tys, false), false)
} }
@ -185,6 +197,7 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
} }
if cases.len() == 2 { if cases.len() == 2 {
// FIXME: disable if size hint present?
let mut discr = 0; let mut discr = 0;
while discr < 2 { while discr < 2 {
if cases[1 - discr].is_zerolen(cx) { if cases[1 - discr].is_zerolen(cx) {
@ -207,8 +220,13 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
} }
// The general case. // The general case.
let discr = ~[ty::mk_uint()]; let hint = ty::lookup_repr_hint(cx.tcx, def_id);
return General(cases.map(|c| mk_struct(cx, discr + c.tys, false))) assert!((cases.len() - 1) as i64 >= 0);
let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64,
slo: 0, shi: (cases.len() - 1) as i64 };
let ity = range_to_inttype(cx, hint, &bounds);
let discr = ~[ty_of_inttype(ity)];
return General(ity, cases.map(|c| mk_struct(cx, discr + c.tys, false)))
} }
_ => cx.sess.bug("adt::represent_type called on non-ADT type") _ => cx.sess.bug("adt::represent_type called on non-ADT type")
} }
@ -225,6 +243,93 @@ fn mk_struct(cx: &mut CrateContext, tys: &[ty::t], packed: bool) -> Struct {
} }
} }
struct IntBounds {
slo: i64,
shi: i64,
ulo: u64,
uhi: u64
}
fn mk_cenum(cx: &mut CrateContext, hint: Hint, bounds: &IntBounds) -> Repr {
let it = range_to_inttype(cx, hint, bounds);
match it {
attr::SignedInt(_) => CEnum(it, bounds.slo as Disr, bounds.shi as Disr),
attr::UnsignedInt(_) => CEnum(it, bounds.ulo, bounds.uhi)
}
}
fn range_to_inttype(cx: &mut CrateContext, hint: Hint, bounds: &IntBounds) -> IntType {
debug!("range_to_inttype: {:?} {:?}", hint, bounds);
// Lists of sizes to try. u64 is always allowed as a fallback.
static choose_shortest: &'static[IntType] = &[
attr::UnsignedInt(ast::ty_u8), attr::SignedInt(ast::ty_i8),
attr::UnsignedInt(ast::ty_u16), attr::SignedInt(ast::ty_i16),
attr::UnsignedInt(ast::ty_u32), attr::SignedInt(ast::ty_i32)];
static at_least_32: &'static[IntType] = &[
attr::UnsignedInt(ast::ty_u32), attr::SignedInt(ast::ty_i32)];
let attempts;
match hint {
attr::ReprInt(span, ity) => {
if !bounds_usable(cx, ity, bounds) {
cx.sess.span_err(span, "representation hint insufficient for discriminant range")
}
return ity;
}
attr::ReprExtern => {
attempts = match cx.sess.targ_cfg.arch {
X86 | X86_64 => at_least_32,
// WARNING: the ARM EABI has two variants; the one corresponding to `at_least_32`
// appears to be used on Linux and NetBSD, but some systems may use the variant
// corresponding to `choose_shortest`. However, we don't run on those yet...?
Arm => at_least_32,
Mips => at_least_32,
}
}
attr::ReprAny => {
attempts = choose_shortest;
}
}
let mut best = attr::UnsignedInt(ast::ty_u64);
for &ity in attempts.iter() {
if bounds_usable(cx, ity, bounds) {
best = ity;
break;
}
}
return best;
}
pub fn ll_inttype(cx: &mut CrateContext, ity: IntType) -> Type {
match ity {
attr::SignedInt(t) => Type::int_from_ty(cx, t),
attr::UnsignedInt(t) => Type::uint_from_ty(cx, t)
}
}
fn bounds_usable(cx: &mut CrateContext, ity: IntType, bounds: &IntBounds) -> bool {
debug!("bounds_usable: {:?} {:?}", ity, bounds);
match ity {
attr::SignedInt(_) => {
let lllo = C_integral(ll_inttype(cx, ity), bounds.slo as u64, true);
let llhi = C_integral(ll_inttype(cx, ity), bounds.shi as u64, true);
bounds.slo == const_to_int(lllo) as i64 && bounds.shi == const_to_int(llhi) as i64
}
attr::UnsignedInt(_) => {
let lllo = C_integral(ll_inttype(cx, ity), bounds.ulo, false);
let llhi = C_integral(ll_inttype(cx, ity), bounds.uhi, false);
bounds.ulo == const_to_uint(lllo) as u64 && bounds.uhi == const_to_uint(llhi) as u64
}
}
}
fn ty_of_inttype(ity: IntType) -> ty::t {
match ity {
attr::SignedInt(t) => ty::mk_mach_int(t),
attr::UnsignedInt(t) => ty::mk_mach_uint(t)
}
}
/** /**
* Returns the fields of a struct for the given representation. * Returns the fields of a struct for the given representation.
* All nominal types are LLVM structs, in order to be able to use * All nominal types are LLVM structs, in order to be able to use
@ -239,10 +344,10 @@ pub fn sizing_fields_of(cx: &mut CrateContext, r: &Repr) -> ~[Type] {
} }
fn generic_fields_of(cx: &mut CrateContext, r: &Repr, sizing: bool) -> ~[Type] { fn generic_fields_of(cx: &mut CrateContext, r: &Repr, sizing: bool) -> ~[Type] {
match *r { match *r {
CEnum(*) => ~[Type::enum_discrim(cx)], CEnum(ity, _, _) => ~[ll_inttype(cx, ity)],
Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing), Univariant(ref st, _dtor) => struct_llfields(cx, st, sizing),
NullablePointer{ nonnull: ref st, _ } => struct_llfields(cx, st, sizing), NullablePointer{ nonnull: ref st, _ } => struct_llfields(cx, st, sizing),
General(ref sts) => { General(_ity, ref sts) => {
// To get "the" type of a general enum, we pick the case // To get "the" type of a general enum, we pick the case
// with the largest alignment (so it will always align // with the largest alignment (so it will always align
// correctly in containing structures) and pad it out. // correctly in containing structures) and pad it out.
@ -288,7 +393,7 @@ pub fn trans_switch(bcx: @mut Block, r: &Repr, scrutinee: ValueRef)
-> (_match::branch_kind, Option<ValueRef>) { -> (_match::branch_kind, Option<ValueRef>) {
match *r { match *r {
CEnum(*) | General(*) => { CEnum(*) | General(*) => {
(_match::switch, Some(trans_get_discr(bcx, r, scrutinee))) (_match::switch, Some(trans_get_discr(bcx, r, scrutinee, None)))
} }
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => { NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
(_match::switch, Some(nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee))) (_match::switch, Some(nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee)))
@ -302,16 +407,31 @@ pub fn trans_switch(bcx: @mut Block, r: &Repr, scrutinee: ValueRef)
/// Obtain the actual discriminant of a value. /// Obtain the actual discriminant of a value.
pub fn trans_get_discr(bcx: @mut Block, r: &Repr, scrutinee: ValueRef) pub fn trans_get_discr(bcx: @mut Block, r: &Repr, scrutinee: ValueRef, cast_to: Option<Type>)
-> ValueRef { -> ValueRef {
let signed;
let val;
match *r { match *r {
CEnum(min, max) => load_discr(bcx, scrutinee, min, max), CEnum(ity, min, max) => {
Univariant(*) => C_disr(bcx.ccx(), 0), val = load_discr(bcx, ity, scrutinee, min, max);
General(ref cases) => load_discr(bcx, scrutinee, 0, (cases.len() - 1) as Disr), signed = ity.is_signed();
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
ZExt(bcx, nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee),
Type::enum_discrim(bcx.ccx()))
} }
General(ity, ref cases) => {
val = load_discr(bcx, ity, scrutinee, 0, (cases.len() - 1) as Disr);
signed = ity.is_signed();
}
Univariant(*) => {
val = C_u8(0);
signed = false;
}
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
val = nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee);
signed = false;
}
}
match cast_to {
None => val,
Some(llty) => if signed { SExt(bcx, val, llty) } else { ZExt(bcx, val, llty) }
} }
} }
@ -324,10 +444,15 @@ fn nullable_bitdiscr(bcx: @mut Block, nonnull: &Struct, nndiscr: Disr, ptrfield:
} }
/// Helper for cases where the discriminant is simply loaded. /// Helper for cases where the discriminant is simply loaded.
fn load_discr(bcx: @mut Block, scrutinee: ValueRef, min: Disr, max: Disr) fn load_discr(bcx: @mut Block, ity: IntType, scrutinee: ValueRef, min: Disr, max: Disr)
-> ValueRef { -> ValueRef {
let ptr = GEPi(bcx, scrutinee, [0, 0]); let ptr = GEPi(bcx, scrutinee, [0, 0]);
if max + 1 == min { let llty = ll_inttype(bcx.ccx(), ity);
assert_eq!(val_ty(ptr), llty.ptr_to());
let bits = machine::llbitsize_of_real(bcx.ccx(), llty);
assert!(bits <= 64);
let mask = (-1u64 >> (64 - bits)) as Disr;
if (max + 1) & mask == min & mask {
// i.e., if the range is everything. The lo==hi case would be // i.e., if the range is everything. The lo==hi case would be
// rejected by the LLVM verifier (it would mean either an // rejected by the LLVM verifier (it would mean either an
// empty set, which is impossible, or the entire range of the // empty set, which is impossible, or the entire range of the
@ -350,15 +475,17 @@ fn load_discr(bcx: @mut Block, scrutinee: ValueRef, min: Disr, max: Disr)
*/ */
pub fn trans_case(bcx: @mut Block, r: &Repr, discr: Disr) -> _match::opt_result { pub fn trans_case(bcx: @mut Block, r: &Repr, discr: Disr) -> _match::opt_result {
match *r { match *r {
CEnum(*) => { CEnum(ity, _, _) => {
_match::single_result(rslt(bcx, C_disr(bcx.ccx(), discr))) _match::single_result(rslt(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
discr as u64, true)))
}
General(ity, _) => {
_match::single_result(rslt(bcx, C_integral(ll_inttype(bcx.ccx(), ity),
discr as u64, true)))
} }
Univariant(*) => { Univariant(*) => {
bcx.ccx().sess.bug("no cases for univariants or structs") bcx.ccx().sess.bug("no cases for univariants or structs")
} }
General(*) => {
_match::single_result(rslt(bcx, C_disr(bcx.ccx(), discr)))
}
NullablePointer{ _ } => { NullablePointer{ _ } => {
assert!(discr == 0 || discr == 1); assert!(discr == 0 || discr == 1);
_match::single_result(rslt(bcx, C_i1(discr != 0))) _match::single_result(rslt(bcx, C_i1(discr != 0)))
@ -373,9 +500,14 @@ pub fn trans_case(bcx: @mut Block, r: &Repr, discr: Disr) -> _match::opt_result
*/ */
pub fn trans_start_init(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr) { pub fn trans_start_init(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr) {
match *r { match *r {
CEnum(min, max) => { CEnum(ity, min, max) => {
assert!(min <= discr && discr <= max); assert_discr_in_range(ity, min, max, discr);
Store(bcx, C_disr(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
GEPi(bcx, val, [0, 0]))
}
General(ity, _) => {
Store(bcx, C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true),
GEPi(bcx, val, [0, 0]))
} }
Univariant(ref st, true) => { Univariant(ref st, true) => {
assert_eq!(discr, 0); assert_eq!(discr, 0);
@ -385,9 +517,6 @@ pub fn trans_start_init(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr) {
Univariant(*) => { Univariant(*) => {
assert_eq!(discr, 0); assert_eq!(discr, 0);
} }
General(*) => {
Store(bcx, C_disr(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
}
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => { NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
if discr != nndiscr { if discr != nndiscr {
let llptrptr = GEPi(bcx, val, [0, ptrfield]); let llptrptr = GEPi(bcx, val, [0, ptrfield]);
@ -398,6 +527,13 @@ pub fn trans_start_init(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr) {
} }
} }
fn assert_discr_in_range(ity: IntType, min: Disr, max: Disr, discr: Disr) {
match ity {
attr::UnsignedInt(_) => assert!(min <= discr && discr <= max),
attr::SignedInt(_) => assert!(min as i64 <= discr as i64 && discr as i64 <= max as i64)
}
}
/** /**
* The number of fields in a given case; for use when obtaining this * The number of fields in a given case; for use when obtaining this
* information from the type or definition is less convenient. * information from the type or definition is less convenient.
@ -409,7 +545,7 @@ pub fn num_args(r: &Repr, discr: Disr) -> uint {
assert_eq!(discr, 0); assert_eq!(discr, 0);
st.fields.len() - (if dtor { 1 } else { 0 }) st.fields.len() - (if dtor { 1 } else { 0 })
} }
General(ref cases) => cases[discr].fields.len() - 1, General(_, ref cases) => cases[discr].fields.len() - 1,
NullablePointer{ nonnull: ref nonnull, nndiscr, nullfields: ref nullfields, _ } => { NullablePointer{ nonnull: ref nonnull, nndiscr, nullfields: ref nullfields, _ } => {
if discr == nndiscr { nonnull.fields.len() } else { nullfields.len() } if discr == nndiscr { nonnull.fields.len() } else { nullfields.len() }
} }
@ -430,7 +566,7 @@ pub fn trans_field_ptr(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr,
assert_eq!(discr, 0); assert_eq!(discr, 0);
struct_field_ptr(bcx, st, val, ix, false) struct_field_ptr(bcx, st, val, ix, false)
} }
General(ref cases) => { General(_, ref cases) => {
struct_field_ptr(bcx, &cases[discr], val, ix + 1, true) struct_field_ptr(bcx, &cases[discr], val, ix + 1, true)
} }
NullablePointer{ nonnull: ref nonnull, nullfields: ref nullfields, nndiscr, _ } => { NullablePointer{ nonnull: ref nonnull, nullfields: ref nullfields, nndiscr, _ } => {
@ -498,24 +634,23 @@ pub fn trans_drop_flag_ptr(bcx: @mut Block, r: &Repr, val: ValueRef) -> ValueRef
pub fn trans_const(ccx: &mut CrateContext, r: &Repr, discr: Disr, pub fn trans_const(ccx: &mut CrateContext, r: &Repr, discr: Disr,
vals: &[ValueRef]) -> ValueRef { vals: &[ValueRef]) -> ValueRef {
match *r { match *r {
CEnum(min, max) => { CEnum(ity, min, max) => {
assert_eq!(vals.len(), 0); assert_eq!(vals.len(), 0);
assert!(min <= discr && discr <= max); assert_discr_in_range(ity, min, max, discr);
C_disr(ccx, discr) C_integral(ll_inttype(ccx, ity), discr as u64, true)
} }
Univariant(ref st, _dro) => { General(ity, ref cases) => {
assert_eq!(discr, 0);
let contents = build_const_struct(ccx, st, vals);
C_struct(contents, st.packed)
}
General(ref cases) => {
let case = &cases[discr]; let case = &cases[discr];
let max_sz = cases.iter().map(|x| x.size).max().unwrap(); let max_sz = cases.iter().map(|x| x.size).max().unwrap();
let discr_ty = C_disr(ccx, discr); let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true);
let contents = build_const_struct(ccx, case, let contents = build_const_struct(ccx, case, ~[lldiscr] + vals);
~[discr_ty] + vals);
C_struct(contents + &[padding(max_sz - case.size)], false) C_struct(contents + &[padding(max_sz - case.size)], false)
} }
Univariant(ref st, _dro) => {
assert!(discr == 0);
let contents = build_const_struct(ccx, st, vals);
C_struct(contents, st.packed)
}
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => { NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
if discr == nndiscr { if discr == nndiscr {
C_struct(build_const_struct(ccx, nonnull, vals), false) C_struct(build_const_struct(ccx, nonnull, vals), false)
@ -585,9 +720,19 @@ fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }
pub fn const_get_discrim(ccx: &mut CrateContext, r: &Repr, val: ValueRef) pub fn const_get_discrim(ccx: &mut CrateContext, r: &Repr, val: ValueRef)
-> Disr { -> Disr {
match *r { match *r {
CEnum(*) => const_to_uint(val) as Disr, CEnum(ity, _, _) => {
match ity {
attr::SignedInt(*) => const_to_int(val) as Disr,
attr::UnsignedInt(*) => const_to_uint(val) as Disr
}
}
General(ity, _) => {
match ity {
attr::SignedInt(*) => const_to_int(const_get_elt(ccx, val, [0])) as Disr,
attr::UnsignedInt(*) => const_to_uint(const_get_elt(ccx, val, [0])) as Disr
}
}
Univariant(*) => 0, Univariant(*) => 0,
General(*) => const_to_uint(const_get_elt(ccx, val, [0])) as Disr,
NullablePointer{ nndiscr, ptrfield, _ } => { NullablePointer{ nndiscr, ptrfield, _ } => {
if is_null(const_struct_field(ccx, val, ptrfield)) { if is_null(const_struct_field(ccx, val, ptrfield)) {
/* subtraction as uint is ok because nndiscr is either 0 or 1 */ /* subtraction as uint is ok because nndiscr is either 0 or 1 */
@ -646,7 +791,3 @@ pub fn is_newtypeish(r: &Repr) -> bool {
_ => false _ => false
} }
} }
fn C_disr(cx: &CrateContext, i: Disr) -> ValueRef {
return C_integral(cx.int_type, i, false);
}

View File

@ -109,6 +109,7 @@ use std::libc::{c_uint, c_ulonglong, c_longlong};
use std::ptr; use std::ptr;
use std::unstable::atomics; use std::unstable::atomics;
use std::vec; use std::vec;
use syntax::attr;
use syntax::codemap::{Span, Pos}; use syntax::codemap::{Span, Pos};
use syntax::{ast, codemap, ast_util, ast_map, opt_vec}; use syntax::{ast, codemap, ast_util, ast_map, opt_vec};
use syntax::parse::token; use syntax::parse::token;
@ -1250,7 +1251,7 @@ impl MemberDescriptionFactory for GeneralMemberDescriptionFactory {
-> ~[MemberDescription] { -> ~[MemberDescription] {
// Capture type_rep, so we don't have to copy the struct_defs array // Capture type_rep, so we don't have to copy the struct_defs array
let struct_defs = match *self.type_rep { let struct_defs = match *self.type_rep {
adt::General(ref struct_defs) => struct_defs, adt::General(_, ref struct_defs) => struct_defs,
_ => cx.sess.bug("unreachable") _ => cx.sess.bug("unreachable")
}; };
@ -1399,14 +1400,6 @@ fn prepare_enum_metadata(cx: &mut CrateContext,
return FinalMetadata(empty_type_metadata); return FinalMetadata(empty_type_metadata);
} }
// Prepare some data (llvm type, size, align, etc) about the discriminant. This data will be
// needed in all of the following cases.
let discriminant_llvm_type = Type::enum_discrim(cx);
let (discriminant_size, discriminant_align) = size_and_align_of(cx, discriminant_llvm_type);
assert!(Type::enum_discrim(cx) == cx.int_type);
let discriminant_base_type_metadata = type_metadata(cx, ty::mk_int(), codemap::dummy_sp());
let variants = ty::enum_variants(cx.tcx, enum_def_id); let variants = ty::enum_variants(cx.tcx, enum_def_id);
let enumerators_metadata: ~[DIDescriptor] = variants let enumerators_metadata: ~[DIDescriptor] = variants
@ -1426,26 +1419,34 @@ fn prepare_enum_metadata(cx: &mut CrateContext,
}) })
.collect(); .collect();
let discriminant_type_metadata = do enum_name.with_c_str |enum_name| { let discriminant_type_metadata = |inttype| {
unsafe { let discriminant_llvm_type = adt::ll_inttype(cx, inttype);
llvm::LLVMDIBuilderCreateEnumerationType( let (discriminant_size, discriminant_align) = size_and_align_of(cx, discriminant_llvm_type);
DIB(cx), let discriminant_base_type_metadata = type_metadata(cx, match inttype {
containing_scope, attr::SignedInt(t) => ty::mk_mach_int(t),
enum_name, attr::UnsignedInt(t) => ty::mk_mach_uint(t)
file_metadata, }, codemap::dummy_sp());
loc.line as c_uint, do enum_name.with_c_str |enum_name| {
bytes_to_bits(discriminant_size), unsafe {
bytes_to_bits(discriminant_align), llvm::LLVMDIBuilderCreateEnumerationType(
create_DIArray(DIB(cx), enumerators_metadata), DIB(cx),
discriminant_base_type_metadata) containing_scope,
enum_name,
file_metadata,
loc.line as c_uint,
bytes_to_bits(discriminant_size),
bytes_to_bits(discriminant_align),
create_DIArray(DIB(cx), enumerators_metadata),
discriminant_base_type_metadata)
}
} }
}; };
let type_rep = adt::represent_type(cx, enum_type); let type_rep = adt::represent_type(cx, enum_type);
return match *type_rep { return match *type_rep {
adt::CEnum(*) => { adt::CEnum(inttype, _, _) => {
FinalMetadata(discriminant_type_metadata) FinalMetadata(discriminant_type_metadata(inttype))
} }
adt::Univariant(ref struct_def, _) => { adt::Univariant(ref struct_def, _) => {
assert!(variants.len() == 1); assert!(variants.len() == 1);
@ -1466,7 +1467,8 @@ fn prepare_enum_metadata(cx: &mut CrateContext,
member_description_factory: member_description_factory member_description_factory: member_description_factory
} }
} }
adt::General(_) => { adt::General(inttype, _) => {
let discriminant_type_metadata = discriminant_type_metadata(inttype);
let enum_llvm_type = type_of::type_of(cx, enum_type); let enum_llvm_type = type_of::type_of(cx, enum_type);
let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); let (enum_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type);

View File

@ -1728,7 +1728,7 @@ fn trans_imm_cast(bcx: @mut Block, expr: &ast::Expr,
let repr = adt::represent_type(ccx, t_in); let repr = adt::represent_type(ccx, t_in);
let slot = Alloca(bcx, ll_t_in, ""); let slot = Alloca(bcx, ll_t_in, "");
Store(bcx, llexpr, slot); Store(bcx, llexpr, slot);
let lldiscrim_a = adt::trans_get_discr(bcx, repr, slot); let lldiscrim_a = adt::trans_get_discr(bcx, repr, slot, Some(Type::i64()));
match k_out { match k_out {
cast_integral => int_cast(bcx, ll_t_out, cast_integral => int_cast(bcx, ll_t_out,
val_ty(lldiscrim_a), val_ty(lldiscrim_a),

View File

@ -25,7 +25,7 @@ use middle::ty;
use util::ppaux::ty_to_str; use util::ppaux::ty_to_str;
use std::libc::c_uint; use std::libc::c_uint;
use std::option::None; use std::option::{Some,None};
use std::vec; use std::vec;
use syntax::ast::DefId; use syntax::ast::DefId;
use syntax::ast; use syntax::ast;
@ -308,7 +308,7 @@ impl Reflector {
}; };
let mut bcx = fcx.entry_bcx.unwrap(); let mut bcx = fcx.entry_bcx.unwrap();
let arg = BitCast(bcx, arg, llptrty); let arg = BitCast(bcx, arg, llptrty);
let ret = adt::trans_get_discr(bcx, repr, arg); let ret = adt::trans_get_discr(bcx, repr, arg, Some(ccx.int_type));
Store(bcx, ret, fcx.llretptr.unwrap()); Store(bcx, ret, fcx.llretptr.unwrap());
match fcx.llreturn { match fcx.llreturn {
Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn), Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn),

View File

@ -0,0 +1,32 @@
// 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.
/*!
* Tests the range assertion wraparound case in trans::middle::adt::load_discr.
*/
#[repr(u8)]
enum Eu { Lu = 0, Hu = 255 }
static CLu: Eu = Lu;
static CHu: Eu = Hu;
#[repr(i8)]
enum Es { Ls = -128, Hs = 127 }
static CLs: Es = Ls;
static CHs: Es = Hs;
pub fn main() {
assert_eq!((Hu as u8) + 1, Lu as u8);
assert_eq!((Hs as i8) + 1, Ls as i8);
assert_eq!(CLu as u8, Lu as u8);
assert_eq!(CHu as u8, Hu as u8);
assert_eq!(CLs as i8, Ls as i8);
assert_eq!(CHs as i8, Hs as i8);
}