diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 5e64dc5c2e26e..f1b280338aa5c 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -14,7 +14,8 @@ * This module determines how to represent enums, structs, and tuples * based on their monomorphized types; it is responsible both for * 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 * enum, so structs/tuples/etc. have one pseudo-variant with @@ -29,8 +30,6 @@ * that might contain one and adjust GEP indices accordingly. See * issue #4578. * - * - Using smaller integer types for discriminants. - * * - Store nested enums' discriminants in the same word. Rather, if * some variants start with enums, and those enums representations * have unused alignment padding between discriminant and body, the @@ -56,16 +55,21 @@ use middle::trans::machine; use middle::trans::type_of; use middle::ty; use middle::ty::Disr; +use syntax::abi::{X86, X86_64, Arm, Mips}; use syntax::ast; +use syntax::attr; +use syntax::attr::IntType; use util::ppaux::ty_to_str; use middle::trans::type_::Type; +type Hint = attr::ReprAttr; + /// Representations. pub enum Repr { /// 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. * @@ -78,7 +82,7 @@ pub enum Repr { * General-case enums: for each case there is a struct, and they * all start with a field for the discriminant. */ - General(~[Struct]), + General(IntType, ~[Struct]), /** * Two cases distinguished by a nullable pointer: the case with discriminant * `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) { // All bodies empty -> intlike 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 { // Equivalent to a struct/tuple/newtype. + // FIXME: should this conflict with a discriminant size hint? assert_eq!(cases[0].discr, 0); 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 { + // FIXME: disable if size hint present? let mut discr = 0; while discr < 2 { 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. - let discr = ~[ty::mk_uint()]; - return General(cases.map(|c| mk_struct(cx, discr + c.tys, false))) + let hint = ty::lookup_repr_hint(cx.tcx, def_id); + 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") } @@ -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. * 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] { match *r { - CEnum(*) => ~[Type::enum_discrim(cx)], + CEnum(ity, _, _) => ~[ll_inttype(cx, ity)], Univariant(ref st, _dtor) => 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 // with the largest alignment (so it will always align // 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) { match *r { 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, _ } => { (_match::switch, Some(nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee))) @@ -302,17 +407,32 @@ pub fn trans_switch(bcx: @mut Block, r: &Repr, scrutinee: ValueRef) /// 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) -> ValueRef { + let signed; + let val; match *r { - CEnum(min, max) => load_discr(bcx, scrutinee, min, max), - Univariant(*) => C_disr(bcx.ccx(), 0), - General(ref cases) => load_discr(bcx, scrutinee, 0, (cases.len() - 1) as Disr), + CEnum(ity, min, max) => { + val = load_discr(bcx, ity, scrutinee, min, max); + signed = ity.is_signed(); + } + 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, _ } => { - ZExt(bcx, nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee), - Type::enum_discrim(bcx.ccx())) + 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) } + } } fn nullable_bitdiscr(bcx: @mut Block, nonnull: &Struct, nndiscr: Disr, ptrfield: uint, @@ -324,10 +444,15 @@ fn nullable_bitdiscr(bcx: @mut Block, nonnull: &Struct, nndiscr: Disr, ptrfield: } /// 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 { 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 // rejected by the LLVM verifier (it would mean either an // 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 { match *r { - CEnum(*) => { - _match::single_result(rslt(bcx, C_disr(bcx.ccx(), discr))) + CEnum(ity, _, _) => { + _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(*) => { bcx.ccx().sess.bug("no cases for univariants or structs") } - General(*) => { - _match::single_result(rslt(bcx, C_disr(bcx.ccx(), discr))) - } NullablePointer{ _ } => { assert!(discr == 0 || discr == 1); _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) { match *r { - CEnum(min, max) => { - assert!(min <= discr && discr <= max); - Store(bcx, C_disr(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) + CEnum(ity, min, max) => { + assert_discr_in_range(ity, min, max, discr); + 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) => { assert_eq!(discr, 0); @@ -385,9 +517,6 @@ pub fn trans_start_init(bcx: @mut Block, r: &Repr, val: ValueRef, discr: Disr) { Univariant(*) => { assert_eq!(discr, 0); } - General(*) => { - Store(bcx, C_disr(bcx.ccx(), discr), GEPi(bcx, val, [0, 0])) - } NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => { if discr != nndiscr { 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 * 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); 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, _ } => { 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); struct_field_ptr(bcx, st, val, ix, false) } - General(ref cases) => { + General(_, ref cases) => { struct_field_ptr(bcx, &cases[discr], val, ix + 1, true) } 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, vals: &[ValueRef]) -> ValueRef { match *r { - CEnum(min, max) => { + CEnum(ity, min, max) => { assert_eq!(vals.len(), 0); - assert!(min <= discr && discr <= max); - C_disr(ccx, discr) - } - Univariant(ref st, _dro) => { - assert_eq!(discr, 0); - let contents = build_const_struct(ccx, st, vals); - C_struct(contents, st.packed) + assert_discr_in_range(ity, min, max, discr); + C_integral(ll_inttype(ccx, ity), discr as u64, true) } - General(ref cases) => { + General(ity, ref cases) => { let case = &cases[discr]; let max_sz = cases.iter().map(|x| x.size).max().unwrap(); - let discr_ty = C_disr(ccx, discr); - let contents = build_const_struct(ccx, case, - ~[discr_ty] + vals); + let lldiscr = C_integral(ll_inttype(ccx, ity), discr as u64, true); + let contents = build_const_struct(ccx, case, ~[lldiscr] + vals); 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, _ } => { if discr == nndiscr { 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) -> Disr { 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, - General(*) => const_to_uint(const_get_elt(ccx, val, [0])) as Disr, NullablePointer{ nndiscr, ptrfield, _ } => { if is_null(const_struct_field(ccx, val, ptrfield)) { /* subtraction as uint is ok because nndiscr is either 0 or 1 */ @@ -646,7 +791,3 @@ pub fn is_newtypeish(r: &Repr) -> bool { _ => false } } - -fn C_disr(cx: &CrateContext, i: Disr) -> ValueRef { - return C_integral(cx.int_type, i, false); -} diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 2138afd2e9bb5..616af475609a2 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -109,6 +109,7 @@ use std::libc::{c_uint, c_ulonglong, c_longlong}; use std::ptr; use std::unstable::atomics; use std::vec; +use syntax::attr; use syntax::codemap::{Span, Pos}; use syntax::{ast, codemap, ast_util, ast_map, opt_vec}; use syntax::parse::token; @@ -1250,7 +1251,7 @@ impl MemberDescriptionFactory for GeneralMemberDescriptionFactory { -> ~[MemberDescription] { // Capture type_rep, so we don't have to copy the struct_defs array 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") }; @@ -1399,14 +1400,6 @@ fn prepare_enum_metadata(cx: &mut CrateContext, 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 enumerators_metadata: ~[DIDescriptor] = variants @@ -1426,26 +1419,34 @@ fn prepare_enum_metadata(cx: &mut CrateContext, }) .collect(); - let discriminant_type_metadata = do enum_name.with_c_str |enum_name| { - unsafe { - llvm::LLVMDIBuilderCreateEnumerationType( - DIB(cx), - 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 discriminant_type_metadata = |inttype| { + let discriminant_llvm_type = adt::ll_inttype(cx, inttype); + let (discriminant_size, discriminant_align) = size_and_align_of(cx, discriminant_llvm_type); + let discriminant_base_type_metadata = type_metadata(cx, match inttype { + attr::SignedInt(t) => ty::mk_mach_int(t), + attr::UnsignedInt(t) => ty::mk_mach_uint(t) + }, codemap::dummy_sp()); + do enum_name.with_c_str |enum_name| { + unsafe { + llvm::LLVMDIBuilderCreateEnumerationType( + DIB(cx), + 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); return match *type_rep { - adt::CEnum(*) => { - FinalMetadata(discriminant_type_metadata) + adt::CEnum(inttype, _, _) => { + FinalMetadata(discriminant_type_metadata(inttype)) } adt::Univariant(ref struct_def, _) => { assert!(variants.len() == 1); @@ -1466,7 +1467,8 @@ fn prepare_enum_metadata(cx: &mut CrateContext, 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_type_size, enum_type_align) = size_and_align_of(cx, enum_llvm_type); diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 1c856f04b0684..e1de66152069d 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -1728,7 +1728,7 @@ fn trans_imm_cast(bcx: @mut Block, expr: &ast::Expr, let repr = adt::represent_type(ccx, t_in); let slot = Alloca(bcx, ll_t_in, ""); 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 { cast_integral => int_cast(bcx, ll_t_out, val_ty(lldiscrim_a), diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index 54ecc15c44f70..1b27e06dca837 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -25,7 +25,7 @@ use middle::ty; use util::ppaux::ty_to_str; use std::libc::c_uint; -use std::option::None; +use std::option::{Some,None}; use std::vec; use syntax::ast::DefId; use syntax::ast; @@ -308,7 +308,7 @@ impl Reflector { }; let mut bcx = fcx.entry_bcx.unwrap(); 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()); match fcx.llreturn { Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn), diff --git a/src/test/run-pass/small-enum-range-edge.rs b/src/test/run-pass/small-enum-range-edge.rs new file mode 100644 index 0000000000000..a9fc13437abb4 --- /dev/null +++ b/src/test/run-pass/small-enum-range-edge.rs @@ -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 or the MIT license +// , 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); +}