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 2 commits into
base: master
Choose a base branch
from

Conversation

Jules-Bertholet
Copy link
Contributor

@Jules-Bertholet Jules-Bertholet commented May 1, 2025

Port C alignas to Rust.

Rendered

@ehuss ehuss added the T-lang Relevant to the language team, which will review and decide on the RFC. label May 2, 2025
@traviscross traviscross added I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. labels May 2, 2025
Comment on lines +133 to +136
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.)

Choose a reason for hiding this comment

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

A field with #[align(N)] does raise the alignment of the containing ADT to be (at least) N, with all the potential padding that entails, right? I don't see how this can work otherwise, but I haven't seen it spelled out explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct.

Comment on lines +96 to +100
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].

Comment on lines +88 to +91
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.

Comment on lines +229 to +232
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.)

Choose a reason for hiding this comment

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

Why make this distinction? Making UB depend on surface syntax for essentially the same thing might UB detection (e.g., miri or rustc-inserted "UB checks") and exploitation more difficult, and since this is a new feature there's not even backwards compatibility concerns.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making it UB even if not accessed enables more optimizations. For example, the compiler could hoist an access to the static outside of an if. And we can always relax this UB in the future, if it's not worth it

Choose a reason for hiding this comment

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

I don’t object to having UB if the item isn’t accessed. I just think it should be the same rules for unsafe extern {} and extern {} so later stages don’t have to carry around the distinction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But old-style extern is safe, and safe code should not be able to cause UB.

Copy link

@hanna-kruppe hanna-kruppe May 2, 2025

Choose a reason for hiding this comment

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

There’s plenty of ways to cause UB with it today (many might be fixable). But we could also say “new extern {} features that can cause UB-by-wrong-declaration require unsafe extern {}”. Seems better to me than complicating Rust’s UB story for the future.

Copy link
Contributor

Choose a reason for hiding this comment

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

@hanna-kruppe is correct; it'd be better to simply require unsafe extern in order to use #[align(..)].

Copy link
Contributor Author

@Jules-Bertholet Jules-Bertholet May 3, 2025

Choose a reason for hiding this comment

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

#[align] isn't special. The UB that can result if an extern static item specifies an incorrect alignment, is the same as the UB if such an item specifies an incorrect type. So restricting this feature to edition ≥2024 would not accomplish anything.

There’s plenty of ways to cause UB with it today (many might be fixable).

AFAIK, all should be fixable.

Copy link
Contributor

Choose a reason for hiding this comment

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

So restricting this feature to edition ≥2024 would not accomplish anything.

unsafe extern can be used in any edition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Restricting to unsafe extern, then.

## 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)]`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
replaces `#[repr(align(…))]` on function items from `#![feature(fn_align)]`.
replaces `#[repr(align(…))]` on function items from `#![feature(fn_align)]`.
This does not change the alignment of the function item type or the corresponding function pointer type, only the machine code they refer to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
I-lang-nominated Indicates that an issue has been nominated for prioritizing at the next lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants