auto merge of #9613 : jld/rust/enum-discrim-size.r0, r=alexcrichton

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.

Also adds a lint pass for using non-FFI safe enums in extern declarations, checks that specified discriminants can be stored in the specified type if any, and fixes assorted code that was assuming int.
This commit is contained in:
bors 2013-10-30 00:31:23 -07:00
commit 5e1a691125
32 changed files with 968 additions and 199 deletions

View File

@ -465,6 +465,17 @@ $(foreach host,$(CFG_HOST_TRIPLES), \
$(eval $(call DEF_TEST_CRATE_RULES,$(stage),$(target),$(host),$(crate))) \
))))))
# FIXME (#10104): Raise the stack size to work around rustpkg bypassing
# the code in rustc that would take care of it.
define DEF_RUSTPKG_STACK_FIX
$$(call TEST_OK_FILE,$(1),$(2),$(3),rustpkg): export RUST_MIN_STACK=8000000
endef
$(foreach host,$(CFG_HOST_TRIPLES), \
$(foreach target,$(CFG_TARGET_TRIPLES), \
$(foreach stage,$(STAGES), \
$(eval $(call DEF_RUSTPKG_STACK_FIX,$(stage),$(target),$(host))))))
######################################################################
# Rules for the compiletest tests (rpass, rfail, etc.)

View File

