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:
commit
5e1a691125
11
mk/tests.mk
11
mk/tests.mk
|
@ -465,6 +465,17 @@ $(foreach host,$(CFG_HOST_TRIPLES), \
|
||||||
$(eval $(call DEF_TEST_CRATE_RULES,$(stage),$(target),$(host),$(crate))) \
|
$(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.)
|
# Rules for the compiletest tests (rpass, rfail, etc.)
|
||||||
|
|
|
@ -281,7 +281,7 @@ fn run_debuginfo_test(config: &config, props: &TestProps, testfile: &Path) {
|
||||||
};
|
};
|
||||||
let config = &mut config;
|
let config = &mut config;
|
||||||
let cmds = props.debugger_cmds.connect("\n");
|
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)
|
// compile test file (it shoud have 'compile-flags:-g' in the header)
|
||||||
let mut ProcRes = compile_test(config, props, testfile);
|
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();
|
let num_check_lines = check_lines.len();
|
||||||
if num_check_lines > 0 {
|
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
|
// check if each line in props.check_lines appears in the
|
||||||
// output (in order)
|
// output (in order)
|
||||||
let mut i = 0u;
|
let mut i = 0u;
|
||||||
for line in ProcRes.stdout.line_iter() {
|
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;
|
i += 1u;
|
||||||
}
|
}
|
||||||
if i == num_check_lines {
|
if i == num_check_lines {
|
||||||
|
|
|
@ -140,6 +140,7 @@ mod test {
|
||||||
use enum_set::*;
|
use enum_set::*;
|
||||||
|
|
||||||
#[deriving(Eq)]
|
#[deriving(Eq)]
|
||||||
|
#[repr(uint)]
|
||||||
enum Foo {
|
enum Foo {
|
||||||
A, B, C
|
A, B, C
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,7 @@ pub static Vector: TypeKind = 13;
|
||||||
pub static Metadata: TypeKind = 14;
|
pub static Metadata: TypeKind = 14;
|
||||||
pub static X86_MMX: TypeKind = 15;
|
pub static X86_MMX: TypeKind = 15;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub enum AtomicBinOp {
|
pub enum AtomicBinOp {
|
||||||
Xchg = 0,
|
Xchg = 0,
|
||||||
Add = 1,
|
Add = 1,
|
||||||
|
@ -161,6 +162,7 @@ pub enum AtomicBinOp {
|
||||||
UMin = 10,
|
UMin = 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub enum AtomicOrdering {
|
pub enum AtomicOrdering {
|
||||||
NotAtomic = 0,
|
NotAtomic = 0,
|
||||||
Unordered = 1,
|
Unordered = 1,
|
||||||
|
@ -173,6 +175,7 @@ pub enum AtomicOrdering {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consts for the LLVMCodeGenFileType type (in include/llvm/c/TargetMachine.h)
|
// Consts for the LLVMCodeGenFileType type (in include/llvm/c/TargetMachine.h)
|
||||||
|
#[repr(C)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
AssemblyFile = 0,
|
AssemblyFile = 0,
|
||||||
ObjectFile = 1
|
ObjectFile = 1
|
||||||
|
@ -194,6 +197,7 @@ pub enum AsmDialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq)]
|
#[deriving(Eq)]
|
||||||
|
#[repr(C)]
|
||||||
pub enum CodeGenOptLevel {
|
pub enum CodeGenOptLevel {
|
||||||
CodeGenLevelNone = 0,
|
CodeGenLevelNone = 0,
|
||||||
CodeGenLevelLess = 1,
|
CodeGenLevelLess = 1,
|
||||||
|
@ -201,6 +205,7 @@ pub enum CodeGenOptLevel {
|
||||||
CodeGenLevelAggressive = 3,
|
CodeGenLevelAggressive = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub enum RelocMode {
|
pub enum RelocMode {
|
||||||
RelocDefault = 0,
|
RelocDefault = 0,
|
||||||
RelocStatic = 1,
|
RelocStatic = 1,
|
||||||
|
@ -208,6 +213,7 @@ pub enum RelocMode {
|
||||||
RelocDynamicNoPic = 3,
|
RelocDynamicNoPic = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
pub enum CodeGenModel {
|
pub enum CodeGenModel {
|
||||||
CodeModelDefault = 0,
|
CodeModelDefault = 0,
|
||||||
CodeModelJITDefault = 1,
|
CodeModelJITDefault = 1,
|
||||||
|
|
|
@ -112,6 +112,7 @@ pub static tag_items_data_item_reexport_name: uint = 0x4f;
|
||||||
|
|
||||||
// used to encode crate_ctxt side tables
|
// used to encode crate_ctxt side tables
|
||||||
#[deriving(Eq)]
|
#[deriving(Eq)]
|
||||||
|
#[repr(uint)]
|
||||||
pub enum astencode_tag { // Reserves 0x50 -- 0x6f
|
pub enum astencode_tag { // Reserves 0x50 -- 0x6f
|
||||||
tag_ast = 0x50,
|
tag_ast = 0x50,
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ impl astencode_tag {
|
||||||
pub fn from_uint(value : uint) -> Option<astencode_tag> {
|
pub fn from_uint(value : uint) -> Option<astencode_tag> {
|
||||||
let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag;
|
let is_a_tag = first_astencode_tag <= value && value <= last_astencode_tag;
|
||||||
if !is_a_tag { None } else {
|
if !is_a_tag { None } else {
|
||||||
Some(unsafe { cast::transmute(value as int) })
|
Some(unsafe { cast::transmute(value) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -989,6 +989,7 @@ fn encode_info_for_item(ecx: &EncodeContext,
|
||||||
encode_family(ebml_w, 't');
|
encode_family(ebml_w, 't');
|
||||||
encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id));
|
encode_bounds_and_type(ebml_w, ecx, &lookup_item_type(tcx, def_id));
|
||||||
encode_name(ecx, ebml_w, item.ident);
|
encode_name(ecx, ebml_w, item.ident);
|
||||||
|
encode_attributes(ebml_w, item.attrs);
|
||||||
for v in (*enum_definition).variants.iter() {
|
for v in (*enum_definition).variants.iter() {
|
||||||
encode_variant_id(ebml_w, local_def(v.node.id));
|
encode_variant_id(ebml_w, local_def(v.node.id));
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
//! Context itself, span_lint should be used instead of add_lint.
|
//! Context itself, span_lint should be used instead of add_lint.
|
||||||
|
|
||||||
use driver::session;
|
use driver::session;
|
||||||
|
use middle::trans::adt; // for `adt::is_ffi_safe`
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
use metadata::csearch;
|
use metadata::csearch;
|
||||||
|
@ -627,6 +628,14 @@ fn check_item_ctypes(cx: &Context, it: &ast::item) {
|
||||||
"found rust type `uint` in foreign module, while \
|
"found rust type `uint` in foreign module, while \
|
||||||
libc::c_uint or libc::c_ulong should be used");
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -141,38 +145,26 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
|
||||||
return Univariant(mk_struct(cx, ftys, packed), dtor)
|
return Univariant(mk_struct(cx, ftys, packed), dtor)
|
||||||
}
|
}
|
||||||
ty::ty_enum(def_id, ref substs) => {
|
ty::ty_enum(def_id, ref substs) => {
|
||||||
struct Case { discr: Disr, tys: ~[ty::t] };
|
let cases = get_cases(cx.tcx, def_id, substs);
|
||||||
impl Case {
|
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
|
||||||
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 }
|
|
||||||
};
|
|
||||||
|
|
||||||
if cases.len() == 0 {
|
if cases.len() == 0 {
|
||||||
// Uninhabitable; represent as unit
|
// Uninhabitable; represent as unit
|
||||||
|
// (Typechecking will reject discriminant-sizing attrs.)
|
||||||
|
assert_eq!(hint, attr::ReprAny);
|
||||||
return Univariant(mk_struct(cx, [], false), false);
|
return Univariant(mk_struct(cx, [], false), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 bounds = IntBounds {
|
||||||
}
|
ulo: *discrs.iter().min().unwrap(),
|
||||||
|
uhi: *discrs.iter().max().unwrap(),
|
||||||
if cases.len() == 1 {
|
slo: discrs.iter().map(|n| *n as i64).min().unwrap(),
|
||||||
// Equivalent to a struct/tuple/newtype.
|
shi: discrs.iter().map(|n| *n as i64).max().unwrap()
|
||||||
assert_eq!(cases[0].discr, 0);
|
};
|
||||||
return Univariant(mk_struct(cx, cases[0].tys, false), false)
|
return mk_cenum(cx, hint, &bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since there's at least one
|
// 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)))
|
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;
|
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,13 +207,67 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The general case.
|
// The general case.
|
||||||
let discr = ~[ty::mk_uint()];
|
assert!((cases.len() - 1) as i64 >= 0);
|
||||||
return General(cases.map(|c| mk_struct(cx, discr + c.tys, false)))
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
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 lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
|
||||||
let llty_rec = Type::struct_(lltys, packed);
|
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.
|
* 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,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] {
|
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
|
// We need a representation that has:
|
||||||
// with the largest alignment (so it will always align
|
// * The alignment of the most-aligned field
|
||||||
// correctly in containing structures) and pad it out.
|
// * The size of the largest variant (rounded up to that alignment)
|
||||||
assert!(sts.len() >= 1);
|
// * No alignment padding anywhere any variant has actual data
|
||||||
let mut most_aligned = None;
|
// (currently matters only for enums small enough to be immediate)
|
||||||
let mut largest_align = 0;
|
// * The discriminant in an obvious place.
|
||||||
let mut largest_size = 0;
|
//
|
||||||
for st in sts.iter() {
|
// So we start with the discriminant, pad it up to the alignment with
|
||||||
if largest_size < st.size {
|
// more of its own type, then use alignment-sized ints to get the rest
|
||||||
largest_size = st.size;
|
// of the size.
|
||||||
}
|
//
|
||||||
if largest_align < st.align {
|
// Note: if/when we start exposing SIMD vector types (or f80, on some
|
||||||
// Clang breaks ties by size; it is unclear if
|
// platforms that have it), this will need some adjustment.
|
||||||
// that accomplishes anything important.
|
let size = sts.iter().map(|st| st.size).max().unwrap();
|
||||||
largest_align = st.align;
|
let most_aligned = sts.iter().max_by(|st| st.align).unwrap();
|
||||||
most_aligned = Some(st);
|
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 most_aligned = most_aligned.unwrap();
|
let pad_ty = match align {
|
||||||
let padding = largest_size - most_aligned.size;
|
1 => Type::i8(),
|
||||||
|
2 => Type::i16(),
|
||||||
struct_llfields(cx, most_aligned, sizing)
|
4 => Type::i32(),
|
||||||
+ &[Type::array(&Type::i8(), padding)]
|
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::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 +450,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 +487,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 +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 {
|
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 +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) {
|
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 +560,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 +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
|
* 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 +588,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 +609,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 +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,
|
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 +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)
|
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 +834,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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -868,6 +868,10 @@ pub fn C_i64(i: i64) -> ValueRef {
|
||||||
return C_integral(Type::i64(), i as u64, true);
|
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 {
|
pub fn C_int(cx: &CrateContext, i: int) -> ValueRef {
|
||||||
return C_integral(cx.int_type, i as u64, true);
|
return C_integral(cx.int_type, i as u64, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1251,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")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1400,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
|
||||||
|
@ -1427,26 +1419,32 @@ 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, adt::ty_of_inttype(inttype),
|
||||||
containing_scope,
|
codemap::dummy_sp());
|
||||||
enum_name,
|
do enum_name.with_c_str |enum_name| {
|
||||||
file_metadata,
|
unsafe {
|
||||||
loc.line as c_uint,
|
llvm::LLVMDIBuilderCreateEnumerationType(
|
||||||
bytes_to_bits(discriminant_size),
|
DIB(cx),
|
||||||
bytes_to_bits(discriminant_align),
|
containing_scope,
|
||||||
create_DIArray(DIB(cx), enumerators_metadata),
|
enum_name,
|
||||||
discriminant_base_type_metadata)
|
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);
|
||||||
|
@ -1467,7 +1465,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);
|
||||||
|
|
||||||
|
|
|
@ -1727,9 +1727,14 @@ fn trans_imm_cast(bcx: @mut Block, expr: &ast::Expr,
|
||||||
(cast_enum, cast_float) => {
|
(cast_enum, cast_float) => {
|
||||||
let bcx = bcx;
|
let bcx = bcx;
|
||||||
let repr = adt::represent_type(ccx, t_in);
|
let repr = adt::represent_type(ccx, t_in);
|
||||||
let slot = Alloca(bcx, ll_t_in, "");
|
let llexpr_ptr;
|
||||||
Store(bcx, llexpr, slot);
|
if type_is_immediate(ccx, t_in) {
|
||||||
let lldiscrim_a = adt::trans_get_discr(bcx, repr, slot);
|
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 {
|
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;
|
||||||
|
@ -292,11 +292,11 @@ impl Reflector {
|
||||||
sub_path,
|
sub_path,
|
||||||
"get_disr");
|
"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,
|
let fcx = new_fn_ctxt(ccx,
|
||||||
~[],
|
~[],
|
||||||
llfdecl,
|
llfdecl,
|
||||||
ty::mk_uint(),
|
ty::mk_u64(),
|
||||||
None);
|
None);
|
||||||
let arg = unsafe {
|
let arg = unsafe {
|
||||||
//
|
//
|
||||||
|
@ -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(Type::i64()));
|
||||||
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),
|
||||||
|
@ -324,7 +324,7 @@ impl Reflector {
|
||||||
for (i, v) in variants.iter().enumerate() {
|
for (i, v) in variants.iter().enumerate() {
|
||||||
let name = ccx.sess.str_of(v.name);
|
let name = ccx.sess.str_of(v.name);
|
||||||
let variant_args = ~[this.c_uint(i),
|
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_uint(v.args.len()),
|
||||||
this.c_slice(name)];
|
this.c_slice(name)];
|
||||||
do this.bracketed("enum_variant", variant_args) |this| {
|
do this.bracketed("enum_variant", variant_args) |this| {
|
||||||
|
|
|
@ -38,6 +38,7 @@ use syntax::ast::*;
|
||||||
use syntax::ast_util::is_local;
|
use syntax::ast_util::is_local;
|
||||||
use syntax::ast_util;
|
use syntax::ast_util;
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
use syntax::attr::AttrMetaMethods;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use syntax::parse::token;
|
use syntax::parse::token;
|
||||||
|
@ -715,6 +716,7 @@ pub struct ParamBounds {
|
||||||
pub type BuiltinBounds = EnumSet<BuiltinBound>;
|
pub type BuiltinBounds = EnumSet<BuiltinBound>;
|
||||||
|
|
||||||
#[deriving(Clone, Eq, IterBytes, ToStr)]
|
#[deriving(Clone, Eq, IterBytes, ToStr)]
|
||||||
|
#[repr(uint)]
|
||||||
pub enum BuiltinBound {
|
pub enum BuiltinBound {
|
||||||
BoundStatic,
|
BoundStatic,
|
||||||
BoundSend,
|
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
|
/// Iterate over meta_items of a definition.
|
||||||
pub fn has_attr(tcx: ctxt, did: DefId, attr: &str) -> bool {
|
// (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) {
|
if is_local(did) {
|
||||||
match tcx.items.find(&did.node) {
|
match tcx.items.find(&did.node) {
|
||||||
Some(
|
Some(&ast_map::node_item(@ast::item {attrs: ref attrs, _}, _)) =>
|
||||||
&ast_map::node_item(@ast::item {
|
attrs.iter().advance(|attr| f(attr.node.value)),
|
||||||
attrs: ref attrs,
|
|
||||||
_
|
|
||||||
}, _)) => attr::contains_name(*attrs, attr),
|
|
||||||
_ => tcx.sess.bug(format!("has_attr: {:?} is not an item",
|
_ => tcx.sess.bug(format!("has_attr: {:?} is not an item",
|
||||||
did))
|
did))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut ret = false;
|
let mut cont = true;
|
||||||
do csearch::get_item_attrs(tcx.cstore, did) |meta_items| {
|
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]`
|
/// Determine whether an item is annotated with `#[packed]`
|
||||||
pub fn lookup_packed(tcx: ctxt, did: DefId) -> bool {
|
pub fn lookup_packed(tcx: ctxt, did: DefId) -> bool {
|
||||||
has_attr(tcx, did, "packed")
|
has_attr(tcx, did, "packed")
|
||||||
|
@ -4133,6 +4150,16 @@ pub fn lookup_simd(tcx: ctxt, did: DefId) -> bool {
|
||||||
has_attr(tcx, did, "simd")
|
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
|
// Look up a field ID, whether or not it's local
|
||||||
// Takes a list of type substs in case the struct is generic
|
// Takes a list of type substs in case the struct is generic
|
||||||
pub fn lookup_field_type(tcx: ctxt,
|
pub fn lookup_field_type(tcx: ctxt,
|
||||||
|
|
|
@ -121,6 +121,7 @@ use syntax::ast;
|
||||||
use syntax::ast_map;
|
use syntax::ast_map;
|
||||||
use syntax::ast_util::local_def;
|
use syntax::ast_util::local_def;
|
||||||
use syntax::ast_util;
|
use syntax::ast_util;
|
||||||
|
use syntax::attr;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::codemap;
|
use syntax::codemap;
|
||||||
use syntax::opt_vec::OptVec;
|
use syntax::opt_vec::OptVec;
|
||||||
|
@ -3169,9 +3170,38 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
vs: &[ast::variant],
|
vs: &[ast::variant],
|
||||||
id: ast::NodeId) {
|
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,
|
fn do_check(ccx: @mut CrateCtxt,
|
||||||
vs: &[ast::variant],
|
vs: &[ast::variant],
|
||||||
id: ast::NodeId)
|
id: ast::NodeId,
|
||||||
|
hint: attr::ReprAttr)
|
||||||
-> ~[@ty::VariantInfo] {
|
-> ~[@ty::VariantInfo] {
|
||||||
|
|
||||||
let rty = ty::node_id_to_type(ccx.tcx, id);
|
let rty = ty::node_id_to_type(ccx.tcx, id);
|
||||||
|
@ -3213,9 +3243,20 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
|
||||||
None => ()
|
None => ()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for duplicate discriminator values
|
// Check for duplicate discriminant values
|
||||||
if disr_vals.contains(¤t_disr_val) {
|
if disr_vals.contains(¤t_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);
|
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 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
|
// cache so that ty::enum_variants won't repeat this work
|
||||||
ccx.tcx.enum_var_cache.insert(local_def(id), @variants);
|
ccx.tcx.enum_var_cache.insert(local_def(id), @variants);
|
||||||
|
|
|
@ -29,7 +29,8 @@ pub static bits : uint = $bits;
|
||||||
pub static bytes : uint = ($bits / 8);
|
pub static bytes : uint = ($bits / 8);
|
||||||
|
|
||||||
pub static min_value: $T = (-1 as $T) << (bits - 1);
|
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 {
|
impl CheckedDiv for $T {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -16,7 +16,7 @@ Runtime type reflection
|
||||||
|
|
||||||
#[allow(missing_doc)];
|
#[allow(missing_doc)];
|
||||||
|
|
||||||
use unstable::intrinsics::{Opaque, TyDesc, TyVisitor};
|
use unstable::intrinsics::{Disr, Opaque, TyDesc, TyVisitor};
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use mem;
|
use mem;
|
||||||
use unstable::raw;
|
use unstable::raw;
|
||||||
|
@ -396,7 +396,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_enter_enum(&mut self, n_variants: uint,
|
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)
|
sz: uint, align: uint)
|
||||||
-> bool {
|
-> bool {
|
||||||
self.align(align);
|
self.align(align);
|
||||||
|
@ -407,7 +407,7 @@ impl<V:TyVisitor + MovePtr> TyVisitor for MovePtrAdaptor<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_enter_enum_variant(&mut self, variant: uint,
|
fn visit_enter_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool {
|
name: &str) -> bool {
|
||||||
if ! self.inner.visit_enter_enum_variant(variant, disr_val,
|
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,
|
fn visit_leave_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool {
|
name: &str) -> bool {
|
||||||
if ! self.inner.visit_leave_enum_variant(variant, disr_val,
|
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,
|
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 {
|
sz: uint, align: uint) -> bool {
|
||||||
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) {
|
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -29,7 +29,7 @@ use reflect::{MovePtr, align};
|
||||||
use str::StrSlice;
|
use str::StrSlice;
|
||||||
use to_str::ToStr;
|
use to_str::ToStr;
|
||||||
use vec::OwnedVector;
|
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;
|
use unstable::raw;
|
||||||
|
|
||||||
/// Representations
|
/// Representations
|
||||||
|
@ -92,7 +92,7 @@ num_repr!(f64, "f64")
|
||||||
// New implementation using reflect::MovePtr
|
// New implementation using reflect::MovePtr
|
||||||
|
|
||||||
enum VariantState {
|
enum VariantState {
|
||||||
SearchingFor(int),
|
SearchingFor(Disr),
|
||||||
Matched,
|
Matched,
|
||||||
AlreadyFound
|
AlreadyFound
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
|
||||||
|
|
||||||
fn visit_enter_enum(&mut self,
|
fn visit_enter_enum(&mut self,
|
||||||
_n_variants: uint,
|
_n_variants: uint,
|
||||||
get_disr: extern unsafe fn(ptr: *Opaque) -> int,
|
get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
|
||||||
_sz: uint,
|
_sz: uint,
|
||||||
_align: uint) -> bool {
|
_align: uint) -> bool {
|
||||||
let disr = unsafe {
|
let disr = unsafe {
|
||||||
|
@ -484,7 +484,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_enter_enum_variant(&mut self, _variant: uint,
|
fn visit_enter_enum_variant(&mut self, _variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool {
|
name: &str) -> bool {
|
||||||
let mut write = false;
|
let mut write = false;
|
||||||
|
@ -531,7 +531,7 @@ impl<'self> TyVisitor for ReprVisitor<'self> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_leave_enum_variant(&mut self, _variant: uint,
|
fn visit_leave_enum_variant(&mut self, _variant: uint,
|
||||||
_disr_val: int,
|
_disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
_name: &str) -> bool {
|
_name: &str) -> bool {
|
||||||
match self.var_stk[self.var_stk.len() - 1] {
|
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,
|
fn visit_leave_enum(&mut self,
|
||||||
_n_variants: uint,
|
_n_variants: uint,
|
||||||
_get_disr: extern unsafe fn(ptr: *Opaque) -> int,
|
_get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
|
||||||
_sz: uint,
|
_sz: uint,
|
||||||
_align: uint)
|
_align: uint)
|
||||||
-> bool {
|
-> bool {
|
||||||
|
|
|
@ -26,6 +26,7 @@ use result::{Err, Ok};
|
||||||
use rt::io::io_error;
|
use rt::io::io_error;
|
||||||
use rt::rtio::{IoFactory, RtioSignal, with_local_io};
|
use rt::rtio::{IoFactory, RtioSignal, with_local_io};
|
||||||
|
|
||||||
|
#[repr(int)]
|
||||||
#[deriving(Eq, IterBytes)]
|
#[deriving(Eq, IterBytes)]
|
||||||
pub enum Signum {
|
pub enum Signum {
|
||||||
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
|
/// Equivalent to SIGBREAK, delivered when the user presses Ctrl-Break.
|
||||||
|
|
|
@ -75,6 +75,11 @@ pub struct TyDesc {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub enum Opaque { }
|
pub enum Opaque { }
|
||||||
|
|
||||||
|
#[cfg(stage0)]
|
||||||
|
pub type Disr = int;
|
||||||
|
#[cfg(not(stage0))]
|
||||||
|
pub type Disr = u64;
|
||||||
|
|
||||||
#[lang="ty_visitor"]
|
#[lang="ty_visitor"]
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
pub trait TyVisitor {
|
pub trait TyVisitor {
|
||||||
|
@ -140,19 +145,19 @@ pub trait TyVisitor {
|
||||||
sz: uint, align: uint) -> bool;
|
sz: uint, align: uint) -> bool;
|
||||||
|
|
||||||
fn visit_enter_enum(&mut self, n_variants: uint,
|
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;
|
sz: uint, align: uint) -> bool;
|
||||||
fn visit_enter_enum_variant(&mut self, variant: uint,
|
fn visit_enter_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool;
|
name: &str) -> bool;
|
||||||
fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool;
|
fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool;
|
||||||
fn visit_leave_enum_variant(&mut self, variant: uint,
|
fn visit_leave_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool;
|
name: &str) -> bool;
|
||||||
fn visit_leave_enum(&mut self, n_variants: uint,
|
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;
|
sz: uint, align: uint) -> bool;
|
||||||
|
|
||||||
fn visit_enter_fn(&mut self, purity: uint, proto: uint,
|
fn visit_enter_fn(&mut self, purity: uint, proto: uint,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use extra;
|
||||||
|
|
||||||
use ast;
|
use ast;
|
||||||
use ast::{Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
|
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 codemap::BytePos;
|
||||||
use diagnostic::span_handler;
|
use diagnostic::span_handler;
|
||||||
use parse::comments::{doc_comment_style, strip_doc_comment_decoration};
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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() { }
|
|
@ -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() { }
|
|
@ -8,7 +8,7 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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 ...
|
// black and white have the same discriminator value ...
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// STACK BY REF
|
// STACK BY REF
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// debugger:print *self
|
// debugger:print *self
|
||||||
// check:$1 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
|
// check:$1 = {{Variant2, [...]}, {Variant2, 117901063}}
|
||||||
// debugger:print arg1
|
// debugger:print arg1
|
||||||
// check:$2 = -1
|
// check:$2 = -1
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
// STACK BY VAL
|
// STACK BY VAL
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// d ebugger:print self -- ignored for now because of issue #8512
|
// 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
|
// debugger:print arg1
|
||||||
// check:$4 = -3
|
// check:$4 = -3
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
// OWNED BY REF
|
// OWNED BY REF
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// debugger:print *self
|
// debugger:print *self
|
||||||
// check:$6 = {{Variant1, x = 1799, y = 1799}, {Variant1, 117901063}}
|
// check:$6 = {{Variant1, x = 1799, y = 1799}, {Variant1, [...]}}
|
||||||
// debugger:print arg1
|
// debugger:print arg1
|
||||||
// check:$7 = -5
|
// check:$7 = -5
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
// OWNED BY VAL
|
// OWNED BY VAL
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// d ebugger:print self -- ignored for now because of issue #8512
|
// 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
|
// debugger:print arg1
|
||||||
// check:$9 = -7
|
// check:$9 = -7
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
// OWNED MOVED
|
// OWNED MOVED
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// debugger:print *self
|
// debugger:print *self
|
||||||
// check:$11 = {{Variant1, x = 1799, y = 1799}, {Variant1, 117901063}}
|
// check:$11 = {{Variant1, x = 1799, y = 1799}, {Variant1, [...]}}
|
||||||
// debugger:print arg1
|
// debugger:print arg1
|
||||||
// check:$12 = -9
|
// check:$12 = -9
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -65,7 +65,7 @@
|
||||||
// MANAGED BY REF
|
// MANAGED BY REF
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// debugger:print *self
|
// debugger:print *self
|
||||||
// check:$14 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
|
// check:$14 = {{Variant2, [...]}, {Variant2, 117901063}}
|
||||||
// debugger:print arg1
|
// debugger:print arg1
|
||||||
// check:$15 = -11
|
// check:$15 = -11
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
// MANAGED BY VAL
|
// MANAGED BY VAL
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// d ebugger:print self -- ignored for now because of issue #8512
|
// 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
|
// debugger:print arg1
|
||||||
// check:$17 = -13
|
// check:$17 = -13
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
// MANAGED SELF
|
// MANAGED SELF
|
||||||
// debugger:finish
|
// debugger:finish
|
||||||
// debugger:print self->val
|
// debugger:print self->val
|
||||||
// check:$19 = {{Variant2, x = 1799, y = 1799}, {Variant2, 117901063}}
|
// check:$19 = {{Variant2, [...]}, {Variant2, 117901063}}
|
||||||
// debugger:print arg1
|
// debugger:print arg1
|
||||||
// check:$20 = -15
|
// check:$20 = -15
|
||||||
// debugger:print arg2
|
// debugger:print arg2
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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>());
|
||||||
|
}
|
|
@ -8,18 +8,48 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// 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() {
|
pub fn main() {
|
||||||
enum E { V = 0x1717171717171717 }
|
check!(a, u8, 0x17);
|
||||||
static C: E = V;
|
check!(b, u8, 0xe8);
|
||||||
let expected: u64 = if mem::size_of::<uint>() < 8 {
|
check!(c, u16, 0x1727);
|
||||||
0x17171717
|
check!(d, u16, 0xe8d8);
|
||||||
} else {
|
check!(e, u32, 0x17273747);
|
||||||
0x1717171717171717
|
check!(f, u32, 0xe8d8c8b8);
|
||||||
};
|
check!(g, u64, 0x1727374757677787u64);
|
||||||
assert_eq!(expected, V as u64);
|
check!(h, u64, 0xe8d8c8b8a8988878u64);
|
||||||
assert_eq!(expected, C as u64);
|
|
||||||
assert_eq!(format!("{:?}", V), ~"V");
|
check!(z, i8, 0x17);
|
||||||
assert_eq!(format!("{:?}", C), ~"V");
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mod pipes {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Eq)]
|
#[deriving(Eq)]
|
||||||
|
#[repr(int)]
|
||||||
pub enum state {
|
pub enum state {
|
||||||
empty,
|
empty,
|
||||||
full,
|
full,
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
use std::libc::c_void;
|
use std::libc::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::mem;
|
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;
|
use std::unstable::raw::Vec;
|
||||||
|
|
||||||
#[doc = "High-level interfaces to `std::unstable::intrinsics::visit_ty` reflection system."]
|
#[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,
|
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)
|
sz: uint, align: uint)
|
||||||
-> bool {
|
-> bool {
|
||||||
self.align(align);
|
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,
|
fn visit_enter_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool {
|
name: &str) -> bool {
|
||||||
if ! self.inner.visit_enter_enum_variant(variant, disr_val,
|
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,
|
fn visit_leave_enum_variant(&mut self, variant: uint,
|
||||||
disr_val: int,
|
disr_val: Disr,
|
||||||
n_fields: uint,
|
n_fields: uint,
|
||||||
name: &str) -> bool {
|
name: &str) -> bool {
|
||||||
if ! self.inner.visit_leave_enum_variant(variant, disr_val,
|
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,
|
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)
|
sz: uint, align: uint)
|
||||||
-> bool {
|
-> bool {
|
||||||
if ! self.inner.visit_leave_enum(n_variants, get_disr, sz, align) { return false; }
|
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 }
|
_sz: uint, _align: uint) -> bool { true }
|
||||||
|
|
||||||
fn visit_enter_enum(&mut self, _n_variants: uint,
|
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 {
|
_sz: uint, _align: uint) -> bool {
|
||||||
// FIXME (#3732): this needs to rewind between enum variants, or something.
|
// FIXME (#3732): this needs to rewind between enum variants, or something.
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
fn visit_enter_enum_variant(&mut self, _variant: uint,
|
fn visit_enter_enum_variant(&mut self, _variant: uint,
|
||||||
_disr_val: int,
|
_disr_val: Disr,
|
||||||
_n_fields: uint,
|
_n_fields: uint,
|
||||||
_name: &str) -> bool { true }
|
_name: &str) -> bool { true }
|
||||||
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool {
|
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool {
|
||||||
self.visit_inner(inner)
|
self.visit_inner(inner)
|
||||||
}
|
}
|
||||||
fn visit_leave_enum_variant(&mut self, _variant: uint,
|
fn visit_leave_enum_variant(&mut self, _variant: uint,
|
||||||
_disr_val: int,
|
_disr_val: Disr,
|
||||||
_n_fields: uint,
|
_n_fields: uint,
|
||||||
_name: &str) -> bool { true }
|
_name: &str) -> bool { true }
|
||||||
fn visit_leave_enum(&mut self, _n_variants: uint,
|
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 }
|
_sz: uint, _align: uint) -> bool { true }
|
||||||
|
|
||||||
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#[feature(managed_boxes)];
|
#[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 {
|
struct MyVisitor {
|
||||||
types: @mut ~[~str],
|
types: @mut ~[~str],
|
||||||
|
@ -114,22 +114,22 @@ impl TyVisitor for MyVisitor {
|
||||||
_sz: uint, _align: uint) -> bool { true }
|
_sz: uint, _align: uint) -> bool { true }
|
||||||
|
|
||||||
fn visit_enter_enum(&mut self, _n_variants: uint,
|
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 }
|
_sz: uint, _align: uint) -> bool { true }
|
||||||
fn visit_enter_enum_variant(&mut self,
|
fn visit_enter_enum_variant(&mut self,
|
||||||
_variant: uint,
|
_variant: uint,
|
||||||
_disr_val: int,
|
_disr_val: Disr,
|
||||||
_n_fields: uint,
|
_n_fields: uint,
|
||||||
_name: &str) -> bool { true }
|
_name: &str) -> bool { true }
|
||||||
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, _inner: *TyDesc) -> bool { true }
|
fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, _inner: *TyDesc) -> bool { true }
|
||||||
fn visit_leave_enum_variant(&mut self,
|
fn visit_leave_enum_variant(&mut self,
|
||||||
_variant: uint,
|
_variant: uint,
|
||||||
_disr_val: int,
|
_disr_val: Disr,
|
||||||
_n_fields: uint,
|
_n_fields: uint,
|
||||||
_name: &str) -> bool { true }
|
_name: &str) -> bool { true }
|
||||||
fn visit_leave_enum(&mut self,
|
fn visit_leave_enum(&mut self,
|
||||||
_n_variants: uint,
|
_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 }
|
_sz: uint, _align: uint) -> bool { true }
|
||||||
|
|
||||||
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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)");
|
||||||
|
}
|
Loading…
Reference in New Issue