diff --git a/src/doc/unstable-book/src/language-features/repr-packed.md b/src/doc/unstable-book/src/language-features/repr-packed.md new file mode 100644 index 0000000000000..2dd763d04b0ab --- /dev/null +++ b/src/doc/unstable-book/src/language-features/repr-packed.md @@ -0,0 +1,8 @@ +# `repr_packed` + +The tracking issue for this feature is [#33158] + +[#33158]: https://github.com/rust-lang/rust/issues/33158 + +------------------------ + diff --git a/src/librustc/session/code_stats.rs b/src/librustc/session/code_stats.rs index 64f405e0f24b6..df4060e71e53e 100644 --- a/src/librustc/session/code_stats.rs +++ b/src/librustc/session/code_stats.rs @@ -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, pub variants: Vec, } @@ -79,6 +80,7 @@ impl CodeStats { type_desc: S, align: Align, overall_size: Size, + packed: bool, opt_discr_size: Option, variants: Vec) { 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; diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 5f9c305d92f04..6ed8730de6a09 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -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 231 - 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 = (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, 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 { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 5b1160f142021..33b59eda7ce2c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -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, 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 { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 6c18f8d285d02..a60ba4c6d1666 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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(); } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 79d7c8e728210..5a53c008f6c31 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -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 diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 2812e1238e9a0..c68a743303a27 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -993,7 +993,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec 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 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 { + 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 { pub enum ReprAttr { ReprInt(IntType), ReprC, - ReprPacked, + ReprPacked(u32), ReprSimd, ReprTransparent, ReprAlign(u32), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 0a3cd66d89717..df39757d1ebf1 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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"); + } + } } } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 4126ce79f3555..66053e037e1b4 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -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", diff --git a/src/test/codegen/packed.rs b/src/test/codegen/packed.rs index 022f581278c2f..0693eae7d7884 100644 --- a/src/test/codegen/packed.rs +++ b/src/test/codegen/packed.rs @@ -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 +} + +#[repr(packed(2))] +pub struct BigPacked2 { dealign: u8, data: Array } -// CHECK-LABEL: @call_pkd +// 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)); + +#[repr(packed(2))] +#[derive(Copy, Clone)] +pub struct Packed2NestedPair((u32, u32)); -// CHECK-LABEL: @pkd_nested_pair +// 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; +} + diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs index 8acc8b7bb1e5c..426f60c6b098c 100644 --- a/src/test/compile-fail/conflicting-repr-hints.rs +++ b/src/test/compile-fail/conflicting-repr-hints.rs @@ -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 diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs index dea8462705f28..2b6a151574ad9 100644 --- a/src/test/run-pass/align-struct.rs +++ b/src/test/run-pass/align-struct.rs @@ -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::(), 16); + assert_eq!(mem::size_of::(), 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::()); + assert_eq!(mem::size_of_val(&a), 32); + assert!(is_aligned_to(&a, 16)); + let mut large = box AlignLarge { stuff: [0; 0x10000], }; diff --git a/src/test/run-pass/auxiliary/packed.rs b/src/test/run-pass/auxiliary/packed.rs index 86f5f93e3cf08..828be41cd4157 100644 --- a/src/test/run-pass/auxiliary/packed.rs +++ b/src/test/run-pass/auxiliary/packed.rs @@ -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 +} diff --git a/src/test/run-pass/issue-48159.rs b/src/test/run-pass/issue-48159.rs new file mode 100644 index 0000000000000..ce4585607e9b1 --- /dev/null +++ b/src/test/run-pass/issue-48159.rs @@ -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 or the MIT license +// , 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::(), 4); +} diff --git a/src/test/run-pass/packed-struct-borrow-element.rs b/src/test/run-pass/packed-struct-borrow-element.rs index e725b25efee52..c8a8643ed6b98 100644 --- a/src/test/run-pass/packed-struct-borrow-element.rs +++ b/src/test/run-pass/packed-struct-borrow-element.rs @@ -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); } diff --git a/src/test/run-pass/packed-struct-generic-size.rs b/src/test/run-pass/packed-struct-generic-size.rs index 4e1f62b28ab91..127d873b2d97a 100644 --- a/src/test/run-pass/packed-struct-generic-size.rs +++ b/src/test/run-pass/packed-struct-generic-size.rs @@ -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 { +struct P1 { a: T, b: u8, c: S } +#[repr(packed(2))] +struct P2 { + a: T, + b: u8, + c: S +} + +#[repr(C, packed(4))] +struct P4C { + 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() { - assert_eq!(mem::size_of::>(), 3); + check!(P1::, 1, 3); + check!(P1::, 1, 11); + + check!(P2::, 1, 3); + check!(P2::, 2, 12); - assert_eq!(mem::size_of::>(), 11); + check!(P4C::, 1, 3); + check!(P4C::, 4, 12); } diff --git a/src/test/run-pass/packed-struct-match.rs b/src/test/run-pass/packed-struct-match.rs index 3cd254014c181..c02d524d763d3 100644 --- a/src/test/run-pass/packed-struct-match.rs +++ b/src/test/run-pass/packed-struct-match.rs @@ -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); } diff --git a/src/test/run-pass/packed-struct-size-xc.rs b/src/test/run-pass/packed-struct-size-xc.rs index 372693433db6f..48f34554ca142 100644 --- a/src/test/run-pass/packed-struct-size-xc.rs +++ b/src/test/run-pass/packed-struct-size-xc.rs @@ -15,6 +15,15 @@ extern crate packed; use std::mem; +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::(), 5); + check!(packed::P1S5, 1, 5); + check!(packed::P2S6, 2, 6); + check!(packed::P2CS8, 2, 8); } diff --git a/src/test/run-pass/packed-struct-size.rs b/src/test/run-pass/packed-struct-size.rs index 754a357333979..f8e23610fe230 100644 --- a/src/test/run-pass/packed-struct-size.rs +++ b/src/test/run-pass/packed-struct-size.rs @@ -7,44 +7,116 @@ // , 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> +} + +#[repr(packed(2))] +struct P2_Option { + a: Option> +} + +#[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::(), 4); - assert_eq!(mem::size_of::(), 5); - assert_eq!(mem::size_of::(), 13); - assert_eq!(mem::size_of::(), 3 + mem::size_of::()); - assert_eq!(mem::size_of::(), 7 + mem::size_of::>>()); + check!(P1S4, 1, 4); + check!(P1S5, 1, 5); + check!(P1S13, 1, 13); + check!(P1S3_Foo, 1, 3 + mem::size_of::()); + check!(P1S7_Option, 1, 7 + mem::size_of::>>()); + + 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::(), 2)); + check!(P2S7_Option, 2, align_to(7 + mem::size_of::(), 2)); } diff --git a/src/test/run-pass/packed-struct-vec.rs b/src/test/run-pass/packed-struct-vec.rs index 57407b8422371..9d8b3d0d07412 100644 --- a/src/test/run-pass/packed-struct-vec.rs +++ b/src/test/run-pass/packed-struct-vec.rs @@ -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::align_of::<[Foo1; 10]>(), 1); + assert_eq!(mem::size_of::<[Foo1; 10]>(), 90); + + for i in 0..10 { + assert_eq!(foo1s[i], Foo1 { 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::size_of::<[Foo; 10]>(), 90); + assert_eq!(mem::align_of::<[Foo4C; 10]>(), 4); + assert_eq!(mem::size_of::<[Foo4C; 10]>(), 120); for i in 0..10 { - assert_eq!(foos[i], Foo { bar: 1, baz: 2}); + assert_eq!(foo4s[i], Foo4C { bar: 1, baz: 2}); } - for &foo in &foos { - assert_eq!(foo, Foo { bar: 1, baz: 2 }); + for &foo in &foo4s { + assert_eq!(foo, Foo4C { bar: 1, baz: 2 }); } } diff --git a/src/test/run-pass/packed-tuple-struct-size.rs b/src/test/run-pass/packed-tuple-struct-size.rs index b0c8684cfe3e5..9def6ac28e552 100644 --- a/src/test/run-pass/packed-tuple-struct-size.rs +++ b/src/test/run-pass/packed-tuple-struct-size.rs @@ -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>); +struct P1S7_Option(f32, u8, u16, Option>); -pub fn main() { - assert_eq!(mem::size_of::(), 4); +#[repr(packed(2))] +struct P2_Option(Option>); + +#[repr(packed(2))] +struct P2S7_Option(f32, u8, u16, Option>); - assert_eq!(mem::size_of::(), 5); +fn align_to(value: usize, align: usize) -> usize { + (value + (align - 1)) & !(align - 1) +} - assert_eq!(mem::size_of::(), 13); +macro_rules! check { + ($t:ty, $align:expr, $size:expr) => ({ + assert_eq!(mem::align_of::<$t>(), $align); + assert_eq!(mem::size_of::<$t>(), $size); + }); +} - assert_eq!(mem::size_of::(), - 3 + mem::size_of::()); +pub fn main() { + check!(P1S4, 1, 4); + check!(P1S5, 1, 5); + check!(P1S13, 1, 13); + check!(P1S3_Foo, 1, 3 + mem::size_of::()); + check!(P1S7_Option, 1, 7 + mem::size_of::>>()); - assert_eq!(mem::size_of::(), - 7 + mem::size_of::>>()); + 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::(), 2)); + check!(P2S7_Option, 2, align_to(7 + mem::size_of::(), 2)); } diff --git a/src/test/run-pass/union/union-packed.rs b/src/test/run-pass/union/union-packed.rs index 6a61280823e50..61bb04fece095 100644 --- a/src/test/run-pass/union/union-packed.rs +++ b/src/test/run-pass/union/union-packed.rs @@ -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::(), 5); - assert_eq!(size_of_val(&sp), 5); - assert_eq!(size_of_val(&CSP), 5); - assert_eq!(align_of::(), 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::(), 5); + assert_eq!(size_of_val(&sp1), 5); + assert_eq!(size_of_val(&CSP1), 5); + assert_eq!(align_of::(), 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::(), 6); + assert_eq!(size_of_val(&sp2), 6); + assert_eq!(size_of_val(&CSP2), 6); + assert_eq!(align_of::(), 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::(), 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::(), 3); - assert_eq!(size_of_val(&up), 3); - assert_eq!(size_of_val(&CUP), 3); - assert_eq!(align_of::(), 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::(), 3); + assert_eq!(size_of_val(&Up1), 3); + assert_eq!(size_of_val(&CUP1), 3); + assert_eq!(align_of::(), 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::(), 4); + assert_eq!(size_of_val(&up2), 4); + assert_eq!(size_of_val(&CUP2), 4); + assert_eq!(align_of::(), 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::(), 4); + assert_eq!(size_of_val(&up4c), 4); + assert_eq!(size_of_val(&CUP4C), 4); + assert_eq!(align_of::(), 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::(), 1); assert_eq!(size_of::(), 3); + assert_eq!(align_of::(), 1); assert_eq!(size_of::(), 3); + assert_eq!(align_of::(), 1); assert_eq!(size_of::(), 4); + + assert_eq!(align_of::(), 2); + assert_eq!(size_of::(), 4); + assert_eq!(align_of::(), 2); + assert_eq!(size_of::(), 4); + assert_eq!(align_of::(), 2); + assert_eq!(size_of::(), 6); } } diff --git a/src/test/ui/feature-gate-repr_packed.rs b/src/test/ui/feature-gate-repr_packed.rs new file mode 100644 index 0000000000000..12bb152b46749 --- /dev/null +++ b/src/test/ui/feature-gate-repr_packed.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/ui/feature-gate-repr_packed.stderr b/src/test/ui/feature-gate-repr_packed.stderr new file mode 100644 index 0000000000000..d0faf9d90bd5c --- /dev/null +++ b/src/test/ui/feature-gate-repr_packed.stderr @@ -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`. diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs index a4288f67899a2..5d8c932625808 100644 --- a/src/test/ui/print_type_sizes/packed.rs +++ b/src/test/ui/print_type_sizes/packed.rs @@ -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 } diff --git a/src/test/ui/print_type_sizes/packed.stdout b/src/test/ui/print_type_sizes/packed.stdout index 83fd333c9c7fc..58e1bac9eb794 100644 --- a/src/test/ui/print_type_sizes/packed.stdout +++ b/src/test/ui/print_type_sizes/packed.stdout @@ -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