@ -281,7 +281,7 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
};
let config = &mut config;
let cmds = props.debugger_cmds.connect("\n");
let check_lines = props.check_lines.clone();
let check_lines = &props.check_lines;
// compile test file (it shoud have 'compile-flags:-g' in the header)
let mut ProcRes = compile_test(config, props, testfile);
@ -315,11 +315,34 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
let num_check_lines = check_lines.len();
if num_check_lines > 0 {
// Allow check lines to leave parts unspecified (e.g., uninitialized
// bits in the wrong case of an enum) with the notation "[...]".
let check_fragments: ~[~[&str]] = check_lines.map(|s| s.split_str_iter("[...]").collect());
// check if each line in props.check_lines appears in the
// output (in order)
let mut i = 0u;
for line in ProcRes.stdout.line_iter() {
if check_lines[i].trim() == line.trim() {
let mut rest = line.trim();
let mut first = true;
let mut failed = false;
for &frag in check_fragments[i].iter() {
let found = if first {
if rest.starts_with(frag) { Some(0) } else { None }
} else {
rest.find_str(frag)
};
match found {
None => {
failed = true;
break;
}
Some(i) => {
rest = rest.slice_from(i + frag.len());
}
}
first = false;
}
if !failed && rest.len() == 0 {
i += 1u;
}
if i == num_check_lines {

View File

@ -140,6 +140,7 @@ mod test {
use enum_set::*;
#[deriving(Eq)]
#[repr(uint)]
enum Foo {
A, B, C
}

View File

@ -147,6 +147,7 @@ pub static Vector: TypeKind = 13;
pub static Metadata: TypeKind = 14;
pub static X86_MMX: TypeKind = 15;
#[repr(C)]
pub enum AtomicBinOp {
Xchg = 0,
Add = 1,
@ -161,6 +162,7 @@ pub enum AtomicBinOp {
UMin = 10,
}
#[repr(C)]
pub enum AtomicOrdering {
NotAtomic = 0,
Unordered = 1,
@ -173,6 +175,7 @@ pub enum AtomicOrdering {
}
// Consts for the LLVMCodeGenFileType type (in include/llvm/c/TargetMachine.h)
#[repr(C)]
pub enum FileType {
AssemblyFile = 0,
ObjectFile = 1
@ -194,6 +197,7 @@ pub enum AsmDialect {
}
#[deriving(Eq)]
#[repr(C)]
pub enum CodeGenOptLevel {
CodeGenLevelNone = 0,
CodeGenLevelLess = 1,
@ -201,6 +205,7 @@ pub enum CodeGenOptLevel {
CodeGenLevelAggressive = 3,
}
#[repr(C)]
pub enum RelocMode {
RelocDefault = 0,
RelocStatic = 1,
@ -208,6 +213,7 @@ pub enum RelocMode {
RelocDynamicNoPic = 3,
}
#[repr(C)]
pub enum CodeGenModel {
CodeModelDefault = 0,
CodeModelJITDefault = 1,

View File

@ -112,6 +112,7 @@ pub static tag_items_data_item_reexport_name: uint = 0x4f;
// used to encode crate_ctxt side tables
#[deriving(Eq)]
#[repr(uint)]
pub enum astencode_tag { // Reserves 0x50 -- 0x6f
tag_ast = 0x50,
@ -143,7 +144,7 @@ impl astencode_tag {
pub fn from_uint(value : uint) -> Option<astencode_tag> {
let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag;
if !is_a_tag { None } else {
Some(unsafe { cast::transmute(value as int) })
Some(unsafe { cast::transmute(value) })
}
}
}

View File

@ -989,6 +989,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_family(ebml_w, 't');
encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id));
encode_name(ecx, ebml_w, item.ident);
encode_attributes(ebml_w, item.attrs);
for v in (*enum_definition).variants.iter() {
encode_variant_id(ebml_w, local_def(v.node.id));
}

View File

@ -34,6 +34,7 @@
//! Context itself, span_lint should be used instead of add_lint.
use driver::session;
use middle::trans::adt; // for `adt::is_ffi_safe`
use middle::ty;
use middle::pat_util;
use metadata::csearch;
@ -627,6 +628,14 @@ fn check_item_ctypes(cx: &Context, it: &ast::item) {
"found rust type `uint` in foreign module, while \
libc::c_uint or libc::c_ulong should be used");
}
ast::DefTy(def_id) => {
if !adt::is_ffi_safe(cx.tcx, def_id) {
cx.span_lint(ctypes, ty.span,
"found enum type without foreign-function-safe \
representation annotation in foreign module");
// NOTE this message could be more helpful
}
}
_ => ()
}
}

View File

@ -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
@ -141,38 +145,26 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
return Univariant(mk_struct(cx, ftys, packed), dtor)
}
ty::ty_enum(def_id, ref substs) => {
struct Case { discr: Disr, tys: ~[ty::t] };
impl Case {
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
mk_struct(cx, self.tys, false).size == 0
}
fn find_ptr(&self) -> Option<uint> {
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
}
}
let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
let arg_tys = do vi.args.map |&raw_ty| {
ty::subst(cx.tcx, substs, raw_ty)
};
Case { discr: vi.disr_val, tys: arg_tys }
};
let cases = get_cases(cx.tcx, def_id, substs);
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
if cases.len() == 0 {
// Uninhabitable; represent as unit
// (Typechecking will reject discriminant-sizing attrs.)
assert_eq!(hint, attr::ReprAny);
return Univariant(mk_struct(cx, [], false), false);
}
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());
}
if cases.len() == 1 {
// Equivalent to a struct/tuple/newtype.
assert_eq!(cases[0].discr, 0);
return Univariant(mk_struct(cx, cases[0].tys, false), false)
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);
}
// Since there's at least one
@ -184,7 +176,15 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
ty::item_path_str(cx.tcx, def_id)))
}
if cases.len() == 2 {
if cases.len() == 1 {
// Equivalent to a struct/tuple/newtype.
// (Typechecking will reject discriminant-sizing attrs.)
assert_eq!(hint, attr::ReprAny);
return Univariant(mk_struct(cx, cases[0].tys, false), false)
}
if cases.len() == 2 && hint == attr::ReprAny {
// Nullable pointer optimization
let mut discr = 0;
while discr < 2 {
if cases[1 - discr].is_zerolen(cx) {
@ -207,13 +207,67 @@ 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)))
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")
}
}
/// Determine, without doing translation, whether an ADT must be FFI-safe.
/// For use in lint or similar, where being sound but slightly incomplete is acceptable.
pub fn is_ffi_safe(tcx: ty::ctxt, def_id: ast::DefId) -> bool {
match ty::get(ty::lookup_item_type(tcx, def_id).ty).sty {
ty::ty_enum(def_id, _) => {
let variants = ty::enum_variants(tcx, def_id);
// Univariant => like struct/tuple.
if variants.len() <= 1 {
return true;
}
let hint = ty::lookup_repr_hint(tcx, def_id);
// Appropriate representation explicitly selected?
if hint.is_ffi_safe() {
return true;
}
// Option<~T> and similar are used in FFI. Rather than try to resolve type parameters
// and recognize this case exactly, this overapproximates -- assuming that if a
// non-C-like enum is being used in FFI then the user knows what they're doing.
if variants.iter().any(|vi| !vi.args.is_empty()) {
return true;
}
false
}
// struct, tuple, etc.
// (is this right in the present of typedefs?)
_ => true
}
}
// NOTE this should probably all be in ty
struct Case { discr: Disr, tys: ~[ty::t] }
impl Case {
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
mk_struct(cx, self.tys, false).size == 0
}
fn find_ptr(&self) -> Option<uint> {
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
}
}
fn get_cases(tcx: ty::ctxt, def_id: ast::DefId, substs: &ty::substs) -> ~[Case] {
do ty::enum_variants(tcx, def_id).map |vi| {
let arg_tys = do vi.args.map |&raw_ty| {
ty::subst(tcx, substs, raw_ty)
};
Case { discr: vi.disr_val, tys: arg_tys }
}
}
fn mk_struct(cx: &mut CrateContext, tys: &[ty::t], packed: bool) -> Struct {
let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
let llty_rec = Type::struct_(lltys, packed);
@ -225,6 +279,92 @@ 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_bug(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;
}
}
for &ity in attempts.iter() {
if bounds_usable(cx, ity, bounds) {
return ity;
}
}
return attr::UnsignedInt(ast::ty_u64);
}
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
}
}
}
pub 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,33 +379,41 @@ 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) => {
// 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.
assert!(sts.len() >= 1);
let mut most_aligned = None;
let mut largest_align = 0;
let mut largest_size = 0;
for st in sts.iter() {
if largest_size < st.size {
largest_size = st.size;
}
if largest_align < st.align {
// Clang breaks ties by size; it is unclear if
// that accomplishes anything important.
largest_align = st.align;
most_aligned = Some(st);
}
}
let most_aligned = most_aligned.unwrap();
let padding = largest_size - most_aligned.size;
struct_llfields(cx, most_aligned, sizing)
+ &[Type::array(&Type::i8(), padding)]
General(ity, ref sts) => {
// We need a representation that has:
// * The alignment of the most-aligned field
// * The size of the largest variant (rounded up to that alignment)
// * No alignment padding anywhere any variant has actual data
// (currently matters only for enums small enough to be immediate)
// * The discriminant in an obvious place.
//
// So we start with the discriminant, pad it up to the alignment with
// more of its own type, then use alignment-sized ints to get the rest
// of the size.
//
// Note: if/when we start exposing SIMD vector types (or f80, on some
// platforms that have it), this will need some adjustment.
let size = sts.iter().map(|st| st.size).max().unwrap();
let most_aligned = sts.iter().max_by(|st| st.align).unwrap();
let align = most_aligned.align;
let discr_ty = ll_inttype(cx, ity);
let discr_size = machine::llsize_of_alloc(cx, discr_ty) as u64;
let pad_ty = match align {
1 => Type::i8(),
2 => Type::i16(),
4 => Type::i32(),
8 if machine::llalign_of_min(cx, Type::i64()) == 8 => Type::i64(),
_ => fail!("Unsupported enum alignment: {:?}", align)
};
assert_eq!(machine::llalign_of_min(cx, pad_ty) as u64, align);
let align_units = (size + align - 1) / align;
assert_eq!(align % discr_size, 0);
~[discr_ty,
Type::array(&discr_ty, align / discr_size - 1),
Type::array(&pad_ty, align_units - 1)]
}
}
}
@ -288,7 +436,7 @@ pub fn trans_switch(bcx: @mut Block, r: &Repr, scrutinee: ValueRef)
-> (_match::branch_kind, Option<ValueRef>) {
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,16 +450,31 @@ 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<Type>)
-> 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),
NullablePointer{ nonnull: ref nonnull, nndiscr, ptrfield, _ } => {
ZExt(bcx, nullable_bitdiscr(bcx, nonnull, nndiscr, ptrfield, scrutinee),
Type::enum_discrim(bcx.ccx()))
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, _ } => {
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 +487,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 +518,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 +543,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 +560,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 +570,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 +588,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 +609,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 +677,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)
assert_discr_in_range(ity, min, max, discr);
C_integral(ll_inttype(ccx, ity), discr as u64, true)
}
Univariant(ref st, _dro) => {
assert_eq!(discr, 0);
let contents = build_const_struct(ccx, st, vals);
C_struct(contents, st.packed)
}
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 +763,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 +834,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);
}

