-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
base: master
Are you sure you want to change the base?
Conversation
75d8fc6
to
f6f7686
Compare
f6f7686
to
3a4f5ac
Compare
800e69d
to
eca25f7
Compare
eca25f7
to
4badbc9
Compare
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.) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct.
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(…))]`.) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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]
.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
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.) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(..)]
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
Port C
alignas
to Rust.Rendered