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 42 commits into
base: master
Choose a base branch
from
Open
Changes from 2 commits
Commits
Show all changes
42 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
3351aa9
Fix typo
Jules-Bertholet Jun 14, 2025
e10fe79
Make sentence less ambiguous
Jules-Bertholet Jun 14, 2025
441e05b
Minor rephrase
Jules-Bertholet Jun 20, 2025
8693706
Address target-specific limitations
Jules-Bertholet Jun 20, 2025
42fb9e2
`align` on trait declarations, semver
Jules-Bertholet Jun 20, 2025
9f5fb37
Overaligned fn ptrs future possibility
Jules-Bertholet Jun 20, 2025
553b5ef
Fix typo
Jules-Bertholet Jun 20, 2025
73973de
Fix typo
Jules-Bertholet Jun 20, 2025
4688f11
Fix typo
Jules-Bertholet Jun 20, 2025
9efcb25
Don’t require repeating `align` in impls
Jules-Bertholet Jun 20, 2025
7c155c8
Clarify "function's code" as "entry symbol"
Jules-Bertholet Jun 20, 2025
45a9d3d
Link to definition of "inert"
Jules-Bertholet Jun 25, 2025
893fd65
Clarify rustdoc
Jules-Bertholet Jun 25, 2025
5827b39
Rationale for 1-ZST `#[align(…)]` function item types
Jules-Bertholet Jun 25, 2025
5da3dcb
Function pointer tagging future possibility
Jules-Bertholet Jun 26, 2025
42f111b
`#[align(…)]` is compatible with `#[naked]`
Jules-Bertholet Jun 28, 2025
e79807a
`#[align(…)]` on functions in `extern` blocks
Jules-Bertholet Jun 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
380 changes: 380 additions & 0 deletions text/3806-align-attr.md
Copy link
Member

@workingjubilee workingjubilee Jun 14, 2025

Choose a reason for hiding this comment

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

I have no objection per se to the introduction of #[align] syntactically but I feel this will have a number of surprising complexities that will equate to the breaking change of size != stride. It feels like this tries to persuade the reader that hiding this break of assumptions in an attribute that doesn't start with #[repr will render it harmless. If you point at something with repr(align(N)), you are guaranteed padding that you can freely clobber, for instance, but pointers to things with this attribute have no such guarantee. This feels very likely to make unsafe code more confusing to write, in a similar way to what has driven us to say that we will not break size == stride.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet Jun 14, 2025

Choose a reason for hiding this comment

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

but pointers to things with this attribute have no such guarantee.

Yes, that is the point. If you need the guarantee, use a #[repr(align)] wrapper type. References to #[align] things have the same type as any other reference, because they provide the same guarantees as any other reference.

Copy link
Member

Choose a reason for hiding this comment

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

I must also note that I have voiced, repeatedly, that attributes do not compose very well in Rust compared to things more deeply embedded in the type, and the idea that this will compose as well as it does in C seems doubtful. I have made a separate note in one case of such. Nonetheless it is obviously the case that there are many positions where this attribute is desirable to have but there is no real way to introduce it to the type, or such is not reasonable. I wish this had been more incremental, in starting with such cases first.

Copy link
Contributor

Choose a reason for hiding this comment

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

This does, actually, seem like it's part of the unnameable type of the function itself, and that really this should be carried into the type of function pointers derived from it. The interaction of this with traits and refinement makes this more apparent.

One could imagine, e.g., the function type implementing a trait like Align16 where Align32: Align16, etc.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's notable as well, in light of other recent discussions, that inverse marker effects -- i.e., constraining properties of functions -- can in theory be modeled with traits.

Probably alignment isn't what we'd think of as an (inverse) effect -- it's not (the absence of) a side effect of the function that exceeds its signature -- but if we squint, it does maybe have some modeling similarities.

Copy link
Member

Choose a reason for hiding this comment

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

I have hoped for some time to be able to implement instead a trait like Aligned<const N>, specifically.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

carried into the type of function pointers derived from it.

Added to the future possibilities.

Copy link

@hanna-kruppe hanna-kruppe Jun 20, 2025

Choose a reason for hiding this comment

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

Exposing the chosen alignment as a property of the fn item type seems like a sensible extension to me (though it pokes a small hole in the arguments against #[repr(align(N))] fn). It doesn't seem obvious to me what would be the best way to expose the information (a hierarchy of AlignN traits looks weird, but const parameter on trait, associated const, just plain align_of, etc. have their own drawbacks) but since this is a backwards compatible extension, these details can be figured out later without blocking this RFC and the feature waiting on it.

I'm more worried about putting this information into fn pointer types. Those already have a lot of dimensions (safe vs unsafe, calling convention, lifetimes, and probably const-ness at some point) and most of those dimensions aren't covered by subtyping, so you can easily run into hard-to-resolve type mismatches with them once you actually use all of that flexibility. Adding alignment to the mix amplifies this problem further, only mitigated by how niche fn pointers with increased alignment would be. I'm not sure what the use cases are but I suspect most of them could be addressed just fine with being able to express "can be turned into a function pointer" and "has alignment at least N" in where clauses. Then libraries can define function pointer wrappers that can be constructed from fn item types that have sufficient alignment, without adding more dimensions to the built-in fn pointer type.

I'm also not sure if changing the "canonical" fn pointer type for a fn item type would be a breaking change (beyond new possibilities for inference failure). If it is, then figuring out the details about "aligned fn pointer types" might be a blocker for stabilizing #[align(N)] fn.

Copy link
Member

@workingjubilee workingjubilee Jun 20, 2025

Choose a reason for hiding this comment

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

It's true, we usually prefer to lean on coercions instead of subtyping for things like function pointers but that can have problems due to having coercions select different implementations for the same item according to fairly subtle rules.

Copy link

@hanna-kruppe hanna-kruppe Jun 20, 2025

Choose a reason for hiding this comment

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

Not just due to which coercion is chosen, but also because coercions don't help when the type that needs coercion is behind any sort of type constructor (including very basic ones like &T or [T; N]).

Original file line number Diff line number Diff line change
@@ -0,0 +1,380 @@
- 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.
Comment on lines +73 to +74
Copy link
Member

Choose a reason for hiding this comment

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

Ehn. Technically true but Deref implementations exist, which allows one to often escape this breakage since naming a static typically makes it a subexpression subject to Deref coercions... I wonder if there's a case where that doesn't apply? Maybe.

- 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
Copy link
Member

Choose a reason for hiding this comment

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

Please follow the template in https://github.com/rust-lang/rfcs/blob/master/0000-template.md and have both guide-level (how I'd explain it to someone) and reference-level (all the gory details of exactly what the operational behaviour is) parts.

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet Jun 25, 2025

Choose a reason for hiding this comment

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

The “Explanation” section is intended to be reference-level. This is a super niche/advanced feature; it is all gory details. Therefore, I don’t feel a separate guide-level explanation would be worthwhile.

[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(…))]`.)

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.
Comment on lines +562 to +564
Copy link
Member

Choose a reason for hiding this comment

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

I... do not expect this to be a realistic possibility.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

C++ allows it

Choose a reason for hiding this comment

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

put differently: there is no precedent in rust for this sort of const evaluation in attributes, and it really does not fit into the current attribute architecture.

But "future possibilities" don't really need to think too much about (short-term) feasibility I think.

- 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.
- We could loosen the restriction that fields of a `packed(n)` struct cannot
specify an alignment greater that `n`. (Apparently, some C compilers allow
something similar.)