allow #[rustc_align_static(N)] on statics

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]`.
This commit is contained in:
Folkert de Vries 2025-06-07 22:56:22 +02:00
parent c559c4a741
commit cbacd00f10
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
21 changed files with 246 additions and 5 deletions

View file

@ -218,6 +218,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser {
sym::rustc_std_internal_symbol,
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
sym::rustc_align,
sym::rustc_align_static,
// obviously compatible with self
sym::naked,
// documentation

View file

@ -331,3 +331,30 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
Some(AttributeKind::Align { align, span })
}
}
#[derive(Default)]
pub(crate) struct AlignStaticParser(AlignParser);
impl AlignStaticParser {
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
fn parse<'c, S: Stage>(
&mut self,
cx: &'c mut AcceptContext<'_, '_, S>,
args: &'c ArgParser<'_>,
) {
self.0.parse(cx, args)
}
}
impl<S: Stage> AttributeParser<S> for AlignStaticParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)];
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (align, span) = self.0.0?;
Some(AttributeKind::Align { align, span })
}
}

View file

@ -47,7 +47,7 @@ use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, ReprParser};
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
RustcObjectLifetimeDefaultParser,
@ -149,6 +149,7 @@ attribute_parsers!(
pub(crate) static ATTRIBUTE_PARSERS = [
// tidy-alphabetical-start
AlignParser,
AlignStaticParser,
BodyStabilityParser,
ConfusablesParser,
ConstStabilityParser,

View file

@ -81,6 +81,8 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> {
if global.to_rvalue().get_type() != val_llty {
global.to_rvalue().set_type(val_llty);
}
// NOTE: Alignment from attributes has already been applied to the allocation.
set_global_alignment(self, global, alloc.align);
global.global_set_initializer_rvalue(value);

View file

@ -452,6 +452,8 @@ impl<'ll> CodegenCx<'ll, '_> {
self.statics_to_rauw.borrow_mut().push((g, new_g));
new_g
};
// NOTE: Alignment from attributes has already been applied to the allocation.
set_global_alignment(self, g, alloc.align);
llvm::set_initializer(g, v);

View file

@ -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);
let mutbl = global_alloc.mutability(*self.tcx, self.typing_env);
let kind = match global_alloc {

View file

@ -1,6 +1,6 @@
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir;
use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer};
use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt};
use tracing::debug;
@ -38,7 +38,14 @@ pub(crate) fn create_static_alloc<'tcx>(
static_def_id: LocalDefId,
layout: TyAndLayout<'tcx>,
) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?;
// Inherit size and align from the `GlobalAlloc::Static` so we can avoid duplicating
// the alignment attribute logic.
let (size, align) =
GlobalAlloc::Static(static_def_id.into()).size_and_align(*ecx.tcx, ecx.typing_env);
assert_eq!(size, layout.size);
assert!(align >= layout.align.abi);
let alloc = Allocation::try_new(size, align, AllocInit::Uninit, ())?;
let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into());
assert_eq!(ecx.machine.static_root_ids, None);
ecx.machine.static_root_ids = Some((alloc_id, static_def_id));

View file

@ -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)),
ungated!(
unsafe(Edition2024) export_name, Normal,
template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"),

View file

@ -632,6 +632,8 @@ declare_features! (
(unstable, simd_ffi, "1.0.0", Some(27731)),
/// Allows specialization of implementations (RFC 1210).
(incomplete, specialization, "1.7.0", Some(31844)),
/// Allows using `#[rustc_align_static(...)]` on static items.
(unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)),
/// Allows attributes on expressions and non-item statements.
(unstable, stmt_expr_attributes, "1.6.0", Some(15701)),
/// Allows lints part of the strict provenance effort.

View file

@ -386,7 +386,16 @@ impl<'tcx> GlobalAlloc<'tcx> {
.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)
// Take over-alignment from attributes into account.
let align = match tcx.codegen_fn_attrs(def_id).alignment {
Some(align_from_attribute) => {
Ord::max(align_from_attribute, layout.align.abi)
}
None => layout.align.abi,
};
(layout.size, align)
}
}
GlobalAlloc::Memory(alloc) => {

View file

@ -501,6 +501,10 @@ passes_repr_align_should_be_align =
`#[repr(align(...))]` is not supported on {$item}
.help = use `#[rustc_align(...)]` instead
passes_repr_align_should_be_align_static =
`#[repr(align(...))]` is not supported on {$item}
.help = use `#[rustc_align_static(...)]` instead
passes_repr_conflicting =
conflicting representation hints

View file

@ -1602,12 +1602,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
ReprAttr::ReprAlign(align) => {
match target {
Target::Struct | Target::Union | Target::Enum => {}
Target::Fn | Target::Method(_) => {
Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => {
self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
span: *repr_span,
item: target.plural_name(),
});
}
Target::Static if self.tcx.features().static_align() => {
self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic {
span: *repr_span,
item: target.plural_name(),
});
}
_ => {
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: *repr_span,

View file

@ -1609,6 +1609,15 @@ pub(crate) struct ReprAlignShouldBeAlign {
pub item: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_repr_align_should_be_align_static)]
pub(crate) struct ReprAlignShouldBeAlignStatic {
#[primary_span]
#[help]
pub span: Span,
pub item: &'static str,
}
#[derive(Diagnostic)]
#[diag(passes_custom_mir_phase_requires_dialect)]
pub(crate) struct CustomMirPhaseRequiresDialect {

View file

@ -1846,6 +1846,7 @@ symbols! {
rustc_abi,
// FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity
rustc_align,
rustc_align_static,
rustc_allocator,
rustc_allocator_zeroed,
rustc_allocator_zeroed_variant,
@ -2097,6 +2098,7 @@ symbols! {
staged_api,
start,
state,
static_align,
static_in_const,
static_nobundle,
static_recursion,

View file

@ -0,0 +1,14 @@
#![feature(static_align)]
// When a static uses `align(N)`, its address should be a multiple of `N`.
#[rustc_align_static(256)]
static FOO: u64 = 0;
#[rustc_align_static(512)]
static BAR: u64 = 0;
fn main() {
assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256));
assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512));
}

