diff options
| author | bors <bors@rust-lang.org> | 2017-04-22 11:50:40 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2017-04-22 11:50:40 +0000 |
| commit | 6d841da4a0d7629f826117f99052e3d4a7997a7e (patch) | |
| tree | 27e04604a86c867e8146bf75ff0c1ea973aaf47c | |
| parent | ff13b7c91813eb178c98a7abc661acaf5c41dc31 (diff) | |
| parent | 946f8e6a59242a8112c6983d1336fef54bc55b9a (diff) | |
| download | rust-6d841da4a0d7629f826117f99052e3d4a7997a7e.tar.gz rust-6d841da4a0d7629f826117f99052e3d4a7997a7e.zip | |
Auto merge of #39999 - bitshifter:struct_align, r=eddyb
Implementation of repr struct alignment RFC 1358. The main changes around rustc::ty::Layout::struct: * Added abi_align field which stores abi alignment before repr align is applied * align field contains transitive repr alignment * Added padding vec which stores padding required after fields The main user of this information is rustc_trans::adt::struct_llfields which determines the LLVM fields to be used by LLVM, including padding fields. A possible future optimisation would be to put the padding Vec in an Option, since it will be unused unless you are using repr align.
30 files changed, 692 insertions, 86 deletions
diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md index 3e041543977..ee4e568a5ca 100644 --- a/src/doc/unstable-book/src/SUMMARY.md +++ b/src/doc/unstable-book/src/SUMMARY.md @@ -72,6 +72,7 @@ - [proc_macro](language-features/proc-macro.md) - [quote](language-features/quote.md) - [relaxed_adts](language-features/relaxed-adts.md) + - [repr_align](language-features/repr-align.md) - [repr_simd](language-features/repr-simd.md) - [rustc_attrs](language-features/rustc-attrs.md) - [rustc_diagnostic_macros](language-features/rustc-diagnostic-macros.md) diff --git a/src/doc/unstable-book/src/language-features/repr-align.md b/src/doc/unstable-book/src/language-features/repr-align.md new file mode 100644 index 00000000000..deea04f4c51 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/repr-align.md @@ -0,0 +1,11 @@ +# `repr_align` + +The tracking issue for this feature is: [#33626] + +[#33626]: https://github.com/rust-lang/rust/issues/33626 + +------------------------ + + + + diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 618561f3b02..2851191dc14 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1847,5 +1847,6 @@ register_diagnostics! { E0489, // type/lifetime parameter not in scope here E0490, // a value of type `..` is borrowed for too long E0495, // cannot infer an appropriate lifetime due to conflicting requirements - E0566 // conflicting representation hints + E0566, // conflicting representation hints + E0587, // conflicting packed and align representation hints } diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 54ae9472140..bf292ccb8d8 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -57,6 +57,9 @@ impl<'a> CheckAttrVisitor<'a> { }; let mut conflicting_reprs = 0; + let mut found_packed = false; + let mut found_align = false; + for word in words { let name = match word.name() { @@ -84,6 +87,7 @@ impl<'a> CheckAttrVisitor<'a> { ("attribute should be applied to struct or union", "a struct or union") } else { + found_packed = true; continue } } @@ -96,6 +100,15 @@ impl<'a> CheckAttrVisitor<'a> { continue } } + "align" => { + found_align = true; + if target != Target::Struct { + ("attribute should be applied to struct", + "a struct") + } else { + continue + } + } "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "isize" | "usize" => { @@ -117,6 +130,10 @@ impl<'a> CheckAttrVisitor<'a> { span_warn!(self.sess, attr.span, E0566, "conflicting representation hints"); } + if found_align && found_packed { + struct_span_err!(self.sess, attr.span, E0587, + "conflicting packed and align representation hints").emit(); + } } fn check_attribute(&self, attr: &ast::Attribute, target: Target) { diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index df60eee8c02..6a206640b3b 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -548,8 +548,12 @@ pub type FieldPath = Vec<u32>; /// A structure, a product type in ADT terms. #[derive(PartialEq, Eq, Hash, Debug)] pub struct Struct { + /// Maximum alignment of fields and repr alignment. pub align: Align, + /// Primitive alignment of fields without repr alignment. + pub primitive_align: Align, + /// If true, no alignment padding is used. pub packed: bool, @@ -583,10 +587,20 @@ impl<'a, 'gcx, 'tcx> Struct { fn new(dl: &TargetDataLayout, fields: &Vec<&'a Layout>, repr: &ReprOptions, kind: StructKind, scapegoat: Ty<'gcx>) -> Result<Struct, LayoutError<'gcx>> { - let packed = repr.packed(); + if repr.packed() && repr.align > 0 { + bug!("Struct cannot be packed and aligned"); + } + + let align = if repr.packed() { + dl.i8_align + } else { + dl.aggregate_align + }; + let mut ret = Struct { - align: if packed { dl.i8_align } else { dl.aggregate_align }, - packed: packed, + align: align, + primitive_align: align, + packed: repr.packed(), sized: true, offsets: vec![], memory_index: vec![], @@ -660,7 +674,9 @@ impl<'a, 'gcx, 'tcx> Struct { // Invariant: offset < dl.obj_size_bound() <= 1<<61 if !ret.packed { let align = field.align(dl); + let primitive_align = field.primitive_align(dl); ret.align = ret.align.max(align); + ret.primitive_align = ret.primitive_align.max(primitive_align); offset = offset.abi_align(align); } @@ -671,6 +687,11 @@ impl<'a, 'gcx, 'tcx> Struct { .map_or(Err(LayoutError::SizeOverflow(scapegoat)), Ok)?; } + if repr.align > 0 { + let repr_align = repr.align as u64; + ret.align = ret.align.max(Align::from_bytes(repr_align, repr_align).unwrap()); + debug!("Struct::new repr_align: {:?}", repr_align); + } debug!("Struct::new min_size: {:?}", offset); ret.min_size = offset; @@ -836,12 +857,23 @@ impl<'a, 'gcx, 'tcx> Struct { } Ok(None) } + + pub fn over_align(&self) -> Option<u32> { + let align = self.align.abi(); + let primitive_align = self.primitive_align.abi(); + if align > primitive_align { + Some(align as u32) + } else { + None + } + } } /// An untagged union. #[derive(PartialEq, Eq, Hash, Debug)] pub struct Union { pub align: Align, + pub primitive_align: Align, pub min_size: Size, @@ -851,8 +883,10 @@ pub struct Union { impl<'a, 'gcx, 'tcx> Union { fn new(dl: &TargetDataLayout, packed: bool) -> Union { + let align = if packed { dl.i8_align } else { dl.aggregate_align }; Union { - align: if packed { dl.i8_align } else { dl.aggregate_align }, + align: align, + primitive_align: align, min_size: Size::from_bytes(0), packed: packed, } @@ -875,6 +909,7 @@ impl<'a, 'gcx, 'tcx> Union { if !self.packed { self.align = self.align.max(field.align(dl)); + self.primitive_align = self.primitive_align.max(field.primitive_align(dl)); } self.min_size = cmp::max(self.min_size, field.size(dl)); } @@ -888,6 +923,16 @@ impl<'a, 'gcx, 'tcx> Union { pub fn stride(&self) -> Size { self.min_size.abi_align(self.align) } + + pub fn over_align(&self) -> Option<u32> { + let align = self.align.abi(); + let primitive_align = self.primitive_align.abi(); + if align > primitive_align { + Some(align as u32) + } else { + None + } + } } /// The first half of a fat pointer. @@ -924,6 +969,7 @@ pub enum Layout { /// If true, the size is exact, otherwise it's only a lower bound. sized: bool, align: Align, + primitive_align: Align, element_size: Size, count: u64 }, @@ -970,7 +1016,8 @@ pub enum Layout { discr: Integer, variants: Vec<Struct>, size: Size, - align: Align + align: Align, + primitive_align: Align, }, /// Two cases distinguished by a nullable pointer: the case with discriminant @@ -1118,6 +1165,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: true, align: element.align(dl), + primitive_align: element.primitive_align(dl), element_size: element_size, count: count } @@ -1127,6 +1175,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: false, align: element.align(dl), + primitive_align: element.primitive_align(dl), element_size: element.size(dl), count: 0 } @@ -1135,6 +1184,7 @@ impl<'a, 'gcx, 'tcx> Layout { Array { sized: false, align: dl.i8_align, + primitive_align: dl.i8_align, element_size: Size::from_bytes(1), count: 0 } @@ -1340,6 +1390,7 @@ impl<'a, 'gcx, 'tcx> Layout { assert!(discr_max >= 0); let (min_ity, _) = Integer::repr_discr(tcx, ty, &def.repr, 0, discr_max); let mut align = dl.aggregate_align; + let mut primitive_align = dl.aggregate_align; let mut size = Size::from_bytes(0); // We're interested in the smallest alignment, so start large. @@ -1369,6 +1420,7 @@ impl<'a, 'gcx, 'tcx> Layout { } size = cmp::max(size, st.min_size); align = align.max(st.align); + primitive_align = primitive_align.max(st.primitive_align); Ok(st) }).collect::<Result<Vec<_>, _>>()?; @@ -1435,7 +1487,8 @@ impl<'a, 'gcx, 'tcx> Layout { discr: ity, variants: variants, size: size, - align: align + align: align, + primitive_align: primitive_align } } @@ -1557,6 +1610,30 @@ impl<'a, 'gcx, 'tcx> Layout { } } + /// Returns alignment before repr alignment is applied + pub fn primitive_align(&self, dl: &TargetDataLayout) -> Align { + match *self { + Array { primitive_align, .. } | General { primitive_align, .. } => primitive_align, + Univariant { ref variant, .. } | + StructWrappedNullablePointer { nonnull: ref variant, .. } => { + variant.primitive_align + }, + + _ => self.align(dl) + } + } + + /// Returns repr alignment if it is greater than the primitive alignment. + pub fn over_align(&self, dl: &TargetDataLayout) -> Option<u32> { + let align = self.align(dl); + let primitive_align = self.primitive_align(dl); + if align.abi() > primitive_align.abi() { + Some(align.abi() as u32) + } else { + None + } + } + pub fn field_offset<C: HasDataLayout>(&self, cx: C, i: usize, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 5c0889976c2..5be73542b93 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -37,6 +37,7 @@ use serialize::{self, Encodable, Encoder}; use std::borrow::Cow; use std::cell::{Cell, RefCell, Ref}; use std::collections::BTreeMap; +use std::cmp; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::rc::Rc; @@ -1470,10 +1471,12 @@ impl_stable_hash_for!(struct ReprFlags { #[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)] pub struct ReprOptions { pub int: Option<attr::IntType>, + pub align: u16, pub flags: ReprFlags, } impl_stable_hash_for!(struct ReprOptions { + align, int, flags }); @@ -1482,7 +1485,7 @@ impl ReprOptions { pub fn new(tcx: TyCtxt, did: DefId) -> ReprOptions { let mut flags = ReprFlags::empty(); let mut size = None; - + let mut max_align = 0; for attr in tcx.get_attrs(did).iter() { for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) { flags.insert(match r { @@ -1493,6 +1496,10 @@ impl ReprOptions { size = Some(i); ReprFlags::empty() }, + attr::ReprAlign(align) => { + max_align = cmp::max(align, max_align); + ReprFlags::empty() + }, }); } } @@ -1506,7 +1513,7 @@ impl ReprOptions { if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) { flags.insert(ReprFlags::IS_LINEAR); } - ReprOptions { int: size, flags: flags } + ReprOptions { int: size, align: max_align, flags: flags } } #[inline] diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs index e0a75f3caa7..998e392b1f9 100644 --- a/src/librustc_trans/abi.rs +++ b/src/librustc_trans/abi.rs @@ -553,7 +553,7 @@ impl<'a, 'tcx> ArgType<'tcx> { // bitcasting to the struct type yields invalid cast errors. // We instead thus allocate some scratch space... - let llscratch = bcx.alloca(ty, "abi_cast"); + let llscratch = bcx.alloca(ty, "abi_cast", None); base::Lifetime::Start.call(bcx, llscratch); // ...where we first store the value... diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 87ca410dece..d1c1dd7436a 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -90,12 +90,12 @@ pub fn compute_fields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, /// and fill in the actual contents in a second pass to prevent /// unbounded recursion; see also the comments in `trans::type_of`. pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { - generic_type_of(cx, t, None, false, false) + generic_type_of(cx, t, None) } pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, name: &str) -> Type { - generic_type_of(cx, t, Some(name), false, false) + generic_type_of(cx, t, Some(name)) } pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, @@ -114,7 +114,7 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, _ => unreachable!() }; let fields = compute_fields(cx, t, nonnull_variant_index as usize, true); - llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant, false, false), + llty.set_struct_body(&struct_llfields(cx, &fields, nonnull_variant), packed) }, _ => bug!("This function cannot handle {} with layout {:#?}", t, l) @@ -123,12 +123,9 @@ pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>, - name: Option<&str>, - sizing: bool, - dst: bool) -> Type { + name: Option<&str>) -> Type { let l = cx.layout_of(t); - debug!("adt::generic_type_of t: {:?} name: {:?} sizing: {} dst: {}", - t, name, sizing, dst); + debug!("adt::generic_type_of t: {:?} name: {:?}", t, name); match *l { layout::CEnum { discr, .. } => Type::from_integer(cx, discr), layout::RawNullablePointer { nndiscr, .. } => { @@ -148,11 +145,10 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, nndiscr as usize, false); match name { None => { - Type::struct_(cx, &struct_llfields(cx, &fields, nonnull, sizing, dst), + Type::struct_(cx, &struct_llfields(cx, &fields, nonnull), nonnull.packed) } Some(name) => { - assert_eq!(sizing, false); Type::named_struct(cx, name) } } @@ -163,13 +159,12 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, let fields = compute_fields(cx, t, 0, true); match name { None => { - let fields = struct_llfields(cx, &fields, &variant, sizing, dst); + let fields = struct_llfields(cx, &fields, &variant); Type::struct_(cx, &fields, variant.packed) } Some(name) => { // Hypothesis: named_struct's can never need a // drop flag. (... needs validation.) - assert_eq!(sizing, false); Type::named_struct(cx, name) } } @@ -190,7 +185,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } } - layout::General { discr, size, align, .. } => { + layout::General { discr, size, align, primitive_align, .. } => { // We need a representation that has: // * The alignment of the most-aligned field // * The size of the largest variant (rounded up to that alignment) @@ -203,14 +198,15 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // of the size. let size = size.bytes(); let align = align.abi(); + let primitive_align = primitive_align.abi(); assert!(align <= std::u32::MAX as u64); let discr_ty = Type::from_integer(cx, discr); let discr_size = discr.size().bytes(); let padded_discr_size = roundup(discr_size, align as u32); let variant_part_size = size-padded_discr_size; - let variant_fill = union_fill(cx, variant_part_size, align); + let variant_fill = union_fill(cx, variant_part_size, primitive_align); - assert_eq!(machine::llalign_of_min(cx, variant_fill), align as u32); + assert_eq!(machine::llalign_of_min(cx, variant_fill), primitive_align as u32); assert_eq!(padded_discr_size % discr_size, 0); // Ensure discr_ty can fill pad evenly let fields: Vec<Type> = [discr_ty, @@ -245,15 +241,60 @@ fn union_fill(cx: &CrateContext, size: u64, align: u64) -> Type { } -fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fields: &Vec<Ty<'tcx>>, - variant: &layout::Struct, - sizing: bool, _dst: bool) -> Vec<Type> { - let fields = variant.field_index_by_increasing_offset().map(|i| fields[i as usize]); - if sizing { - bug!() +// Double index to account for padding (FieldPath already uses `Struct::memory_index`) +fn struct_llfields_path(discrfield: &layout::FieldPath) -> Vec<usize> { + discrfield.iter().map(|&i| (i as usize) << 1).collect::<Vec<_>>() +} + + +// Lookup `Struct::memory_index` and double it to account for padding +pub fn struct_llfields_index(variant: &layout::Struct, index: usize) -> usize { + (variant.memory_index[index] as usize) << 1 +} + + +pub fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, field_tys: &Vec<Ty<'tcx>>, + variant: &layout::Struct) -> Vec<Type> { + debug!("struct_llfields: variant: {:?}", variant); + let mut first_field = true; + let mut min_offset = 0; + let mut result: Vec<Type> = Vec::with_capacity(field_tys.len() * 2); + let field_iter = variant.field_index_by_increasing_offset().map(|i| { + (i, field_tys[i as usize], variant.offsets[i as usize].bytes()) }); + for (index, ty, target_offset) in field_iter { + if first_field { + debug!("struct_llfields: {} ty: {} min_offset: {} target_offset: {}", + index, ty, min_offset, target_offset); + first_field = false; + } else { + assert!(target_offset >= min_offset); + let padding_bytes = if variant.packed { 0 } else { target_offset - min_offset }; + result.push(Type::array(&Type::i8(cx), padding_bytes)); + debug!("struct_llfields: {} ty: {} pad_bytes: {} min_offset: {} target_offset: {}", + index, ty, padding_bytes, min_offset, target_offset); + } + let llty = type_of::in_memory_type_of(cx, ty); + result.push(llty); + let layout = cx.layout_of(ty); + let target_size = layout.size(&cx.tcx().data_layout).bytes(); + min_offset = target_offset + target_size; + } + if variant.sized && !field_tys.is_empty() { + if variant.stride().bytes() < min_offset { + bug!("variant: {:?} stride: {} min_offset: {}", variant, variant.stride().bytes(), + min_offset); + } + let padding_bytes = variant.stride().bytes() - min_offset; + debug!("struct_llfields: pad_bytes: {} min_offset: {} min_size: {} stride: {}\n", + padding_bytes, min_offset, variant.min_size.bytes(), variant.stride().bytes()); + result.push(Type::array(&Type::i8(cx), padding_bytes)); + assert!(result.len() == (field_tys.len() * 2)); } else { - fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect() + debug!("struct_llfields: min_offset: {} min_size: {} stride: {}\n", + min_offset, variant.min_size.bytes(), variant.stride().bytes()); } + + result } pub fn is_discr_signed<'tcx>(l: &layout::Layout) -> bool { @@ -309,8 +350,8 @@ fn struct_wrapped_nullable_bitdiscr( scrutinee: ValueRef, alignment: Alignment, ) -> ValueRef { - let llptrptr = bcx.gepi(scrutinee, - &discrfield.iter().map(|f| *f as usize).collect::<Vec<_>>()); + let path = struct_llfields_path(discrfield); + let llptrptr = bcx.gepi(scrutinee, &path); let llptr = bcx.load(llptrptr, alignment.to_align()); let cmp = if nndiscr == 0 { IntEQ } else { IntNE }; bcx.icmp(cmp, llptr, C_null(val_ty(llptr))) @@ -380,7 +421,7 @@ pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: Valu let align = C_i32(bcx.ccx, nonnull.align.abi() as i32); base::call_memset(bcx, llptr, fill_byte, size, align, false); } else { - let path = discrfield.iter().map(|&i| i as usize).collect::<Vec<_>>(); + let path = struct_llfields_path(discrfield); let llptrptr = bcx.gepi(val, &path); let llptrty = val_ty(llptrptr).element_type(); bcx.store(C_null(llptrty), llptrptr, None); diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index 8b1010d89fd..5103ca5c5e1 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -477,24 +477,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - pub fn alloca(&self, ty: Type, name: &str) -> ValueRef { + pub fn alloca(&self, ty: Type, name: &str, align: Option<u32>) -> ValueRef { let builder = Builder::with_ccx(self.ccx); builder.position_at_start(unsafe { llvm::LLVMGetFirstBasicBlock(self.llfn()) }); - builder.dynamic_alloca(ty, name) + builder.dynamic_alloca(ty, name, align) } - pub fn dynamic_alloca(&self, ty: Type, name: &str) -> ValueRef { + pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Option<u32>) -> ValueRef { self.count_insn("alloca"); unsafe { - if name.is_empty() { + let alloca = if name.is_empty() { llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) } else { let name = CString::new(name).unwrap(); llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), name.as_ptr()) + }; + if let Some(align) = align { + llvm::LLVMSetAlignment(alloca, align as c_uint); } + alloca } } diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 7077eade611..54e20f590c6 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -778,7 +778,7 @@ fn trans_msvc_try<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // // More information can be found in libstd's seh.rs implementation. let i64p = Type::i64(ccx).ptr_to(); - let slot = bcx.alloca(i64p, "slot"); + let slot = bcx.alloca(i64p, "slot", None); bcx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 0f5a38ac7f6..d94d7f4430b 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -15,6 +15,7 @@ use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::{self, LayoutTyper}; use rustc::mir; use abi::{Abi, FnType, ArgType}; +use adt; use base::{self, Lifetime}; use callee; use builder::Builder; @@ -177,7 +178,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { }; let llslot = match op.val { Immediate(_) | Pair(..) => { - let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret"); + let llscratch = bcx.alloca(ret.memory_ty(bcx.ccx), "ret", None); self.store_operand(&bcx, llscratch, None, op); llscratch } @@ -630,7 +631,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => { if arg.is_indirect() || arg.cast.is_some() { - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg"); + let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); self.store_operand(bcx, llscratch, None, op); (llscratch, Alignment::AbiAligned, true) } else { @@ -642,7 +643,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't // have scary latent bugs around. - let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg"); + let llscratch = bcx.alloca(arg.memory_ty(bcx.ccx), "arg", None); base::memcpy_ty(bcx, llscratch, llval, op.ty, Some(1)); (llscratch, Alignment::AbiAligned, true) } @@ -711,7 +712,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bug!("Not a tuple."); }; for (n, &ty) in arg_types.iter().enumerate() { - let mut elem = bcx.extract_value(llval, v.memory_index[n] as usize); + let mut elem = bcx.extract_value( + llval, adt::struct_llfields_index(v, n)); // Truncate bools to i1, if needed if ty.is_bool() && common::val_ty(elem) != Type::i1(bcx.ccx) { elem = bcx.trunc(elem, Type::i1(bcx.ccx)); @@ -750,7 +752,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { slot } else { let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let slot = bcx.alloca(llretty, "personalityslot"); + let slot = bcx.alloca(llretty, "personalityslot", None); self.llpersonalityslot = Some(slot); slot } diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index fc889604ab8..88e46b5c99a 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -97,7 +97,8 @@ impl<'a, 'tcx> LvalueRef<'tcx> { pub fn alloca(bcx: &Builder<'a, 'tcx>, ty: Ty<'tcx>, name: &str) -> LvalueRef<'tcx> { debug!("alloca({:?}: {:?})", name, ty); - let tmp = bcx.alloca(type_of::type_of(bcx.ccx, ty), name); + let tmp = bcx.alloca( + type_of::type_of(bcx.ccx, ty), name, bcx.ccx.over_align_of(ty)); assert!(!ty.has_param_types()); Self::new_sized_ty(tmp, ty, Alignment::AbiAligned) } @@ -131,11 +132,9 @@ impl<'a, 'tcx> LvalueRef<'tcx> { let alignment = self.alignment | Alignment::from_packed(st.packed); + let llfields = adt::struct_llfields(ccx, fields, st); let ptr_val = if needs_cast { - let fields = st.field_index_by_increasing_offset().map(|i| { - type_of::in_memory_type_of(ccx, fields[i]) - }).collect::<Vec<_>>(); - let real_ty = Type::struct_(ccx, &fields[..], st.packed); + let real_ty = Type::struct_(ccx, &llfields[..], st.packed); bcx.pointercast(self.llval, real_ty.ptr_to()) } else { self.llval @@ -147,14 +146,16 @@ impl<'a, 'tcx> LvalueRef<'tcx> { // * Field is sized - pointer is properly aligned already if st.offsets[ix] == layout::Size::from_bytes(0) || st.packed || bcx.ccx.shared().type_is_sized(fty) { - return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); + return (bcx.struct_gep( + ptr_val, adt::struct_llfields_index(st, ix)), alignment); } // If the type of the last field is [T] or str, then we don't need to do // any adjusments match fty.sty { ty::TySlice(..) | ty::TyStr => { - return (bcx.struct_gep(ptr_val, st.memory_index[ix] as usize), alignment); + return (bcx.struct_gep( + ptr_val, adt::struct_llfields_index(st, ix)), alignment); } _ => () } @@ -163,7 +164,7 @@ impl<'a, 'tcx> LvalueRef<'tcx> { if !self.has_extra() { debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", ix, Value(ptr_val)); - return (bcx.struct_gep(ptr_val, ix), alignment); + return (bcx.struct_gep(ptr_val, adt::struct_llfields_index(st, ix)), alignment); } // We need to get the pointer manually now. diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 3d8c5085462..d39a91405c1 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -524,7 +524,7 @@ fn arg_local_refs<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, // doesn't actually strip the offset when splitting the closure // environment into its components so it ends up out of bounds. let env_ptr = if !env_ref { - let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr"); + let alloc = bcx.alloca(common::val_ty(llval), "__debuginfo_env_ptr", None); bcx.store(llval, alloc, None); alloc } else { diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index c31142323c8..6889b5064b6 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -15,6 +15,7 @@ use rustc::mir; use rustc::mir::tcx::LvalueTy; use rustc_data_structures::indexed_vec::Idx; +use adt; use base; use common::{self, CrateContext, C_null}; use builder::Builder; @@ -134,6 +135,12 @@ impl<'a, 'tcx> OperandRef<'tcx> { if common::val_ty(elem) == Type::i1(bcx.ccx) { elem = bcx.zext(elem, Type::i8(bcx.ccx)); } + let layout = bcx.ccx.layout_of(self.ty); + let i = if let Layout::Univariant { ref variant, .. } = *layout { + adt::struct_llfields_index(variant, i) + } else { + i + }; llpair = bcx.insert_value(llpair, elem, i); } self.val = OperandValue::Immediate(llpair); @@ -183,14 +190,17 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { let (lldata, llextra) = base::load_fat_ptr(bcx, llval, align, ty); OperandValue::Pair(lldata, llextra) } else if common::type_is_imm_pair(bcx.ccx, ty) { - let f_align = match *bcx.ccx.layout_of(ty) { - Layout::Univariant { ref variant, .. } => - Alignment::from_packed(variant.packed) | align, - _ => align + let (ix0, ix1, f_align) = match *bcx.ccx.layout_of(ty) { + Layout::Univariant { ref variant, .. } => { + (adt::struct_llfields_index(variant, 0), + adt::struct_llfields_index(variant, 1), + Alignment::from_packed(variant.packed) | align) + }, + _ => (0, 1, align) }; let [a_ty, b_ty] = common::type_pair_fields(bcx.ccx, ty).unwrap(); - let a_ptr = bcx.struct_gep(llval, 0); - let b_ptr = bcx.struct_gep(llval, 1); + let a_ptr = bcx.struct_gep(llval, ix0); + let b_ptr = bcx.struct_gep(llval, ix1); OperandValue::Pair( base::load_ty(bcx, a_ptr, f_align, a_ty), @@ -302,17 +312,19 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { bcx.store(base::from_immediate(bcx, s), lldest, align); } OperandValue::Pair(a, b) => { - let f_align = match *bcx.ccx.layout_of(operand.ty) { - Layout::Univariant { ref variant, .. } if variant.packed => { - Some(1) + let (ix0, ix1, f_align) = match *bcx.ccx.layout_of(operand.ty) { + Layout::Univariant { ref variant, .. } => { + (adt::struct_llfields_index(variant, 0), + adt::struct_llfields_index(variant, 1), + if variant.packed { Some(1) } else { None }) } - _ => align + _ => (0, 1, align) }; let a = base::from_immediate(bcx, a); let b = base::from_immediate(bcx, b); - bcx.store(a, bcx.struct_gep(lldest, 0), f_align); - bcx.store(b, bcx.struct_gep(lldest, 1), f_align); + bcx.store(a, bcx.struct_gep(lldest, ix0), f_align); + bcx.store(b, bcx.struct_gep(lldest, ix1), f_align); } } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 98e9008f829..de1c1e492f3 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -130,10 +130,12 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { _ => { // If this is a tuple or closure, we need to translate GEP indices. let layout = bcx.ccx.layout_of(dest.ty.to_ty(bcx.tcx())); - let translation = if let Layout::Univariant { ref variant, .. } = *layout { - Some(&variant.memory_index) - } else { - None + let get_memory_index = |i| { + if let Layout::Univariant { ref variant, .. } = *layout { + adt::struct_llfields_index(variant, i) + } else { + i + } }; let alignment = dest.alignment; for (i, operand) in operands.iter().enumerate() { @@ -143,11 +145,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { // Note: perhaps this should be StructGep, but // note that in some cases the values here will // not be structs but arrays. - let i = if let Some(ref t) = translation { - t[i] as usize - } else { - i - }; + let i = get_memory_index(i); let dest = bcx.gepi(dest.llval, &[0, i]); self.store_operand(&bcx, dest, alignment.to_align(), op); } diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index d4ab6b07828..9f9126ba83a 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -214,6 +214,16 @@ impl<'a, 'tcx> CrateContext<'a, 'tcx> { pub fn size_of(&self, ty: Ty<'tcx>) -> machine::llsize { self.layout_of(ty).size(self).bytes() as machine::llsize } + + pub fn over_align_of(&self, t: Ty<'tcx>) + -> Option<machine::llalign> { + let layout = self.layout_of(t); + if let Some(align) = layout.over_align(&self.tcx().data_layout) { + Some(align as machine::llalign) + } else { + None + } + } } fn llvm_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> String { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 902cfb889f8..839af0fa670 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -953,6 +953,12 @@ fn check_struct<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, if def.repr.simd() { check_simd(tcx, span, def_id); } + + // if struct is packed and not aligned, check fields for alignment. + // Checks for combining packed and align attrs on single struct are done elsewhere. + if tcx.lookup_adt_def(def_id).repr.packed() && tcx.lookup_adt_def(def_id).repr.align == 0 { + check_packed(tcx, span, def_id); + } } fn check_union<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -1371,6 +1377,47 @@ 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 check_packed_inner(tcx, def_id, &mut Vec::new()) { + struct_span_err!(tcx.sess, sp, E0588, + "packed struct cannot transitively contain a `[repr(align)]` struct").emit(); + } +} + +fn check_packed_inner<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId, + stack: &mut Vec<DefId>) -> bool { + let t = tcx.item_type(def_id); + if stack.contains(&def_id) { + debug!("check_packed_inner: {:?} is recursive", t); + return false; + } + match t.sty { + ty::TyAdt(def, substs) if def.is_struct() => { + if tcx.lookup_adt_def(def.did).repr.align > 0 { + return true; + } + // push struct def_id before checking fields + stack.push(def_id); + for field in &def.struct_variant().fields { + let f = field.ty(tcx, substs); + match f.sty { + ty::TyAdt(def, _) => { + if check_packed_inner(tcx, def.did, stack) { + return true; + } + } + _ => () + } + } + // only need to pop if not early out + stack.pop(); + } + _ => () + } + false +} + #[allow(trivial_numeric_casts)] pub fn check_enum<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, sp: Span, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 68afcae2b67..c50156fa5f2 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4168,5 +4168,6 @@ register_diagnostics! { // but `{}` was found in the type `{}` E0567, // auto traits can not have type parameters E0568, // auto-traits can not have predicates, + E0588, // packed struct cannot transitively contain a `[repr(align)]` struct E0592, // duplicate definitions with name `{}` } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 6f5f52ff1e9..82492d97627 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -147,6 +147,24 @@ impl NestedMetaItem { self.meta_item().and_then(|meta_item| meta_item.value_str()) } + /// Returns a name and single literal value tuple of the MetaItem. + pub fn name_value_literal(&self) -> Option<(Name, &Lit)> { + self.meta_item().and_then( + |meta_item| meta_item.meta_item_list().and_then( + |meta_item_list| { + if meta_item_list.len() == 1 { + let nested_item = &meta_item_list[0]; + if nested_item.is_literal() { + Some((meta_item.name(), nested_item.literal().unwrap())) + } else { + None + } + } + else { + None + }})) + } + /// Returns a MetaItem if self is a MetaItem with Kind Word. pub fn word(&self) -> Option<&MetaItem> { self.meta_item().and_then(|meta_item| if meta_item.is_word() { @@ -931,6 +949,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> continue } + let mut recognised = false; if let Some(mi) = item.word() { let word = &*mi.name().as_str(); let hint = match word { @@ -941,20 +960,43 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> _ => match int_type_of_word(word) { Some(ity) => Some(ReprInt(ity)), None => { - // Not a word we recognize - span_err!(diagnostic, item.span, E0552, - "unrecognized representation hint"); None } } }; if let Some(h) = hint { + recognised = true; acc.push(h); } - } else { - span_err!(diagnostic, item.span, E0553, - "unrecognized enum representation hint"); + } 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() { + // rustc::ty::layout::Align restricts align to <= 32768 + if align <= 32768 { + acc.push(ReprAlign(align as u16)); + } else { + align_error = Some("larger than 32768"); + } + } else { + align_error = Some("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); + } + } + } + if !recognised { + // Not a word we recognize + span_err!(diagnostic, item.span, E0552, + "unrecognized representation hint"); } } } @@ -986,6 +1028,7 @@ pub enum ReprAttr { ReprExtern, ReprPacked, ReprSimd, + ReprAlign(u16), } #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)] diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 2d59051ec4a..01d1277ea62 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -287,10 +287,10 @@ register_diagnostics! { E0550, // multiple deprecated attributes E0551, // incorrect meta item E0552, // unrecognized representation hint - E0553, // unrecognized enum representation hint E0554, // #[feature] may not be used on the [] release channel E0555, // malformed feature attribute, expected #![feature(...)] E0556, // malformed feature, expected just one word E0557, // feature has been removed E0584, // file for module `..` found at both .. and .. + E0589, // invalid `repr(align)` attribute } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 175447e1112..9b55a860b35 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -338,6 +338,9 @@ declare_features! ( // Allows the `catch {...}` expression (active, catch_expr, "1.17.0", Some(31436)), + // Allows `repr(align(u16))` struct attribute (RFC 1358) + (active, repr_align, "1.17.0", Some(33626)), + // See rust-lang/rfcs#1414. Allows code like `let x: &'static u32 = &42` to work. (active, rvalue_static_promotion, "1.15.1", Some(38865)), @@ -1189,6 +1192,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { and possibly buggy"); } + if item.check_name("align") { + gate_feature_post!(&self, repr_align, i.span, + "the struct `#[repr(align(u16))]` attribute \ + is experimental"); + } } } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 1ff0fec1c96..e96883c26f3 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -773,7 +773,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 => continue, + attr::ReprPacked | attr::ReprSimd | attr::ReprAlign(_) => continue, attr::ReprExtern => "i32", attr::ReprInt(attr::SignedInt(ast::IntTy::Is)) => "isize", diff --git a/src/test/compile-fail/attr-usage-repr.rs b/src/test/compile-fail/attr-usage-repr.rs index b07d3e2f906..c0bfd3690c8 100644 --- a/src/test/compile-fail/attr-usage-repr.rs +++ b/src/test/compile-fail/attr-usage-repr.rs @@ -9,6 +9,7 @@ // except according to those terms. #![allow(dead_code)] +#![feature(attr_literals)] #![feature(repr_simd)] #[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union @@ -29,6 +30,9 @@ struct SInt(f64, f64); #[repr(C)] enum EExtern { A, B } +#[repr(align(8))] //~ ERROR: attribute should be applied to struct +enum EAlign { A, B } + #[repr(packed)] //~ ERROR: attribute should be applied to struct enum EPacked { A, B } diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs index 9e0c0d845ca..01fa3ffbaa6 100644 --- a/src/test/compile-fail/conflicting-repr-hints.rs +++ b/src/test/compile-fail/conflicting-repr-hints.rs @@ -8,8 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] #![allow(dead_code)] +#![feature(attr_literals)] +#![feature(repr_align)] #[repr(C)] enum A { A } @@ -26,5 +27,7 @@ enum D { D } #[repr(C, packed)] struct E(i32); -#[rustc_error] -fn main() {} //~ ERROR compilation successful +#[repr(packed, align(8))] //~ ERROR conflicting packed and align representation hints +struct F(i32); + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-repr_align.rs b/src/test/compile-fail/feature-gate-repr_align.rs new file mode 100644 index 00000000000..8e986e197f2 --- /dev/null +++ b/src/test/compile-fail/feature-gate-repr_align.rs @@ -0,0 +1,15 @@ +// Copyright 2017 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(attr_literals)] + +#[repr(align(64))] +struct Foo(u64, u64); //~ error: the struct `#[repr(align(u16))]` attribute is experimental + +fn main() {} diff --git a/src/test/compile-fail/repr-align.rs b/src/test/compile-fail/repr-align.rs new file mode 100644 index 00000000000..eb0b27fe9c0 --- /dev/null +++ b/src/test/compile-fail/repr-align.rs @@ -0,0 +1,23 @@ +// Copyright 2017 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. +#![allow(dead_code)] +#![feature(attr_literals)] +#![feature(repr_align)] + +#[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer +struct A(i32); + +#[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two +struct B(i32); + +#[repr(align(65536))] //~ ERROR: invalid `repr(align)` attribute: larger than 32768 +struct C(i32); + +fn main() {} diff --git a/src/test/compile-fail/repr-packed-contains-align.rs b/src/test/compile-fail/repr-packed-contains-align.rs new file mode 100644 index 00000000000..c584dcf3e59 --- /dev/null +++ b/src/test/compile-fail/repr-packed-contains-align.rs @@ -0,0 +1,25 @@ +// Copyright 2017 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(attr_literals)] +#![feature(repr_align)] +#![allow(dead_code)] + +#[repr(align(16))] +struct A(i32); + +struct B(A); + +#[repr(packed)] +struct C(A); //~ ERROR: packed struct cannot transitively contain a `[repr(align)]` struct + +#[repr(packed)] +struct D(B); //~ ERROR: packed struct cannot transitively contain a `[repr(align)]` struct + +fn main() {} diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs new file mode 100644 index 00000000000..0b9a3594502 --- /dev/null +++ b/src/test/run-pass/align-struct.rs @@ -0,0 +1,196 @@ +// Copyright 2017 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(attr_literals)] +#![feature(repr_align)] + +use std::mem; + +// Raising alignment +#[repr(align(16))] +struct Align16(i32); + +// Lowering has no effect +#[repr(align(1))] +struct Align1(i32); + +// Multiple attributes take the max +#[repr(align(4))] +#[repr(align(16))] +#[repr(align(8))] +struct AlignMany(i32); + +// Raising alignment may not alter size. +#[repr(align(8))] +#[allow(dead_code)] +struct Align8Many { + a: i32, + b: i32, + c: i32, + d: u8, +} + +enum Enum { + #[allow(dead_code)] + A(i32), + B(Align16) +} + +// Nested alignment - use `#[repr(C)]` to suppress field reordering for sizeof test +#[repr(C)] +struct Nested { + a: i32, + b: i32, + c: Align16, + d: i8, +} + +#[repr(packed)] +struct Packed(i32); + +#[repr(align(16))] +struct AlignContainsPacked { + a: Packed, + b: Packed, +} + +impl Align16 { + // return aligned type + pub fn new(i: i32) -> Align16 { + Align16(i) + } + // pass aligned type + pub fn consume(a: Align16) -> i32 { + a.0 + } +} + +const CONST_ALIGN16: Align16 = Align16(7); +static STATIC_ALIGN16: Align16 = Align16(8); + +// Check the actual address is aligned +fn is_aligned_to<T>(p: &T, align: usize) -> bool { + let addr = p as *const T as usize; + (addr & (align - 1)) == 0 +} + +pub fn main() { + // check alignment and size by type and value + assert_eq!(mem::align_of::<Align16>(), 16); + assert_eq!(mem::size_of::<Align16>(), 16); + + let a = Align16(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + + assert!(is_aligned_to(&a, 16)); + + // lowering should have no effect + assert_eq!(mem::align_of::<Align1>(), 4); + assert_eq!(mem::size_of::<Align1>(), 4); + let a = Align1(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 4); + assert_eq!(mem::size_of_val(&a), 4); + assert!(is_aligned_to(&a, 4)); + + // when multiple attributes are specified the max should be used + assert_eq!(mem::align_of::<AlignMany>(), 16); + assert_eq!(mem::size_of::<AlignMany>(), 16); + let a = AlignMany(7); + assert_eq!(a.0, 7); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 16)); + + // raising alignment should not reduce size + assert_eq!(mem::align_of::<Align8Many>(), 8); + assert_eq!(mem::size_of::<Align8Many>(), 16); + let a = Align8Many { a: 1, b: 2, c: 3, d: 4 }; + assert_eq!(a.a, 1); + assert_eq!(mem::align_of_val(&a), 8); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 8)); + + // return type + let a = Align16::new(1); + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::size_of_val(&a), 16); + assert_eq!(a.0, 1); + assert!(is_aligned_to(&a, 16)); + assert_eq!(Align16::consume(a), 1); + + // check const alignment, size and value + assert_eq!(mem::align_of_val(&CONST_ALIGN16), 16); + assert_eq!(mem::size_of_val(&CONST_ALIGN16), 16); + assert_eq!(CONST_ALIGN16.0, 7); + assert!(is_aligned_to(&CONST_ALIGN16, 16)); + + // check global static alignment, size and value + assert_eq!(mem::align_of_val(&STATIC_ALIGN16), 16); + assert_eq!(mem::size_of_val(&STATIC_ALIGN16), 16); + assert_eq!(STATIC_ALIGN16.0, 8); + assert!(is_aligned_to(&STATIC_ALIGN16, 16)); + + // Note that the size of Nested may change if struct field re-ordering is enabled + assert_eq!(mem::align_of::<Nested>(), 16); + assert_eq!(mem::size_of::<Nested>(), 48); + let a = Nested{ a: 1, b: 2, c: Align16(3), d: 4}; + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::align_of_val(&a.b), 4); + assert_eq!(mem::align_of_val(&a.c), 16); + assert_eq!(mem::size_of_val(&a), 48); + assert!(is_aligned_to(&a, 16)); + // check the correct fields are indexed + assert_eq!(a.a, 1); + assert_eq!(a.b, 2); + assert_eq!(a.c.0, 3); + assert_eq!(a.d, 4); + + // enum should be aligned to max alignment + assert_eq!(mem::align_of::<Enum>(), 16); + assert_eq!(mem::align_of_val(&Enum::B(Align16(0))), 16); + let e = Enum::B(Align16(15)); + match e { + Enum::B(ref a) => { + assert_eq!(a.0, 15); + assert_eq!(mem::align_of_val(a), 16); + assert_eq!(mem::size_of_val(a), 16); + }, + _ => () + } + assert!(is_aligned_to(&e, 16)); + + // arrays of aligned elements should also be aligned + assert_eq!(mem::align_of::<[Align16;2]>(), 16); + assert_eq!(mem::size_of::<[Align16;2]>(), 32); + + let a = [Align16(0), Align16(1)]; + assert_eq!(mem::align_of_val(&a[0]), 16); + assert_eq!(mem::align_of_val(&a[1]), 16); + assert!(is_aligned_to(&a, 16)); + + // check heap value is aligned + assert_eq!(mem::align_of_val(Box::new(Align16(0)).as_ref()), 16); + + // check heap array is aligned + let a = vec!(Align16(0), Align16(1)); + assert_eq!(mem::align_of_val(&a[0]), 16); + assert_eq!(mem::align_of_val(&a[1]), 16); + + assert_eq!(mem::align_of::<AlignContainsPacked>(), 16); + assert_eq!(mem::size_of::<AlignContainsPacked>(), 16); + let a = AlignContainsPacked { a: Packed(1), b: Packed(2) }; + assert_eq!(mem::align_of_val(&a), 16); + assert_eq!(mem::align_of_val(&a.a), 1); + assert_eq!(mem::align_of_val(&a.b), 1); + assert_eq!(mem::size_of_val(&a), 16); + assert!(is_aligned_to(&a, 16)); +} diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs new file mode 100644 index 00000000000..e9b43145de4 --- /dev/null +++ b/src/test/ui/print_type_sizes/repr-align.rs @@ -0,0 +1,43 @@ +// Copyright 2016 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. + +// compile-flags: -Z print-type-sizes + +// This file illustrates how padding is handled: alignment +// requirements can lead to the introduction of padding, either before +// fields or at the end of the structure as a whole. +// +// It avoids using u64/i64 because on some targets that is only 4-byte +// aligned (while on most it is 8-byte aligned) and so the resulting +// padding and overall computed sizes can be quite different. +#![feature(attr_literals)] +#![feature(repr_align)] +#![allow(dead_code)] + +#[repr(align(16))] +#[derive(Default)] +struct A(i32); + +enum E { + A(i32), + B(A) +} + +#[derive(Default)] +struct S { + a: i32, + b: i32, + c: A, + d: i8, +} + +fn main() { + let _s: S = Default::default(); +} diff --git a/src/test/ui/print_type_sizes/repr-align.stdout b/src/test/ui/print_type_sizes/repr-align.stdout new file mode 100644 index 00000000000..7df12f040b1 --- /dev/null +++ b/src/test/ui/print_type_sizes/repr-align.stdout @@ -0,0 +1,16 @@ +print-type-size type: `E`: 32 bytes, alignment: 16 bytes +print-type-size discriminant: 4 bytes +print-type-size variant `A`: 4 bytes +print-type-size field `.0`: 4 bytes +print-type-size variant `B`: 28 bytes +print-type-size padding: 12 bytes +print-type-size field `.0`: 16 bytes, alignment: 16 bytes +print-type-size type: `S`: 32 bytes, alignment: 16 bytes +print-type-size field `.c`: 16 bytes +print-type-size field `.a`: 4 bytes +print-type-size field `.b`: 4 bytes +print-type-size field `.d`: 1 bytes +print-type-size end padding: 7 bytes +print-type-size type: `A`: 16 bytes, alignment: 16 bytes +print-type-size field `.0`: 4 bytes +print-type-size end padding: 12 bytes |
