diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 3034242b59408..e4b1cdabb0466 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -121,9 +121,10 @@ impl<'a> CheckAttrVisitor<'a> { } "align" => { found_align = true; - if target != Target::Struct { - ("attribute should be applied to struct", - "a struct") + if target != Target::Struct && + target != Target::Union { + ("attribute should be applied to struct or union", + "a struct or union") } else { continue } diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 4b8b39c1f590b..009b0619bd75f 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -908,13 +908,30 @@ pub struct Union { } impl<'a, 'tcx> Union { - fn new(dl: &TargetDataLayout, packed: bool) -> Union { - let align = if packed { dl.i8_align } else { dl.aggregate_align }; + fn new(dl: &TargetDataLayout, repr: &ReprOptions) -> Union { + if repr.packed() && repr.align > 0 { + bug!("Union cannot be packed and aligned"); + } + + let primitive_align = if repr.packed() { + dl.i8_align + } else { + dl.aggregate_align + }; + + let align = if repr.align > 0 { + let repr_align = repr.align as u64; + debug!("Union::new repr_align: {:?}", repr_align); + primitive_align.max(Align::from_bytes(repr_align, repr_align).unwrap()) + } else { + primitive_align + }; + Union { align, - primitive_align: align, + primitive_align, min_size: Size::from_bytes(0), - packed, + packed: repr.packed(), } } @@ -1311,7 +1328,7 @@ impl<'a, 'tcx> Layout { field.ty(tcx, substs).layout(tcx, param_env) }).collect::, _>>()?; let layout = if def.is_union() { - let mut un = Union::new(dl, def.repr.packed()); + let mut un = Union::new(dl, &def.repr); un.extend(dl, fields.iter().map(|&f| Ok(f)), ty)?; UntaggedUnion { variants: un } } else { diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs index 59d0539916730..e42aa868c47ee 100644 --- a/src/test/run-pass/align-struct.rs +++ b/src/test/run-pass/align-struct.rs @@ -15,6 +15,7 @@ use std::mem; // Raising alignment #[repr(align(16))] +#[derive(Clone, Copy, Debug)] struct Align16(i32); // Lowering has no effect @@ -68,6 +69,11 @@ struct AlignLarge { stuff: [u8; 0x10000], } +union UnionContainsAlign { + a: Align16, + b: f32 +} + impl Align16 { // return aligned type pub fn new(i: i32) -> Align16 { @@ -176,6 +182,18 @@ pub fn main() { } assert!(is_aligned_to(&e, 16)); + // check union alignment + assert_eq!(mem::align_of::(), 16); + assert_eq!(mem::size_of::(), 16); + let u = UnionContainsAlign { a: Align16(10) }; + unsafe { + assert_eq!(mem::align_of_val(&u.a), 16); + assert_eq!(mem::size_of_val(&u.a), 16); + assert_eq!(u.a.0, 10); + let UnionContainsAlign { a } = u; + assert_eq!(a.0, 10); + } + // arrays of aligned elements should also be aligned assert_eq!(mem::align_of::<[Align16;2]>(), 16); assert_eq!(mem::size_of::<[Align16;2]>(), 32); diff --git a/src/test/run-pass/union/union-align.rs b/src/test/run-pass/union/union-align.rs new file mode 100644 index 0000000000000..c0100df53e7c1 --- /dev/null +++ b/src/test/run-pass/union/union-align.rs @@ -0,0 +1,72 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(attr_literals)] +#![feature(repr_align)] +#![feature(untagged_unions)] + +use std::mem::{size_of, size_of_val, align_of, align_of_val}; + +#[repr(align(16))] +pub union U16 { + a: u8, + b: u32 +} + +fn main() { + assert_eq!(align_of::(), 16); + assert_eq!(size_of::(), 16); + let u = U16 { a: 10 }; + unsafe { + assert_eq!(align_of_val(&u.a), 1); + assert_eq!(size_of_val(&u.a), 1); + assert_eq!(u.a, 10); + } + + let u = U16 { b: 11 }; + unsafe { + assert_eq!(align_of_val(&u.b), 4); + assert_eq!(size_of_val(&u.b), 4); + assert_eq!(u.b, 11); + } + + hybrid::check_hybrid(); +} + +mod hybrid { + use std::mem::{size_of, align_of}; + + #[repr(align(16))] + struct S1 { + a: u16, + b: u8, + } + + #[repr(align(32))] + union U { + s: S1, + c: u16, + } + + #[repr(align(64))] + struct S2 { + d: u8, + u: U, + } + + pub fn check_hybrid() { + assert_eq!(align_of::(), 16); + assert_eq!(size_of::(), 16); + assert_eq!(align_of::(), 32); + assert_eq!(size_of::(), 32); + assert_eq!(align_of::(), 64); + assert_eq!(size_of::(), 64); + } +}