View file

@ -0,0 +1,31 @@
//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0
#![crate_type = "lib"]
#![feature(static_align)]
// CHECK: @STATIC_ALIGN =
// CHECK-SAME: align 16
#[no_mangle]
#[rustc_align_static(16)]
pub static STATIC_ALIGN: u64 = 0;
// CHECK: @ALIGN_SPECIFIED_TWICE_1 =
// CHECK-SAME: align 64
#[no_mangle]
#[rustc_align_static(32)]
#[rustc_align_static(64)]
pub static ALIGN_SPECIFIED_TWICE_1: u64 = 0;
// CHECK: @ALIGN_SPECIFIED_TWICE_2 =
// CHECK-SAME: align 128
#[no_mangle]
#[rustc_align_static(128)]
#[rustc_align_static(32)]
pub static ALIGN_SPECIFIED_TWICE_2: u64 = 0;
// CHECK: @ALIGN_SPECIFIED_TWICE_3 =
// CHECK-SAME: align 256
#[no_mangle]
#[rustc_align_static(32)]
#[rustc_align_static(256)]
pub static ALIGN_SPECIFIED_TWICE_3: u64 = 0;

View file

@ -0,0 +1,17 @@
#![feature(static_align)]
#![crate_type = "lib"]
#[rustc_align_static = 16] //~ ERROR malformed `rustc_align_static` attribute input
static S1: () = ();
#[rustc_align_static("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer
static S2: () = ();
#[rustc_align_static(0)] //~ ERROR invalid alignment value: not a power of two
static S3: () = ();
#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on static
static S4: () = ();
#[rustc_align_static(16)] //~ ERROR `#[rustc_align_static]` attribute cannot be used on structs
struct Struct1;

View file

@ -0,0 +1,45 @@
error[E0539]: malformed `rustc_align_static` attribute input
--> $DIR/malformed-static-align.rs:4:1
|
LL | #[rustc_align_static = 16]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| expected this to be a list
| help: must be of the form: `#[rustc_align_static(<alignment in bytes>)]`
error[E0589]: invalid alignment value: not an unsuffixed integer
--> $DIR/malformed-static-align.rs:7:22
|
LL | #[rustc_align_static("hello")]
| ^^^^^^^
error[E0589]: invalid alignment value: not a power of two
--> $DIR/malformed-static-align.rs:10:22
|
LL | #[rustc_align_static(0)]
| ^
error: `#[rustc_align_static]` attribute cannot be used on structs
--> $DIR/malformed-static-align.rs:16:1
|
LL | #[rustc_align_static(16)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: `#[rustc_align_static]` can be applied to statics and foreign statics
error: `#[repr(align(...))]` is not supported on statics
--> $DIR/malformed-static-align.rs:13:8
|
LL | #[repr(align(16))]
| ^^^^^^^^^
|
help: use `#[rustc_align_static(...)]` instead
--> $DIR/malformed-static-align.rs:13:8
|
LL | #[repr(align(16))]
| ^^^^^^^^^
error: aborting due to 5 previous errors
Some errors have detailed explanations: E0539, E0589.
For more information about an error, try `rustc --explain E0539`.

View file

@ -0,0 +1,11 @@
#![crate_type = "lib"]
#[rustc_align_static(16)]
//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
static REQUIRES_ALIGNMENT: u64 = 0;
extern "C" {
#[rustc_align_static(16)]
//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
static FOREIGN_STATIC: u32;
}

View file

@ -0,0 +1,23 @@
error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature
--> $DIR/feature-gate-static_align.rs:3:1
|
LL | #[rustc_align_static(16)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information
= help: add `#![feature(static_align)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature
--> $DIR/feature-gate-static_align.rs:8:5
|
LL | #[rustc_align_static(16)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information
= help: add `#![feature(static_align)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,26 @@
//@ run-pass
#![feature(static_align)]
#[rustc_align_static(64)]
static A: u8 = 0;
#[rustc_align_static(64)]
static B: u8 = 0;
#[rustc_align_static(128)]
#[no_mangle]
static EXPORTED: u64 = 0;
unsafe extern "C" {
#[rustc_align_static(128)]
#[link_name = "EXPORTED"]
static C: u64;
}
fn main() {
assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64));
assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64));
assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128));
unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) };
}