]> git.lizzy.rs Git - rust.git/commitdiff
Support repr alignment on unions.
authorCameron Hart <cameron.hart@gmail.com>
Sun, 16 Jul 2017 14:44:13 +0000 (00:44 +1000)
committerCameron Hart <cameron.hart@gmail.com>
Sun, 16 Jul 2017 21:55:49 +0000 (07:55 +1000)
src/librustc/hir/check_attr.rs
src/librustc/ty/layout.rs
src/test/run-pass/align-struct.rs
src/test/run-pass/union/union-align.rs [new file with mode: 0644]

index 3034242b59408dbc8363ace84eb98fa8337c7f96..e4b1cdabb0466f4682db62133ea05f4897578d95 100644 (file)
@@ -121,9 +121,10 @@ fn check_repr(&self, attr: &ast::Attribute, target: Target) {
                 }
                 "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
                     }
index 4b8b39c1f590bdaddaf9a63f599bea78452957ca..009b0619bd75f11b7547c5ef285927c2054a02ba 100644 (file)
@@ -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 @@ pub fn compute_uncached(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                         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 {
index 59d053991673042bd3b1d12fee85055b3112efb9..e42aa868c47eecd38b304f0f0f86ad0be8db852f 100644 (file)
@@ -15,6 +15,7 @@
 
 // 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 (file)
index 0000000..c0100df
--- /dev/null
@@ -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);
+    }
+}