Skip to content

Conversation

folkertdev
Copy link
Contributor

Tracking issue: #146177

#![feature(static_align)]

#[rustc_align_static(64)]
static SO_ALIGNED: u64 = 0;

We need a different attribute than rustc_align because unstable attributes are tied to their feature (we can't have two unstable features use the same unstable attribute). Otherwise this uses all of the same infrastructure as #[rustc_align].

r? @traviscross

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 3, 2025
@rustbot
Copy link
Collaborator

rustbot commented Sep 3, 2025

The Miri subtree was changed

cc @rust-lang/miri

Some changes occurred in compiler/rustc_attr_parsing

cc @jdonszelmann

Some changes occurred in compiler/rustc_passes/src/check_attr.rs

cc @jdonszelmann

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_gcc

cc @antoyo, @GuillaumeGomez

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

@RalfJung
Copy link
Member

RalfJung commented Sep 3, 2025

we can't have two unstable features use the same unstable attribute

What do you mean by "use"? How does a feature "use" an attribute?
All rustc_ attributes are unstable anyway...

@@ -621,6 +621,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)),
gated!(rustc_align_static, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, static_align, experimental!(rustc_align_static)),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@RalfJung this is where the attribute gets declared, and the feature name on this line (static_align in this case) must be unique

Copy link
Member

Choose a reason for hiding this comment

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

That's very odd, why does it have to be unique? That's not how features work anywhere else in the compiler, AFAIK.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, sorry, what I meant by unique is that an attribute must be tied to one unstable feature. #[rustc_align] is already tied to fn_align, so it can't also be used for static_align.

So, one unstable feature can "contain" multiple attributes, but an attribute always is tied to only one unstable feature.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, by "attribute used for feature" you mean "attribute is gated by feature". That's a rather confusing and non-standard way of saying things.^^

So what you want is to have a separate feature gate for aligning functions vs aligning statics, and the attribute registration macro isn't flexible enough to allow that. rustc_align is for functions only, despite its name sounding more general.

Since these are all temporary names anyway, I guess it doesn't matter too much, but when it comes to stabilization we very much might need the ability to stabilize an attribute in some positions while leaving it unstable elsewhere. Otherwise we have no way of unstably extending where an existing stable attribute can be used. So this limitation of attribute feature gating seems like it could become problematic. Cc @jdonszelmann

@Jules-Bertholet
Copy link
Contributor

This needs tests for:

  • Thread locals (#[thread_local] and thread_local!)
  • extern block statics

@rust-log-analyzer

This comment has been minimized.

@folkertdev
Copy link
Contributor Author

extern

Is there any interesting behavior to test here? (I'll add a test for the attribute being valid).

thread_local!

hmm, the thread_local! macro actually applies the attribute to a constant

($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
},

@traviscross traviscross changed the title implement #[rustc_align_static(N)] on statics Implement #[rustc_align_static(N)] on statics Sep 3, 2025
@traviscross traviscross added the F-static_align #![feature(static_align)] label Sep 3, 2025
@rust-log-analyzer

This comment has been minimized.

We need a different attribute than `rustc_align` because unstable attributes are
tied to their feature (we can't have two unstable features use the same
unstable attribute). Otherwise this uses all of the same infrastructure
as `#[rustc_align]`.
Comment on lines 41 to 45
let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?;
// Take alignment from attributes into account here so that there is one source
// of truth for the alignment of this allocation.
let align = match ecx.tcx.codegen_fn_attrs(static_def_id).alignment {
Some(align_from_attribute) => Ord::max(align_from_attribute, layout.align.abi),
None => layout.align.abi,
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@RalfJung we have the information here to retrieve the alignment. Or perhaps you still prefer passing it in as an argument?

The final line of this function

    ecx.machine.static_root_ids = Some((alloc_id, static_def_id));
    assert!(ecx.memory.alloc_map.insert(alloc_id, (MemoryKind::Stack, alloc)).is_none());
    interp_ok(ecx.ptr_to_mplace(Pointer::from(alloc_id).into(), layout))

uses the passed-in layout, which is not over-aligned. is that OK?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, what should we do here?

GlobalAlloc::Static(def_id) => {
let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else {
bug!("GlobalAlloc::Static is not a static")
};
if nested {
// Nested anonymous statics are untyped, so let's get their
// size and alignment from the allocation itself. This always
// succeeds, as the query is fed at DefId creation time, so no
// evaluation actually occurs.
let alloc = tcx.eval_static_initializer(def_id).unwrap();
(alloc.0.size(), alloc.0.align)
} else {
// Use size and align of the type for everything else. We need
// to do that to
// * avoid cycle errors in case of self-referential statics,
// * be able to get information on extern statics.
let ty = tcx
.type_of(def_id)
.no_bound_vars()
.expect("statics should not have generic parameters");
let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap();
assert!(layout.is_sized());
(layout.size, layout.align.abi)
}
}

Obviously the type does not take the additional alignment into account. The comment also suggests that unconditionally using the allocation is incorrect. So do we check the attributes again here (thus kind of sacrificing the one source of truth)?

Copy link
Member

Choose a reason for hiding this comment

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

Nested statics can't carry an attribute so I don't think you have to do anything here.

Copy link
Member

Choose a reason for hiding this comment

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

we have the information here to retrieve the alignment.

Nice!

uses the passed-in layout, which is not over-aligned. is that OK?

Yes, that just determines the type of the place, which is not changed by the over-alignment.

@rust-log-analyzer

This comment has been minimized.

@Jules-Bertholet
Copy link
Contributor

Here is an implementation for thread_local!: Jules-Bertholet@ad038e6

@@ -953,6 +953,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {

// # Global allocations
if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) {
// NOTE: static alignment from attributes has already been applied to the allocation.
let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env);
Copy link
Member

Choose a reason for hiding this comment

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

The GlobalAlloc::Static case in size_and_align still needs to check the attribute I think.

I can't think of a good way to deduplicate that with create_static_alloc.

Copy link
Member

Choose a reason for hiding this comment

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

I think I found a way to dedup this (pushed to the PR), let's see if that works.

@rustbot
Copy link
Collaborator

rustbot commented Sep 5, 2025

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. F-static_align #![feature(static_align)] S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants