Auto merge of #48528 - bitshifter:repr_packed, r=eddyb
Implementation of `#[repr(packed(n))]` RFC 1399. Tracking issue https://github.com/rust-lang/rust/issues/33158.
This commit is contained in:
commit
47778811d9
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 align: u64,
|
||||
pub overall_size: u64,
|
||||
pub packed: bool,
|
||||
pub opt_discr_size: Option<u64>,
|
||||
pub variants: Vec<VariantInfo>,
|
||||
}
|
||||
|
@ -79,6 +80,7 @@ impl CodeStats {
|
|||
type_desc: S,
|
||||
align: Align,
|
||||
overall_size: Size,
|
||||
packed: bool,
|
||||
opt_discr_size: Option<Size>,
|
||||
variants: Vec<VariantInfo>) {
|
||||
let info = TypeSizeInfo {
|
||||
|
@ -86,6 +88,7 @@ impl CodeStats {
|
|||
type_description: type_desc.to_string(),
|
||||
align: align.abi(),
|
||||
overall_size: overall_size.bytes(),
|
||||
packed: packed,
|
||||
opt_discr_size: opt_discr_size.map(|s| s.bytes()),
|
||||
variants,
|
||||
};
|
||||
|
@ -153,24 +156,26 @@ impl CodeStats {
|
|||
for field in fields.iter() {
|
||||
let FieldInfo { ref name, offset, size, align } = *field;
|
||||
|
||||
// Include field alignment in output only if it caused padding injection
|
||||
if min_offset != offset {
|
||||
if offset > min_offset {
|
||||
let pad = offset - min_offset;
|
||||
println!("print-type-size {}padding: {} bytes",
|
||||
indent, pad);
|
||||
println!("print-type-size {}field `.{}`: {} bytes, \
|
||||
alignment: {} bytes",
|
||||
indent, name, size, align);
|
||||
} else {
|
||||
println!("print-type-size {}field `.{}`: {} bytes, \
|
||||
offset: {} bytes, \
|
||||
alignment: {} bytes",
|
||||
indent, name, size, offset, align);
|
||||
}
|
||||
} else {
|
||||
if offset > min_offset {
|
||||
let pad = offset - min_offset;
|
||||
println!("print-type-size {}padding: {} bytes",
|
||||
indent, pad);
|
||||
}
|
||||
|
||||
if offset < min_offset {
|
||||
// if this happens something is very wrong
|
||||
println!("print-type-size {}field `.{}`: {} bytes, \
|
||||
offset: {} bytes, \
|
||||
alignment: {} bytes",
|
||||
indent, name, size, offset, align);
|
||||
} else if info.packed || offset == min_offset {
|
||||
println!("print-type-size {}field `.{}`: {} bytes",
|
||||
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;
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use self::Integer::*;
|
|||
pub use self::Primitive::*;
|
||||
|
||||
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::attr;
|
||||
|
@ -344,8 +344,8 @@ impl AddAssign for Size {
|
|||
/// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct Align {
|
||||
abi: u8,
|
||||
pref: u8,
|
||||
abi_pow2: u8,
|
||||
pref_pow2: u8,
|
||||
}
|
||||
|
||||
impl Align {
|
||||
|
@ -377,17 +377,17 @@ impl Align {
|
|||
};
|
||||
|
||||
Ok(Align {
|
||||
abi: log2(abi)?,
|
||||
pref: log2(pref)?,
|
||||
abi_pow2: log2(abi)?,
|
||||
pref_pow2: log2(pref)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn abi(self) -> u64 {
|
||||
1 << self.abi
|
||||
1 << self.abi_pow2
|
||||
}
|
||||
|
||||
pub fn pref(self) -> u64 {
|
||||
1 << self.pref
|
||||
1 << self.pref_pow2
|
||||
}
|
||||
|
||||
pub fn abi_bits(self) -> u64 {
|
||||
|
@ -400,15 +400,15 @@ impl Align {
|
|||
|
||||
pub fn min(self, other: Align) -> Align {
|
||||
Align {
|
||||
abi: cmp::min(self.abi, other.abi),
|
||||
pref: cmp::min(self.pref, other.pref),
|
||||
abi_pow2: cmp::min(self.abi_pow2, other.abi_pow2),
|
||||
pref_pow2: cmp::min(self.pref_pow2, other.pref_pow2),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max(self, other: Align) -> Align {
|
||||
Align {
|
||||
abi: cmp::max(self.abi, other.abi),
|
||||
pref: cmp::max(self.pref, other.pref),
|
||||
abi_pow2: cmp::max(self.abi_pow2, other.abi_pow2),
|
||||
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");
|
||||
}
|
||||
|
||||
let pack = {
|
||||
let pack = repr.pack as u64;
|
||||
Align::from_bytes(pack, pack).unwrap()
|
||||
};
|
||||
|
||||
let mut align = if packed {
|
||||
dl.i8_align
|
||||
} 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 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.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
|
||||
let mut optimize = !repr.inhibit_struct_field_reordering_opt();
|
||||
if let StructKind::Prefixed(_, align) = kind {
|
||||
optimize &= align.abi() == 1;
|
||||
}
|
||||
|
@ -997,6 +1001,9 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
fields.len()
|
||||
};
|
||||
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 {
|
||||
StructKind::AlwaysSized |
|
||||
StructKind::MaybeUnsized => {
|
||||
|
@ -1004,11 +1011,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
// Place ZSTs first to avoid "interesting offsets",
|
||||
// especially with only one or two non-ZST fields.
|
||||
let f = &fields[x as usize];
|
||||
(!f.is_zst(), cmp::Reverse(f.align.abi()))
|
||||
})
|
||||
(!f.is_zst(), cmp::Reverse(field_align(f)))
|
||||
});
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
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
|
||||
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);
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
dl.aggregate_align
|
||||
|
@ -1393,7 +1413,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
for field in &variants[0] {
|
||||
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);
|
||||
}
|
||||
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>) {
|
||||
// (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);
|
||||
self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
|
||||
type_desc,
|
||||
layout.align,
|
||||
layout.size,
|
||||
packed,
|
||||
opt_discr_size,
|
||||
variants);
|
||||
};
|
||||
|
@ -1758,7 +1782,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
|
||||
ty::TyClosure(..) => {
|
||||
debug!("print-type-size t: `{:?}` record closure", layout.ty);
|
||||
record(DataTypeKind::Closure, None, vec![]);
|
||||
record(DataTypeKind::Closure, false, None, vec![]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1769,6 +1793,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
};
|
||||
|
||||
let adt_kind = adt_def.adt_kind();
|
||||
let adt_packed = adt_def.repr.packed();
|
||||
|
||||
let build_variant_info = |n: Option<ast::Name>,
|
||||
flds: &[ast::Name],
|
||||
|
@ -1821,6 +1846,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
let fields: Vec<_> =
|
||||
variant_def.fields.iter().map(|f| f.name).collect();
|
||||
record(adt_kind.into(),
|
||||
adt_packed,
|
||||
None,
|
||||
vec![build_variant_info(Some(variant_def.name),
|
||||
&fields,
|
||||
|
@ -1828,7 +1854,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
|
|||
} else {
|
||||
// (This case arises for *empty* enums; so give it
|
||||
// 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))
|
||||
})
|
||||
.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)),
|
||||
_ => None
|
||||
}, variant_infos);
|
||||
|
@ -2518,8 +2544,8 @@ impl_stable_hash_for!(enum ::ty::layout::Primitive {
|
|||
});
|
||||
|
||||
impl_stable_hash_for!(struct ::ty::layout::Align {
|
||||
abi,
|
||||
pref
|
||||
abi_pow2,
|
||||
pref_pow2
|
||||
});
|
||||
|
||||
impl_stable_hash_for!(struct ::ty::layout::Size {
|
||||
|
|
|
@ -1623,15 +1623,13 @@ bitflags! {
|
|||
#[derive(RustcEncodable, RustcDecodable, Default)]
|
||||
pub struct ReprFlags: u8 {
|
||||
const IS_C = 1 << 0;
|
||||
const IS_PACKED = 1 << 1;
|
||||
const IS_SIMD = 1 << 2;
|
||||
const IS_TRANSPARENT = 1 << 3;
|
||||
const IS_SIMD = 1 << 1;
|
||||
const IS_TRANSPARENT = 1 << 2;
|
||||
// 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.
|
||||
const IS_UNOPTIMISABLE = ReprFlags::IS_C.bits |
|
||||
ReprFlags::IS_PACKED.bits |
|
||||
ReprFlags::IS_SIMD.bits |
|
||||
ReprFlags::IS_LINEAR.bits;
|
||||
}
|
||||
|
@ -1648,11 +1646,13 @@ impl_stable_hash_for!(struct ReprFlags {
|
|||
pub struct ReprOptions {
|
||||
pub int: Option<attr::IntType>,
|
||||
pub align: u32,
|
||||
pub pack: u32,
|
||||
pub flags: ReprFlags,
|
||||
}
|
||||
|
||||
impl_stable_hash_for!(struct ReprOptions {
|
||||
align,
|
||||
pack,
|
||||
int,
|
||||
flags
|
||||
});
|
||||
|
@ -1662,11 +1662,19 @@ impl ReprOptions {
|
|||
let mut flags = ReprFlags::empty();
|
||||
let mut size = None;
|
||||
let mut max_align = 0;
|
||||
let mut min_pack = 0;
|
||||
for attr in tcx.get_attrs(did).iter() {
|
||||
for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
|
||||
flags.insert(match r {
|
||||
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::ReprSimd => ReprFlags::IS_SIMD,
|
||||
attr::ReprInt(i) => {
|
||||
|
@ -1685,7 +1693,7 @@ impl ReprOptions {
|
|||
if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
|
||||
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]
|
||||
|
@ -1693,7 +1701,7 @@ impl ReprOptions {
|
|||
#[inline]
|
||||
pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
|
||||
#[inline]
|
||||
pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
|
||||
pub fn packed(&self) -> bool { self.pack > 0 }
|
||||
#[inline]
|
||||
pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
|
||||
#[inline]
|
||||
|
@ -1709,6 +1717,12 @@ impl ReprOptions {
|
|||
pub fn inhibit_enum_layout_opt(&self) -> bool {
|
||||
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 {
|
||||
|
|
|
@ -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) {
|
||||
if tcx.adt_def(def_id).repr.packed() {
|
||||
if tcx.adt_def(def_id).repr.align > 0 {
|
||||
let repr = tcx.adt_def(def_id).repr;
|
||||
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,
|
||||
"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
|
||||
E0564, // only named lifetimes are allowed in `impl Trait`,
|
||||
// but `{}` was found in the type `{}`
|
||||
E0587, // struct has conflicting packed and align representation hints
|
||||
E0588, // packed struct cannot transitively contain a `[repr(align)]` struct
|
||||
E0587, // type has conflicting packed and align representation hints
|
||||
E0588, // packed type cannot transitively contain a `[repr(align)]` type
|
||||
E0592, // duplicate definitions with name `{}`
|
||||
// E0613, // Removed (merged with E0609)
|
||||
E0640, // infer outlives
|
||||
E0627, // yield statement outside of generator literal
|
||||
E0632, // cannot provide explicit type parameters when `impl Trait` is used in
|
||||
// argument position.
|
||||
E0634, // type has conflicting packed representaton hints
|
||||
E0641, // cannot cast to/from a pointer with an unknown kind
|
||||
E0645, // trait aliases not finished
|
||||
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 hint = match word {
|
||||
"C" => Some(ReprC),
|
||||
"packed" => Some(ReprPacked),
|
||||
"packed" => Some(ReprPacked(1)),
|
||||
"simd" => Some(ReprSimd),
|
||||
"transparent" => Some(ReprTransparent),
|
||||
_ => match int_type_of_word(word) {
|
||||
|
@ -1009,27 +1009,41 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
|
|||
acc.push(h);
|
||||
}
|
||||
} else if let Some((name, value)) = item.name_value_literal() {
|
||||
if name == "align" {
|
||||
recognised = true;
|
||||
let mut align_error = None;
|
||||
if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node {
|
||||
if align.is_power_of_two() {
|
||||
let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
|
||||
if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
|
||||
if literal.is_power_of_two() {
|
||||
// rustc::ty::layout::Align restricts align to <= 2147483647
|
||||
if align <= 2147483647 {
|
||||
acc.push(ReprAlign(align as u32));
|
||||
if *literal <= 2147483647 {
|
||||
Ok(*literal as u32)
|
||||
} else {
|
||||
align_error = Some("larger than 2147483647");
|
||||
Err("larger than 2147483647")
|
||||
}
|
||||
} else {
|
||||
align_error = Some("not a power of two");
|
||||
Err("not a power of two")
|
||||
}
|
||||
} else {
|
||||
align_error = Some("not an unsuffixed integer");
|
||||
}
|
||||
if let Some(align_error) = align_error {
|
||||
span_err!(diagnostic, item.span, E0589,
|
||||
"invalid `repr(align)` attribute: {}", align_error);
|
||||
Err("not an unsuffixed integer")
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -1065,7 +1079,7 @@ fn int_type_of_word(s: &str) -> Option<IntType> {
|
|||
pub enum ReprAttr {
|
||||
ReprInt(IntType),
|
||||
ReprC,
|
||||
ReprPacked,
|
||||
ReprPacked(u32),
|
||||
ReprSimd,
|
||||
ReprTransparent,
|
||||
ReprAlign(u32),
|
||||
|
|
|
@ -432,6 +432,9 @@ declare_features! (
|
|||
// Parentheses in patterns
|
||||
(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 _;`
|
||||
(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))]
|
||||
let mut is_repr_align = false;
|
||||
// allow attr_literals in #[repr(align(x))] and #[repr(packed(n))]
|
||||
let mut allow_attr_literal = false;
|
||||
if attr.path == "repr" {
|
||||
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
|
||||
}
|
||||
|
||||
if !is_repr_align {
|
||||
if !allow_attr_literal {
|
||||
let meta = panictry!(attr.parse_meta(self.context.parse_sess));
|
||||
if contains_novel_literal(&meta) {
|
||||
gate_feature_post!(&self, attr_literals, attr.span,
|
||||
|
@ -1535,6 +1539,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
|||
"the `#[repr(transparent)]` attribute \
|
||||
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 {
|
||||
Annotatable::Item(ref item) => {
|
||||
let is_packed = item.attrs.iter().any(|attr| {
|
||||
attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr)
|
||||
.contains(&attr::ReprPacked)
|
||||
for r in attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr) {
|
||||
if let attr::ReprPacked(_) = r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
let has_no_type_params = match item.node {
|
||||
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 r in &attr::find_repr_attrs(diagnostic, a) {
|
||||
repr_type_name = match *r {
|
||||
attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
|
||||
attr::ReprPacked(_) | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
|
||||
continue,
|
||||
|
||||
attr::ReprC => "i32",
|
||||
|
|
|
@ -11,16 +11,23 @@
|
|||
// compile-flags: -C no-prepopulate-passes
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(repr_packed)]
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Packed {
|
||||
pub struct Packed1 {
|
||||
dealign: u8,
|
||||
data: u32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @write_pkd
|
||||
#[repr(packed(2))]
|
||||
pub struct Packed2 {
|
||||
dealign: u8,
|
||||
data: u32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @write_pkd1
|
||||
#[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: store i32 42, i32* %{{.*}}, align 1
|
||||
let result = pkd.data;
|
||||
|
@ -28,43 +35,94 @@ pub fn write_pkd(pkd: &mut Packed) -> u32 {
|
|||
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]);
|
||||
#[repr(packed)]
|
||||
pub struct BigPacked {
|
||||
pub struct BigPacked1 {
|
||||
dealign: u8,
|
||||
data: Array
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @call_pkd
|
||||
#[repr(packed(2))]
|
||||
pub struct BigPacked2 {
|
||||
dealign: u8,
|
||||
data: Array
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @call_pkd1
|
||||
#[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: 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 that calls whose destination is a field of a packed struct
|
||||
// go through an alloca rather than calling the function with an
|
||||
// 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)]
|
||||
#[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]
|
||||
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)
|
||||
*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)]
|
||||
#[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]
|
||||
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)
|
||||
*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.
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![feature(repr_packed)]
|
||||
|
||||
#[repr(C)]
|
||||
enum A { A }
|
||||
|
@ -36,6 +37,16 @@ struct G(i32); //~ ERROR type has conflicting packed and align representation hi
|
|||
#[repr(packed)]
|
||||
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))]
|
||||
union X { //~ ERROR type has conflicting packed and align representation hints
|
||||
i: i32
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![feature(box_syntax)]
|
||||
#![feature(repr_packed)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
@ -60,6 +61,18 @@ struct AlignContainsPacked {
|
|||
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).
|
||||
// Check that it works with big numbers.
|
||||
#[repr(align(0x10000))]
|
||||
|
@ -218,6 +231,15 @@ pub fn main() {
|
|||
assert_eq!(mem::size_of_val(&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 {
|
||||
stuff: [0; 0x10000],
|
||||
};
|
||||
|
|
|
@ -8,8 +8,24 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_packed)]
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct S {
|
||||
pub struct P1S5 {
|
||||
a: u8,
|
||||
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?
|
||||
|
||||
#![feature(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,
|
||||
baz: usize
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let foo = Foo { bar: 1, baz: 2 };
|
||||
let foo = Foo1 { bar: 1, baz: 2 };
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -8,18 +8,45 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_packed)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed)]
|
||||
struct S<T, S> {
|
||||
struct P1<T, S> {
|
||||
a: T,
|
||||
b: u8,
|
||||
c: S
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(mem::size_of::<S<u8, u8>>(), 3);
|
||||
|
||||
assert_eq!(mem::size_of::<S<u64, u16>>(), 11);
|
||||
#[repr(packed(2))]
|
||||
struct P2<T, S> {
|
||||
a: T,
|
||||
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
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(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,
|
||||
baz: usize
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let foo = Foo { bar: 1, baz: 2 };
|
||||
match foo {
|
||||
Foo {bar, baz} => {
|
||||
let foo1 = Foo1 { bar: 1, baz: 2 };
|
||||
match foo1 {
|
||||
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!(baz, 2);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,15 @@ extern crate packed;
|
|||
|
||||
use std::mem;
|
||||
|
||||
pub fn main() {
|
||||
assert_eq!(mem::size_of::<packed::S>(), 5);
|
||||
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!(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
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_packed)]
|
||||
|
||||
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed)]
|
||||
struct S4 {
|
||||
struct P1S4 {
|
||||
a: u8,
|
||||
b: [u8; 3],
|
||||
}
|
||||
|
||||
#[repr(packed(2))]
|
||||
struct P2S4 {
|
||||
a: u8,
|
||||
b: [u8; 3],
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct S5 {
|
||||
struct P1S5 {
|
||||
a: u8,
|
||||
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)]
|
||||
struct S13 {
|
||||
struct P1S13 {
|
||||
a: i64,
|
||||
b: f32,
|
||||
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 {
|
||||
Bar = 1,
|
||||
Baz = 2
|
||||
}
|
||||
|
||||
#[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,
|
||||
b: u16,
|
||||
c: Foo
|
||||
}
|
||||
|
||||
#[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,
|
||||
b: u8,
|
||||
c: u16,
|
||||
|
@ -52,15 +124,41 @@ struct S7_Option {
|
|||
}
|
||||
|
||||
// Placing packed structs in statics should work
|
||||
static TEST_S4: S4 = S4 { a: 1, b: [2, 3, 4] };
|
||||
static TEST_S5: S5 = S5 { a: 3, b: 67 };
|
||||
static TEST_S3_Foo: S3_Foo = S3_Foo { a: 1, b: 2, c: Foo::Baz };
|
||||
static TEST_P1S4: P1S4 = P1S4 { a: 1, b: [2, 3, 4] };
|
||||
static TEST_P1S5: P1S5 = P1S5 { a: 3, b: 67 };
|
||||
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() {
|
||||
assert_eq!(mem::size_of::<S4>(), 4);
|
||||
assert_eq!(mem::size_of::<S5>(), 5);
|
||||
assert_eq!(mem::size_of::<S13>(), 13);
|
||||
assert_eq!(mem::size_of::<S3_Foo>(), 3 + mem::size_of::<Foo>());
|
||||
assert_eq!(mem::size_of::<S7_Option>(), 7 + mem::size_of::<Option<Box<f64>>>());
|
||||
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>>>());
|
||||
|
||||
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
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(repr_packed)]
|
||||
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct Foo {
|
||||
struct Foo1 {
|
||||
bar: u8,
|
||||
baz: u64
|
||||
}
|
||||
|
||||
impl PartialEq for Foo {
|
||||
fn eq(&self, other: &Foo) -> bool {
|
||||
impl PartialEq for Foo1 {
|
||||
fn eq(&self, other: &Foo1) -> bool {
|
||||
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 {
|
||||
let bar = self.bar;
|
||||
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("baz", &baz)
|
||||
.finish()
|
||||
|
@ -37,15 +89,42 @@ impl fmt::Debug for Foo {
|
|||
}
|
||||
|
||||
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 {
|
||||
assert_eq!(foos[i], Foo { bar: 1, baz: 2});
|
||||
assert_eq!(foo1s[i], Foo1 { bar: 1, baz: 2});
|
||||
}
|
||||
|
||||
for &foo in &foos {
|
||||
assert_eq!(foo, Foo { bar: 1, baz: 2 });
|
||||
for &foo in &foo1s {
|
||||
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
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
#![feature(repr_packed)]
|
||||
|
||||
use std::mem;
|
||||
|
||||
#[repr(packed)]
|
||||
struct S4(u8,[u8; 3]);
|
||||
struct P1S4(u8,[u8; 3]);
|
||||
|
||||
#[repr(packed(2))]
|
||||
struct P2S4(u8,[u8; 3]);
|
||||
|
||||
#[repr(packed)]
|
||||
struct S5(u8, u32);
|
||||
struct P1S5(u8, u32);
|
||||
|
||||
#[repr(packed(2))]
|
||||
struct P2S6(u8, u32);
|
||||
|
||||
#[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 {
|
||||
Bar = 1,
|
||||
|
@ -27,21 +42,46 @@ enum Foo {
|
|||
}
|
||||
|
||||
#[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)]
|
||||
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() {
|
||||
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);
|
||||
|
||||
assert_eq!(mem::size_of::<S13>(), 13);
|
||||
|
||||
assert_eq!(mem::size_of::<S3_Foo>(),
|
||||
3 + mem::size_of::<Foo>());
|
||||
|
||||
assert_eq!(mem::size_of::<S7_Option>(),
|
||||
7 + mem::size_of::<Option<Box<f64>>>());
|
||||
check!(P2S4, 1, 4);
|
||||
check!(P2S6, 2, 6);
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// except according to those terms.
|
||||
|
||||
#![feature(untagged_unions)]
|
||||
#![feature(repr_packed)]
|
||||
|
||||
use std::mem::{size_of, size_of_val, align_of, align_of_val};
|
||||
|
||||
|
@ -18,7 +19,13 @@ struct S {
|
|||
}
|
||||
|
||||
#[repr(packed)]
|
||||
struct Sp {
|
||||
struct Sp1 {
|
||||
a: u16,
|
||||
b: [u8; 3],
|
||||
}
|
||||
|
||||
#[repr(packed(2))]
|
||||
struct Sp2 {
|
||||
a: u16,
|
||||
b: [u8; 3],
|
||||
}
|
||||
|
@ -29,15 +36,30 @@ union U {
|
|||
}
|
||||
|
||||
#[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,
|
||||
b: [u8; 3],
|
||||
}
|
||||
|
||||
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 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() {
|
||||
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(&CS), 2);
|
||||
|
||||
let sp = Sp { a: 0, b: [0, 0, 0] };
|
||||
assert_eq!(size_of::<Sp>(), 5);
|
||||
assert_eq!(size_of_val(&sp), 5);
|
||||
assert_eq!(size_of_val(&CSP), 5);
|
||||
assert_eq!(align_of::<Sp>(), 1);
|
||||
assert_eq!(align_of_val(&sp), 1);
|
||||
assert_eq!(align_of_val(&CSP), 1);
|
||||
let sp1 = Sp1 { a: 0, b: [0, 0, 0] };
|
||||
assert_eq!(size_of::<Sp1>(), 5);
|
||||
assert_eq!(size_of_val(&sp1), 5);
|
||||
assert_eq!(size_of_val(&CSP1), 5);
|
||||
assert_eq!(align_of::<Sp1>(), 1);
|
||||
assert_eq!(align_of_val(&sp1), 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] };
|
||||
assert_eq!(size_of::<U>(), 4);
|
||||
|
@ -64,19 +94,35 @@ fn main() {
|
|||
assert_eq!(align_of_val(&u), 2);
|
||||
assert_eq!(align_of_val(&CU), 2);
|
||||
|
||||
let up = Up { b: [0, 0, 0] };
|
||||
assert_eq!(size_of::<Up>(), 3);
|
||||
assert_eq!(size_of_val(&up), 3);
|
||||
assert_eq!(size_of_val(&CUP), 3);
|
||||
assert_eq!(align_of::<Up>(), 1);
|
||||
assert_eq!(align_of_val(&up), 1);
|
||||
assert_eq!(align_of_val(&CUP), 1);
|
||||
let Up1 = Up1 { b: [0, 0, 0] };
|
||||
assert_eq!(size_of::<Up1>(), 3);
|
||||
assert_eq!(size_of_val(&Up1), 3);
|
||||
assert_eq!(size_of_val(&CUP1), 3);
|
||||
assert_eq!(align_of::<Up1>(), 1);
|
||||
assert_eq!(align_of_val(&Up1), 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();
|
||||
}
|
||||
|
||||
mod hybrid {
|
||||
use std::mem::size_of;
|
||||
use std::mem::{size_of, align_of};
|
||||
|
||||
#[repr(packed)]
|
||||
struct S1 {
|
||||
|
@ -96,9 +142,37 @@ mod hybrid {
|
|||
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() {
|
||||
assert_eq!(align_of::<S1>(), 1);
|
||||
assert_eq!(size_of::<S1>(), 3);
|
||||
assert_eq!(align_of::<U>(), 1);
|
||||
assert_eq!(size_of::<U>(), 3);
|
||||
assert_eq!(align_of::<S2>(), 1);
|
||||
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)]
|
||||
#![feature(start)]
|
||||
#![feature(repr_packed)]
|
||||
|
||||
#[derive(Default)]
|
||||
#[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,
|
||||
b: u8,
|
||||
g: i32,
|
||||
|
@ -45,7 +69,9 @@ struct Padded {
|
|||
|
||||
#[start]
|
||||
fn start(_: isize, _: *const *const u8) -> isize {
|
||||
let _c: Packed = Default::default();
|
||||
let _d: Padded = Default::default();
|
||||
let _c: Packed1 = Default::default();
|
||||
let _d: Packed2 = Default::default();
|
||||
let _e: Packed2C = Default::default();
|
||||
let _f: Padded = Default::default();
|
||||
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 field `.g`: 4 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 `.d`: 1 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 `.b`: 1 bytes
|
||||
print-type-size field `.g`: 4 bytes
|
||||
print-type-size field `.c`: 1 bytes
|
||||
print-type-size field `.h`: 2 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…
Reference in a new issue