about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCameron Hart <cameron.hart@gmail.com>2017-07-17 00:44:13 +1000
committerCameron Hart <cameron.hart@gmail.com>2017-07-17 07:55:49 +1000
commitebc2f7d6ed52e24ac0ba2b267ad28dc14be44387 (patch)
tree2ce16d64e26314e4d518cc80da4de7172cef4555
parent8f1339af2e5d1b33ec9ee3c8a3c531bcd61770fc (diff)
downloadrust-ebc2f7d6ed52e24ac0ba2b267ad28dc14be44387.tar.gz
rust-ebc2f7d6ed52e24ac0ba2b267ad28dc14be44387.zip
Support repr alignment on unions.
-rw-r--r--src/librustc/hir/check_attr.rs7
-rw-r--r--src/librustc/ty/layout.rs27
-rw-r--r--src/test/run-pass/align-struct.rs18
-rw-r--r--src/test/run-pass/union/union-align.rs72
4 files changed, 116 insertions, 8 deletions
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index 3034242b594..e4b1cdabb04 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 4b8b39c1f59..009b0619bd7 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::<Result<Vec<_>, _>>()?;
                     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 59d05399167..e42aa868c47 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::<UnionContainsAlign>(), 16);
+    assert_eq!(mem::size_of::<UnionContainsAlign>(), 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 00000000000..c0100df53e7
--- /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 <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)]
+#![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::<U16>(), 16);
+    assert_eq!(size_of::<U16>(), 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::<S1>(), 16);
+        assert_eq!(size_of::<S1>(), 16);
+        assert_eq!(align_of::<U>(), 32);
+        assert_eq!(size_of::<U>(), 32);
+        assert_eq!(align_of::<S2>(), 64);
+        assert_eq!(size_of::<S2>(), 64);
+    }
+}