Skip to content

Commit

Permalink
Auto merge of #57998 - niklasf:align-enum, r=nagisa
Browse files Browse the repository at this point in the history
Allow #[repr(align(x))] on enums (#57996)

Tracking issue: #57996

Implements an extension of [RFC 1358](https://github.com/rust-lang/rfcs/blob/master/text/1358-repr-align.md) behind a feature flag (`repr_align_enum`). Originally introduced here for structs: #39999.

It seems like only HIR-level changes are required, since enums are already aware of their alignment (due to alignment of their limbs).

cc @bitshifter
  • Loading branch information
bors committed Feb 6, 2019
2 parents b139669 + a6fd6ec commit 631a162
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 28 deletions.
42 changes: 42 additions & 0 deletions src/doc/unstable-book/src/language-features/repr-align-enum.md
Original file line number Diff line number Diff line change
@@ -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);
}
```
12 changes: 2 additions & 10 deletions src/librustc/hir/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,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 {
Expand All @@ -212,14 +212,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 {
Expand Down
14 changes: 14 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,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! (
Expand Down Expand Up @@ -1698,6 +1701,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,
Expand Down
36 changes: 36 additions & 0 deletions src/test/codegen/align-enum.rs
Original file line number Diff line number Diff line change
@@ -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
}
55 changes: 55 additions & 0 deletions src/test/run-pass/structs-enums/align-enum.rs
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 2 additions & 1 deletion src/test/ui/attr-usage-repr.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Expand All @@ -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
Expand Down
18 changes: 5 additions & 13 deletions src/test/ui/attr-usage-repr.stderr
Original file line number Diff line number Diff line change
@@ -1,43 +1,35 @@
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
| ^
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
| ^^
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
| ^^^^^^
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`.
10 changes: 10 additions & 0 deletions src/test/ui/feature-gates/feature-gate-repr_align_enum.rs
Original file line number Diff line number Diff line change
@@ -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() { }
11 changes: 11 additions & 0 deletions src/test/ui/feature-gates/feature-gate-repr_align_enum.stderr
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 4 additions & 0 deletions src/test/ui/repr/repr-align.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(repr_align_enum)]
#![allow(dead_code)]

#[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer
Expand All @@ -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() {}
14 changes: 10 additions & 4 deletions src/test/ui/repr/repr-align.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 631a162

Please sign in to comment.