about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCameron Hart <cameron.hart@gmail.com>2018-02-04 22:10:28 +1100
committerCameron Hart <cameron.hart@gmail.com>2018-04-11 22:13:13 +1000
commit15d1c4d2139611fcb87a2c802bd015b5f4f0aed8 (patch)
tree5822428a8e6a8ef94f13ea80f22d8ec01545f33b
parentca26ef321c44358404ef788d315c4557eb015fb2 (diff)
downloadrust-15d1c4d2139611fcb87a2c802bd015b5f4f0aed8.tar.gz
rust-15d1c4d2139611fcb87a2c802bd015b5f4f0aed8.zip
Implementation of `#[repr(packed(n))]` RFC 1399.
-rw-r--r--src/doc/unstable-book/src/language-features/repr-packed.md8
-rw-r--r--src/librustc/session/code_stats.rs37
-rw-r--r--src/librustc/ty/layout.rs78
-rw-r--r--src/librustc/ty/mod.rs30
-rw-r--r--src/librustc_typeck/check/mod.rs15
-rw-r--r--src/librustc_typeck/diagnostics.rs5
-rw-r--r--src/libsyntax/attr.rs46
-rw-r--r--src/libsyntax/feature_gate.rs19
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs10
-rw-r--r--src/test/codegen/packed.rs84
-rw-r--r--src/test/compile-fail/conflicting-repr-hints.rs11
-rw-r--r--src/test/run-pass/align-struct.rs22
-rw-r--r--src/test/run-pass/auxiliary/packed.rs18
-rw-r--r--src/test/run-pass/issue-48159.rs37
-rw-r--r--src/test/run-pass/packed-struct-borrow-element.rs25
-rw-r--r--src/test/run-pass/packed-struct-generic-size.rs33
-rw-r--r--src/test/run-pass/packed-struct-match.rs37
-rw-r--r--src/test/run-pass/packed-struct-size-xc.rs11
-rw-r--r--src/test/run-pass/packed-struct-size.rs126
-rw-r--r--src/test/run-pass/packed-struct-vec.rs99
-rw-r--r--src/test/run-pass/packed-tuple-struct-size.rs68
-rw-r--r--src/test/run-pass/union/union-packed.rs112
-rw-r--r--src/test/ui/feature-gate-repr_packed.rs14
-rw-r--r--src/test/ui/feature-gate-repr_packed.stderr11
-rw-r--r--src/test/ui/print_type_sizes/packed.rs32
-rw-r--r--src/test/ui/print_type_sizes/packed.stdout18
26 files changed, 844 insertions, 162 deletions
diff --git a/src/doc/unstable-book/src/language-features/repr-packed.md b/src/doc/unstable-book/src/language-features/repr-packed.md
new file mode 100644
index 00000000000..2dd763d04b0
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/repr-packed.md
@@ -0,0 +1,8 @@
+# `repr_packed`
+
+The tracking issue for this feature is [#33158]
+
+[#33158]: https://github.com/rust-lang/rust/issues/33158
+
+------------------------
+
diff --git a/src/librustc/session/code_stats.rs b/src/librustc/session/code_stats.rs
index 64f405e0f24..df4060e71e5 100644
--- a/src/librustc/session/code_stats.rs
+++ b/src/librustc/session/code_stats.rs
@@ -62,6 +62,7 @@ pub struct TypeSizeInfo {
     pub type_description: String,
     pub align: u64,
     pub overall_size: u64,
+    pub packed: bool,
     pub opt_discr_size: Option<u64>,
     pub variants: Vec<VariantInfo>,
 }
@@ -79,6 +80,7 @@ impl CodeStats {
                                          type_desc: S,
                                          align: Align,
                                          overall_size: Size,
+                                         packed: bool,
                                          opt_discr_size: Option<Size>,
                                          variants: Vec<VariantInfo>) {
         let info = TypeSizeInfo {
@@ -86,6 +88,7 @@ impl CodeStats {
             type_description: type_desc.to_string(),
             align: align.abi(),
             overall_size: overall_size.bytes(),
+            packed: packed,
             opt_discr_size: opt_discr_size.map(|s| s.bytes()),
             variants,
         };
@@ -153,24 +156,26 @@ impl CodeStats {
                 for field in fields.iter() {
                     let FieldInfo { ref name, offset, size, align } = *field;
 
-                    // Include field alignment in output only if it caused padding injection
-                    if min_offset != offset {
-                        if offset > min_offset {
-                            let pad = offset - min_offset;
-                            println!("print-type-size {}padding: {} bytes",
-                                     indent, pad);
-                            println!("print-type-size {}field `.{}`: {} bytes, \
-                                      alignment: {} bytes",
-                                     indent, name, size, align);
-                        } else {
-                            println!("print-type-size {}field `.{}`: {} bytes, \
-                                      offset: {} bytes, \
-                                      alignment: {} bytes",
-                                     indent, name, size, offset, align);
-                        }
-                    } else {
+                    if offset > min_offset {
+                        let pad = offset - min_offset;
+                        println!("print-type-size {}padding: {} bytes",
+                                 indent, pad);
+                    }
+
+                    if offset < min_offset {
+                        // if this happens something is very wrong
+                        println!("print-type-size {}field `.{}`: {} bytes, \
+                                  offset: {} bytes, \
+                                  alignment: {} bytes",
+                                 indent, name, size, offset, align);
+                    } else if info.packed || offset == min_offset {
                         println!("print-type-size {}field `.{}`: {} bytes",
                                  indent, name, size);
+                    } else {
+                        // Include field alignment in output only if it caused padding injection
+                        println!("print-type-size {}field `.{}`: {} bytes, \
+                                  alignment: {} bytes",
+                                 indent, name, size, align);
                     }
 
                     min_offset = offset + size;
diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index 5f9c305d92f..6ed8730de6a 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -12,7 +12,7 @@ pub use self::Integer::*;
 pub use self::Primitive::*;
 
 use session::{self, DataTypeKind, Session};
-use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions, ReprFlags};
+use ty::{self, Ty, TyCtxt, TypeFoldable, ReprOptions};
 
 use syntax::ast::{self, FloatTy, IntTy, UintTy};
 use syntax::attr;
@@ -344,8 +344,8 @@ impl AddAssign for Size {
 /// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct Align {
-    abi: u8,
-    pref: u8,
+    abi_pow2: u8,
+    pref_pow2: u8,
 }
 
 impl Align {
@@ -377,17 +377,17 @@ impl Align {
         };
 
         Ok(Align {
-            abi: log2(abi)?,
-            pref: log2(pref)?,
+            abi_pow2: log2(abi)?,
+            pref_pow2: log2(pref)?,
         })
     }
 
     pub fn abi(self) -> u64 {
-        1 << self.abi
+        1 << self.abi_pow2
     }
 
     pub fn pref(self) -> u64 {
-        1 << self.pref
+        1 << self.pref_pow2
     }
 
     pub fn abi_bits(self) -> u64 {
@@ -400,15 +400,15 @@ impl Align {
 
     pub fn min(self, other: Align) -> Align {
         Align {
-            abi: cmp::min(self.abi, other.abi),
-            pref: cmp::min(self.pref, other.pref),
+            abi_pow2: cmp::min(self.abi_pow2, other.abi_pow2),
+            pref_pow2: cmp::min(self.pref_pow2, other.pref_pow2),
         }
     }
 
     pub fn max(self, other: Align) -> Align {
         Align {
-            abi: cmp::max(self.abi, other.abi),
-            pref: cmp::max(self.pref, other.pref),
+            abi_pow2: cmp::max(self.abi_pow2, other.abi_pow2),
+            pref_pow2: cmp::max(self.pref_pow2, other.pref_pow2),
         }
     }
 }
@@ -974,6 +974,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                 bug!("struct cannot be packed and aligned");
             }
 
+            let pack = {
+                let pack = repr.pack as u64;
+                Align::from_bytes(pack, pack).unwrap()
+            };
+
             let mut align = if packed {
                 dl.i8_align
             } else {
@@ -984,8 +989,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
             let mut offsets = vec![Size::from_bytes(0); fields.len()];
             let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
 
-            // Anything with repr(C) or repr(packed) doesn't optimize.
-            let mut optimize = (repr.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty();
+            let mut optimize = !repr.inhibit_struct_field_reordering_opt();
             if let StructKind::Prefixed(_, align) = kind {
                 optimize &= align.abi() == 1;
             }
@@ -997,6 +1001,9 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                     fields.len()
                 };
                 let optimizing = &mut inverse_memory_index[..end];
+                let field_align = |f: &TyLayout| {
+                    if packed { f.align.min(pack).abi() } else { f.align.abi() }
+                };
                 match kind {
                     StructKind::AlwaysSized |
                     StructKind::MaybeUnsized => {
@@ -1004,11 +1011,11 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                             // Place ZSTs first to avoid "interesting offsets",
                             // especially with only one or two non-ZST fields.
                             let f = &fields[x as usize];
-                            (!f.is_zst(), cmp::Reverse(f.align.abi()))
-                        })
+                            (!f.is_zst(), cmp::Reverse(field_align(f)))
+                        });
                     }
                     StructKind::Prefixed(..) => {
-                        optimizing.sort_by_key(|&x| fields[x as usize].align.abi());
+                        optimizing.sort_by_key(|&x| field_align(&fields[x as usize]));
                     }
                 }
             }
@@ -1022,7 +1029,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
             let mut offset = Size::from_bytes(0);
 
             if let StructKind::Prefixed(prefix_size, prefix_align) = kind {
-                if !packed {
+                if packed {
+                    let prefix_align = prefix_align.min(pack);
+                    align = align.max(prefix_align);
+                } else {
                     align = align.max(prefix_align);
                 }
                 offset = prefix_size.abi_align(prefix_align);
@@ -1044,7 +1054,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                 }
 
                 // Invariant: offset < dl.obj_size_bound() <= 1<<61
-                if !packed {
+                if packed {
+                    let field_pack = field.align.min(pack);
+                    offset = offset.abi_align(field_pack);
+                    align = align.max(field_pack);
+                }
+                else {
                     offset = offset.abi_align(field.align);
                     align = align.max(field.align);
                 }
@@ -1377,7 +1392,12 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                         bug!("Union cannot be packed and aligned");
                     }
 
-                    let mut align = if def.repr.packed() {
+                    let pack = {
+                        let pack = def.repr.pack as u64;
+                        Align::from_bytes(pack, pack).unwrap()
+                    };
+
+                    let mut align = if packed {
                         dl.i8_align
                     } else {
                         dl.aggregate_align
@@ -1393,7 +1413,10 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                     for field in &variants[0] {
                         assert!(!field.is_unsized());
 
-                        if !packed {
+                        if packed {
+                            let field_pack = field.align.min(pack);
+                            align = align.max(field_pack);
+                        } else {
                             align = align.max(field.align);
                         }
                         size = cmp::max(size, field.size);
@@ -1740,12 +1763,13 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
 
     fn record_layout_for_printing_outlined(self, layout: TyLayout<'tcx>) {
         // (delay format until we actually need it)
-        let record = |kind, opt_discr_size, variants| {
+        let record = |kind, packed, opt_discr_size, variants| {
             let type_desc = format!("{:?}", layout.ty);
             self.tcx.sess.code_stats.borrow_mut().record_type_size(kind,
                                                                    type_desc,
                                                                    layout.align,
                                                                    layout.size,
+                                                                   packed,
                                                                    opt_discr_size,
                                                                    variants);
         };
@@ -1758,7 +1782,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
 
             ty::TyClosure(..) => {
                 debug!("print-type-size t: `{:?}` record closure", layout.ty);
-                record(DataTypeKind::Closure, None, vec![]);
+                record(DataTypeKind::Closure, false, None, vec![]);
                 return;
             }
 
@@ -1769,6 +1793,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
         };
 
         let adt_kind = adt_def.adt_kind();
+        let adt_packed = adt_def.repr.packed();
 
         let build_variant_info = |n: Option<ast::Name>,
                                   flds: &[ast::Name],
@@ -1821,6 +1846,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                     let fields: Vec<_> =
                         variant_def.fields.iter().map(|f| f.name).collect();
                     record(adt_kind.into(),
+                           adt_packed,
                            None,
                            vec![build_variant_info(Some(variant_def.name),
                                                    &fields,
@@ -1828,7 +1854,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                 } else {
                     // (This case arises for *empty* enums; so give it
                     // zero variants.)
-                    record(adt_kind.into(), None, vec![]);
+                    record(adt_kind.into(), adt_packed, None, vec![]);
                 }
             }
 
@@ -1845,7 +1871,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
                                             layout.for_variant(self, i))
                     })
                     .collect();
-                record(adt_kind.into(), match layout.variants {
+                record(adt_kind.into(), adt_packed, match layout.variants {
                     Variants::Tagged { ref discr, .. } => Some(discr.value.size(self)),
                     _ => None
                 }, variant_infos);
@@ -2518,8 +2544,8 @@ impl_stable_hash_for!(enum ::ty::layout::Primitive {
 });
 
 impl_stable_hash_for!(struct ::ty::layout::Align {
-    abi,
-    pref
+    abi_pow2,
+    pref_pow2
 });
 
 impl_stable_hash_for!(struct ::ty::layout::Size {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 5b1160f1420..33b59eda7ce 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1623,15 +1623,13 @@ bitflags! {
     #[derive(RustcEncodable, RustcDecodable, Default)]
     pub struct ReprFlags: u8 {
         const IS_C               = 1 << 0;
-        const IS_PACKED          = 1 << 1;
-        const IS_SIMD            = 1 << 2;
-        const IS_TRANSPARENT     = 1 << 3;
+        const IS_SIMD            = 1 << 1;
+        const IS_TRANSPARENT     = 1 << 2;
         // Internal only for now. If true, don't reorder fields.
-        const IS_LINEAR          = 1 << 4;
+        const IS_LINEAR          = 1 << 3;
 
         // Any of these flags being set prevent field reordering optimisation.
         const IS_UNOPTIMISABLE   = ReprFlags::IS_C.bits |
-                                   ReprFlags::IS_PACKED.bits |
                                    ReprFlags::IS_SIMD.bits |
                                    ReprFlags::IS_LINEAR.bits;
     }
@@ -1648,11 +1646,13 @@ impl_stable_hash_for!(struct ReprFlags {
 pub struct ReprOptions {
     pub int: Option<attr::IntType>,
     pub align: u32,
+    pub pack: u32,
     pub flags: ReprFlags,
 }
 
 impl_stable_hash_for!(struct ReprOptions {
     align,
+    pack,
     int,
     flags
 });
@@ -1662,11 +1662,19 @@ impl ReprOptions {
         let mut flags = ReprFlags::empty();
         let mut size = None;
         let mut max_align = 0;
+        let mut min_pack = 0;
         for attr in tcx.get_attrs(did).iter() {
             for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
                 flags.insert(match r {
                     attr::ReprC => ReprFlags::IS_C,
-                    attr::ReprPacked => ReprFlags::IS_PACKED,
+                    attr::ReprPacked(pack) => {
+                        min_pack = if min_pack > 0 {
+                            cmp::min(pack, min_pack)
+                        } else {
+                            pack
+                        };
+                        ReprFlags::empty()
+                    },
                     attr::ReprTransparent => ReprFlags::IS_TRANSPARENT,
                     attr::ReprSimd => ReprFlags::IS_SIMD,
                     attr::ReprInt(i) => {
@@ -1685,7 +1693,7 @@ impl ReprOptions {
         if !tcx.consider_optimizing(|| format!("Reorder fields of {:?}", tcx.item_path_str(did))) {
             flags.insert(ReprFlags::IS_LINEAR);
         }
-        ReprOptions { int: size, align: max_align, flags: flags }
+        ReprOptions { int: size, align: max_align, pack: min_pack, flags: flags }
     }
 
     #[inline]
@@ -1693,7 +1701,7 @@ impl ReprOptions {
     #[inline]
     pub fn c(&self) -> bool { self.flags.contains(ReprFlags::IS_C) }
     #[inline]
-    pub fn packed(&self) -> bool { self.flags.contains(ReprFlags::IS_PACKED) }
+    pub fn packed(&self) -> bool { self.pack > 0 }
     #[inline]
     pub fn transparent(&self) -> bool { self.flags.contains(ReprFlags::IS_TRANSPARENT) }
     #[inline]
@@ -1709,6 +1717,12 @@ impl ReprOptions {
     pub fn inhibit_enum_layout_opt(&self) -> bool {
         self.c() || self.int.is_some()
     }
+
+    /// Returns true if this `#[repr()]` should inhibit struct field reordering
+    /// optimizations, such as with repr(C) or repr(packed(1)).
+    pub fn inhibit_struct_field_reordering_opt(&self) -> bool {
+        !(self.flags & ReprFlags::IS_UNOPTIMISABLE).is_empty() || (self.pack == 1)
+    }
 }
 
 impl<'a, 'gcx, 'tcx> AdtDef {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 6c18f8d285d..a60ba4c6d16 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1553,8 +1553,19 @@ 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 tcx.adt_def(def_id).repr.packed() {
-        if tcx.adt_def(def_id).repr.align > 0 {
+    let repr = tcx.adt_def(def_id).repr;
+    if repr.packed() {
+        for attr in tcx.get_attrs(def_id).iter() {
+            for r in attr::find_repr_attrs(tcx.sess.diagnostic(), attr) {
+                if let attr::ReprPacked(pack) = r {
+                    if pack != repr.pack {
+                        struct_span_err!(tcx.sess, sp, E0634,
+                                         "type has conflicting packed representation hints").emit();
+                    }
+                }
+            }
+        }
+        if repr.align > 0 {
             struct_span_err!(tcx.sess, sp, E0587,
                              "type has conflicting packed and align representation hints").emit();
         }
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index 79d7c8e7282..5a53c008f6c 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -4836,14 +4836,15 @@ register_diagnostics! {
 //  E0563, // cannot determine a type for this `impl Trait`: {} // removed in 6383de15
     E0564, // only named lifetimes are allowed in `impl Trait`,
            // but `{}` was found in the type `{}`
-    E0587, // struct has conflicting packed and align representation hints
-    E0588, // packed struct cannot transitively contain a `[repr(align)]` struct
+    E0587, // type has conflicting packed and align representation hints
+    E0588, // packed type cannot transitively contain a `[repr(align)]` type
     E0592, // duplicate definitions with name `{}`
 //  E0613, // Removed (merged with E0609)
     E0640, // infer outlives
     E0627, // yield statement outside of generator literal
     E0632, // cannot provide explicit type parameters when `impl Trait` is used in
            // argument position.
+    E0634, // type has conflicting packed representaton hints
     E0641, // cannot cast to/from a pointer with an unknown kind
     E0645, // trait aliases not finished
     E0907, // type inside generator must be known in this context
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 2812e1238e9..c68a743303a 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -993,7 +993,7 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
                     let word = &*mi.ident.name.as_str();
                     let hint = match word {
                         "C" => Some(ReprC),
-                        "packed" => Some(ReprPacked),
+                        "packed" => Some(ReprPacked(1)),
                         "simd" => Some(ReprSimd),
                         "transparent" => Some(ReprTransparent),
                         _ => match int_type_of_word(word) {
@@ -1009,27 +1009,41 @@ pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr>
                         acc.push(h);
                     }
                 } 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() {
+                    let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
+                        if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
+                            if literal.is_power_of_two() {
                                 // rustc::ty::layout::Align restricts align to <= 2147483647
-                                if align <= 2147483647 {
-                                    acc.push(ReprAlign(align as u32));
+                                if *literal <= 2147483647 {
+                                    Ok(*literal as u32)
                                 } else {
-                                    align_error = Some("larger than 2147483647");
+                                    Err("larger than 2147483647")
                                 }
                             } else {
-                                align_error = Some("not a power of two");
+                                Err("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);
+                            Err("not an unsuffixed integer")
                         }
+                    };
+
+                    let mut literal_error = None;
+                    if name == "align" {
+                        recognised = true;
+                        match parse_alignment(&value.node) {
+                            Ok(literal) => acc.push(ReprAlign(literal)),
+                            Err(message) => literal_error = Some(message)
+                        };
+                    }
+                    else if name == "packed" {
+                        recognised = true;
+                        match parse_alignment(&value.node) {
+                            Ok(literal) => acc.push(ReprPacked(literal)),
+                            Err(message) => literal_error = Some(message)
+                        };
+                    }
+                    if let Some(literal_error) = literal_error {
+                        span_err!(diagnostic, item.span, E0589,
+                                  "invalid `repr(align)` attribute: {}", literal_error);
                     }
                 }
                 if !recognised {
@@ -1065,7 +1079,7 @@ fn int_type_of_word(s: &str) -> Option<IntType> {
 pub enum ReprAttr {
     ReprInt(IntType),
     ReprC,
-    ReprPacked,
+    ReprPacked(u32),
     ReprSimd,
     ReprTransparent,
     ReprAlign(u32),
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 0a3cd66d897..df39757d1eb 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -432,6 +432,9 @@ declare_features! (
     // Parentheses in patterns
     (active, pattern_parentheses, "1.26.0", None, None),
 
+    // Allows `#[repr(packed)]` attribute on structs
+    (active, repr_packed, "1.26.0", Some(33158), None),
+
     // `use path as _;` and `extern crate c as _;`
     (active, underscore_imports, "1.26.0", Some(48216), None),
 
@@ -1439,11 +1442,12 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             }
         }
 
-        // allow attr_literals in #[repr(align(x))]
-        let mut is_repr_align = false;
+        // allow attr_literals in #[repr(align(x))] and #[repr(packed(n))]
+        let mut allow_attr_literal = false;
         if attr.path == "repr" {
             if let Some(content) = attr.meta_item_list() {
-                is_repr_align = content.iter().any(|c| c.check_name("align"));
+                allow_attr_literal = content.iter().any(
+                    |c| c.check_name("align") || c.check_name("packed"));
             }
         }
 
@@ -1451,7 +1455,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             return
         }
 
-        if !is_repr_align {
+        if !allow_attr_literal {
             let meta = panictry!(attr.parse_meta(self.context.parse_sess));
             if contains_novel_literal(&meta) {
                 gate_feature_post!(&self, attr_literals, attr.span,
@@ -1535,6 +1539,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                                                "the `#[repr(transparent)]` attribute \
                                                is experimental");
                         }
+                        if let Some((name, _)) = item.name_value_literal() {
+                            if name == "packed" {
+                                gate_feature_post!(&self, repr_packed, attr.span,
+                                                   "the `#[repr(packed(n))]` attribute \
+                                                   is experimental");
+                            }
+                        }
                     }
                 }
             }
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 4126ce79f35..66053e037e1 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -413,8 +413,12 @@ impl<'a> TraitDef<'a> {
         match *item {
             Annotatable::Item(ref item) => {
                 let is_packed = item.attrs.iter().any(|attr| {
-                    attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr)
-                        .contains(&attr::ReprPacked)
+                    for r in attr::find_repr_attrs(&cx.parse_sess.span_diagnostic, attr) {
+                        if let attr::ReprPacked(_) = r {
+                            return true;
+                        }
+                    }
+                    false
                 });
                 let has_no_type_params = match item.node {
                     ast::ItemKind::Struct(_, ref generics) |
@@ -831,7 +835,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 | attr::ReprAlign(_) | attr::ReprTransparent =>
+                attr::ReprPacked(_) | attr::ReprSimd | attr::ReprAlign(_) | attr::ReprTransparent =>
                     continue,
 
                 attr::ReprC => "i32",
diff --git a/src/test/codegen/packed.rs b/src/test/codegen/packed.rs
index 022f581278c..0693eae7d78 100644
--- a/src/test/codegen/packed.rs
+++ b/src/test/codegen/packed.rs
@@ -11,16 +11,23 @@
 // compile-flags: -C no-prepopulate-passes
 
 #![crate_type = "lib"]
+#![feature(repr_packed)]
 
 #[repr(packed)]
-pub struct Packed {
+pub struct Packed1 {
     dealign: u8,
     data: u32
 }
 
-// CHECK-LABEL: @write_pkd
+#[repr(packed(2))]
+pub struct Packed2 {
+    dealign: u8,
+    data: u32
+}
+
+// CHECK-LABEL: @write_pkd1
 #[no_mangle]
-pub fn write_pkd(pkd: &mut Packed) -> u32 {
+pub fn write_pkd1(pkd: &mut Packed1) -> u32 {
 // CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 1
 // CHECK: store i32 42, i32* %{{.*}}, align 1
     let result = pkd.data;
@@ -28,43 +35,94 @@ pub fn write_pkd(pkd: &mut Packed) -> u32 {
     result
 }
 
+// CHECK-LABEL: @write_pkd2
+#[no_mangle]
+pub fn write_pkd2(pkd: &mut Packed2) -> u32 {
+// CHECK: %{{.*}} = load i32, i32* %{{.*}}, align 2
+// CHECK: store i32 42, i32* %{{.*}}, align 2
+    let result = pkd.data;
+    pkd.data = 42;
+    result
+}
+
 pub struct Array([i32; 8]);
 #[repr(packed)]
-pub struct BigPacked {
+pub struct BigPacked1 {
+    dealign: u8,
+    data: Array
+}
+
+#[repr(packed(2))]
+pub struct BigPacked2 {
     dealign: u8,
     data: Array
 }
 
-// CHECK-LABEL: @call_pkd
+// CHECK-LABEL: @call_pkd1
 #[no_mangle]
-pub fn call_pkd(f: fn() -> Array) -> BigPacked {
+pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
 // CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
 // CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
 // CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 32, i32 1, i1 false)
     // check that calls whose destination is a field of a packed struct
     // go through an alloca rather than calling the function with an
     // unaligned destination.
-    BigPacked { dealign: 0, data: f() }
+    BigPacked1 { dealign: 0, data: f() }
+}
+
+// CHECK-LABEL: @call_pkd2
+#[no_mangle]
+pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 {
+// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
+// CHECK: call void %{{.*}}(%Array* noalias nocapture sret dereferenceable(32) [[ALLOCA]])
+// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 32, i32 2, i1 false)
+    // check that calls whose destination is a field of a packed struct
+    // go through an alloca rather than calling the function with an
+    // unaligned destination.
+    BigPacked2 { dealign: 0, data: f() }
 }
 
 #[repr(packed)]
 #[derive(Copy, Clone)]
-pub struct PackedPair(u8, u32);
+pub struct Packed1Pair(u8, u32);
 
-// CHECK-LABEL: @pkd_pair
+#[repr(packed(2))]
+#[derive(Copy, Clone)]
+pub struct Packed2Pair(u8, u32);
+
+// CHECK-LABEL: @pkd1_pair
 #[no_mangle]
-pub fn pkd_pair(pair1: &mut PackedPair, pair2: &mut PackedPair) {
+pub fn pkd1_pair(pair1: &mut Packed1Pair, pair2: &mut Packed1Pair) {
 // CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 5, i32 1, i1 false)
     *pair2 = *pair1;
 }
 
+// CHECK-LABEL: @pkd2_pair
+#[no_mangle]
+pub fn pkd2_pair(pair1: &mut Packed2Pair, pair2: &mut Packed2Pair) {
+// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 6, i32 2, i1 false)
+    *pair2 = *pair1;
+}
+
 #[repr(packed)]
 #[derive(Copy, Clone)]
-pub struct PackedNestedPair((u32, u32));
+pub struct Packed1NestedPair((u32, u32));
+
+#[repr(packed(2))]
+#[derive(Copy, Clone)]
+pub struct Packed2NestedPair((u32, u32));
 
-// CHECK-LABEL: @pkd_nested_pair
+// CHECK-LABEL: @pkd1_nested_pair
 #[no_mangle]
-pub fn pkd_nested_pair(pair1: &mut PackedNestedPair, pair2: &mut PackedNestedPair) {
+pub fn pkd1_nested_pair(pair1: &mut Packed1NestedPair, pair2: &mut Packed1NestedPair) {
 // CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 1, i1 false)
     *pair2 = *pair1;
 }
+
+// CHECK-LABEL: @pkd2_nested_pair
+#[no_mangle]
+pub fn pkd2_nested_pair(pair1: &mut Packed2NestedPair, pair2: &mut Packed2NestedPair) {
+// CHECK: call void @llvm.memcpy.{{.*}}(i8* %{{.*}}, i8* %{{.*}}, i{{[0-9]+}} 8, i32 2, i1 false)
+    *pair2 = *pair1;
+}
+
diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs
index 8acc8b7bb1e..426f60c6b09 100644
--- a/src/test/compile-fail/conflicting-repr-hints.rs
+++ b/src/test/compile-fail/conflicting-repr-hints.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![allow(dead_code)]
+#![feature(repr_packed)]
 
 #[repr(C)]
 enum A { A }
@@ -36,6 +37,16 @@ struct G(i32); //~ ERROR type has conflicting packed and align representation hi
 #[repr(packed)]
 struct H(i32); //~ ERROR type has conflicting packed and align representation hints
 
+#[repr(packed, packed(2))]
+struct I(i32); //~ ERROR type has conflicting packed representation hints
+
+#[repr(packed(2))]
+#[repr(packed)]
+struct J(i32); //~ ERROR type has conflicting packed representation hints
+
+#[repr(packed, packed(1))]
+struct K(i32);
+
 #[repr(packed, align(8))]
 union X { //~ ERROR type has conflicting packed and align representation hints
     i: i32
diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs
index dea8462705f..2b6a151574a 100644
--- a/src/test/run-pass/align-struct.rs
+++ b/src/test/run-pass/align-struct.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 #![feature(box_syntax)]
+#![feature(repr_packed)]
 
 use std::mem;
 
@@ -60,6 +61,18 @@ struct AlignContainsPacked {
     b: Packed,
 }
 
+#[repr(C, packed(4))]
+struct Packed4C {
+    a: u32,
+    b: u64,
+}
+
+#[repr(align(16))]
+struct AlignContainsPacked4C {
+    a: Packed4C,
+    b: u64,
+}
+
 // The align limit was originally smaller (2^15).
 // Check that it works with big numbers.
 #[repr(align(0x10000))]
@@ -218,6 +231,15 @@ pub fn main() {
     assert_eq!(mem::size_of_val(&a), 16);
     assert!(is_aligned_to(&a, 16));
 
+    assert_eq!(mem::align_of::<AlignContainsPacked4C>(), 16);
+    assert_eq!(mem::size_of::<AlignContainsPacked4C>(), 32);
+    let a = AlignContainsPacked4C { a: Packed4C{ a: 1, b: 2 }, b: 3 };
+    assert_eq!(mem::align_of_val(&a), 16);
+    assert_eq!(mem::align_of_val(&a.a), 4);
+    assert_eq!(mem::align_of_val(&a.b), mem::align_of::<u64>());
+    assert_eq!(mem::size_of_val(&a), 32);
+    assert!(is_aligned_to(&a, 16));
+
     let mut large = box AlignLarge {
         stuff: [0; 0x10000],
     };
diff --git a/src/test/run-pass/auxiliary/packed.rs b/src/test/run-pass/auxiliary/packed.rs
index 86f5f93e3cf..828be41cd41 100644
--- a/src/test/run-pass/auxiliary/packed.rs
+++ b/src/test/run-pass/auxiliary/packed.rs
@@ -8,8 +8,24 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(repr_packed)]
+
 #[repr(packed)]
-pub struct S {
+pub struct P1S5 {
     a: u8,
     b: u32
 }
+
+#[repr(packed(2))]
+pub struct P2S6 {
+    a: u8,
+    b: u32,
+    c: u8
+}
+
+#[repr(C, packed(2))]
+pub struct P2CS8 {
+    a: u8,
+    b: u32,
+    c: u8
+}
diff --git a/src/test/run-pass/issue-48159.rs b/src/test/run-pass/issue-48159.rs
new file mode 100644
index 00000000000..ce4585607e9
--- /dev/null
+++ b/src/test/run-pass/issue-48159.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 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(repr_packed)]
+#![allow(non_camel_case_types)]
+
+use std::mem;
+
+pub enum c_void {}
+
+type uintptr_t = usize;
+type int16_t = u16;
+type uint16_t = int16_t;
+type uint32_t = u32;
+type intptr_t = uintptr_t;
+
+#[repr(C)]
+#[repr(packed(4))]
+pub struct kevent {
+    pub ident: uintptr_t,
+    pub filter: int16_t,
+    pub flags: uint16_t,
+    pub fflags: uint32_t,
+    pub data: intptr_t,
+    pub udata: *mut c_void,
+}
+
+fn main() {
+    assert_eq!(mem::align_of::<kevent>(), 4);
+}
diff --git a/src/test/run-pass/packed-struct-borrow-element.rs b/src/test/run-pass/packed-struct-borrow-element.rs
index e725b25efee..c8a8643ed6b 100644
--- a/src/test/run-pass/packed-struct-borrow-element.rs
+++ b/src/test/run-pass/packed-struct-borrow-element.rs
@@ -10,15 +10,36 @@
 
 // ignore-emscripten weird assertion?
 
+#![feature(repr_packed)]
+
 #[repr(packed)]
-struct Foo {
+struct Foo1 {
+    bar: u8,
+    baz: usize
+}
+
+#[repr(packed(2))]
+struct Foo2 {
+    bar: u8,
+    baz: usize
+}
+
+#[repr(C, packed(4))]
+struct Foo4C {
     bar: u8,
     baz: usize
 }
 
 pub fn main() {
-    let foo = Foo { bar: 1, baz: 2 };
+    let foo = Foo1 { bar: 1, baz: 2 };
     let brw = unsafe { &foo.baz };
+    assert_eq!(*brw, 2);
 
+    let foo = Foo2 { bar: 1, baz: 2 };
+    let brw = unsafe { &foo.baz };
+    assert_eq!(*brw, 2);
+
+    let foo = Foo4C { bar: 1, baz: 2 };
+    let brw = unsafe { &foo.baz };
     assert_eq!(*brw, 2);
 }
diff --git a/src/test/run-pass/packed-struct-generic-size.rs b/src/test/run-pass/packed-struct-generic-size.rs
index 4e1f62b28ab..127d873b2d9 100644
--- a/src/test/run-pass/packed-struct-generic-size.rs
+++ b/src/test/run-pass/packed-struct-generic-size.rs
@@ -8,18 +8,45 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(repr_packed)]
 
 use std::mem;
 
 #[repr(packed)]
-struct S<T, S> {
+struct P1<T, S> {
     a: T,
     b: u8,
     c: S
 }
 
+#[repr(packed(2))]
+struct P2<T, S> {
+    a: T,
+    b: u8,
+    c: S
+}
+
+#[repr(C, packed(4))]
+struct P4C<T, S> {
+    a: T,
+    b: u8,
+    c: S
+}
+
+macro_rules! check {
+    ($t:ty, $align:expr, $size:expr) => ({
+        assert_eq!(mem::align_of::<$t>(), $align);
+        assert_eq!(mem::size_of::<$t>(), $size);
+    });
+}
+
 pub fn main() {
-    assert_eq!(mem::size_of::<S<u8, u8>>(), 3);
+    check!(P1::<u8, u8>, 1, 3);
+    check!(P1::<u64, u16>, 1, 11);
+
+    check!(P2::<u8, u8>, 1, 3);
+    check!(P2::<u64, u16>, 2, 12);
 
-    assert_eq!(mem::size_of::<S<u64, u16>>(), 11);
+    check!(P4C::<u8, u8>, 1, 3);
+    check!(P4C::<u16, u64>, 4, 12);
 }
diff --git a/src/test/run-pass/packed-struct-match.rs b/src/test/run-pass/packed-struct-match.rs
index 3cd254014c1..c02d524d763 100644
--- a/src/test/run-pass/packed-struct-match.rs
+++ b/src/test/run-pass/packed-struct-match.rs
@@ -8,17 +8,46 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(repr_packed)]
 
 #[repr(packed)]
-struct Foo {
+struct Foo1 {
+    bar: u8,
+    baz: usize
+}
+
+#[repr(packed(2))]
+struct Foo2 {
+    bar: u8,
+    baz: usize
+}
+
+#[repr(C, packed(4))]
+struct Foo4C {
     bar: u8,
     baz: usize
 }
 
 pub fn main() {
-    let foo = Foo { bar: 1, baz: 2 };
-    match foo {
-        Foo {bar, baz} => {
+    let foo1 = Foo1 { bar: 1, baz: 2 };
+    match foo1 {
+        Foo1 {bar, baz} => {
+            assert_eq!(bar, 1);
+            assert_eq!(baz, 2);
+        }
+    }
+
+    let foo2 = Foo2 { bar: 1, baz: 2 };
+    match foo2 {
+        Foo2 {bar, baz} => {
+            assert_eq!(bar, 1);
+            assert_eq!(baz, 2);
+        }
+    }
+
+    let foo4 = Foo4C { bar: 1, baz: 2 };
+    match foo4 {
+        Foo4C {bar, baz} => {
             assert_eq!(bar, 1);
             assert_eq!(baz, 2);
         }
diff --git a/src/test/run-pass/packed-struct-size-xc.rs b/src/test/run-pass/packed-struct-size-xc.rs
index 372693433db..48f34554ca1 100644
--- a/src/test/run-pass/packed-struct-size-xc.rs
+++ b/src/test/run-pass/packed-struct-size-xc.rs
@@ -15,6 +15,15 @@ extern crate packed;
 
 use std::mem;
 
+macro_rules! check {
+    ($t:ty, $align:expr, $size:expr) => ({
+        assert_eq!(mem::align_of::<$t>(), $align);
+        assert_eq!(mem::size_of::<$t>(), $size);
+    });
+}
+
 pub fn main() {
-    assert_eq!(mem::size_of::<packed::S>(), 5);
+    check!(packed::P1S5, 1, 5);
+    check!(packed::P2S6, 2, 6);
+    check!(packed::P2CS8, 2, 8);
 }
diff --git a/src/test/run-pass/packed-struct-size.rs b/src/test/run-pass/packed-struct-size.rs
index 754a3573339..f8e23610fe2 100644
--- a/src/test/run-pass/packed-struct-size.rs
+++ b/src/test/run-pass/packed-struct-size.rs
@@ -7,44 +7,116 @@
 // <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(repr_packed)]
 
 
 use std::mem;
 
 #[repr(packed)]
-struct S4 {
+struct P1S4 {
+    a: u8,
+    b: [u8;  3],
+}
+
+#[repr(packed(2))]
+struct P2S4 {
     a: u8,
     b: [u8;  3],
 }
 
 #[repr(packed)]
-struct S5 {
+struct P1S5 {
+    a: u8,
+    b: u32
+}
+
+#[repr(packed(2))]
+struct P2S2 {
+    a: u8,
+    b: u8
+}
+
+#[repr(packed(2))]
+struct P2S6 {
     a: u8,
     b: u32
 }
 
+#[repr(packed(2))]
+struct P2S12 {
+    a: u32,
+    b: u64
+}
+
 #[repr(packed)]
-struct S13 {
+struct P1S13 {
     a: i64,
     b: f32,
     c: u8,
 }
 
+#[repr(packed(2))]
+struct P2S14 {
+    a: i64,
+    b: f32,
+    c: u8,
+}
+
+#[repr(packed(4))]
+struct P4S16 {
+    a: u8,
+    b: f32,
+    c: i64,
+    d: u16,
+}
+
+#[repr(C, packed(4))]
+struct P4CS20 {
+    a: u8,
+    b: f32,
+    c: i64,
+    d: u16,
+}
+
 enum Foo {
     Bar = 1,
     Baz = 2
 }
 
 #[repr(packed)]
-struct S3_Foo {
+struct P1S3_Foo {
+    a: u8,
+    b: u16,
+    c: Foo
+}
+
+#[repr(packed(2))]
+struct P2_Foo {
+    a: Foo,
+}
+
+#[repr(packed(2))]
+struct P2S3_Foo {
     a: u8,
     b: u16,
     c: Foo
 }
 
 #[repr(packed)]
-struct S7_Option {
+struct P1S7_Option {
+    a: f32,
+    b: u8,
+    c: u16,
+    d: Option<Box<f64>>
+}
+
+#[repr(packed(2))]
+struct P2_Option {
+    a: Option<Box<f64>>
+}
+
+#[repr(packed(2))]
+struct P2S7_Option {
     a: f32,
     b: u8,
     c: u16,
@@ -52,15 +124,41 @@ struct S7_Option {
 }
 
 // Placing packed structs in statics should work
-static TEST_S4: S4 = S4 { a: 1, b: [2, 3, 4] };
-static TEST_S5: S5 = S5 { a: 3, b: 67 };
-static TEST_S3_Foo: S3_Foo = S3_Foo { a: 1, b: 2, c: Foo::Baz };
+static TEST_P1S4: P1S4 = P1S4 { a: 1, b: [2, 3, 4] };
+static TEST_P1S5: P1S5 = P1S5 { a: 3, b: 67 };
+static TEST_P1S3_Foo: P1S3_Foo = P1S3_Foo { a: 1, b: 2, c: Foo::Baz };
+static TEST_P2S2: P2S2 = P2S2 { a: 1, b: 2 };
+static TEST_P2S4: P2S4 = P2S4 { a: 1, b: [2, 3, 4] };
+static TEST_P2S6: P2S6 = P2S6 { a: 1, b: 2 };
+static TEST_P2S12: P2S12 = P2S12 { a: 1, b: 2 };
+static TEST_P4S16: P4S16 = P4S16 { a: 1, b: 2.0, c: 3, d: 4 };
+static TEST_P4CS20: P4CS20 = P4CS20 { a: 1, b: 2.0, c: 3, d: 4 };
 
+fn align_to(value: usize, align: usize) -> usize {
+    (value + (align - 1)) & !(align - 1)
+}
+
+macro_rules! check {
+    ($t:ty, $align:expr, $size:expr) => ({
+        assert_eq!(mem::align_of::<$t>(), $align);
+        assert_eq!(mem::size_of::<$t>(), $size);
+    });
+}
 
 pub fn main() {
-    assert_eq!(mem::size_of::<S4>(), 4);
-    assert_eq!(mem::size_of::<S5>(), 5);
-    assert_eq!(mem::size_of::<S13>(), 13);
-    assert_eq!(mem::size_of::<S3_Foo>(), 3 + mem::size_of::<Foo>());
-    assert_eq!(mem::size_of::<S7_Option>(), 7 + mem::size_of::<Option<Box<f64>>>());
+    check!(P1S4, 1, 4);
+    check!(P1S5, 1, 5);
+    check!(P1S13, 1, 13);
+    check!(P1S3_Foo, 1, 3 + mem::size_of::<Foo>());
+    check!(P1S7_Option, 1, 7 + mem::size_of::<Option<Box<f64>>>());
+
+    check!(P2S2, 1, 2);
+    check!(P2S4, 1, 4);
+    check!(P2S6, 2, 6);
+    check!(P2S12, 2, 12);
+    check!(P2S14, 2, 14);
+    check!(P4S16, 4, 16);
+    check!(P4CS20, 4, 20);
+    check!(P2S3_Foo, 2, align_to(3 + mem::size_of::<P2_Foo>(), 2));
+    check!(P2S7_Option, 2, align_to(7 + mem::size_of::<P2_Option>(), 2));
 }
diff --git a/src/test/run-pass/packed-struct-vec.rs b/src/test/run-pass/packed-struct-vec.rs
index 57407b84223..9d8b3d0d074 100644
--- a/src/test/run-pass/packed-struct-vec.rs
+++ b/src/test/run-pass/packed-struct-vec.rs
@@ -8,28 +8,80 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(repr_packed)]
+
 use std::fmt;
 use std::mem;
 
 #[repr(packed)]
 #[derive(Copy, Clone)]
-struct Foo {
+struct Foo1 {
     bar: u8,
     baz: u64
 }
 
-impl PartialEq for Foo {
-    fn eq(&self, other: &Foo) -> bool {
+impl PartialEq for Foo1 {
+    fn eq(&self, other: &Foo1) -> bool {
         self.bar == other.bar && self.baz == other.baz
     }
 }
 
-impl fmt::Debug for Foo {
+impl fmt::Debug for Foo1 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let bar = self.bar;
         let baz = self.baz;
 
-        f.debug_struct("Foo")
+        f.debug_struct("Foo1")
+            .field("bar", &bar)
+            .field("baz", &baz)
+            .finish()
+    }
+}
+
+#[repr(packed(2))]
+#[derive(Copy, Clone)]
+struct Foo2 {
+    bar: u8,
+    baz: u64
+}
+
+impl PartialEq for Foo2 {
+    fn eq(&self, other: &Foo2) -> bool {
+        self.bar == other.bar && self.baz == other.baz
+    }
+}
+
+impl fmt::Debug for Foo2 {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let bar = self.bar;
+        let baz = self.baz;
+
+        f.debug_struct("Foo2")
+            .field("bar", &bar)
+            .field("baz", &baz)
+            .finish()
+    }
+}
+
+#[repr(C, packed(4))]
+#[derive(Copy, Clone)]
+struct Foo4C {
+    bar: u8,
+    baz: u64
+}
+
+impl PartialEq for Foo4C {
+    fn eq(&self, other: &Foo4C) -> bool {
+        self.bar == other.bar && self.baz == other.baz
+    }
+}
+
+impl fmt::Debug for Foo4C {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let bar = self.bar;
+        let baz = self.baz;
+
+        f.debug_struct("Foo4C")
             .field("bar", &bar)
             .field("baz", &baz)
             .finish()
@@ -37,15 +89,42 @@ impl fmt::Debug for Foo {
 }
 
 pub fn main() {
-    let foos = [Foo { bar: 1, baz: 2 }; 10];
+    let foo1s = [Foo1 { bar: 1, baz: 2 }; 10];
+
+    assert_eq!(mem::align_of::<[Foo1; 10]>(), 1);
+    assert_eq!(mem::size_of::<[Foo1; 10]>(), 90);
+
+    for i in 0..10 {
+        assert_eq!(foo1s[i], Foo1 { bar: 1, baz: 2});
+    }
+
+    for &foo in &foo1s {
+        assert_eq!(foo, Foo1 { bar: 1, baz: 2 });
+    }
+
+    let foo2s = [Foo2 { bar: 1, baz: 2 }; 10];
+
+    assert_eq!(mem::align_of::<[Foo2; 10]>(), 2);
+    assert_eq!(mem::size_of::<[Foo2; 10]>(), 100);
+
+    for i in 0..10 {
+        assert_eq!(foo2s[i], Foo2 { bar: 1, baz: 2});
+    }
+
+    for &foo in &foo2s {
+        assert_eq!(foo, Foo2 { bar: 1, baz: 2 });
+    }
+
+    let foo4s = [Foo4C { bar: 1, baz: 2 }; 10];
 
-    assert_eq!(mem::size_of::<[Foo; 10]>(), 90);
+    assert_eq!(mem::align_of::<[Foo4C; 10]>(), 4);
+    assert_eq!(mem::size_of::<[Foo4C; 10]>(), 120);
 
     for i in 0..10 {
-        assert_eq!(foos[i], Foo { bar: 1, baz: 2});
+        assert_eq!(foo4s[i], Foo4C { bar: 1, baz: 2});
     }
 
-    for &foo in &foos {
-        assert_eq!(foo, Foo { bar: 1, baz: 2 });
+    for &foo in &foo4s {
+        assert_eq!(foo, Foo4C { bar: 1, baz: 2 });
     }
 }
diff --git a/src/test/run-pass/packed-tuple-struct-size.rs b/src/test/run-pass/packed-tuple-struct-size.rs
index b0c8684cfe3..9def6ac28e5 100644
--- a/src/test/run-pass/packed-tuple-struct-size.rs
+++ b/src/test/run-pass/packed-tuple-struct-size.rs
@@ -8,18 +8,33 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-
+#![feature(repr_packed)]
 
 use std::mem;
 
 #[repr(packed)]
-struct S4(u8,[u8;  3]);
+struct P1S4(u8,[u8;  3]);
+
+#[repr(packed(2))]
+struct P2S4(u8,[u8;  3]);
 
 #[repr(packed)]
-struct S5(u8, u32);
+struct P1S5(u8, u32);
+
+#[repr(packed(2))]
+struct P2S6(u8, u32);
 
 #[repr(packed)]
-struct S13(i64, f32, u8);
+struct P1S13(i64, f32, u8);
+
+#[repr(packed(2))]
+struct P2S14(i64, f32, u8);
+
+#[repr(packed(4))]
+struct P4S16(u8, f32, i64, u16);
+
+#[repr(C, packed(4))]
+struct P4CS20(u8, f32, i64, u16);
 
 enum Foo {
     Bar = 1,
@@ -27,21 +42,46 @@ enum Foo {
 }
 
 #[repr(packed)]
-struct S3_Foo(u8, u16, Foo);
+struct P1S3_Foo(u8, u16, Foo);
+
+#[repr(packed(2))]
+struct P2_Foo(Foo);
+
+#[repr(packed(2))]
+struct P2S3_Foo(u8, u16, Foo);
 
 #[repr(packed)]
-struct S7_Option(f32, u8, u16, Option<Box<f64>>);
+struct P1S7_Option(f32, u8, u16, Option<Box<f64>>);
 
-pub fn main() {
-    assert_eq!(mem::size_of::<S4>(), 4);
+#[repr(packed(2))]
+struct P2_Option(Option<Box<f64>>);
+
+#[repr(packed(2))]
+struct P2S7_Option(f32, u8, u16, Option<Box<f64>>);
 
-    assert_eq!(mem::size_of::<S5>(), 5);
+fn align_to(value: usize, align: usize) -> usize {
+    (value + (align - 1)) & !(align - 1)
+}
 
-    assert_eq!(mem::size_of::<S13>(), 13);
+macro_rules! check {
+    ($t:ty, $align:expr, $size:expr) => ({
+        assert_eq!(mem::align_of::<$t>(), $align);
+        assert_eq!(mem::size_of::<$t>(), $size);
+    });
+}
 
-    assert_eq!(mem::size_of::<S3_Foo>(),
-               3 + mem::size_of::<Foo>());
+pub fn main() {
+    check!(P1S4, 1, 4);
+    check!(P1S5, 1, 5);
+    check!(P1S13, 1, 13);
+    check!(P1S3_Foo, 1, 3 + mem::size_of::<Foo>());
+    check!(P1S7_Option, 1, 7 + mem::size_of::<Option<Box<f64>>>());
 
-    assert_eq!(mem::size_of::<S7_Option>(),
-              7 + mem::size_of::<Option<Box<f64>>>());
+    check!(P2S4, 1, 4);
+    check!(P2S6, 2, 6);
+    check!(P2S14, 2, 14);
+    check!(P4S16, 4, 16);
+    check!(P4CS20, 4, 20);
+    check!(P2S3_Foo, 2, align_to(3 + mem::size_of::<P2_Foo>(), 2));
+    check!(P2S7_Option, 2, align_to(7 + mem::size_of::<P2_Option>(), 2));
 }
diff --git a/src/test/run-pass/union/union-packed.rs b/src/test/run-pass/union/union-packed.rs
index 6a61280823e..61bb04fece0 100644
--- a/src/test/run-pass/union/union-packed.rs
+++ b/src/test/run-pass/union/union-packed.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 #![feature(untagged_unions)]
+#![feature(repr_packed)]
 
 use std::mem::{size_of, size_of_val, align_of, align_of_val};
 
@@ -18,7 +19,13 @@ struct S {
 }
 
 #[repr(packed)]
-struct Sp {
+struct Sp1 {
+    a: u16,
+    b: [u8; 3],
+}
+
+#[repr(packed(2))]
+struct Sp2 {
     a: u16,
     b: [u8; 3],
 }
@@ -29,15 +36,30 @@ union U {
 }
 
 #[repr(packed)]
-union Up {
+union Up1 {
+    a: u16,
+    b: [u8; 3],
+}
+
+#[repr(packed(2))]
+union Up2 {
+    a: u16,
+    b: [u8; 3],
+}
+
+#[repr(C, packed(4))]
+union Up4c {
     a: u16,
     b: [u8; 3],
 }
 
 const CS: S = S { a: 0, b: [0, 0, 0] };
-const CSP: Sp = Sp { a: 0, b: [0, 0, 0] };
+const CSP1: Sp1 = Sp1 { a: 0, b: [0, 0, 0] };
+const CSP2: Sp2 = Sp2 { a: 0, b: [0, 0, 0] };
 const CU: U = U { b: [0, 0, 0] };
-const CUP: Up = Up { b: [0, 0, 0] };
+const CUP1: Up1 = Up1 { b: [0, 0, 0] };
+const CUP2: Up2 = Up2 { b: [0, 0, 0] };
+const CUP4C: Up4c = Up4c { b: [0, 0, 0] };
 
 fn main() {
     let s = S { a: 0, b: [0, 0, 0] };
@@ -48,13 +70,21 @@ fn main() {
     assert_eq!(align_of_val(&s), 2);
     assert_eq!(align_of_val(&CS), 2);
 
-    let sp = Sp { a: 0, b: [0, 0, 0] };
-    assert_eq!(size_of::<Sp>(), 5);
-    assert_eq!(size_of_val(&sp), 5);
-    assert_eq!(size_of_val(&CSP), 5);
-    assert_eq!(align_of::<Sp>(), 1);
-    assert_eq!(align_of_val(&sp), 1);
-    assert_eq!(align_of_val(&CSP), 1);
+    let sp1 = Sp1 { a: 0, b: [0, 0, 0] };
+    assert_eq!(size_of::<Sp1>(), 5);
+    assert_eq!(size_of_val(&sp1), 5);
+    assert_eq!(size_of_val(&CSP1), 5);
+    assert_eq!(align_of::<Sp1>(), 1);
+    assert_eq!(align_of_val(&sp1), 1);
+    assert_eq!(align_of_val(&CSP1), 1);
+
+    let sp2 = Sp2 { a: 0, b: [0, 0, 0] };
+    assert_eq!(size_of::<Sp2>(), 6);
+    assert_eq!(size_of_val(&sp2), 6);
+    assert_eq!(size_of_val(&CSP2), 6);
+    assert_eq!(align_of::<Sp2>(), 2);
+    assert_eq!(align_of_val(&sp2), 2);
+    assert_eq!(align_of_val(&CSP2), 2);
 
     let u = U { b: [0, 0, 0] };
     assert_eq!(size_of::<U>(), 4);
@@ -64,19 +94,35 @@ fn main() {
     assert_eq!(align_of_val(&u), 2);
     assert_eq!(align_of_val(&CU), 2);
 
-    let up = Up { b: [0, 0, 0] };
-    assert_eq!(size_of::<Up>(), 3);
-    assert_eq!(size_of_val(&up), 3);
-    assert_eq!(size_of_val(&CUP), 3);
-    assert_eq!(align_of::<Up>(), 1);
-    assert_eq!(align_of_val(&up), 1);
-    assert_eq!(align_of_val(&CUP), 1);
+    let Up1 = Up1 { b: [0, 0, 0] };
+    assert_eq!(size_of::<Up1>(), 3);
+    assert_eq!(size_of_val(&Up1), 3);
+    assert_eq!(size_of_val(&CUP1), 3);
+    assert_eq!(align_of::<Up1>(), 1);
+    assert_eq!(align_of_val(&Up1), 1);
+    assert_eq!(align_of_val(&CUP1), 1);
+
+    let up2 = Up2 { b: [0, 0, 0] };
+    assert_eq!(size_of::<Up2>(), 4);
+    assert_eq!(size_of_val(&up2), 4);
+    assert_eq!(size_of_val(&CUP2), 4);
+    assert_eq!(align_of::<Up2>(), 2);
+    assert_eq!(align_of_val(&up2), 2);
+    assert_eq!(align_of_val(&CUP2), 2);
+
+    let up4c = Up4c { b: [0, 0, 0] };
+    assert_eq!(size_of::<Up4c>(), 4);
+    assert_eq!(size_of_val(&up4c), 4);
+    assert_eq!(size_of_val(&CUP4C), 4);
+    assert_eq!(align_of::<Up4c>(), 2);
+    assert_eq!(align_of_val(&up4c), 2);
+    assert_eq!(align_of_val(&CUP4C), 2);
 
     hybrid::check_hybrid();
 }
 
 mod hybrid {
-    use std::mem::size_of;
+    use std::mem::{size_of, align_of};
 
     #[repr(packed)]
     struct S1 {
@@ -96,9 +142,37 @@ mod hybrid {
         u: U,
     }
 
+    #[repr(C, packed(2))]
+    struct S1C {
+        a: u16,
+        b: u8,
+    }
+
+    #[repr(C, packed(2))]
+    union UC {
+        s: S1,
+        c: u16,
+    }
+
+    #[repr(C, packed(2))]
+    struct S2C {
+        d: u8,
+        u: UC,
+    }
+
     pub fn check_hybrid() {
+        assert_eq!(align_of::<S1>(), 1);
         assert_eq!(size_of::<S1>(), 3);
+        assert_eq!(align_of::<U>(), 1);
         assert_eq!(size_of::<U>(), 3);
+        assert_eq!(align_of::<S2>(), 1);
         assert_eq!(size_of::<S2>(), 4);
+
+        assert_eq!(align_of::<S1C>(), 2);
+        assert_eq!(size_of::<S1C>(), 4);
+        assert_eq!(align_of::<UC>(), 2);
+        assert_eq!(size_of::<UC>(), 4);
+        assert_eq!(align_of::<S2C>(), 2);
+        assert_eq!(size_of::<S2C>(), 6);
     }
 }
diff --git a/src/test/ui/feature-gate-repr_packed.rs b/src/test/ui/feature-gate-repr_packed.rs
new file mode 100644
index 00000000000..12bb152b467
--- /dev/null
+++ b/src/test/ui/feature-gate-repr_packed.rs
@@ -0,0 +1,14 @@
+// Copyright 2018 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.
+
+#[repr(packed(1))] //~ error: the `#[repr(packed(n))]` attribute is experimental
+struct Foo(u64);
+
+fn main() {}
diff --git a/src/test/ui/feature-gate-repr_packed.stderr b/src/test/ui/feature-gate-repr_packed.stderr
new file mode 100644
index 00000000000..d0faf9d90bd
--- /dev/null
+++ b/src/test/ui/feature-gate-repr_packed.stderr
@@ -0,0 +1,11 @@
+error[E0658]: the `#[repr(packed(n))]` attribute is experimental (see issue #33158)
+  --> $DIR/feature-gate-repr_packed.rs:11:1
+   |
+LL | #[repr(packed(1))] //~ error: the `#[repr(packed(n))]` attribute is experimental
+   | ^^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(repr_packed)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs
index a4288f67899..5d8c9326258 100644
--- a/src/test/ui/print_type_sizes/packed.rs
+++ b/src/test/ui/print_type_sizes/packed.rs
@@ -21,10 +21,34 @@
 
 #![allow(dead_code)]
 #![feature(start)]
+#![feature(repr_packed)]
 
 #[derive(Default)]
 #[repr(packed)]
-struct Packed {
+struct Packed1 {
+    a: u8,
+    b: u8,
+    g: i32,
+    c: u8,
+    h: i16,
+    d: u8,
+}
+
+#[derive(Default)]
+#[repr(packed(2))]
+struct Packed2 {
+    a: u8,
+    b: u8,
+    g: i32,
+    c: u8,
+    h: i16,
+    d: u8,
+}
+
+#[derive(Default)]
+#[repr(packed(2))]
+#[repr(C)]
+struct Packed2C {
     a: u8,
     b: u8,
     g: i32,
@@ -45,7 +69,9 @@ struct Padded {
 
 #[start]
 fn start(_: isize, _: *const *const u8) -> isize {
-    let _c: Packed = Default::default();
-    let _d: Padded = Default::default();
+    let _c: Packed1 = Default::default();
+    let _d: Packed2 = Default::default();
+    let _e: Packed2C = Default::default();
+    let _f: Padded = Default::default();
     0
 }
diff --git a/src/test/ui/print_type_sizes/packed.stdout b/src/test/ui/print_type_sizes/packed.stdout
index 83fd333c9c7..58e1bac9eb7 100644
--- a/src/test/ui/print_type_sizes/packed.stdout
+++ b/src/test/ui/print_type_sizes/packed.stdout
@@ -1,3 +1,12 @@
+print-type-size type: `Packed2C`: 12 bytes, alignment: 2 bytes
+print-type-size     field `.a`: 1 bytes
+print-type-size     field `.b`: 1 bytes
+print-type-size     field `.g`: 4 bytes
+print-type-size     field `.c`: 1 bytes
+print-type-size     padding: 1 bytes
+print-type-size     field `.h`: 2 bytes
+print-type-size     field `.d`: 1 bytes
+print-type-size     end padding: 1 bytes
 print-type-size type: `Padded`: 12 bytes, alignment: 4 bytes
 print-type-size     field `.g`: 4 bytes
 print-type-size     field `.h`: 2 bytes
@@ -6,10 +15,17 @@ print-type-size     field `.b`: 1 bytes
 print-type-size     field `.c`: 1 bytes
 print-type-size     field `.d`: 1 bytes
 print-type-size     end padding: 2 bytes
-print-type-size type: `Packed`: 10 bytes, alignment: 1 bytes
+print-type-size type: `Packed1`: 10 bytes, alignment: 1 bytes
 print-type-size     field `.a`: 1 bytes
 print-type-size     field `.b`: 1 bytes
 print-type-size     field `.g`: 4 bytes
 print-type-size     field `.c`: 1 bytes
 print-type-size     field `.h`: 2 bytes
 print-type-size     field `.d`: 1 bytes
+print-type-size type: `Packed2`: 10 bytes, alignment: 2 bytes
+print-type-size     field `.g`: 4 bytes
+print-type-size     field `.h`: 2 bytes
+print-type-size     field `.a`: 1 bytes
+print-type-size     field `.b`: 1 bytes
+print-type-size     field `.c`: 1 bytes
+print-type-size     field `.d`: 1 bytes