View File

@ -868,6 +868,10 @@ pub fn C_i64(i: i64) -> ValueRef {
return C_integral(Type::i64(), i as u64, true);
}
pub fn C_u64(i: u64) -> ValueRef {
return C_integral(Type::i64(), i, false);
}
pub fn C_int(cx: &CrateContext, i: int) -> ValueRef {
return C_integral(cx.int_type, i as u64, true);
}

View File

@ -1251,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")
};
@ -1400,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
@ -1427,26 +1419,32 @@ 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, adt::ty_of_inttype(inttype),
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);
@ -1467,7 +1465,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);

View File

@ -1727,9 +1727,14 @@ fn trans_imm_cast(bcx: @mut Block, expr: &ast::Expr,
(cast_enum, cast_float) => {
let bcx = bcx;
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 llexpr_ptr;
if type_is_immediate(ccx, t_in) {
llexpr_ptr = Alloca(bcx, ll_t_in, "");
Store(bcx, llexpr, llexpr_ptr);
} else {
llexpr_ptr = llexpr;
}
let lldiscrim_a = adt::trans_get_discr(bcx, repr, llexpr_ptr, Some(Type::i64()));
match k_out {
cast_integral => int_cast(bcx, ll_t_out,
val_ty(lldiscrim_a),

View File

@ -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;
@ -292,11 +292,11 @@ impl Reflector {
sub_path,
"get_disr");
let llfdecl = decl_internal_rust_fn(ccx, [opaqueptrty], ty::mk_int(), sym);
let llfdecl = decl_internal_rust_fn(ccx, [opaqueptrty], ty::mk_u64(), sym);
let fcx = new_fn_ctxt(ccx,
~[],
llfdecl,
ty::mk_uint(),
ty::mk_u64(),
None);
let arg = unsafe {
//
@ -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(Type::i64()));
Store(bcx, ret, fcx.llretptr.unwrap());
match fcx.llreturn {
Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn),
@ -324,7 +324,7 @@ impl Reflector {
for (i, v) in variants.iter().enumerate() {
let name = ccx.sess.str_of(v.name);
let variant_args = ~[this.c_uint(i),
C_integral(self.bcx.ccx().int_type, v.disr_val, false),
C_u64(v.disr_val),
this.c_uint(v.args.len()),
this.c_slice(name)];
do this.bracketed("enum_variant", variant_args) |this| {

View File

@ -38,6 +38,7 @@ use syntax::ast::*;
use syntax::ast_util::is_local;
use syntax::ast_util;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use syntax::codemap::Span;
use syntax::codemap;
use syntax::parse::token;
@ -715,6 +716,7 @@ pub struct ParamBounds {
pub type BuiltinBounds = EnumSet<BuiltinBound>;
#[deriving(Clone, Eq, IterBytes, ToStr)]
#[repr(uint)]
pub enum BuiltinBound {
BoundStatic,
BoundSend,
@ -4102,27 +4104,42 @@ pub fn lookup_trait_def(cx: ctxt, did: ast::DefId) -> @ty::TraitDef {
}
}
/// Determine whether an item is annotated with an attribute
pub fn has_attr(tcx: ctxt, did: DefId, attr: &str) -> bool {
/// Iterate over meta_items of a definition.
// (This should really be an iterator, but that would require csearch and
// decoder to use iterators instead of higher-order functions.)
pub fn each_attr(tcx: ctxt, did: DefId, f: &fn(@MetaItem) -> bool) -> bool {
if is_local(did) {
match tcx.items.find(&did.node) {
Some(
&ast_map::node_item(@ast::item {
attrs: ref attrs,
_
}, _)) => attr::contains_name(*attrs, attr),
Some(&ast_map::node_item(@ast::item {attrs: ref attrs, _}, _)) =>
attrs.iter().advance(|attr| f(attr.node.value)),
_ => tcx.sess.bug(format!("has_attr: {:?} is not an item",
did))
did))
}
} else {
let mut ret = false;
let mut cont = true;
do csearch::get_item_attrs(tcx.cstore, did) |meta_items| {
ret = ret || attr::contains_name(meta_items, attr);
if cont {
cont = meta_items.iter().advance(|ptrptr| f(*ptrptr));
}
}
ret
return cont;
}
}
/// Determine whether an item is annotated with an attribute
pub fn has_attr(tcx: ctxt, did: DefId, attr: &str) -> bool {
let mut found = false;
each_attr(tcx, did, |item| {
if attr == item.name() {
found = true;
false
} else {
true
}
});
return found;
}
/// Determine whether an item is annotated with `#[packed]`
pub fn lookup_packed(tcx: ctxt, did: DefId) -> bool {
has_attr(tcx, did, "packed")
@ -4133,6 +4150,16 @@ pub fn lookup_simd(tcx: ctxt, did: DefId) -> bool {
has_attr(tcx, did, "simd")
}
// Obtain the the representation annotation for a definition.
pub fn lookup_repr_hint(tcx: ctxt, did: DefId) -> attr::ReprAttr {
let mut acc = attr::ReprAny;
ty::each_attr(tcx, did, |meta| {
acc = attr::find_repr_attr(tcx.sess.diagnostic(), meta, acc);
true
});
return acc;
}
// Look up a field ID, whether or not it's local
// Takes a list of type substs in case the struct is generic
pub fn lookup_field_type(tcx: ctxt,

View File

@ -121,6 +121,7 @@ use syntax::ast;
use syntax::ast_map;
use syntax::ast_util::local_def;
use syntax::ast_util;
use syntax::attr;
use syntax::codemap::Span;
use syntax::codemap;
use syntax::opt_vec::OptVec;
@ -3169,9 +3170,38 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
sp: Span,
vs: &[ast::variant],
id: ast::NodeId) {
fn disr_in_range(ccx: @mut CrateCtxt,
ty: attr::IntType,
disr: ty::Disr) -> bool {
fn uint_in_range(ccx: @mut CrateCtxt, ty: ast::uint_ty, disr: ty::Disr) -> bool {
match ty {
ast::ty_u8 => disr as u8 as Disr == disr,
ast::ty_u16 => disr as u16 as Disr == disr,
ast::ty_u32 => disr as u32 as Disr == disr,
ast::ty_u64 => disr as u64 as Disr == disr,
ast::ty_u => uint_in_range(ccx, ccx.tcx.sess.targ_cfg.uint_type, disr)
}
}
fn int_in_range(ccx: @mut CrateCtxt, ty: ast::int_ty, disr: ty::Disr) -> bool {
match ty {
ast::ty_i8 => disr as i8 as Disr == disr,
ast::ty_i16 => disr as i16 as Disr == disr,
ast::ty_i32 => disr as i32 as Disr == disr,
ast::ty_i64 => disr as i64 as Disr == disr,
ast::ty_i => int_in_range(ccx, ccx.tcx.sess.targ_cfg.int_type, disr)
}
}
match ty {
attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr),
attr::SignedInt(ty) => int_in_range(ccx, ty, disr)
}
}
fn do_check(ccx: @mut CrateCtxt,
vs: &[ast::variant],
id: ast::NodeId)
id: ast::NodeId,
hint: attr::ReprAttr)
-> ~[@ty::VariantInfo] {
let rty = ty::node_id_to_type(ccx.tcx, id);
@ -3213,9 +3243,20 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
None => ()
};
// Check for duplicate discriminator values
// Check for duplicate discriminant values
if disr_vals.contains(&current_disr_val) {
ccx.tcx.sess.span_err(v.span, "discriminator value already exists");
ccx.tcx.sess.span_err(v.span, "discriminant value already exists");
}
// Check for unrepresentable discriminant values
match hint {
attr::ReprAny | attr::ReprExtern => (),
attr::ReprInt(sp, ity) => {
if !disr_in_range(ccx, ity, current_disr_val) {
ccx.tcx.sess.span_err(v.span,
"discriminant value outside specified type");
ccx.tcx.sess.span_note(sp, "discriminant type specified here");
}
}
}
disr_vals.push(current_disr_val);
@ -3229,8 +3270,13 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
}
let rty = ty::node_id_to_type(ccx.tcx, id);
let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { crate: ast::LOCAL_CRATE, node: id });
if hint != attr::ReprAny && vs.len() <= 1 {
ccx.tcx.sess.span_err(sp, format!("unsupported representation for {}variant enum",
if vs.len() == 1 { "uni" } else { "zero-" }))
}
let variants = do_check(ccx, vs, id);
let variants = do_check(ccx, vs, id, hint);
// cache so that ty::enum_variants won't repeat this work
ccx.tcx.enum_var_cache.insert(local_def(id), @variants);

View File

@ -29,7 +29,8 @@ pub static bits : uint = $bits;
pub static bytes : uint = ($bits / 8);
pub static min_value: $T = (-1 as $T) << (bits - 1);
pub static max_value: $T = min_value - 1 as $T;
// FIXME(#9837): Compute min_value like this so the high bits that shouldn't exist are 0.
pub static max_value: $T = !min_value;
impl CheckedDiv for $T {
#[inline]

View File

@ -16,7 +16,7 @@ Runtime type reflection
#[allow(missing_doc)];
use unstable::intrinsics::{Opaque, TyDesc, TyVisitor};
use unstable::intrinsics::{Disr, Opaque, TyDesc, TyVisitor};
use libc::c_void;
use mem;
use unstable::raw;
@ -396,7 +396,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
}
fn visit_enter_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint)
-> bool {
self.align(align);
@ -407,7 +407,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
}
fn visit_enter_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool {
if ! self.inner.visit_enter_enum_variant(variant, disr_val,
@ -426,7 +426,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
}
fn visit_leave_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool {
if ! self.inner.visit_leave_enum_variant(variant, disr_val,
@ -437,7 +437,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
}
fn visit_leave_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint) -> bool {
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) {
return false;

View File

@ -29,7 +29,7 @@ use reflect::{MovePtr, align};
use str::StrSlice;
use to_str::ToStr;
use vec::OwnedVector;
use unstable::intrinsics::{Opaque, TyDesc, TyVisitor, get_tydesc, visit_tydesc};
use unstable::intrinsics::{Disr, Opaque, TyDesc, TyVisitor, get_tydesc, visit_tydesc};
use unstable::raw;
/// Representations
@ -92,7 +92,7 @@ num_repr!(f64, "f64")
// New implementation using reflect::MovePtr
enum VariantState {
SearchingFor(int),
SearchingFor(Disr),
Matched,
AlreadyFound
}
@ -473,7 +473,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
fn visit_enter_enum(&mut self,
_n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint,
_align: uint) -> bool {
let disr = unsafe {
@ -484,7 +484,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
}
fn visit_enter_enum_variant(&mut self, _variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool {
let mut write = false;
@ -531,7 +531,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
}
fn visit_leave_enum_variant(&mut self, _variant: uint,
_disr_val: int,
_disr_val: Disr,
n_fields: uint,
_name: &str) -> bool {
match self.var_stk[self.var_stk.len() - 1] {
@ -547,7 +547,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
fn visit_leave_enum(&mut self,
_n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint,
_align: uint)
-> bool {

View File

@ -26,6 +26,7 @@ use result::{Err, Ok};
use rt::io::io_error;
use rt::rtio::{IoFactory, RtioSignal, with_local_io};
#[repr(int)]
#[deriving(Eq, IterBytes)]
pub enum Signum {
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.

View File

@ -75,6 +75,11 @@ pub struct TyDesc {
#[cfg(not(test))]
pub enum Opaque { }
#[cfg(stage0)]
pub type Disr = int;
#[cfg(not(stage0))]
pub type Disr = u64;
#[lang="ty_visitor"]
#[cfg(not(test))]
pub trait TyVisitor {
@ -140,19 +145,19 @@ pub trait TyVisitor {
sz: uint, align: uint) -> bool;
fn visit_enter_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint) -> bool;
fn visit_enter_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool;
fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool;
fn visit_leave_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool;
fn visit_leave_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint) -> bool;
fn visit_enter_fn(&mut self, purity: uint, proto: uint,

View File

@ -14,7 +14,7 @@ use extra;
use ast;
use ast::{Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
use codemap::{Spanned, spanned, dummy_spanned};
use codemap::{Span, Spanned, spanned, dummy_spanned};
use codemap::BytePos;
use diagnostic::span_handler;
use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
@ -363,3 +363,115 @@ pub fn require_unique_names(diagnostic: @mut span_handler,
}
}
}
/**
* Fold this over attributes to parse #[repr(...)] forms.
*
* Valid repr contents: any of the primitive integral type names (see
* `int_type_of_word`, below) to specify the discriminant type; and `C`, to use
* the same discriminant size that the corresponding C enum would. These are
* not allowed on univariant or zero-variant enums, which have no discriminant.
*
* If a discriminant type is so specified, then the discriminant will be
* present (before fields, if any) with that type; reprensentation
* optimizations which would remove it will not be done.
*/
pub fn find_repr_attr(diagnostic: @mut span_handler, attr: @ast::MetaItem, acc: ReprAttr)
-> ReprAttr {
let mut acc = acc;
match attr.node {
ast::MetaList(s, ref items) if "repr" == s => {
for item in items.iter() {
match item.node {
ast::MetaWord(word) => {
let hint = match word.as_slice() {
// Can't use "extern" because it's not a lexical identifier.
"C" => ReprExtern,
_ => match int_type_of_word(word) {
Some(ity) => ReprInt(item.span, ity),
None => {
// Not a word we recognize
diagnostic.span_err(item.span,
"unrecognized representation hint");
ReprAny
}
}
};
if hint != ReprAny {
if acc == ReprAny {
acc = hint;
} else if acc != hint {
diagnostic.span_warn(item.span,
"conflicting representation hint ignored")
}
}
}
// Not a word:
_ => diagnostic.span_err(item.span, "unrecognized representation hint")
}
}
}
// Not a "repr" hint: ignore.
_ => { }
}
return acc;
}
fn int_type_of_word(s: &str) -> Option<IntType> {
match s {
"i8" => Some(SignedInt(ast::ty_i8)),
"u8" => Some(UnsignedInt(ast::ty_u8)),
"i16" => Some(SignedInt(ast::ty_i16)),
"u16" => Some(UnsignedInt(ast::ty_u16)),
"i32" => Some(SignedInt(ast::ty_i32)),
"u32" => Some(UnsignedInt(ast::ty_u32)),
"i64" => Some(SignedInt(ast::ty_i64)),
"u64" => Some(UnsignedInt(ast::ty_u64)),
"int" => Some(SignedInt(ast::ty_i)),
"uint" => Some(UnsignedInt(ast::ty_u)),
_ => None
}
}
#[deriving(Eq)]
pub enum ReprAttr {
ReprAny,
ReprInt(Span, IntType),
ReprExtern
}
impl ReprAttr {
pub fn is_ffi_safe(&self) -> bool {
match *self {
ReprAny => false,
ReprInt(_sp, ity) => ity.is_ffi_safe(),
ReprExtern => true
}
}
}
#[deriving(Eq)]
pub enum IntType {
SignedInt(ast::int_ty),
UnsignedInt(ast::uint_ty)
}
impl IntType {
#[inline]
pub fn is_signed(self) -> bool {
match self {
SignedInt(*) => true,
UnsignedInt(*) => false
}
}
fn is_ffi_safe(self) -> bool {
match self {
SignedInt(ast::ty_i8) | UnsignedInt(ast::ty_u8) |
SignedInt(ast::ty_i16) | UnsignedInt(ast::ty_u16) |
SignedInt(ast::ty_i32) | UnsignedInt(ast::ty_u32) |
SignedInt(ast::ty_i64) | UnsignedInt(ast::ty_u64) => true,
_ => false
}
}
}

View File

@ -0,0 +1,58 @@
// 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.
#[repr(u8)] //~ NOTE discriminant type specified here
enum Eu8 {
Au8 = 23,
Bu8 = 223,
Cu8 = -23, //~ ERROR discriminant value outside specified type
}
#[repr(i8)] //~ NOTE discriminant type specified here
enum Ei8 {
Ai8 = 23,
Bi8 = -23,
Ci8 = 223, //~ ERROR discriminant value outside specified type
}
#[repr(u16)] //~ NOTE discriminant type specified here
enum Eu16 {
Au16 = 23,
Bu16 = 55555,
Cu16 = -22333, //~ ERROR discriminant value outside specified type
}
#[repr(i16)] //~ NOTE discriminant type specified here
enum Ei16 {
Ai16 = 23,
Bi16 = -22333,
Ci16 = 55555, //~ ERROR discriminant value outside specified type
}
#[repr(u32)] //~ NOTE discriminant type specified here
enum Eu32 {
Au32 = 23,
Bu32 = 3_000_000_000,
Cu32 = -2_000_000_000, //~ ERROR discriminant value outside specified type
}
#[repr(i32)] //~ NOTE discriminant type specified here
enum Ei32 {
Ai32 = 23,
Bi32 = -2_000_000_000,
Ci32 = 3_000_000_000, //~ ERROR discriminant value outside specified type
}
// u64 currently allows negative numbers, and i64 allows numbers greater than `1<<63`. This is a
// little counterintuitive, but since the discriminant can store all the bits, and extracting it
// with a cast requires specifying the signedness, there is no loss of information in those cases.
// This also applies to int and uint on 64-bit targets.
pub fn main() { }

View File

@ -0,0 +1,25 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[deny(ctypes)];
enum Z { }
enum U { A }
enum B { C, D }
enum T { E, F, G }
extern {
fn zf(x: Z);
fn uf(x: U);
fn bf(x: B); //~ ERROR found enum type without foreign-function-safe
fn tf(x: T); //~ ERROR found enum type without foreign-function-safe
}
pub fn main() { }

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//error-pattern:discriminator value already exists
//error-pattern:discriminant value already exists
// black and white have the same discriminator value ...

View File

@ -15,7 +15,7 @@
// STACK BY REF
// debugger:finish
// debugger:print *self
// check:$1 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
// check:$1 = {{Variant2, [...]}, {Variant2, 117901063}}
// debugger:print arg1
// check:$2 = -1
// debugger:print arg2
@ -25,7 +25,7 @@
// STACK BY VAL
// debugger:finish
// d ebugger:print self -- ignored for now because of issue #8512
// c heck:$X = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
// c heck:$X = {{Variant2, [...]}, {Variant2, 117901063}}
// debugger:print arg1
// check:$4 = -3
// debugger:print arg2
@ -35,7 +35,7 @@
// OWNED BY REF
// debugger:finish
// debugger:print *self
// check:$6 = {{Variant1, x = 1799, y = 1799}, {Variant1, 117901063}}
// check:$6 = {{Variant1, x = 1799, y = 1799}, {Variant1, [...]}}
// debugger:print arg1
// check:$7 = -5
// debugger:print arg2
@ -45,7 +45,7 @@
// OWNED BY VAL
// debugger:finish
// d ebugger:print self -- ignored for now because of issue #8512
// c heck:$X = {{Variant1, x = 1799, y = 1799}, {Variant1, 117901063}}
// c heck:$X = {{Variant1, x = 1799, y = 1799}, {Variant1, [...]}}
// debugger:print arg1
// check:$9 = -7
// debugger:print arg2
@ -55,7 +55,7 @@
// OWNED MOVED
// debugger:finish
// debugger:print *self
// check:$11 = {{Variant1, x = 1799, y = 1799}, {Variant1, 117901063}}
// check:$11 = {{Variant1, x = 1799, y = 1799}, {Variant1, [...]}}
// debugger:print arg1
// check:$12 = -9
// debugger:print arg2
@ -65,7 +65,7 @@
// MANAGED BY REF
// debugger:finish
// debugger:print *self
// check:$14 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
// check:$14 = {{Variant2, [...]}, {Variant2, 117901063}}
// debugger:print arg1
// check:$15 = -11
// debugger:print arg2
@ -75,7 +75,7 @@
// MANAGED BY VAL
// debugger:finish
// d ebugger:print self -- ignored for now because of issue #8512
// c heck:$X = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
// c heck:$X = {{Variant2, [...]}, {Variant2, 117901063}}
// debugger:print arg1
// check:$17 = -13
// debugger:print arg2
@ -85,7 +85,7 @@
// MANAGED SELF
// debugger:finish
// debugger:print self->val
// check:$19 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
// check:$19 = {{Variant2, [...]}, {Variant2, 117901063}}
// debugger:print arg1
// check:$20 = -15
// debugger:print arg2

View File

@ -0,0 +1,62 @@
// 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.
use std::mem::size_of;
enum Ei8 {
Ai8 = -1,
Bi8 = 0
}
enum Eu8 {
Au8 = 0,
Bu8 = 0x80
}
enum Ei16 {
Ai16 = -1,
Bi16 = 0x80
}
enum Eu16 {
Au16 = 0,
Bu16 = 0x8000
}
enum Ei32 {
Ai32 = -1,
Bi32 = 0x8000
}
enum Eu32 {
Au32 = 0,
Bu32 = 0x8000_0000
}
enum Ei64 {
Ai64 = -1,
Bi64 = 0x8000_0000
}
enum Eu64 {
Au64 = 0,
Bu64 = 0x8000_0000_0000_0000
}
pub fn main() {
assert_eq!(size_of::<Ei8>(), 1);
assert_eq!(size_of::<Eu8>(), 1);
assert_eq!(size_of::<Ei16>(), 2);
assert_eq!(size_of::<Eu16>(), 2);
assert_eq!(size_of::<Ei32>(), 4);
assert_eq!(size_of::<Eu32>(), 4);
assert_eq!(size_of::<Ei64>(), 8);
assert_eq!(size_of::<Eu64>(), 8);
}

View File

@ -0,0 +1,84 @@
// 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.
use std::mem::size_of;
#[repr(i8)]
enum Ei8 {
Ai8 = 0,
Bi8 = 1
}
#[repr(u8)]
enum Eu8 {
Au8 = 0,
Bu8 = 1
}
#[repr(i16)]
enum Ei16 {
Ai16 = 0,
Bi16 = 1
}
#[repr(u16)]
enum Eu16 {
Au16 = 0,
Bu16 = 1
}
#[repr(i32)]
enum Ei32 {
Ai32 = 0,
Bi32 = 1
}
#[repr(u32)]
enum Eu32 {
Au32 = 0,
Bu32 = 1
}
#[repr(i64)]
enum Ei64 {
Ai64 = 0,
Bi64 = 1
}
#[repr(u64)]
enum Eu64 {
Au64 = 0,
Bu64 = 1
}
#[repr(int)]
enum Eint {
Aint = 0,
Bint = 1
}
#[repr(uint)]
enum Euint {
Auint = 0,
Buint = 1
}
pub fn main() {
assert_eq!(size_of::<Ei8>(), 1);
assert_eq!(size_of::<Eu8>(), 1);
assert_eq!(size_of::<Ei16>(), 2);
assert_eq!(size_of::<Eu16>(), 2);
assert_eq!(size_of::<Ei32>(), 4);
assert_eq!(size_of::<Eu32>(), 4);
assert_eq!(size_of::<Ei64>(), 8);
assert_eq!(size_of::<Eu64>(), 8);
assert_eq!(size_of::<Eint>(), size_of::<int>());
assert_eq!(size_of::<Euint>(), size_of::<uint>());
}

View File

@ -8,18 +8,48 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::mem;
#[feature(macro_rules)];
macro_rules! check {
($m:ident, $t:ty, $v:expr) => {{
mod $m {
use std::mem::size_of;
enum E {
V = $v,
A = 0
}
static C: E = V;
pub fn check() {
assert_eq!(size_of::<E>(), size_of::<$t>());
assert_eq!(V as $t, $v);
assert_eq!(C as $t, $v);
assert_eq!(format!("{:?}", V), ~"V");
assert_eq!(format!("{:?}", C), ~"V");
}
}
$m::check();
}}
}
pub fn main() {
enum E { V = 0x1717171717171717 }
static C: E = V;
let expected: u64 = if mem::size_of::<uint>() < 8 {
0x17171717
} else {
0x1717171717171717
};
assert_eq!(expected, V as u64);
assert_eq!(expected, C as u64);
assert_eq!(format!("{:?}", V), ~"V");
assert_eq!(format!("{:?}", C), ~"V");
check!(a, u8, 0x17);
check!(b, u8, 0xe8);
check!(c, u16, 0x1727);
check!(d, u16, 0xe8d8);
check!(e, u32, 0x17273747);
check!(f, u32, 0xe8d8c8b8);
check!(g, u64, 0x1727374757677787u64);
check!(h, u64, 0xe8d8c8b8a8988878u64);
check!(z, i8, 0x17);
check!(y, i8, -0x17);
check!(x, i16, 0x1727);
check!(w, i16, -0x1727);
check!(v, i32, 0x17273747);
check!(u, i32, -0x17273747);
check!(t, i64, 0x1727374757677787);
check!(s, i64, -0x1727374757677787);
enum Simple { A, B }
assert_eq!(::std::mem::size_of::<Simple>(), 1);
}

View File

@ -27,6 +27,7 @@ pub mod pipes {
}
#[deriving(Eq)]
#[repr(int)]
pub enum state {
empty,
full,

View File

@ -15,7 +15,7 @@
use std::libc::c_void;
use std::ptr;
use std::mem;
use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Opaque};
use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Disr, Opaque};
use std::unstable::raw::Vec;
#[doc = "High-level interfaces to `std::unstable::intrinsics::visit_ty` reflection system."]
@ -380,7 +380,7 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
}
fn visit_enter_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint)
-> bool {
self.align(align);
@ -389,7 +389,7 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
}
fn visit_enter_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool {
if ! self.inner.visit_enter_enum_variant(variant, disr_val,
@ -405,7 +405,7 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
}
fn visit_leave_enum_variant(&mut self, variant: uint,
disr_val: int,
disr_val: Disr,
n_fields: uint,
name: &str) -> bool {
if ! self.inner.visit_leave_enum_variant(variant, disr_val,
@ -416,7 +416,7 @@ impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {
}
fn visit_leave_enum(&mut self, n_variants: uint,
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
sz: uint, align: uint)
-> bool {
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) { return false; }
@ -578,24 +578,24 @@ impl TyVisitor for my_visitor {
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_enum(&mut self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint, _align: uint) -> bool {
// FIXME (#3732): this needs to rewind between enum variants, or something.
true
}
fn visit_enter_enum_variant(&mut self, _variant: uint,
_disr_val: int,
_disr_val: Disr,
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool {
self.visit_inner(inner)
}
fn visit_leave_enum_variant(&mut self, _variant: uint,
_disr_val: int,
_disr_val: Disr,
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_leave_enum(&mut self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,

View File

@ -10,7 +10,7 @@
#[feature(managed_boxes)];
use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Opaque};
use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Disr, Opaque};
struct MyVisitor {
types: @mut ~[~str],
@ -114,22 +114,22 @@ impl TyVisitor for MyVisitor {
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_enum(&mut self, _n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_enum_variant(&mut self,
_variant: uint,
_disr_val: int,
_disr_val: Disr,
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, _inner: *TyDesc) -> bool { true }
fn visit_leave_enum_variant(&mut self,
_variant: uint,
_disr_val: int,
_disr_val: Disr,
_n_fields: uint,
_name: &str) -> bool { true }
fn visit_leave_enum(&mut self,
_n_variants: uint,
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
_sz: uint, _align: uint) -> bool { true }
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,

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);
}

View File

@ -0,0 +1,41 @@
// 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.
#[feature(macro_rules)];
use std::mem::size_of;
macro_rules! check {
($t:ty, $sz:expr, $($e:expr, $s:expr),*) => {{
assert_eq!(size_of::<$t>(), $sz);
$({
static S: $t = $e;
let v: $t = $e;
assert_eq!(S, v);
assert_eq!(format!("{:?}", v), ~$s);
assert_eq!(format!("{:?}", S), ~$s);
});*
}}
}
pub fn main() {
check!(Option<u8>, 2,
None, "None",
Some(129u8), "Some(129u8)");
check!(Option<i16>, 4,
None, "None",
Some(-20000i16), "Some(-20000i16)");
check!(Either<u8, i8>, 2,
Left(132u8), "Left(132u8)",
Right(-32i8), "Right(-32i8)");
check!(Either<u8, i16>, 4,
Left(132u8), "Left(132u8)",
Right(-20000i16), "Right(-20000i16)");
}