diff --git a/src/doc/unstable-book/src/language-features/repr-align-enum.md b/src/doc/unstable-book/src/language-features/repr-align-enum.md
new file mode 100644
index 0000000000000..415c6ebe8b4bc
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/repr-align-enum.md
@@ -0,0 +1,42 @@
+# `repr_align_enum`
+
+The tracking issue for this feature is: [#57996]
+
+[#57996]: https://github.com/rust-lang/rust/issues/57996
+
+------------------------
+
+The `repr_align_enum` feature allows using the `#[repr(align(x))]` attribute
+on enums, similarly to structs.
+
+# Examples
+
+```rust
+#![feature(repr_align_enum)]
+
+#[repr(align(8))]
+enum Aligned {
+    Foo,
+    Bar { value: u32 },
+}
+
+fn main() {
+    assert_eq!(std::mem::align_of::<Aligned>(), 8);
+}
+```
+
+This is equivalent to using an aligned wrapper struct everywhere:
+
+```rust
+#[repr(align(8))]
+struct Aligned(Unaligned);
+
+enum Unaligned {
+    Foo,
+    Bar { value: u32 },
+}
+
+fn main() {
+    assert_eq!(std::mem::align_of::<Aligned>(), 8);
+}
+```
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index df111b2be319f..9fac88fbbe9ab 100644
--- a/src/librustc/hir/check_attr.rs
+++ b/src/librustc/hir/check_attr.rs
@@ -187,8 +187,8 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
             };
 
             let (article, allowed_targets) = match &*name.as_str() {
-                "C" => {
-                    is_c = true;
+                "C" | "align" => {
+                    is_c |= name == "C";
                     if target != Target::Struct &&
                             target != Target::Union &&
                             target != Target::Enum {
@@ -213,14 +213,6 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
                         continue
                     }
                 }
-                "align" => {
-                    if target != Target::Struct &&
-                            target != Target::Union {
-                        ("a", "struct or union")
-                    } else {
-                        continue
-                    }
-                }
                 "transparent" => {
                     is_transparent = true;
                     if target != Target::Struct {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 2820924824697..218a3f52ee0b2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -461,6 +461,9 @@ declare_features! (
 
     // #[optimize(X)]
     (active, optimize_attribute, "1.34.0", Some(54882), None),
+
+    // #[repr(align(X))] on enums
+    (active, repr_align_enum, "1.34.0", Some(57996), None),
 );
 
 declare_features! (
@@ -1697,6 +1700,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
 
+            ast::ItemKind::Enum(..) => {
+                for attr in attr::filter_by_name(&i.attrs[..], "repr") {
+                    for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
+                        if item.check_name("align") {
+                            gate_feature_post!(&self, repr_align_enum, attr.span,
+                                               "`#[repr(align(x))]` on enums is experimental");
+                        }
+                    }
+                }
+            }
+
             ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => {
                 if polarity == ast::ImplPolarity::Negative {
                     gate_feature_post!(&self, optin_builtin_traits,
diff --git a/src/test/codegen/align-enum.rs b/src/test/codegen/align-enum.rs
new file mode 100644
index 0000000000000..2251c54229ee0
--- /dev/null
+++ b/src/test/codegen/align-enum.rs
@@ -0,0 +1,36 @@
+// compile-flags: -C no-prepopulate-passes
+// ignore-tidy-linelength
+// min-llvm-version 7.0
+
+#![crate_type = "lib"]
+#![feature(repr_align_enum)]
+
+#[repr(align(64))]
+pub enum Align64 {
+    A(u32),
+    B(u32),
+}
+// CHECK: %Align64 = type { [0 x i32], i32, [15 x i32] }
+
+pub struct Nested64 {
+    a: u8,
+    b: Align64,
+    c: u16,
+}
+
+// CHECK-LABEL: @align64
+#[no_mangle]
+pub fn align64(a: u32) -> Align64 {
+// CHECK: %a64 = alloca %Align64, align 64
+// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 64 %{{.*}}, i8* align 64 %{{.*}}, i{{[0-9]+}} 64, i1 false)
+    let a64 = Align64::A(a);
+    a64
+}
+
+// CHECK-LABEL: @nested64
+#[no_mangle]
+pub fn nested64(a: u8, b: u32, c: u16) -> Nested64 {
+// CHECK: %n64 = alloca %Nested64, align 64
+    let n64 = Nested64 { a, b: Align64::B(b), c };
+    n64
+}
diff --git a/src/test/run-pass/structs-enums/align-enum.rs b/src/test/run-pass/structs-enums/align-enum.rs
new file mode 100644
index 0000000000000..8d72b1f6f0d24
--- /dev/null
+++ b/src/test/run-pass/structs-enums/align-enum.rs
@@ -0,0 +1,55 @@
+// run-pass
+#![allow(dead_code)]
+#![feature(repr_align_enum)]
+
+use std::mem;
+
+// Raising alignment
+#[repr(align(16))]
+enum Align16 {
+    Foo { foo: u32 },
+    Bar { bar: u32 },
+}
+
+// Raise alignment by maximum
+#[repr(align(1), align(16))]
+#[repr(align(32))]
+#[repr(align(4))]
+enum Align32 {
+    Foo,
+    Bar,
+}
+
+// Not reducing alignment
+#[repr(align(4))]
+enum AlsoAlign16 {
+    Foo { limb_with_align16: Align16 },
+    Bar,
+}
+
+// No niche for discriminant when used as limb
+#[repr(align(16))]
+struct NoNiche16(u64, u64);
+
+// Discriminant will require extra space, but enum needs to stay compatible
+// with alignment 16
+#[repr(align(1))]
+enum AnotherAlign16 {
+    Foo { limb_with_noniche16: NoNiche16 },
+    Bar,
+    Baz,
+}
+
+fn main() {
+    assert_eq!(mem::align_of::<Align16>(), 16);
+    assert_eq!(mem::size_of::<Align16>(), 16);
+
+    assert_eq!(mem::align_of::<Align32>(), 32);
+    assert_eq!(mem::size_of::<Align32>(), 32);
+
+    assert_eq!(mem::align_of::<AlsoAlign16>(), 16);
+    assert_eq!(mem::size_of::<AlsoAlign16>(), 16);
+
+    assert_eq!(mem::align_of::<AnotherAlign16>(), 16);
+    assert_eq!(mem::size_of::<AnotherAlign16>(), 32);
+}
diff --git a/src/test/ui/attr-usage-repr.rs b/src/test/ui/attr-usage-repr.rs
index 498bf4d284a73..1df2947cbe2dd 100644
--- a/src/test/ui/attr-usage-repr.rs
+++ b/src/test/ui/attr-usage-repr.rs
@@ -1,4 +1,5 @@
 #![feature(repr_simd)]
+#![feature(repr_align_enum)]
 
 #[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union
 fn f() {}
@@ -18,7 +19,7 @@ struct SInt(f64, f64);
 #[repr(C)]
 enum EExtern { A, B }
 
-#[repr(align(8))] //~ ERROR: attribute should be applied to struct
+#[repr(align(8))]
 enum EAlign { A, B }
 
 #[repr(packed)] //~ ERROR: attribute should be applied to struct
diff --git a/src/test/ui/attr-usage-repr.stderr b/src/test/ui/attr-usage-repr.stderr
index 990984bbb2b3d..abb8685e4cef0 100644
--- a/src/test/ui/attr-usage-repr.stderr
+++ b/src/test/ui/attr-usage-repr.stderr
@@ -1,5 +1,5 @@
 error[E0517]: attribute should be applied to struct, enum or union
-  --> $DIR/attr-usage-repr.rs:3:8
+  --> $DIR/attr-usage-repr.rs:4:8
    |
 LL | #[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union
    |        ^
@@ -7,7 +7,7 @@ LL | fn f() {}
    | --------- not a struct, enum or union
 
 error[E0517]: attribute should be applied to enum
-  --> $DIR/attr-usage-repr.rs:15:8
+  --> $DIR/attr-usage-repr.rs:16:8
    |
 LL | #[repr(i8)] //~ ERROR: attribute should be applied to enum
    |        ^^
@@ -15,15 +15,7 @@ LL | struct SInt(f64, f64);
    | ---------------------- not an enum
 
 error[E0517]: attribute should be applied to struct or union
-  --> $DIR/attr-usage-repr.rs:21:8
-   |
-LL | #[repr(align(8))] //~ ERROR: attribute should be applied to struct
-   |        ^^^^^^^^
-LL | enum EAlign { A, B }
-   | -------------------- not a struct or union
-
-error[E0517]: attribute should be applied to struct or union
-  --> $DIR/attr-usage-repr.rs:24:8
+  --> $DIR/attr-usage-repr.rs:25:8
    |
 LL | #[repr(packed)] //~ ERROR: attribute should be applied to struct
    |        ^^^^^^
@@ -31,13 +23,13 @@ LL | enum EPacked { A, B }
    | --------------------- not a struct or union
 
 error[E0517]: attribute should be applied to struct
-  --> $DIR/attr-usage-repr.rs:27:8
+  --> $DIR/attr-usage-repr.rs:28:8
    |
 LL | #[repr(simd)] //~ ERROR: attribute should be applied to struct
    |        ^^^^
 LL | enum ESimd { A, B }
    | ------------------- not a struct
 
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0517`.
diff --git a/src/test/ui/feature-gates/feature-gate-repr_align_enum.rs b/src/test/ui/feature-gates/feature-gate-repr_align_enum.rs
new file mode 100644
index 0000000000000..f8e68a9de015b
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-repr_align_enum.rs
@@ -0,0 +1,10 @@
+#[repr(align(16))]
+struct Foo(u64);
+
+#[repr(align(8))] //~ ERROR `#[repr(align(x))]` on enums is experimental (see issue #57996)
+enum Bar {
+    Foo { foo: Foo },
+    Baz,
+}
+
+fn main() { }
diff --git a/src/test/ui/feature-gates/feature-gate-repr_align_enum.stderr b/src/test/ui/feature-gates/feature-gate-repr_align_enum.stderr
new file mode 100644
index 0000000000000..6def25f965118
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-repr_align_enum.stderr
@@ -0,0 +1,11 @@
+error[E0658]: `#[repr(align(x))]` on enums is experimental (see issue #57996)
+  --> $DIR/feature-gate-repr_align_enum.rs:4:1
+   |
+LL | #[repr(align(8))] //~ ERROR `#[repr(align(x))]` on enums is experimental (see issue #57996)
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(repr_align_enum)] 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/repr/repr-align.rs b/src/test/ui/repr/repr-align.rs
index 78ed6c7e1c417..9ce89e82ca225 100644
--- a/src/test/ui/repr/repr-align.rs
+++ b/src/test/ui/repr/repr-align.rs
@@ -1,3 +1,4 @@
+#![feature(repr_align_enum)]
 #![allow(dead_code)]
 
 #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
@@ -12,4 +13,7 @@ struct C(i32);
 #[repr(align(536870912))] // ok: this is the largest accepted alignment
 struct D(i32);
 
+#[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+enum E { Left, Right }
+
 fn main() {}
diff --git a/src/test/ui/repr/repr-align.stderr b/src/test/ui/repr/repr-align.stderr
index e8dbf74232bfa..f1a5d88ace1fd 100644
--- a/src/test/ui/repr/repr-align.stderr
+++ b/src/test/ui/repr/repr-align.stderr
@@ -1,21 +1,27 @@
 error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer
-  --> $DIR/repr-align.rs:3:8
+  --> $DIR/repr-align.rs:4:8
    |
 LL | #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
    |        ^^^^^^^^^^^
 
 error[E0589]: invalid `repr(align)` attribute: not a power of two
-  --> $DIR/repr-align.rs:6:8
+  --> $DIR/repr-align.rs:7:8
    |
 LL | #[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
    |        ^^^^^^^^^
 
 error[E0589]: invalid `repr(align)` attribute: larger than 2^29
-  --> $DIR/repr-align.rs:9:8
+  --> $DIR/repr-align.rs:10:8
    |
 LL | #[repr(align(4294967296))] //~ ERROR: invalid `repr(align)` attribute: larger than 2^29
    |        ^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error[E0589]: invalid `repr(align)` attribute: not a power of two
+  --> $DIR/repr-align.rs:16:8
+   |
+LL | #[repr(align(15))] //~ ERROR: invalid `repr(align)` attribute: not a power of two
+   |        ^^^^^^^^^
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0589`.