Implementation of #[repr(packed(n))]
RFC 1399.
This commit is contained in:
parent
ca26ef321c
commit
15d1c4d213
26 changed files with 849 additions and 167 deletions
|
@ -0,0 +1,8 @@
|
||||||
|
# `repr_packed`
|
||||||
|
|
||||||
|
The tracking issue for this feature is [#33158]
|
||||||
|
|
||||||
|
[#33158]: https://github.com/rust-lang/rust/issues/33158
|
||||||
|
|
||||||
|
------------------------
|
||||||
|
|
|
@ -62,6 +62,7 @@ pub struct TypeSizeInfo {
|
||||||
pub type_description: String,
|
pub type_description: String,
|
||||||
pub align: u64,
|
pub align: u64,
|
||||||
pub overall_size: u64,
|
pub overall_size: u64,
|
||||||
|
pub packed: bool,
|
||||||
pub opt_discr_size: Option<u64>,
|
pub opt_discr_size: Option<u64>,
|
||||||
pub variants: Vec<VariantInfo>,
|
pub variants: Vec<VariantInfo>,
|
||||||
}
|
}
|
||||||
|
@ -79,6 +80,7 @@ impl CodeStats {
|
||||||
type_desc: S,
|
type_desc: S,
|
||||||
align: Align,
|
align: Align,
|
||||||
overall_size: Size,
|
overall_size: Size,
|
||||||
|
packed: bool,
|
||||||
opt_discr_size: Option<Size>,
|
opt_discr_size: Option<Size>,
|
||||||
variants: Vec<VariantInfo>) {
|
variants: Vec<VariantInfo>) {
|
||||||
let info = TypeSizeInfo {
|
let info = TypeSizeInfo {
|
||||||
|
@ -86,6 +88,7 @@ impl CodeStats {
|
||||||
type_description: type_desc.to_string(),
|
type_description: type_desc.to_string(),
|
||||||
align: align.abi(),
|
align: align.abi(),
|
||||||
overall_size: overall_size.bytes(),
|
overall_size: overall_size.bytes(),
|
||||||
|
packed: packed,
|
||||||
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
|
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
|
||||||
variants,
|
variants,
|
||||||
};
|
};
|
||||||
|
@ -153,24 +156,26 @@ impl CodeStats {
|
||||||
for field in fields.iter() {
|
for field in fields.iter() {
|
||||||
let FieldInfo { ref name, offset, size, align } = *field;
|
let FieldInfo { ref name, offset, size, align } = *field;
|
||||||
|
|
||||||
// Include field alignment in output only if it caused padding injection
|
if offset > min_offset {
|
||||||
if min_offset != offset {
|
let pad = offset - min_offset;
|
||||||
if offset > min_offset {
|
println!("print-type-size {}padding: {} bytes",
|
||||||
let pad = offset - min_offset;
|
indent, pad);
|
||||||
println!("print-type-size {}padding: {} bytes",
|
}
|
||||||
indent, pad);
|
|
||||||
println!("print-type-size {}field `.{}`: {} bytes, \
|
if offset < min_offset {
|
||||||
alignment: {} bytes",
|
// if this happens something is very wrong
|
||||||
indent, name, size, align);
|
println!("print-type-size {}field `.{}`: {} bytes, \
|
||||||
} else {
|
offset: {} bytes, \
|
||||||
println!("print-type-size {}field `.{}`: {} bytes, \
|
alignment: {} bytes",
|
||||||
offset: {} bytes, \
|
indent, name, size, offset, align);
|
||||||
alignment: {} bytes",
|
} else if info.packed || offset == min_offset {
|
||||||
indent, name, size, offset, align);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("print-type-size {}field `.{}`: {} bytes",
|
println!("print-type-size {}field `.{}`: {} bytes",
|
||||||
indent, name, size);
|
indent, name, size);
|
||||||
|
} else {
|
||||||
|
// Include field alignment in output only if it caused padding injection
|
||||||
|
println!("print-type-size {}field `.{}`: {} bytes, \
|
||||||
|
alignment: {} bytes",
|
||||||
|
indent, name, size, align);
|
||||||
}
|
}
|
||||||
|
|
||||||
min_offset = offset + size;
|
min_offset = offset + size;
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub use self::Integer::*;
|
||||||
pub use self::Primitive::*;
|
pub use self::Primitive::*;
|
||||||
|
|
||||||
use session::{self, DataTypeKind, Session};
|
use session::{self, DataTypeKind, Session};
|
||||||
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
|
use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};
|
||||||
|
|
||||||
use syntax::ast::{self, FloatTy, IntTy, UintTy};
|
use syntax::ast::{self, FloatTy, IntTy, UintTy};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
@ -344,8 +344,8 @@ impl AddAssign for Size {
|
||||||
/// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
|
/// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
|
||||||
pub struct Align {
|
pub struct Align {
|
||||||
abi: u8,
|
abi_pow2: u8,
|
||||||
pref: u8,
|
pref_pow2: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Align {
|
impl Align {
|
||||||
|
@ -377,17 +377,17 @@ impl Align {
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Align {
|
Ok(Align {
|
||||||
abi: log2(abi)?,
|
abi_pow2: log2(abi)?,
|
||||||
pref: log2(pref)?,
|
pref_pow2: log2(pref)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abi(self) -> u64 {
|
pub fn abi(self) -> u64 {
|
||||||
1 << self.abi
|
1 << self.abi_pow2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pref(self) -> u64 {
|
pub fn pref(self) -> u64 {
|
||||||
1 << self.pref
|
1 << self.pref_pow2
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abi_bits(self) -> u64 {
|
pub fn abi_bits(self) -> u64 {
|
||||||
|
@ -400,15 +400,15 @@ impl Align {
|
||||||
|
|
||||||
pub fn min(self, other: Align) -> Align {
|
pub fn min(self, other: Align) -> Align {
|
||||||
Align {
|
Align {
|
||||||
abi: cmp::min(self.abi, other.abi),
|
abi_pow2: cmp::min(self.abi_pow2, other.abi_pow2),
|
||||||
pref: cmp::min(self.pref, other.pref),
|
pref_pow2: cmp::min(self.pref_pow2, other.pref_pow2),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max(self, other: Align) -> Align {
|
pub fn max(self, other: Align) -> Align {
|
||||||
Align {
|
Align {
|
||||||
abi: cmp::max(self.abi, other.abi),
|
abi_pow2: cmp::max(self.abi_pow2, other.abi_pow2),
|
||||||
pref: cmp::max(self.pref, other.pref),
|
pref_pow2: cmp::max(self.pref_pow2, other.pref_pow2),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -974,6 +974,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
bug!("struct cannot be packed and aligned");
|
bug!("struct cannot be packed and aligned");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pack = {
|
||||||
|
let pack = repr.pack as u64;
|
||||||
|
Align::from_bytes(pack, pack).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let mut align = if packed {
|
let mut align = if packed {
|
||||||
dl.i8_align
|
dl.i8_align
|
||||||
} else {
|
} else {
|
||||||
|
@ -984,8 +989,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
let mut offsets = vec![Size::from_bytes(0); fields.len()];
|
let mut offsets = vec![Size::from_bytes(0); fields.len()];
|
||||||
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
|
let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
|
||||||
|
|
||||||
// Anything with repr(C) or repr(packed) doesn't optimize.
|
let mut optimize = !repr.inhibit_struct_field_reordering_opt();
|
||||||
let mut optimize = (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
|
|
||||||
if let StructKind::Prefixed(_, align) = kind {
|
if let StructKind::Prefixed(_, align) = kind {
|
||||||
optimize &= align.abi() == 1;
|
optimize &= align.abi() == 1;
|
||||||
}
|
}
|
||||||
|
@ -997,6 +1001,9 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
fields.len()
|
fields.len()
|
||||||
};
|
};
|
||||||
let optimizing = &mut inverse_memory_index[..end];
|
let optimizing = &mut inverse_memory_index[..end];
|
||||||
|
let field_align = |f: &TyLayout| {
|
||||||
|
if packed { f.align.min(pack).abi() } else { f.align.abi() }
|
||||||
|
};
|
||||||
match kind {
|
match kind {
|
||||||
StructKind::AlwaysSized |
|
StructKind::AlwaysSized |
|
||||||
StructKind::MaybeUnsized => {
|
StructKind::MaybeUnsized => {
|
||||||
|
@ -1004,11 +1011,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
// Place ZSTs first to avoid "interesting offsets",
|
// Place ZSTs first to avoid "interesting offsets",
|
||||||
// especially with only one or two non-ZST fields.
|
// especially with only one or two non-ZST fields.
|
||||||
let f = &fields[x as usize];
|
let f = &fields[x as usize];
|
||||||
(!f.is_zst(), cmp::Reverse(f.align.abi()))
|
(!f.is_zst(), cmp::Reverse(field_align(f)))
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
StructKind::Prefixed(..) => {
|
StructKind::Prefixed(..) => {
|
||||||
optimizing.sort_by_key(|&x| fields[x as usize].align.abi());
|
optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1022,7 +1029,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
let mut offset = Size::from_bytes(0);
|
let mut offset = Size::from_bytes(0);
|
||||||
|
|
||||||
if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
|
if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
|
||||||
if !packed {
|
if packed {
|
||||||
|
let prefix_align = prefix_align.min(pack);
|
||||||
|
align = align.max(prefix_align);
|
||||||
|
} else {
|
||||||
align = align.max(prefix_align);
|
align = align.max(prefix_align);
|
||||||
}
|
}
|
||||||
offset = prefix_size.abi_align(prefix_align);
|
offset = prefix_size.abi_align(prefix_align);
|
||||||
|
@ -1044,7 +1054,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invariant: offset < dl.obj_size_bound() <= 1<<61
|
// Invariant: offset < dl.obj_size_bound() <= 1<<61
|
||||||
if !packed {
|
if packed {
|
||||||
|
let field_pack = field.align.min(pack);
|
||||||
|
offset = offset.abi_align(field_pack);
|
||||||
|
align = align.max(field_pack);
|
||||||
|
}
|
||||||
|
else {
|
||||||
offset = offset.abi_align(field.align);
|
offset = offset.abi_align(field.align);
|
||||||
align = align.max(field.align);
|
align = align.max(field.align);
|
||||||
}
|
}
|
||||||
|
@ -1377,7 +1392,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
bug!("Union cannot be packed and aligned");
|
bug!("Union cannot be packed and aligned");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut align = if def.repr.packed() {
|
let pack = {
|
||||||
|
let pack = def.repr.pack as u64;
|
||||||
|
Align::from_bytes(pack, pack).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut align = if packed {
|
||||||
dl.i8_align
|
dl.i8_align
|
||||||
} else {
|
} else {
|
||||||
dl.aggregate_align
|
dl.aggregate_align
|
||||||
|
@ -1393,7 +1413,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
for field in &variants[0] {
|
for field in &variants[0] {
|
||||||
assert!(!field.is_unsized());
|
assert!(!field.is_unsized());
|
||||||
|
|
||||||
if !packed {
|
if packed {
|
||||||
|
let field_pack = field.align.min(pack);
|
||||||
|
align = align.max(field_pack);
|
||||||
|
} else {
|
||||||
align = align.max(field.align);
|
align = align.max(field.align);
|
||||||
}
|
}
|
||||||
size = cmp::max(size, field.size);
|
size = cmp::max(size, field.size);
|
||||||
|
@ -1740,12 +1763,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
|
|
||||||
fn record_layout_for_printing_outlined(self, layout: TyLayout<'tcx>) {
|
fn record_layout_for_printing_outlined(self, layout: TyLayout<'tcx>) {
|
||||||
// (delay format until we actually need it)
|
// (delay format until we actually need it)
|
||||||
let record = |kind, opt_discr_size, variants| {
|
let record = |kind, packed, opt_discr_size, variants| {
|
||||||
let type_desc = format!("{:?}", layout.ty);
|
let type_desc = format!("{:?}", layout.ty);
|
||||||
self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
|
self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
|
||||||
type_desc,
|
type_desc,
|
||||||
layout.align,
|
layout.align,
|
||||||
layout.size,
|
layout.size,
|
||||||
|
packed,
|
||||||
opt_discr_size,
|
opt_discr_size,
|
||||||
variants);
|
variants);
|
||||||
};
|
};
|
||||||
|
@ -1758,7 +1782,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
|
|
||||||
ty::TyClosure(..) => {
|
ty::TyClosure(..) => {
|
||||||
debug!("print-type-size t: `{:?}` record closure", layout.ty);
|
debug!("print-type-size t: `{:?}` record closure", layout.ty);
|
||||||
record(DataTypeKind::Closure, None, vec![]);
|
record(DataTypeKind::Closure, false, None, vec![]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1769,6 +1793,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let adt_kind = adt_def.adt_kind();
|
let adt_kind = adt_def.adt_kind();
|
||||||
|
let adt_packed = adt_def.repr.packed();
|
||||||
|
|
||||||
let build_variant_info = |n: Option<ast::Name>,
|
let build_variant_info = |n: Option<ast::Name>,
|
||||||
flds: &[ast::Name],
|
flds: &[ast::Name],
|
||||||
|
@ -1821,6 +1846,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
let fields: Vec<_> =
|
let fields: Vec<_> =
|
||||||
variant_def.fields.iter().map(|f| f.name).collect();
|
variant_def.fields.iter().map(|f| f.name).collect();
|
||||||
record(adt_kind.into(),
|
record(adt_kind.into(),
|
||||||
|
adt_packed,
|
||||||
None,
|
None,
|
||||||
vec![build_variant_info(Some(variant_def.name),
|
vec![build_variant_info(Some(variant_def.name),
|
||||||
&fields,
|
&fields,
|
||||||
|
@ -1828,7 +1854,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
} else {
|
} else {
|
||||||
// (This case arises for *empty* enums; so give it
|
// (This case arises for *empty* enums; so give it
|
||||||
// zero variants.)
|
// zero variants.)
|
||||||
record(adt_kind.into(), None, vec![]);
|
record(adt_kind.into(), adt_packed, None, vec![]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1845,7 +1871,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
||||||
layout.for_variant(self, i))
|
layout.for_variant(self, i))
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
record(adt_kind.into(), match layout.variants {
|
record(adt_kind.into(), adt_packed, match layout.variants {
|
||||||
Variants::Tagged { ref discr, .. } => Some(discr.value.size(self)),
|
Variants::Tagged { ref discr, .. } => Some(discr.value.size(self)),
|
||||||
_ => None
|
_ => None
|
||||||
}, variant_infos);
|
}, variant_infos);
|
||||||
|
@ -2518,8 +2544,8 @@ impl_stable_hash_for!(enum ::ty::layout::Primitive {
|
||||||
});
|
});
|
||||||
|
|
||||||
impl_stable_hash_for!(struct ::ty::layout::Align {
|
impl_stable_hash_for!(struct ::ty::layout::Align {
|
||||||
abi,
|
abi_pow2,
|
||||||
pref
|
pref_pow2
|
||||||
});
|
});
|
||||||
|
|
||||||
impl_stable_hash_for!(struct ::ty::layout::Size {
|
impl_stable_hash_for!(struct ::ty::layout::Size {
|
||||||
|
|
|
@ -1623,15 +1623,13 @@ bitflags! {
|
||||||
#[derive(RustcEncodable, RustcDecodable, Default)]
|
#[derive(RustcEncodable, RustcDecodable, Default)]
|
||||||
pub struct ReprFlags: u8 {
|
pub struct ReprFlags: u8 {
|
||||||
const IS_C = 1 << 0;
|
const IS_C = 1 << 0;
|
||||||
const IS_PACKED = 1 << 1;
|
const IS_SIMD = 1 << 1;
|
||||||
const IS_SIMD = 1 << 2;
|
const IS_TRANSPARENT = 1 << 2;
|
||||||
const IS_TRANSPARENT = 1 << 3;
|
|
||||||
// Internal only for now. If true, don't reorder fields.
|
// Internal only for now. If true, don't reorder fields.
|
||||||
const IS_LINEAR = 1 << 4;
|
const IS_LINEAR = 1 << 3;
|
||||||
|
|
||||||
// Any of these flags being set prevent field reordering optimisation.
|
// Any of these flags being set prevent field reordering optimisation.
|
||||||
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
|
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
|
||||||
ReprFlags::IS_PACKED.bits |
|
|
||||||
ReprFlags::IS_SIMD.bits |
|
ReprFlags::IS_SIMD.bits |
|
||||||
ReprFlags::IS_LINEAR.bits;
|
ReprFlags::IS_LINEAR.bits;
|
||||||
}
|
}
|
||||||
|
@ -1648,11 +1646,13 @@ impl_stable_hash_for!(struct ReprFlags {
|
||||||
pub struct ReprOptions {
|
pub struct ReprOptions {
|
||||||
pub int: Option<attr::IntType>,
|
pub int: Option<attr::IntType>,
|
||||||
pub align: u32,
|
pub align: u32,
|
||||||
|
pub pack: u32,
|
||||||
pub flags: ReprFlags,
|
pub flags: ReprFlags,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_stable_hash_for!(struct ReprOptions {
|
impl_stable_hash_for!(struct ReprOptions {
|
||||||
align,
|
align,
|
||||||
|
pack,
|
||||||
int,
|
int,
|
||||||
flags
|
flags
|
||||||
});
|
});
|
||||||
|
@ -1662,11 +1662,19 @@ impl ReprOptions {
|
||||||
let mut flags = ReprFlags::empty();
|
let mut flags = ReprFlags::empty();
|
||||||
let mut size = None;
|
let mut size = None;
|
||||||
let mut max_align = 0;
|
let mut max_align = 0;
|
||||||
|
let mut min_pack = 0;
|
||||||
for attr in tcx.get_attrs(did).iter() {
|
for attr in tcx.get_attrs(did).iter() {
|
||||||
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
|
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
|
||||||
flags.insert(match r {
|
flags.insert(match r {
|
||||||
attr::ReprC => ReprFlags::IS_C,
|
attr::ReprC => ReprFlags::IS_C,
|
||||||
attr::ReprPacked => ReprFlags::IS_PACKED,
|
attr::ReprPacked(pack) => {
|
||||||
|
min_pack = if min_pack > 0 {
|
||||||
|
cmp::min(pack, min_pack)
|
||||||
|
} else {
|
||||||
|
pack
|
||||||
|
};
|
||||||
|
ReprFlags::empty()
|
||||||
|
},
|
||||||
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
|
attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
|
||||||
attr::ReprSimd => ReprFlags::IS_SIMD,
|
attr::ReprSimd => ReprFlags::IS_SIMD,
|
||||||
attr::ReprInt(i) => {
|
attr::ReprInt(i) => {
|
||||||
|
@ -1685,7 +1693,7 @@ impl ReprOptions {
|
||||||
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
|
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
|
||||||
flags.insert(ReprFlags::IS_LINEAR);
|
flags.insert(ReprFlags::IS_LINEAR);
|
||||||
}
|
}
|
||||||
ReprOptions { int: size, align: max_align, flags: flags }
|
ReprOptions { int: size, align: max_align, pack: min_pack, flags: flags }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1693,7 +1701,7 @@ impl ReprOptions {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
|
pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
|
pub fn packed(&self) -> bool { self.pack > 0 }
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
|
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1709,6 +1717,12 @@ impl ReprOptions {
|
||||||
pub fn inhibit_enum_layout_opt(&self) -> bool {
|
pub fn inhibit_enum_layout_opt(&self) -> bool {
|
||||||
self.c() || self.int.is_some()
|
self.c() || self.int.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this `#[repr()]` should inhibit struct field reordering
|
||||||
|
/// optimizations, such as with repr(C) or repr(packed(1)).
|
||||||
|
pub fn inhibit_struct_field_reordering_opt(&self) -> bool {
|
||||||
|
!(self.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty() || (self.pack == 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> AdtDef {
|
impl<'a, 'gcx, 'tcx> AdtDef {
|
||||||
|
|
|
@ -1553,8 +1553,19 @@ pub fn check_simd<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_packed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId) {
|
fn check_packed<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, def_id: DefId) {
|
||||||
if tcx.adt_def(def_id).repr.packed() {
|
let repr = tcx.adt_def(def_id).repr;
|
||||||
if tcx.adt_def(def_id).repr.align > 0 {
|
if repr.packed() {
|
||||||
|
for attr in tcx.get_attrs(def_id).iter() {
|
||||||
|
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
|
||||||
|
if let attr::ReprPacked(pack) = r {
|
||||||
|
if pack != repr.pack {
|
||||||
|
struct_span_err!(tcx.sess, sp, E0634,
|
||||||
|
"type has conflicting packed representation hints").emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if repr.align > 0 {
|
||||||
struct_span_err!(tcx.sess, sp, E0587,
|
struct_span_err!(tcx.sess, sp, E0587,
|
||||||
"type has conflicting packed and align representation hints").emit();
|
"type has conflicting packed and align representation hints").emit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4836,14 +4836,15 @@ register_diagnostics! {
|
||||||
// E0563, // cannot determine a type for this `impl Trait`: {} // removed in 6383de15
|
// E0563, // cannot determine a type for this `impl Trait`: {} // removed in 6383de15
|
||||||
E0564, // only named lifetimes are allowed in `impl Trait`,
|
E0564, // only named lifetimes are allowed in `impl Trait`,
|
||||||
// but `{}` was found in the type `{}`
|
// but `{}` was found in the type `{}`
|
||||||
E0587, // struct has conflicting packed and align representation hints
|
E0587, // type has conflicting packed and align representation hints
|
||||||
E0588, // packed struct cannot transitively contain a `[repr(align)]` struct
|
E0588, // packed type cannot transitively contain a `[repr(align)]` type
|
||||||
E0592, // duplicate definitions with name `{}`
|
E0592, // duplicate definitions with name `{}`
|
||||||
// E0613, // Removed (merged with E0609)
|
// E0613, // Removed (merged with E0609)
|
||||||
E0640, // infer outlives
|
E0640, // infer outlives
|
||||||
E0627, // yield statement outside of generator literal
|
E0627, // yield statement outside of generator literal
|
||||||
E0632, // cannot provide explicit type parameters when `impl Trait` is used in
|
E0632, // cannot provide explicit type parameters when `impl Trait` is used in
|
||||||
// argument position.
|
// argument position.
|
||||||
|
E0634, // type has conflicting packed representaton hints
|
||||||
E0641, // cannot cast to/from a pointer with an unknown kind
|
E0641, // cannot cast to/from a pointer with an unknown kind
|
||||||
E0645, // trait aliases not finished
|
E0645, // trait aliases not finished
|
||||||
E0907, // type inside generator must be known in this context
|
E0907, // type inside generator must be known in this context
|
||||||
|
|
|
@ -993,7 +993,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
||||||
let word = &*mi.ident.name.as_str();
|
let word = &*mi.ident.name.as_str();
|
||||||
let hint = match word {
|
let hint = match word {
|
||||||
"C" => Some(ReprC),
|
"C" => Some(ReprC),
|
||||||
"packed" => Some(ReprPacked),
|
"packed" => Some(ReprPacked(1)),
|
||||||
"simd" => Some(ReprSimd),
|
"simd" => Some(ReprSimd),
|
||||||
"transparent" => Some(ReprTransparent),
|
"transparent" => Some(ReprTransparent),
|
||||||
_ => match int_type_of_word(word) {
|
_ => match int_type_of_word(word) {
|
||||||
|
@ -1009,27 +1009,41 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
||||||
acc.push(h);
|
acc.push(h);
|
||||||
}
|
}
|
||||||
} else if let Some((name, value)) = item.name_value_literal() {
|
} else if let Some((name, value)) = item.name_value_literal() {
|
||||||
if name == "align" {
|
let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
|
||||||
recognised = true;
|
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
|
||||||
let mut align_error = None;
|
if literal.is_power_of_two() {
|
||||||
if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node {
|
|
||||||
if align.is_power_of_two() {
|
|
||||||
// rustc::ty::layout::Align restricts align to <= 2147483647
|
// rustc::ty::layout::Align restricts align to <= 2147483647
|
||||||
if align <= 2147483647 {
|
if *literal <= 2147483647 {
|
||||||
acc.push(ReprAlign(align as u32));
|
Ok(*literal as u32)
|
||||||
} else {
|
} else {
|
||||||
align_error = Some("larger than 2147483647");
|
Err("larger than 2147483647")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
align_error = Some("not a power of two");
|
Err("not a power of two")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
align_error = Some("not an unsuffixed integer");
|
Err("not an unsuffixed integer")
|
||||||
}
|
|
||||||
if let Some(align_error) = align_error {
|
|
||||||
span_err!(diagnostic, item.span, E0589,
|
|
||||||
"invalid `repr(align)` attribute: {}", align_error);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut literal_error = None;
|
||||||
|
if name == "align" {
|
||||||
|
recognised = true;
|
||||||
|
match parse_alignment(&value.node) {
|
||||||
|
Ok(literal) => acc.push(ReprAlign(literal)),
|
||||||
|
Err(message) => literal_error = Some(message)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if name == "packed" {
|
||||||
|
recognised = true;
|
||||||
|
match parse_alignment(&value.node) {
|
||||||
|
Ok(literal) => acc.push(ReprPacked(literal)),
|
||||||
|
Err(message) => literal_error = Some(message)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if let Some(literal_error) = literal_error {
|
||||||
|
span_err!(diagnostic, item.span, E0589,
|
||||||
|
"invalid `repr(align)` attribute: {}", literal_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !recognised {
|
if !recognised {
|
||||||
|
@ -1065,7 +1079,7 @@ fn int_type_of_word(s: &str) -> Option<IntType> {
|
||||||
pub enum ReprAttr {
|
pub enum ReprAttr {
|
||||||
ReprInt(IntType),
|
ReprInt(IntType),
|
||||||
ReprC,
|
ReprC,
|
||||||
ReprPacked,
|
ReprPacked(u32),
|
||||||
ReprSimd,
|
ReprSimd,
|
||||||
ReprTransparent,
|
ReprTransparent,
|
||||||
ReprAlign(u32),
|
ReprAlign(u32),
|
||||||
|
|
|
@ -432,6 +432,9 @@ declare_features! (
|
||||||
// Parentheses in patterns
|
// Parentheses in patterns
|
||||||
(active, pattern_parentheses, "1.26.0", None, None),
|
(active, pattern_parentheses, "1.26.0", None, None),
|
||||||
|
|
||||||
|
// Allows `#[repr(packed)]` attribute on structs
|
||||||
|
(active, repr_packed, "1.26.0", Some(33158), None),
|
||||||
|
|
||||||
// `use path as _;` and `extern crate c as _;`
|
// `use path as _;` and `extern crate c as _;`
|
||||||
(active, underscore_imports, "1.26.0", Some(48216), None),
|
(active, underscore_imports, "1.26.0", Some(48216), None),
|
||||||
|
|
||||||
|
@ -1439,11 +1442,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow attr_literals in #[repr(align(x))]
|
// allow attr_literals in #[repr(align(x))] and #[repr(packed(n))]
|
||||||
let mut is_repr_align = false;
|
let mut allow_attr_literal = false;
|
||||||
if attr.path == "repr" {
|
if attr.path == "repr" {
|
||||||
if let Some(content) = attr.meta_item_list() {
|
if let Some(content) = attr.meta_item_list() {
|
||||||
is_repr_align = content.iter().any(|c| c.check_name("align"));
|
allow_attr_literal = content.iter().any(
|
||||||
|
|c| c.check_name("align") || c.check_name("packed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,7 +1455,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_repr_align {
|
if !allow_attr_literal {
|
||||||
let meta = panictry!(attr.parse_meta(self.context.parse_sess));
|
let meta = panictry!(attr.parse_meta(self.context.parse_sess));
|
||||||
if contains_novel_literal(&meta) {
|
if contains_novel_literal(&meta) {
|
||||||
gate_feature_post!(&self, attr_literals, attr.span,
|
gate_feature_post!(&self, attr_literals, attr.span,
|
||||||
|
@ -1535,6 +1539,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||||
"the `#[repr(transparent)]` attribute \
|
"the `#[repr(transparent)]` attribute \
|
||||||
is experimental");
|
is experimental");
|
||||||
}
|
}
|
||||||
|
if let Some((name, _)) = item.name_value_literal() {
|
||||||
|
if name == "packed" {
|
||||||
|
gate_feature_post!(&self, repr_packed, attr.span,
|
||||||
|
"the `#[repr(packed(n))]` attribute \
|
||||||
|
is experimental");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -413,8 +413,12 @@ impl<'a> TraitDef<'a> {
|
||||||
match *item {
|
match *item {
|
||||||
Annotatable::Item(ref item) => {
|
Annotatable::Item(ref item) => {
|
||||||
let is_packed = item.attrs.iter().any(|attr| {
|
let is_packed = item.attrs.iter().any(|attr| {
|
||||||
attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr)
|
for r in attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr) {
|
||||||
.contains(&attr::ReprPacked)
|
if let attr::ReprPacked(_) = r {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
});
|
});
|
||||||
let has_no_type_params = match item.node {
|
let has_no_type_params = match item.node {
|
||||||
ast::ItemKind::Struct(_, ref generics) |
|
ast::ItemKind::Struct(_, ref generics) |
|
||||||
|
@ -831,7 +835,7 @@ fn find_repr_type_name(diagnostic: &Handler, type_attrs: &[ast::Attribute]) -> &
|
||||||
for a in type_attrs {
|
for a in type_attrs {
|
||||||
for r in &attr::find_repr_attrs(diagnostic, a) {
|
for r in &attr::find_repr_attrs(diagnostic, a) {
|
||||||
repr_type_name = match *r {
|
repr_type_name = match *r {
|
||||||
attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
|
attr::ReprPacked(_) | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
|
||||||
continue,
|
continue,
|
||||||
|
|
||||||
attr::ReprC => "i32",
|
attr::ReprC => "i32",
|
||||||
|
|
|
@ -11,16 +11,23 @@
|
||||||
// compile-flags: -C no-prepopulate-passes
|
// compile-flags: -C no-prepopulate-passes
|
||||||
|
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct Packed {
|
pub struct Packed1 {
|
||||||
dealign: u8,
|
dealign: u8,
|
||||||
data: u32
|
data: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: @write_pkd
|
#[repr(packed(2))]
|
||||||
|
pub struct Packed2 {
|
||||||
|
dealign: u8,
|
||||||
|
data: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @write_pkd1
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn write_pkd(pkd: &mut Packed) -> u32 {
|
pub fn write_pkd1(pkd: &mut Packed1) -> u32 {
|
||||||
// CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 1
|
// CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 1
|
||||||
// CHECK: store i32 42, i32* %{{.*}}, align 1
|
// CHECK: store i32 42, i32* %{{.*}}, align 1
|
||||||
let result = pkd.data;
|
let result = pkd.data;
|
||||||
|
@ -28,43 +35,94 @@ pub fn write_pkd(pkd: &mut Packed) -> u32 {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @write_pkd2
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn write_pkd2(pkd: &mut Packed2) -> u32 {
|
||||||
|
// CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 2
|
||||||
|
// CHECK: store i32 42, i32* %{{.*}}, align 2
|
||||||
|
let result = pkd.data;
|
||||||
|
pkd.data = 42;
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Array([i32; 8]);
|
pub struct Array([i32; 8]);
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct BigPacked {
|
pub struct BigPacked1 {
|
||||||
dealign: u8,
|
dealign: u8,
|
||||||
data: Array
|
data: Array
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECK-LABEL: @call_pkd
|
#[repr(packed(2))]
|
||||||
|
pub struct BigPacked2 {
|
||||||
|
dealign: u8,
|
||||||
|
data: Array
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @call_pkd1
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn call_pkd(f: fn() -> Array) -> BigPacked {
|
pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
|
||||||
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
|
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
|
||||||
// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
|
// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
|
||||||
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 32, i32 1, i1 false)
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 32, i32 1, i1 false)
|
||||||
// check that calls whose destination is a field of a packed struct
|
// check that calls whose destination is a field of a packed struct
|
||||||
// go through an alloca rather than calling the function with an
|
// go through an alloca rather than calling the function with an
|
||||||
// unaligned destination.
|
// unaligned destination.
|
||||||
BigPacked { dealign: 0, data: f() }
|
BigPacked1 { dealign: 0, data: f() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @call_pkd2
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 {
|
||||||
|
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
|
||||||
|
// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
|
||||||
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 32, i32 2, i1 false)
|
||||||
|
// check that calls whose destination is a field of a packed struct
|
||||||
|
// go through an alloca rather than calling the function with an
|
||||||
|
// unaligned destination.
|
||||||
|
BigPacked2 { dealign: 0, data: f() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PackedPair(u8, u32);
|
pub struct Packed1Pair(u8, u32);
|
||||||
|
|
||||||
// CHECK-LABEL: @pkd_pair
|
#[repr(packed(2))]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Packed2Pair(u8, u32);
|
||||||
|
|
||||||
|
// CHECK-LABEL: @pkd1_pair
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn pkd_pair(pair1: &mut PackedPair, pair2: &mut PackedPair) {
|
pub fn pkd1_pair(pair1: &mut Packed1Pair, pair2: &mut Packed1Pair) {
|
||||||
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 5, i32 1, i1 false)
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 5, i32 1, i1 false)
|
||||||
*pair2 = *pair1;
|
*pair2 = *pair1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @pkd2_pair
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn pkd2_pair(pair1: &mut Packed2Pair, pair2: &mut Packed2Pair) {
|
||||||
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 6, i32 2, i1 false)
|
||||||
|
*pair2 = *pair1;
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PackedNestedPair((u32, u32));
|
pub struct Packed1NestedPair((u32, u32));
|
||||||
|
|
||||||
// CHECK-LABEL: @pkd_nested_pair
|
#[repr(packed(2))]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Packed2NestedPair((u32, u32));
|
||||||
|
|
||||||
|
// CHECK-LABEL: @pkd1_nested_pair
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub fn pkd_nested_pair(pair1: &mut PackedNestedPair, pair2: &mut PackedNestedPair) {
|
pub fn pkd1_nested_pair(pair1: &mut Packed1NestedPair, pair2: &mut Packed1NestedPair) {
|
||||||
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 1, i1 false)
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 1, i1 false)
|
||||||
*pair2 = *pair1;
|
*pair2 = *pair1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: @pkd2_nested_pair
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn pkd2_nested_pair(pair1: &mut Packed2NestedPair, pair2: &mut Packed2NestedPair) {
|
||||||
|
// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 2, i1 false)
|
||||||
|
*pair2 = *pair1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
enum A { A }
|
enum A { A }
|
||||||
|
@ -36,6 +37,16 @@ struct G(i32); //~ ERROR type has conflicting packed and align representation hi
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct H(i32); //~ ERROR type has conflicting packed and align representation hints
|
struct H(i32); //~ ERROR type has conflicting packed and align representation hints
|
||||||
|
|
||||||
|
#[repr(packed, packed(2))]
|
||||||
|
struct I(i32); //~ ERROR type has conflicting packed representation hints
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
#[repr(packed)]
|
||||||
|
struct J(i32); //~ ERROR type has conflicting packed representation hints
|
||||||
|
|
||||||
|
#[repr(packed, packed(1))]
|
||||||
|
struct K(i32);
|
||||||
|
|
||||||
#[repr(packed, align(8))]
|
#[repr(packed, align(8))]
|
||||||
union X { //~ ERROR type has conflicting packed and align representation hints
|
union X { //~ ERROR type has conflicting packed and align representation hints
|
||||||
i: i32
|
i: i32
|
||||||
|
|
|
@ -8,6 +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.
|
||||||
#![feature(box_syntax)]
|
#![feature(box_syntax)]
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -60,6 +61,18 @@ struct AlignContainsPacked {
|
||||||
b: Packed,
|
b: Packed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct Packed4C {
|
||||||
|
a: u32,
|
||||||
|
b: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(align(16))]
|
||||||
|
struct AlignContainsPacked4C {
|
||||||
|
a: Packed4C,
|
||||||
|
b: u64,
|
||||||
|
}
|
||||||
|
|
||||||
// The align limit was originally smaller (2^15).
|
// The align limit was originally smaller (2^15).
|
||||||
// Check that it works with big numbers.
|
// Check that it works with big numbers.
|
||||||
#[repr(align(0x10000))]
|
#[repr(align(0x10000))]
|
||||||
|
@ -218,6 +231,15 @@ pub fn main() {
|
||||||
assert_eq!(mem::size_of_val(&a), 16);
|
assert_eq!(mem::size_of_val(&a), 16);
|
||||||
assert!(is_aligned_to(&a, 16));
|
assert!(is_aligned_to(&a, 16));
|
||||||
|
|
||||||
|
assert_eq!(mem::align_of::<AlignContainsPacked4C>(), 16);
|
||||||
|
assert_eq!(mem::size_of::<AlignContainsPacked4C>(), 32);
|
||||||
|
let a = AlignContainsPacked4C { a: Packed4C{ a: 1, b: 2 }, b: 3 };
|
||||||
|
assert_eq!(mem::align_of_val(&a), 16);
|
||||||
|
assert_eq!(mem::align_of_val(&a.a), 4);
|
||||||
|
assert_eq!(mem::align_of_val(&a.b), mem::align_of::<u64>());
|
||||||
|
assert_eq!(mem::size_of_val(&a), 32);
|
||||||
|
assert!(is_aligned_to(&a, 16));
|
||||||
|
|
||||||
let mut large = box AlignLarge {
|
let mut large = box AlignLarge {
|
||||||
stuff: [0; 0x10000],
|
stuff: [0; 0x10000],
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,8 +8,24 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub struct S {
|
pub struct P1S5 {
|
||||||
a: u8,
|
a: u8,
|
||||||
b: u32
|
b: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
pub struct P2S6 {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
pub struct P2CS8 {
|
||||||
|
a: u8,
|
||||||
|
b: u32,
|
||||||
|
c: u8
|
||||||
|
}
|
||||||
|
|
37
src/test/run-pass/issue-48159.rs
Normal file
37
src/test/run-pass/issue-48159.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2018 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(repr_packed)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
pub enum c_void {}
|
||||||
|
|
||||||
|
type uintptr_t = usize;
|
||||||
|
type int16_t = u16;
|
||||||
|
type uint16_t = int16_t;
|
||||||
|
type uint32_t = u32;
|
||||||
|
type intptr_t = uintptr_t;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[repr(packed(4))]
|
||||||
|
pub struct kevent {
|
||||||
|
pub ident: uintptr_t,
|
||||||
|
pub filter: int16_t,
|
||||||
|
pub flags: uint16_t,
|
||||||
|
pub fflags: uint32_t,
|
||||||
|
pub data: intptr_t,
|
||||||
|
pub udata: *mut c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(mem::align_of::<kevent>(), 4);
|
||||||
|
}
|
|
@ -10,15 +10,36 @@
|
||||||
|
|
||||||
// ignore-emscripten weird assertion?
|
// ignore-emscripten weird assertion?
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct Foo {
|
struct Foo1 {
|
||||||
|
bar: u8,
|
||||||
|
baz: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct Foo2 {
|
||||||
|
bar: u8,
|
||||||
|
baz: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct Foo4C {
|
||||||
bar: u8,
|
bar: u8,
|
||||||
baz: usize
|
baz: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let foo = Foo { bar: 1, baz: 2 };
|
let foo = Foo1 { bar: 1, baz: 2 };
|
||||||
let brw = unsafe { &foo.baz };
|
let brw = unsafe { &foo.baz };
|
||||||
|
assert_eq!(*brw, 2);
|
||||||
|
|
||||||
|
let foo = Foo2 { bar: 1, baz: 2 };
|
||||||
|
let brw = unsafe { &foo.baz };
|
||||||
|
assert_eq!(*brw, 2);
|
||||||
|
|
||||||
|
let foo = Foo4C { bar: 1, baz: 2 };
|
||||||
|
let brw = unsafe { &foo.baz };
|
||||||
assert_eq!(*brw, 2);
|
assert_eq!(*brw, 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,45 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S<T, S> {
|
struct P1<T, S> {
|
||||||
a: T,
|
a: T,
|
||||||
b: u8,
|
b: u8,
|
||||||
c: S
|
c: S
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
#[repr(packed(2))]
|
||||||
assert_eq!(mem::size_of::<S<u8, u8>>(), 3);
|
struct P2<T, S> {
|
||||||
|
a: T,
|
||||||
assert_eq!(mem::size_of::<S<u64, u16>>(), 11);
|
b: u8,
|
||||||
|
c: S
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct P4C<T, S> {
|
||||||
|
a: T,
|
||||||
|
b: u8,
|
||||||
|
c: S
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! check {
|
||||||
|
($t:ty, $align:expr, $size:expr) => ({
|
||||||
|
assert_eq!(mem::align_of::<$t>(), $align);
|
||||||
|
assert_eq!(mem::size_of::<$t>(), $size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
check!(P1::<u8, u8>, 1, 3);
|
||||||
|
check!(P1::<u64, u16>, 1, 11);
|
||||||
|
|
||||||
|
check!(P2::<u8, u8>, 1, 3);
|
||||||
|
check!(P2::<u64, u16>, 2, 12);
|
||||||
|
|
||||||
|
check!(P4C::<u8, u8>, 1, 3);
|
||||||
|
check!(P4C::<u16, u64>, 4, 12);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,46 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct Foo {
|
struct Foo1 {
|
||||||
|
bar: u8,
|
||||||
|
baz: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct Foo2 {
|
||||||
|
bar: u8,
|
||||||
|
baz: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct Foo4C {
|
||||||
bar: u8,
|
bar: u8,
|
||||||
baz: usize
|
baz: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let foo = Foo { bar: 1, baz: 2 };
|
let foo1 = Foo1 { bar: 1, baz: 2 };
|
||||||
match foo {
|
match foo1 {
|
||||||
Foo {bar, baz} => {
|
Foo1 {bar, baz} => {
|
||||||
|
assert_eq!(bar, 1);
|
||||||
|
assert_eq!(baz, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let foo2 = Foo2 { bar: 1, baz: 2 };
|
||||||
|
match foo2 {
|
||||||
|
Foo2 {bar, baz} => {
|
||||||
|
assert_eq!(bar, 1);
|
||||||
|
assert_eq!(baz, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let foo4 = Foo4C { bar: 1, baz: 2 };
|
||||||
|
match foo4 {
|
||||||
|
Foo4C {bar, baz} => {
|
||||||
assert_eq!(bar, 1);
|
assert_eq!(bar, 1);
|
||||||
assert_eq!(baz, 2);
|
assert_eq!(baz, 2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,15 @@ extern crate packed;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
pub fn main() {
|
macro_rules! check {
|
||||||
assert_eq!(mem::size_of::<packed::S>(), 5);
|
($t:ty, $align:expr, $size:expr) => ({
|
||||||
|
assert_eq!(mem::align_of::<$t>(), $align);
|
||||||
|
assert_eq!(mem::size_of::<$t>(), $size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
check!(packed::P1S5, 1, 5);
|
||||||
|
check!(packed::P2S6, 2, 6);
|
||||||
|
check!(packed::P2CS8, 2, 8);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,44 +7,116 @@
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
// 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.
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S4 {
|
struct P1S4 {
|
||||||
|
a: u8,
|
||||||
|
b: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S4 {
|
||||||
a: u8,
|
a: u8,
|
||||||
b: [u8; 3],
|
b: [u8; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S5 {
|
struct P1S5 {
|
||||||
a: u8,
|
a: u8,
|
||||||
b: u32
|
b: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S2 {
|
||||||
|
a: u8,
|
||||||
|
b: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S6 {
|
||||||
|
a: u8,
|
||||||
|
b: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S12 {
|
||||||
|
a: u32,
|
||||||
|
b: u64
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S13 {
|
struct P1S13 {
|
||||||
a: i64,
|
a: i64,
|
||||||
b: f32,
|
b: f32,
|
||||||
c: u8,
|
c: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S14 {
|
||||||
|
a: i64,
|
||||||
|
b: f32,
|
||||||
|
c: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(4))]
|
||||||
|
struct P4S16 {
|
||||||
|
a: u8,
|
||||||
|
b: f32,
|
||||||
|
c: i64,
|
||||||
|
d: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct P4CS20 {
|
||||||
|
a: u8,
|
||||||
|
b: f32,
|
||||||
|
c: i64,
|
||||||
|
d: u16,
|
||||||
|
}
|
||||||
|
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar = 1,
|
Bar = 1,
|
||||||
Baz = 2
|
Baz = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S3_Foo {
|
struct P1S3_Foo {
|
||||||
|
a: u8,
|
||||||
|
b: u16,
|
||||||
|
c: Foo
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2_Foo {
|
||||||
|
a: Foo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S3_Foo {
|
||||||
a: u8,
|
a: u8,
|
||||||
b: u16,
|
b: u16,
|
||||||
c: Foo
|
c: Foo
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S7_Option {
|
struct P1S7_Option {
|
||||||
|
a: f32,
|
||||||
|
b: u8,
|
||||||
|
c: u16,
|
||||||
|
d: Option<Box<f64>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2_Option {
|
||||||
|
a: Option<Box<f64>>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S7_Option {
|
||||||
a: f32,
|
a: f32,
|
||||||
b: u8,
|
b: u8,
|
||||||
c: u16,
|
c: u16,
|
||||||
|
@ -52,15 +124,41 @@ struct S7_Option {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placing packed structs in statics should work
|
// Placing packed structs in statics should work
|
||||||
static TEST_S4: S4 = S4 { a: 1, b: [2, 3, 4] };
|
static TEST_P1S4: P1S4 = P1S4 { a: 1, b: [2, 3, 4] };
|
||||||
static TEST_S5: S5 = S5 { a: 3, b: 67 };
|
static TEST_P1S5: P1S5 = P1S5 { a: 3, b: 67 };
|
||||||
static TEST_S3_Foo: S3_Foo = S3_Foo { a: 1, b: 2, c: Foo::Baz };
|
static TEST_P1S3_Foo: P1S3_Foo = P1S3_Foo { a: 1, b: 2, c: Foo::Baz };
|
||||||
|
static TEST_P2S2: P2S2 = P2S2 { a: 1, b: 2 };
|
||||||
|
static TEST_P2S4: P2S4 = P2S4 { a: 1, b: [2, 3, 4] };
|
||||||
|
static TEST_P2S6: P2S6 = P2S6 { a: 1, b: 2 };
|
||||||
|
static TEST_P2S12: P2S12 = P2S12 { a: 1, b: 2 };
|
||||||
|
static TEST_P4S16: P4S16 = P4S16 { a: 1, b: 2.0, c: 3, d: 4 };
|
||||||
|
static TEST_P4CS20: P4CS20 = P4CS20 { a: 1, b: 2.0, c: 3, d: 4 };
|
||||||
|
|
||||||
|
fn align_to(value: usize, align: usize) -> usize {
|
||||||
|
(value + (align - 1)) & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! check {
|
||||||
|
($t:ty, $align:expr, $size:expr) => ({
|
||||||
|
assert_eq!(mem::align_of::<$t>(), $align);
|
||||||
|
assert_eq!(mem::size_of::<$t>(), $size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
assert_eq!(mem::size_of::<S4>(), 4);
|
check!(P1S4, 1, 4);
|
||||||
assert_eq!(mem::size_of::<S5>(), 5);
|
check!(P1S5, 1, 5);
|
||||||
assert_eq!(mem::size_of::<S13>(), 13);
|
check!(P1S13, 1, 13);
|
||||||
assert_eq!(mem::size_of::<S3_Foo>(), 3 + mem::size_of::<Foo>());
|
check!(P1S3_Foo, 1, 3 + mem::size_of::<Foo>());
|
||||||
assert_eq!(mem::size_of::<S7_Option>(), 7 + mem::size_of::<Option<Box<f64>>>());
|
check!(P1S7_Option, 1, 7 + mem::size_of::<Option<Box<f64>>>());
|
||||||
|
|
||||||
|
check!(P2S2, 1, 2);
|
||||||
|
check!(P2S4, 1, 4);
|
||||||
|
check!(P2S6, 2, 6);
|
||||||
|
check!(P2S12, 2, 12);
|
||||||
|
check!(P2S14, 2, 14);
|
||||||
|
check!(P4S16, 4, 16);
|
||||||
|
check!(P4CS20, 4, 20);
|
||||||
|
check!(P2S3_Foo, 2, align_to(3 + mem::size_of::<P2_Foo>(), 2));
|
||||||
|
check!(P2S7_Option, 2, align_to(7 + mem::size_of::<P2_Option>(), 2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,28 +8,80 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct Foo {
|
struct Foo1 {
|
||||||
bar: u8,
|
bar: u8,
|
||||||
baz: u64
|
baz: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Foo {
|
impl PartialEq for Foo1 {
|
||||||
fn eq(&self, other: &Foo) -> bool {
|
fn eq(&self, other: &Foo1) -> bool {
|
||||||
self.bar == other.bar && self.baz == other.baz
|
self.bar == other.bar && self.baz == other.baz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Foo {
|
impl fmt::Debug for Foo1 {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let bar = self.bar;
|
let bar = self.bar;
|
||||||
let baz = self.baz;
|
let baz = self.baz;
|
||||||
|
|
||||||
f.debug_struct("Foo")
|
f.debug_struct("Foo1")
|
||||||
|
.field("bar", &bar)
|
||||||
|
.field("baz", &baz)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Foo2 {
|
||||||
|
bar: u8,
|
||||||
|
baz: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Foo2 {
|
||||||
|
fn eq(&self, other: &Foo2) -> bool {
|
||||||
|
self.bar == other.bar && self.baz == other.baz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Foo2 {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let bar = self.bar;
|
||||||
|
let baz = self.baz;
|
||||||
|
|
||||||
|
f.debug_struct("Foo2")
|
||||||
|
.field("bar", &bar)
|
||||||
|
.field("baz", &baz)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct Foo4C {
|
||||||
|
bar: u8,
|
||||||
|
baz: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Foo4C {
|
||||||
|
fn eq(&self, other: &Foo4C) -> bool {
|
||||||
|
self.bar == other.bar && self.baz == other.baz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Foo4C {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let bar = self.bar;
|
||||||
|
let baz = self.baz;
|
||||||
|
|
||||||
|
f.debug_struct("Foo4C")
|
||||||
.field("bar", &bar)
|
.field("bar", &bar)
|
||||||
.field("baz", &baz)
|
.field("baz", &baz)
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -37,15 +89,42 @@ impl fmt::Debug for Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let foos = [Foo { bar: 1, baz: 2 }; 10];
|
let foo1s = [Foo1 { bar: 1, baz: 2 }; 10];
|
||||||
|
|
||||||
assert_eq!(mem::size_of::<[Foo; 10]>(), 90);
|
assert_eq!(mem::align_of::<[Foo1; 10]>(), 1);
|
||||||
|
assert_eq!(mem::size_of::<[Foo1; 10]>(), 90);
|
||||||
|
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
assert_eq!(foos[i], Foo { bar: 1, baz: 2});
|
assert_eq!(foo1s[i], Foo1 { bar: 1, baz: 2});
|
||||||
}
|
}
|
||||||
|
|
||||||
for &foo in &foos {
|
for &foo in &foo1s {
|
||||||
assert_eq!(foo, Foo { bar: 1, baz: 2 });
|
assert_eq!(foo, Foo1 { bar: 1, baz: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let foo2s = [Foo2 { bar: 1, baz: 2 }; 10];
|
||||||
|
|
||||||
|
assert_eq!(mem::align_of::<[Foo2; 10]>(), 2);
|
||||||
|
assert_eq!(mem::size_of::<[Foo2; 10]>(), 100);
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
assert_eq!(foo2s[i], Foo2 { bar: 1, baz: 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
for &foo in &foo2s {
|
||||||
|
assert_eq!(foo, Foo2 { bar: 1, baz: 2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
let foo4s = [Foo4C { bar: 1, baz: 2 }; 10];
|
||||||
|
|
||||||
|
assert_eq!(mem::align_of::<[Foo4C; 10]>(), 4);
|
||||||
|
assert_eq!(mem::size_of::<[Foo4C; 10]>(), 120);
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
assert_eq!(foo4s[i], Foo4C { bar: 1, baz: 2});
|
||||||
|
}
|
||||||
|
|
||||||
|
for &foo in &foo4s {
|
||||||
|
assert_eq!(foo, Foo4C { bar: 1, baz: 2 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,33 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S4(u8,[u8; 3]);
|
struct P1S4(u8,[u8; 3]);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S4(u8,[u8; 3]);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S5(u8, u32);
|
struct P1S5(u8, u32);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S6(u8, u32);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S13(i64, f32, u8);
|
struct P1S13(i64, f32, u8);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S14(i64, f32, u8);
|
||||||
|
|
||||||
|
#[repr(packed(4))]
|
||||||
|
struct P4S16(u8, f32, i64, u16);
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
struct P4CS20(u8, f32, i64, u16);
|
||||||
|
|
||||||
enum Foo {
|
enum Foo {
|
||||||
Bar = 1,
|
Bar = 1,
|
||||||
|
@ -27,21 +42,46 @@ enum Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S3_Foo(u8, u16, Foo);
|
struct P1S3_Foo(u8, u16, Foo);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2_Foo(Foo);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S3_Foo(u8, u16, Foo);
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S7_Option(f32, u8, u16, Option<Box<f64>>);
|
struct P1S7_Option(f32, u8, u16, Option<Box<f64>>);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2_Option(Option<Box<f64>>);
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct P2S7_Option(f32, u8, u16, Option<Box<f64>>);
|
||||||
|
|
||||||
|
fn align_to(value: usize, align: usize) -> usize {
|
||||||
|
(value + (align - 1)) & !(align - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! check {
|
||||||
|
($t:ty, $align:expr, $size:expr) => ({
|
||||||
|
assert_eq!(mem::align_of::<$t>(), $align);
|
||||||
|
assert_eq!(mem::size_of::<$t>(), $size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
assert_eq!(mem::size_of::<S4>(), 4);
|
check!(P1S4, 1, 4);
|
||||||
|
check!(P1S5, 1, 5);
|
||||||
|
check!(P1S13, 1, 13);
|
||||||
|
check!(P1S3_Foo, 1, 3 + mem::size_of::<Foo>());
|
||||||
|
check!(P1S7_Option, 1, 7 + mem::size_of::<Option<Box<f64>>>());
|
||||||
|
|
||||||
assert_eq!(mem::size_of::<S5>(), 5);
|
check!(P2S4, 1, 4);
|
||||||
|
check!(P2S6, 2, 6);
|
||||||
assert_eq!(mem::size_of::<S13>(), 13);
|
check!(P2S14, 2, 14);
|
||||||
|
check!(P4S16, 4, 16);
|
||||||
assert_eq!(mem::size_of::<S3_Foo>(),
|
check!(P4CS20, 4, 20);
|
||||||
3 + mem::size_of::<Foo>());
|
check!(P2S3_Foo, 2, align_to(3 + mem::size_of::<P2_Foo>(), 2));
|
||||||
|
check!(P2S7_Option, 2, align_to(7 + mem::size_of::<P2_Option>(), 2));
|
||||||
assert_eq!(mem::size_of::<S7_Option>(),
|
|
||||||
7 + mem::size_of::<Option<Box<f64>>>());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(untagged_unions)]
|
#![feature(untagged_unions)]
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
use std::mem::{size_of, size_of_val, align_of, align_of_val};
|
use std::mem::{size_of, size_of_val, align_of, align_of_val};
|
||||||
|
|
||||||
|
@ -18,7 +19,13 @@ struct S {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct Sp {
|
struct Sp1 {
|
||||||
|
a: u16,
|
||||||
|
b: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct Sp2 {
|
||||||
a: u16,
|
a: u16,
|
||||||
b: [u8; 3],
|
b: [u8; 3],
|
||||||
}
|
}
|
||||||
|
@ -29,15 +36,30 @@ union U {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
union Up {
|
union Up1 {
|
||||||
|
a: u16,
|
||||||
|
b: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(packed(2))]
|
||||||
|
union Up2 {
|
||||||
|
a: u16,
|
||||||
|
b: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(4))]
|
||||||
|
union Up4c {
|
||||||
a: u16,
|
a: u16,
|
||||||
b: [u8; 3],
|
b: [u8; 3],
|
||||||
}
|
}
|
||||||
|
|
||||||
const CS: S = S { a: 0, b: [0, 0, 0] };
|
const CS: S = S { a: 0, b: [0, 0, 0] };
|
||||||
const CSP: Sp = Sp { a: 0, b: [0, 0, 0] };
|
const CSP1: Sp1 = Sp1 { a: 0, b: [0, 0, 0] };
|
||||||
|
const CSP2: Sp2 = Sp2 { a: 0, b: [0, 0, 0] };
|
||||||
const CU: U = U { b: [0, 0, 0] };
|
const CU: U = U { b: [0, 0, 0] };
|
||||||
const CUP: Up = Up { b: [0, 0, 0] };
|
const CUP1: Up1 = Up1 { b: [0, 0, 0] };
|
||||||
|
const CUP2: Up2 = Up2 { b: [0, 0, 0] };
|
||||||
|
const CUP4C: Up4c = Up4c { b: [0, 0, 0] };
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let s = S { a: 0, b: [0, 0, 0] };
|
let s = S { a: 0, b: [0, 0, 0] };
|
||||||
|
@ -48,13 +70,21 @@ fn main() {
|
||||||
assert_eq!(align_of_val(&s), 2);
|
assert_eq!(align_of_val(&s), 2);
|
||||||
assert_eq!(align_of_val(&CS), 2);
|
assert_eq!(align_of_val(&CS), 2);
|
||||||
|
|
||||||
let sp = Sp { a: 0, b: [0, 0, 0] };
|
let sp1 = Sp1 { a: 0, b: [0, 0, 0] };
|
||||||
assert_eq!(size_of::<Sp>(), 5);
|
assert_eq!(size_of::<Sp1>(), 5);
|
||||||
assert_eq!(size_of_val(&sp), 5);
|
assert_eq!(size_of_val(&sp1), 5);
|
||||||
assert_eq!(size_of_val(&CSP), 5);
|
assert_eq!(size_of_val(&CSP1), 5);
|
||||||
assert_eq!(align_of::<Sp>(), 1);
|
assert_eq!(align_of::<Sp1>(), 1);
|
||||||
assert_eq!(align_of_val(&sp), 1);
|
assert_eq!(align_of_val(&sp1), 1);
|
||||||
assert_eq!(align_of_val(&CSP), 1);
|
assert_eq!(align_of_val(&CSP1), 1);
|
||||||
|
|
||||||
|
let sp2 = Sp2 { a: 0, b: [0, 0, 0] };
|
||||||
|
assert_eq!(size_of::<Sp2>(), 6);
|
||||||
|
assert_eq!(size_of_val(&sp2), 6);
|
||||||
|
assert_eq!(size_of_val(&CSP2), 6);
|
||||||
|
assert_eq!(align_of::<Sp2>(), 2);
|
||||||
|
assert_eq!(align_of_val(&sp2), 2);
|
||||||
|
assert_eq!(align_of_val(&CSP2), 2);
|
||||||
|
|
||||||
let u = U { b: [0, 0, 0] };
|
let u = U { b: [0, 0, 0] };
|
||||||
assert_eq!(size_of::<U>(), 4);
|
assert_eq!(size_of::<U>(), 4);
|
||||||
|
@ -64,19 +94,35 @@ fn main() {
|
||||||
assert_eq!(align_of_val(&u), 2);
|
assert_eq!(align_of_val(&u), 2);
|
||||||
assert_eq!(align_of_val(&CU), 2);
|
assert_eq!(align_of_val(&CU), 2);
|
||||||
|
|
||||||
let up = Up { b: [0, 0, 0] };
|
let Up1 = Up1 { b: [0, 0, 0] };
|
||||||
assert_eq!(size_of::<Up>(), 3);
|
assert_eq!(size_of::<Up1>(), 3);
|
||||||
assert_eq!(size_of_val(&up), 3);
|
assert_eq!(size_of_val(&Up1), 3);
|
||||||
assert_eq!(size_of_val(&CUP), 3);
|
assert_eq!(size_of_val(&CUP1), 3);
|
||||||
assert_eq!(align_of::<Up>(), 1);
|
assert_eq!(align_of::<Up1>(), 1);
|
||||||
assert_eq!(align_of_val(&up), 1);
|
assert_eq!(align_of_val(&Up1), 1);
|
||||||
assert_eq!(align_of_val(&CUP), 1);
|
assert_eq!(align_of_val(&CUP1), 1);
|
||||||
|
|
||||||
|
let up2 = Up2 { b: [0, 0, 0] };
|
||||||
|
assert_eq!(size_of::<Up2>(), 4);
|
||||||
|
assert_eq!(size_of_val(&up2), 4);
|
||||||
|
assert_eq!(size_of_val(&CUP2), 4);
|
||||||
|
assert_eq!(align_of::<Up2>(), 2);
|
||||||
|
assert_eq!(align_of_val(&up2), 2);
|
||||||
|
assert_eq!(align_of_val(&CUP2), 2);
|
||||||
|
|
||||||
|
let up4c = Up4c { b: [0, 0, 0] };
|
||||||
|
assert_eq!(size_of::<Up4c>(), 4);
|
||||||
|
assert_eq!(size_of_val(&up4c), 4);
|
||||||
|
assert_eq!(size_of_val(&CUP4C), 4);
|
||||||
|
assert_eq!(align_of::<Up4c>(), 2);
|
||||||
|
assert_eq!(align_of_val(&up4c), 2);
|
||||||
|
assert_eq!(align_of_val(&CUP4C), 2);
|
||||||
|
|
||||||
hybrid::check_hybrid();
|
hybrid::check_hybrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
mod hybrid {
|
mod hybrid {
|
||||||
use std::mem::size_of;
|
use std::mem::{size_of, align_of};
|
||||||
|
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct S1 {
|
struct S1 {
|
||||||
|
@ -96,9 +142,37 @@ mod hybrid {
|
||||||
u: U,
|
u: U,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
struct S1C {
|
||||||
|
a: u16,
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
union UC {
|
||||||
|
s: S1,
|
||||||
|
c: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed(2))]
|
||||||
|
struct S2C {
|
||||||
|
d: u8,
|
||||||
|
u: UC,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_hybrid() {
|
pub fn check_hybrid() {
|
||||||
|
assert_eq!(align_of::<S1>(), 1);
|
||||||
assert_eq!(size_of::<S1>(), 3);
|
assert_eq!(size_of::<S1>(), 3);
|
||||||
|
assert_eq!(align_of::<U>(), 1);
|
||||||
assert_eq!(size_of::<U>(), 3);
|
assert_eq!(size_of::<U>(), 3);
|
||||||
|
assert_eq!(align_of::<S2>(), 1);
|
||||||
assert_eq!(size_of::<S2>(), 4);
|
assert_eq!(size_of::<S2>(), 4);
|
||||||
|
|
||||||
|
assert_eq!(align_of::<S1C>(), 2);
|
||||||
|
assert_eq!(size_of::<S1C>(), 4);
|
||||||
|
assert_eq!(align_of::<UC>(), 2);
|
||||||
|
assert_eq!(size_of::<UC>(), 4);
|
||||||
|
assert_eq!(align_of::<S2C>(), 2);
|
||||||
|
assert_eq!(size_of::<S2C>(), 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
src/test/ui/feature-gate-repr_packed.rs
Normal file
14
src/test/ui/feature-gate-repr_packed.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright 2018 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(packed(1))] //~ error: the `#[repr(packed(n))]` attribute is experimental
|
||||||
|
struct Foo(u64);
|
||||||
|
|
||||||
|
fn main() {}
|
11
src/test/ui/feature-gate-repr_packed.stderr
Normal file
11
src/test/ui/feature-gate-repr_packed.stderr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error[E0658]: the `#[repr(packed(n))]` attribute is experimental (see issue #33158)
|
||||||
|
--> $DIR/feature-gate-repr_packed.rs:11:1
|
||||||
|
|
|
||||||
|
LL | #[repr(packed(1))] //~ error: the `#[repr(packed(n))]` attribute is experimental
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: add #![feature(repr_packed)] to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
|
@ -21,10 +21,34 @@
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![feature(start)]
|
#![feature(start)]
|
||||||
|
#![feature(repr_packed)]
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
struct Packed {
|
struct Packed1 {
|
||||||
|
a: u8,
|
||||||
|
b: u8,
|
||||||
|
g: i32,
|
||||||
|
c: u8,
|
||||||
|
h: i16,
|
||||||
|
d: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
#[repr(packed(2))]
|
||||||
|
struct Packed2 {
|
||||||
|
a: u8,
|
||||||
|
b: u8,
|
||||||
|
g: i32,
|
||||||
|
c: u8,
|
||||||
|
h: i16,
|
||||||
|
d: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
#[repr(packed(2))]
|
||||||
|
#[repr(C)]
|
||||||
|
struct Packed2C {
|
||||||
a: u8,
|
a: u8,
|
||||||
b: u8,
|
b: u8,
|
||||||
g: i32,
|
g: i32,
|
||||||
|
@ -45,7 +69,9 @@ struct Padded {
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
fn start(_: isize, _: *const *const u8) -> isize {
|
fn start(_: isize, _: *const *const u8) -> isize {
|
||||||
let _c: Packed = Default::default();
|
let _c: Packed1 = Default::default();
|
||||||
let _d: Padded = Default::default();
|
let _d: Packed2 = Default::default();
|
||||||
|
let _e: Packed2C = Default::default();
|
||||||
|
let _f: Padded = Default::default();
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
|
print-type-size type: `Packed2C`: 12 bytes, alignment: 2 bytes
|
||||||
|
print-type-size field `.a`: 1 bytes
|
||||||
|
print-type-size field `.b`: 1 bytes
|
||||||
|
print-type-size field `.g`: 4 bytes
|
||||||
|
print-type-size field `.c`: 1 bytes
|
||||||
|
print-type-size padding: 1 bytes
|
||||||
|
print-type-size field `.h`: 2 bytes
|
||||||
|
print-type-size field `.d`: 1 bytes
|
||||||
|
print-type-size end padding: 1 bytes
|
||||||
print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes
|
print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes
|
||||||
print-type-size field `.g`: 4 bytes
|
print-type-size field `.g`: 4 bytes
|
||||||
print-type-size field `.h`: 2 bytes
|
print-type-size field `.h`: 2 bytes
|
||||||
|
@ -6,10 +15,17 @@ print-type-size field `.b`: 1 bytes
|
||||||
print-type-size field `.c`: 1 bytes
|
print-type-size field `.c`: 1 bytes
|
||||||
print-type-size field `.d`: 1 bytes
|
print-type-size field `.d`: 1 bytes
|
||||||
print-type-size end padding: 2 bytes
|
print-type-size end padding: 2 bytes
|
||||||
print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes
|
print-type-size type: `Packed1`: 10 bytes, alignment: 1 bytes
|
||||||
print-type-size field `.a`: 1 bytes
|
print-type-size field `.a`: 1 bytes
|
||||||
print-type-size field `.b`: 1 bytes
|
print-type-size field `.b`: 1 bytes
|
||||||
print-type-size field `.g`: 4 bytes
|
print-type-size field `.g`: 4 bytes
|
||||||
print-type-size field `.c`: 1 bytes
|
print-type-size field `.c`: 1 bytes
|
||||||
print-type-size field `.h`: 2 bytes
|
print-type-size field `.h`: 2 bytes
|
||||||
print-type-size field `.d`: 1 bytes
|
print-type-size field `.d`: 1 bytes
|
||||||
|
print-type-size type: `Packed2`: 10 bytes, alignment: 2 bytes
|
||||||
|
print-type-size field `.g`: 4 bytes
|
||||||
|
print-type-size field `.h`: 2 bytes
|
||||||
|
print-type-size field `.a`: 1 bytes
|
||||||
|
print-type-size field `.b`: 1 bytes
|
||||||
|
print-type-size field `.c`: 1 bytes
|
||||||
|
print-type-size field `.d`: 1 bytes
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue