Skip to content

RFC: Add an attribute for raising the alignment of various items #3806

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3a4f5ac
Add `align_attr` RFC
Jules-Bertholet Jun 12, 2024
4badbc9
Add more mixing packed and aligned to future possibilities
Jules-Bertholet May 2, 2025
2b647a8
Avoid making commitments for interaction with `extern static`
Jules-Bertholet May 5, 2025
13cc4f2
Expand comparison with C and C++
Jules-Bertholet May 8, 2025
bbe09ee
Minor clarifications
Jules-Bertholet May 9, 2025
81748b6
Delete double spaces
Jules-Bertholet May 9, 2025
51b8069
Thumb weirdness, expand discussion of `repr`
Jules-Bertholet May 9, 2025
855a766
Rephrase
Jules-Bertholet May 9, 2025
1c7a402
Typo
Jules-Bertholet May 9, 2025
dee1e70
Address `async` and closures
Jules-Bertholet May 16, 2025
4701919
Elaborate on function parameters prohibition
Jules-Bertholet May 16, 2025
59b1230
Fix typo
Jules-Bertholet May 17, 2025
16f3bee
Justify `async fn` behavior
Jules-Bertholet May 17, 2025
5229770
Elaborate on `ref`/`ref mut`
Jules-Bertholet May 17, 2025
96350ee
Fix tpo
Jules-Bertholet May 18, 2025
0c21fb3
Fix example
Jules-Bertholet May 18, 2025
959a94c
Cache line clarifications
Jules-Bertholet May 18, 2025
75f615a
Minor rephrase
Jules-Bertholet May 18, 2025
8b1fa67
Fix typos
Jules-Bertholet May 19, 2025
95f3972
Link to WGSL alignment rules
Jules-Bertholet May 19, 2025
fefe945
Function parameter prohibition is semantic
Jules-Bertholet May 19, 2025
a478717
Justify not allowing attribute on `_`
Jules-Bertholet May 19, 2025
32b2a93
More cache clarifications
Jules-Bertholet May 19, 2025
f140b23
Rename a heading
Jules-Bertholet May 19, 2025
60f23d2
Fix typo
Jules-Bertholet May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
377 changes: 377 additions & 0 deletions text/3806-align-attr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,377 @@
- Feature Name: `align_attr`
- Start Date: 2025-05-01
- RFC PR: [rust-lang/rfcs#3806](https://github.com/rust-lang/rfcs/pull/3806)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Add an `#[align(…)]` attribute to set the minimum alignment of `struct` and `enum`
fields, `static`s, functions, and local variables.

# Motivation
[motivation]: #motivation

## Bindings to C and C++

[C](https://en.cppreference.com/w/c/language/_Alignas) and [C++](https://en.cppreference.com/w/cpp/language/alignas)
provide an `alignas` modifier to set the alignment of specific struct fields. To
represent such structures in Rust, `bindgen` is sometimes forced to add explicit
padding fields:

```c
// C code
#include <stdint.h>
struct foo {
uint8_t x;
_Alignas(128) uint8_t y;
uint8_t z;
};
```

```rust
// Rust bindings generated by `bindgen`
#[repr(C, align(128))]
pub struct foo {
pub x: u8,
pub __bindgen_padding_0: [u8; 127usize],
pub y: u8,
pub z: u8,
}
```

The `__bindgen_padding_0` field makes the generated bindings more confusing and
less ergonomic. Also, it is unsound: the padding should be using `MaybeUninit`.
And even then, there is no guarantee of ABI compatibility on all potential
platforms.

## Packing values into fewer cache lines

When working with large values (lookup tables, for example), it is often
desirable, for optimal performance, to pack them into as few cache lines as
possible. One way of doing this is to force the alignment of the value to be at
least the size of the cache line, or perhaps the greatest common denominator of
the value and cache line sizes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am very confused by this paragraph, and don't understand what it tries to say. By raising the alignment, the items will be further apart from each other (since each one must be properly aligned), and therefore it will take more cache lines, not fewer?

Copy link

@folkertdev folkertdev May 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that you don't want a field to span across 2 cache lines if it could fit into just one. An easy way to achieve that is to make the field start at the start of a new cache line.

Aligning a field also means that a reference to it is aligned, which can improve performance in certain cases.

edit: i agree the text should explain this in more detail

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve reworded this paragraph.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so you want to avoid false sharing, that makes sense.

The new text helps, thanks. The section title still says "Packing values into fewer cache lines" though, which doesn't seem to match the text.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not about false sharing -- if that was a problem, trailing padding until the next cache line boundary would be a plus. It's about possibly bringing fewer cache lines into the cache for common access patterns. This is a minor micro-optimization at best.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's about possibly bringing fewer cache lines into the cache for common access patterns.

This is exactly it.

Copy link
Contributor

@traviscross traviscross May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Packing values into fewer cache lines"

As it pertains to the header, the ambiguity is the difference between packing the collection of all values into (maybe) fewer cache lines, or packing the bits of each value into (maybe) fewer cache lines.

The word "packing" tends to imply arranging many individual discrete things to fit in less space, and so is perhaps a misleading word for this when combined with "values".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the paragraph again to clarify further.


The simplest way of accomplishing this in Rust today is to use a wrapper struct
with a `#[repr(align(…))]` attribute:

```rust
type SomeLargeType = [[u8; 64]; 21];

#[repr(align(128))]
struct CacheAligned<T>(T);

static LOOKUP_TABLE: CacheAligned<SomeLargeType> = CacheAligned(SomeLargeType {
data: todo!(),
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example is confusing since SomeLargeType is declared as an array but then later we pretend it is a struct. I don't understand what this is trying to say.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet May 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, sorry. The contents of the type are immaterial, only the size matters

```

However, this approach has several downsides:

- It requires defining a separate wrapper type.
- It changes the type of the item, which may not be allowed if it is part of the
crate's public API.
- It may add padding to the value, which might not be necessary or desirable.

In some cases, it can also improve performance to align function items in the
same way.

## Required alignment for low-level use cases

Some low-level use-cases (for example, the [RISC-V `mtvec`
register](https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/machine.html#machine-trap-vector-base-address-register-mtvec))
require functions or statics to have a certain minimum alignment.

## Interoperating with systems that have types where size is not a multiple of alignment

In Rust, a type’s size is always a multiple of its alignment. However, there are
other languages that can interoperate with Rust, where this is not the case
(WGSL, for example). It’s important for Rust to be able to represent such
structures.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me how this would work while keeping Rust's "size is multiple of align" rule intact. I guess if it's about individual fields in a larger aggregate that maintains the rule in total? I don't know anything about WGSL so an example would be appreciated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s exactly it. The WSGL example was taken from this comment on Internals: https://internals.rust-lang.org/t/pre-rfc-align-attribute/21004/20

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a worked example would indeed help readers of the RFC on this point.

Copy link
Contributor

@kpreid kpreid May 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a concrete example of implementing Rust-WGSL compatibility using the #[align] attribute defined in this RFC. These structs have the same layout, and together demonstrate both inserting required padding (between foo and bar), and allowing a following field to be placed where a wrapper type would demand padding (baz immediately after bar):

// WGSL
struct Example {     // size = 32, alignment = 16
    foo: vec3<f32>,  // offset = 0, size = 12
    bar: vec3<f32>,  // offset = 16, size = 12
    baz: f32,        // offset = 28, size = 4
}
// Rust
#[repr(linear)] // as defined in this RFC; repr(C) in current Rust
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub(crate) struct Example {
    #[align(16)]
    foo: [f32; 3],

    // #[align] below causes 4 bytes of padding to be inserted here to satisfy it.

    #[align(16)]
    bar: [f32; 3],

    baz: f32,      // If we used a wrapper for bar, this field would be at offset 32, wrongly
}

It is often possible to order structure fields to fill gaps so that no inter-field padding is needed — such as if the fields in this example were declared in the order {foo, baz, bar} — and this is preferable when possible to avoid wasted memory, but the advantage of using #[align] in this scenario is that when used systematically, it can imitate WGSL's layout and thus will be correct even if the field ordering is not optimal.

(Please feel free to use any of the above text in the RFC.)


# Explanation
[explanation]: #explanation

The `align` attribute is a new inert, built-in attribute that can be applied to
ADT fields, `static` items, function items, and local variable declarations. The
attribute accepts a single required parameter, which must be a power-of-2
integer literal from 1 up to 2<sup>29</sup>. (This is the same as
`#[repr(align(…))]`.)
Comment on lines +101 to +105

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2^29 limit is way too high. The consistency with #[repr(align(..))] is a good default but alignments larger than a page or two have never worked properly in local variables (rust-lang/rust#70143) and in statics (rust-lang/rust#70022, rust-lang/rust#70144). While there are some use cases for larger alignments on types (if they're heap allocated) and an error on putting such a type in a local or static is ugly, for this new attribute we could just avoid the problem from the start.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a struct field, both GCC and clang supported _Alignas(N) for N ≤ 228 (268435456).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bug with local variables (rust-lang/rust#70143) seems to have been fixed everywhere except Windows, and just waiting on someone to fix it there as well in LLVM. (And even on Windows where the issue is not fixed, the only effect is to break the stack overflow protection, bringing it down to the same level as many Tier 2 targets.)

So the only remaining issue is with statics, where it looks like a target-specific max alignment might be necessary. Once implemented, that solution can be used to address align as well.

Overall, I don't think any of this is sufficient motivation to impose a stricter maximum on #[align].

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that fixing the soundness issue for locals just means that putting a local with huge alignment in a stack frame is very likely to trigger the stack overflow check and abort the program. There is no use case for such massively over-aligned locals or statics, which is why those soundness issues been mostly theoretical problems and why the only progress toward fixing them over many years has been side effects of unrelated improvements (inline stack checks).

The only reason why the repr(align(..)) limit is so enormous is because it’s plausibly useful for heap allocations. Adding a second , lower limit for types put in statics and locals nowadays is somewhat tricky to design and drive to consensus (e.g., there’s theoretical backwards compatibility concerns) and not a priority for anyone, so who knows when it’ll happen. For #[align] we have the benefit of hindsight and could just mostly side-step the whole mess. I don’t see this as “needlessly restricting the new feature” but rather as “not pointlessly expanding upon an existing soundness issue for no reason”.

Copy link
Member

@programmerjake programmerjake May 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no use case for such massively over-aligned locals or statics

one use case I can think of is having a massive array that is faster because it's properly aligned so the OS can use huge pages (on x86_64, those require alignment $2^{19}$ or $2^{30}$), reducing TLB pressure. admittedly, that would only realistically be useful for statics or heap-allocated/mmap-ed memory.

Copy link

@hanna-kruppe hanna-kruppe May 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To use huge pages for static data, you'd want to align the ELF segment containing the relevant sections (or equivalent in other formats), so the right tool there is a linker script or similar platform-specific mechanism. Over-aligning individual statics is a bad alternative:

  1. It risks wasting a lot more (physical!) memory than expected if you end up with multiple statics in the program doing it and there's not enough other data to fill the padding required between them or they go in different sections.
  2. If the linker/loader ignores the requested section alignment then that leads to UB if you used Rust-level #[align(N)]/#[repr(align(N))] and the code was optimized under that assumption.
  3. While aligning statics appears easier and more portable than linker scripts, the reality is that platform/toolchain support for this is spotty anyway, so you really ought to carefully consider when and where to apply this trick.

In any case, I'm sure I'm technically wrong to claim that nobody could ever come up with a use case for massively over-aligned statics. But there's a reason why Linux and glibc have only started supporting it at all in the last few years, and other environments like musl-based Linux and Windows apparently doesn't support it at all (see discussion in aforementioned issues).


Multiple `align` attributes may be present on the same item; the highest
alignment among them will be used. The compiler may signal this case with a
warn-by-default lint.

## On ADT fields

The `align` attribute may be applied to any field of any `struct`, `enum`, or
`union` that is not `#[repr(transparent)]`.

```rust
#[repr(C)]
struct Foo {
#[align(8)]
a: u32,
}

enum Bar {
Variant(#[align(16)] u128),
}

union Baz {
#[align(16)]
a: u32,
}
```

The effect of the attribute is to force the address of the field to have at
least the specified alignment. If the field already has at least that
alignment, due to the required alignment of its type or to a `repr` attribute on
the containing type, the attribute has no effect.

In contrast to a `repr(align(…))` wrapper struct, an `align` annotation does *not*
necessarily add extra padding to force the field to have a size that is a
multiple of its alignment. (The size of the containing ADT must still be a
multiple of its alignment; that hasn't changed.)

`align` attributes for fields of a `#[repr(packed(n))]` ADT may not specify an
alignment higher than `n`.

```rust
#[repr(packed(4))]
struct Sardines {
#[align(2)] // OK
a: u8,
#[align(4)] // OK
b: u16,
#[align(8)] //~ ERROR
c: u32,
}
```

`align()` attributes on ADT fields are shown in `rustdoc`-generated documentation.

## Interaction with `repr(C)`

`repr(C)` currently has two contradictory meanings: “a simple, linear layout
algorithm that works the same everywhere” and “an ABI matching that of the
target’s standard C compiler”. This RFC does not aim to reslove that conflict;
that is being discussed as part of [RFC
3718](https://github.com/rust-lang/rfcs/pull/3718). Henceforth, we will use
`repr(C_for_real)` to denote “match the system C compiler”, and `repr(linear)`
to denote “simple, portable layout algorithm”; but those names are not
normative.

### `repr(C_for_real)`

The layout of a `repr(C_for_real)` ADT with `align` attributes on its fields is
identical to that of the corresponding C ADT declared with `alignas`
annotations. For example, the struct below is equivalent to the C `struct foo`
from the motivation section:

```rust
#[repr(C_for_real)]
pub struct foo {
pub x: u8,
#[align(128)]
pub y: u8,
pub z: u8,
}
```

### `repr(linear)`

In a `repr(linear)` ADT, a field with an `align` attribute has its alignment, as
well as the alignment of the containing ADT, increased to at least what the
attribute specifies.

For example, the following two structs have the same layout in memory (though
not necessarily the same ABI):

```rust
#[repr(linear)]
pub struct foo {
pub x: u8,
#[align(128)]
pub y: u8,
pub z: u8,
}
```

```rust
#[repr(linear, align(128))]
pub struct foo2 {
pub x: u8,
pub _padding: [MaybeUninit<u8>; 127usize],
pub y: u8,
pub z: u8,
}
```

## On `static`s

Any `static` item (including `static`s inside `extern` blocks) may have an
`align` attribute applied:

```rust
#[align(32)]
static BAZ: [u32; 12] = [0xDEADBEEF; 12];

unsafe extern "C" {
#[align(2)]
safe static BOZZLE: u8;
}
```

The effect of the attribute is to force the `static` to be stored with at least
the specified alignment. The attribute does not force padding bytes to be added
after the `static`. For `static`s inside `unsafe extern` blocks, if the `static`
does not meet the specified alignment, the behavior is undefined. (For
misaligned `static` items declared inside old-style `extern` blocks, UB occurs
only if the item is used.)

The `align` attribute may also be applied to thread-local `static`s created with
the `thread_local!` macro; the attribute affects the alignment of the underlying
value, not that of the outer `std::thread::LocalKey`.

```rust
thread_local! {
#[align(64)]
static FOO: u8 = 42;
}

fn main() {
FOO.with(|r| {
let p: *const u8 = r;
assert_eq!(p.align_offset(64), 0);
});
}
```

`align()` attributes on `static`s are shown in `rustdoc`-generated documentation.

## On function items

On function items, `#[align(…)]` sets the alignment of the function’s code. This
replaces `#[repr(align(…))]` on function items from `#![feature(fn_align)]`.

`align` attributes on function items are shown in `rustdoc`-generated documentation.

## On local variables

The `align` attribute may also be applied to local variable declarations inside
`let` bindings. The attribute forces the local to have at least the alignment
specified:

```rust
fn main() {
let (a, #[align(4)] b, #[align(2)] mut c) = (4u8, 2u8, 1u8);
c *= 2;
dbg!(a, b, c);

if let Some(#[align(4)] x @ 1..) = Some(42u8) {
dbg!(x);
let p: *const u8 = x;
assert_eq!(p.align_offset(4), 0);
}
}
```

`align` attributes may not be applied to function parameters.

```rust
fn foo(#[align(8)] _a: u32) {} //~ ERROR
```

They also may not be applied to `_` bindings.

```rust
let #[align(4)] _ = true; //~ ERROR
```

# Drawbacks
[drawbacks]: #drawbacks

- This feature adds additional complexity to the languge.
- The distinction between `align` and `repr(align)` may be confusing for users.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

Compared to the wrapper type approach, the `align` attribute adds additional
flexibility, because it does not force the insertion of padding. If we don't
adopt this feature, `bindgen` will continue to generate suboptimal bindings, and
users will continue to be forced to choose between suboptimal alignment and
additional padding.

## `#[align(…)]` vs `#[repr(align(…))]`

One potential alternative would be to use `#[repr(align(…))]` everywhere,
instead of introducing a new attribute.

Benefits of this alternative:

- No new attribute polluting the namespace.
- Requesting a certain alignment is spelled the same everywhere.
- `#[repr(…)]` on fields might accept additional options in the future.

Drawbacks:

- `#[repr(align(…))]` is a longer and noisier syntax.
- `#[repr(…)]` on non-ADTs, with the possible exception of field definitions, will
probably only ever accept `align(…)` as an argument. It would not be consistent
with the existing `#[repr(…)]` on ADTs.
- `#[align(…)]` *only* aligns, while `#[repr(align(…))]` also pads to a multiple
of the alignment. Having different syntax makes that distinction more clear.

## `#[align(n)]` vs `#[align = n]`

`align = n` might be misinterpreted as requesting an alignment of *exactly* `n`,
instead of *at least* `n`.

# Prior art
[prior-art]: #prior-art

This proposal is the Rust equivalent of [C
`alignas`](https://en.cppreference.com/w/c/language/_Alignas_).

# Unresolved questions
[unresolved-questions]: #unresolved-questions

1. What should the syntax be for applying the `align` attribute to `ref`/`ref
mut` bindings?

- Option A: the attribute goes inside the `ref`/`ref mut`.

```rust
fn foo(x: &u8) {
let ref #[align(4)] _a = *x;
}
```

- Option B: the attribute goes outside the `ref`/`ref mut`.

```rust
fn foo(x: &u8) {
let #[align(4)] ref _a = *x;
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever we do, I'd expect it to be the same as for mut. So it's probably not worth deferring this question, as we need to handle it there.

As for where to put it, it seems like a bit of a coin toss. Anyone have a good argument for which way it should go?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m comfortable deferring it because I see no use-case for it, and I don’t want to hold up the RFC on something with no use case.

Copy link
Contributor

@traviscross traviscross May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but still, I repeat my question, as we need to answer it for mut in any case, about whether there are good arguments for on which side of mut the attribute should appear.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mut case does have actual use-cases, so I think we should handle the issue in the context of that, not this RFC.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, wait, I think there may be a misunderstanding here. By “the same as for mut”, are you referring to combining mut with ref/ref mut?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This RFC specifies this is allowed (quoting from an example in the RFC):

let (a, #[align(4)] b, #[align(2)] mut c) = (4u8, 2u8, 1u8);

My question is whether there are good arguments about whether we should prefer that, or should instead prefer:

let (a, #[align(4)] b, mut #[align(2)] c) = (4u8, 2u8, 1u8);

The RFC should discuss any reasons we might want to prefer one over the other.


Separately, and secondarily, my feeling is that if we chose

let #[align(..)] mut a = ..;

then we would also choose:

let #[align(..)] ref a = ..;

And if we instead chose

let mut #[align(..)] a = ..;

then we would choose:

let ref #[align(..)] a = ..;

So my feeling is that in settling the question of how to syntactically combine #[align(..)] and mut, we are de facto settling the question of how to combine #[align(..)] with any other binding mode token.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t agree that we would necessarily want to make the same choice in both cases. I actually think it depends on how mut and ref/ref mut should be combined.

If the combination looks like

let ref (mut x) = …;
let ref mut (mut x) = …;

Then we should also do

let ref (#[align()] x) = …;
let ref mut (#[align()] x) = …;

But if it looks like

let mut ref x = …;
let mut ref mut x = …;

Then we should do

let #[align()] ref x = …;
let #[align()] ref mut x = …;

Copy link
Contributor

@traviscross traviscross May 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that event, and in your model, that would still leave us deciding between:

let ref (mut #[align(..)] x) = ..; // 1
// vs
let ref (#[align(..)] mut x) = ..; // 2

And between:

let #[align(..)] mut ref x = ..; // 3
// vs
let mut #[align(..)] ref x = ..; // 4

I would estimate that we'd comfortably favor 1, 3 over 2, 4.

There are also, of course, these possibilities:

let #[align(..)] ref (mut x) = ..; // 5
let mut ref #[align(..)] x = ..; // 6

If in this RFC we pick #[align(..) mut x, that would rule out for me option 1 if we later did ref (mut x) (and I wouldn't pick option 2 anyway). If we pick mut #[align(..)] x, that would rule out for me option 3 if we later did mut ref x (and I wouldn't pick option 4 anyway).

That is, even in this future possibility, I'm going to want to keep all of the binding mode tokens either to the left or to the right of the attribute.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll elaborate in the RFC, but my preference is for 2 or 3.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve added a section on this to the RFC.


(I believe the simplest option is to forbid this combination entirely for now.)

2. Does MSVC do something weird with `alignas`? In other words, is the concern
about `repr(C)` vs `repr(linear)` purely theoretical at this point, or does
it matter in practice today?

# Future possibilities
[future-possibilities]: #future-possibilities

- The `align(…)` and `repr(align(…))` attributes currently accept only integer
literals as parameters. In the future, they could support `const` expressions
as well.
- We could provide additional facilities for controlling the layout of ADTs; for
example, a way to specify exact field offsets or arbitrary padding.
- We could add type-safe APIs for over-aligned pointers; for example,
over-aligned reference types that are subtypes of `&`/`&mut`.
- We could also add similar APIs for over-aligned function pointers.