mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-31 06:22:00 +00:00
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:
parent
f1124a2f55
commit
01740acd5a
@ -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);
|
|
||||||
}
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
|
32
src/test/run-pass/small-enum-range-edge.rs
Normal file
32
src/test/run-pass/small-enum-range-edge.rs
Normal 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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user