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.
This commit is contained in:
bors 2017-04-22 11:50:40 +00:00
commit 6d841da4a0
30 changed files with 693 additions and 87 deletions

View File

@ -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)

View File

@ -0,0 +1,11 @@
# `repr_align`
The tracking issue for this feature is: [#33626]
[#33626]: https://github.com/rust-lang/rust/issues/33626
------------------------

View File

@ -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
}

View File

@ -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) {

View File

@ -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,

View File

@ -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]

View File

@ -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...

View File

@ -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!()
} else {
fields.map(|ty| type_of::in_memory_type_of(cx, ty)).collect()
// 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 {
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);

View File

@ -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
}
}

View File

@ -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);

View File

@ -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
}

View File

@ -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.

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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,

View File

@ -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 `{}`
}

View File

@ -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)]

View File

@ -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
}

View File

@ -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");
}
}
}
}

View File

@ -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",

View File

@ -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 }

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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));
}

View File

@ -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();
}

View File

@ -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