Skip to content

Commit 61e20fa

Browse files
committed
add #[align] attribute
Right now it's used for functions with `fn_align`, in the future it will get more uses (statics, struct fields, etc.)
1 parent 8da6239 commit 61e20fa

29 files changed

+293
-132
lines changed

compiler/rustc_attr_data_structures/src/attributes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ impl Deprecation {
152152
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
153153
pub enum AttributeKind {
154154
// tidy-alphabetical-start
155+
/// Represents `#[align(N)]`.
156+
Align { align: Align, span: Span },
157+
155158
/// Represents `#[rustc_allow_const_fn_unstable]`.
156159
AllowConstFnUnstable(ThinVec<Symbol>),
157160

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ attr_parsing_incorrect_repr_format_packed_expect_integer =
4242
attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
4343
incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
4444
45+
attr_parsing_invalid_alignment_value =
46+
invalid alignment value: {$error_part}
47+
4548
attr_parsing_invalid_issue_string =
4649
`issue` must be a non-zero numeric string or "none"
4750
.must_not_be_zero = `issue` must not be "0", use "none" instead

compiler/rustc_attr_parsing/src/attributes/repr.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
33
use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr};
44
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
55

6-
use super::{CombineAttributeParser, ConvertFn};
6+
use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
77
use crate::context::{AcceptContext, Stage};
88
use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
99
use crate::session_diagnostics;
@@ -199,7 +199,7 @@ fn parse_repr_align<S: Stage>(
199199
});
200200
}
201201
Align => {
202-
cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
202+
cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
203203
span: param_span,
204204
});
205205
}
@@ -262,3 +262,53 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
262262
Err("not an unsuffixed integer")
263263
}
264264
}
265+
266+
/// Parse #[align(N)].
267+
#[derive(Default)]
268+
pub(crate) struct AlignParser(Option<(Align, Span)>);
269+
270+
impl AlignParser {
271+
const PATH: &'static [Symbol] = &[sym::align];
272+
273+
fn parse<'c, S: Stage>(
274+
&mut self,
275+
cx: &'c mut AcceptContext<'_, '_, S>,
276+
args: &'c ArgParser<'_>,
277+
) {
278+
// The builtin attributes parser already emits an error in this case.
279+
let Some(list) = args.list() else { return };
280+
281+
let Some(align) = list.single() else {
282+
cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg { span: list.span });
283+
284+
return;
285+
};
286+
287+
let Some(lit) = align.lit() else {
288+
cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger {
289+
span: align.span(),
290+
});
291+
292+
return;
293+
};
294+
295+
match parse_alignment(&lit.kind) {
296+
Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
297+
Err(message) => {
298+
cx.emit_err(session_diagnostics::InvalidAlignmentValue {
299+
span: lit.span,
300+
error_part: message,
301+
});
302+
}
303+
}
304+
}
305+
}
306+
307+
impl<S: Stage> AttributeParser<S> for AlignParser {
308+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::parse)];
309+
310+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
311+
let (align, span) = self.0?;
312+
Some(AttributeKind::Align { align, span })
313+
}
314+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1818
use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser};
1919
use crate::attributes::confusables::ConfusablesParser;
2020
use crate::attributes::deprecation::DeprecationParser;
21-
use crate::attributes::repr::ReprParser;
21+
use crate::attributes::repr::{AlignParser, ReprParser};
2222
use crate::attributes::stability::{
2323
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
2424
};
@@ -89,6 +89,7 @@ macro_rules! attribute_parsers {
8989
attribute_parsers!(
9090
pub(crate) static ATTRIBUTE_PARSERS = [
9191
// tidy-alphabetical-start
92+
AlignParser,
9293
BodyStabilityParser,
9394
ConfusablesParser,
9495
ConstStabilityParser,

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,11 @@ pub(crate) struct UnrecognizedReprHint {
490490
#[primary_span]
491491
pub span: Span,
492492
}
493+
494+
#[derive(Diagnostic)]
495+
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
496+
pub(crate) struct InvalidAlignmentValue {
497+
#[primary_span]
498+
pub span: Span,
499+
pub error_part: &'static str,
500+
}

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::str::FromStr;
33
use rustc_abi::ExternAbi;
44
use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode};
55
use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr};
6-
use rustc_attr_data_structures::ReprAttr::ReprAlign;
76
use rustc_attr_data_structures::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr};
87
use rustc_data_structures::fx::FxHashMap;
98
use rustc_hir::def::DefKind;
@@ -110,17 +109,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
110109
}
111110
};
112111

113-
if let hir::Attribute::Parsed(p) = attr {
114-
match p {
115-
AttributeKind::Repr(reprs) => {
116-
codegen_fn_attrs.alignment = reprs
117-
.iter()
118-
.filter_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None })
119-
.max();
120-
}
121-
122-
_ => {}
123-
}
112+
if let hir::Attribute::Parsed(AttributeKind::Align { align, .. }) = attr {
113+
codegen_fn_attrs.alignment = Some(*align);
124114
}
125115

126116
let Some(Ident { name, .. }) = attr.ident() else {

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
473473
),
474474
ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
475475
ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No),
476+
gated!(align, Normal, template!(List: "alignment"), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(align)),
476477
ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
477478
ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No),
478479
ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ pub struct CodegenFnAttrs {
4747
/// be generated against a specific instruction set. Only usable on architectures which allow
4848
/// switching between multiple instruction sets.
4949
pub instruction_set: Option<InstructionSetAttr>,
50-
/// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
51-
/// aligned to.
50+
/// The `#[align(...)]` attribute. Determines the alignment of the function body.
5251
pub alignment: Option<Align>,
5352
/// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
5453
/// the function entry.

compiler/rustc_passes/messages.ftl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ passes_abi_ne =
1313
passes_abi_of =
1414
fn_abi_of({$fn_name}) = {$fn_abi}
1515
16+
passes_align_should_be_repr_align =
17+
`#[align(...)]` is not supported on {$item} items
18+
.suggestion = use `#[repr(align(...))]` instead
19+
1620
passes_allow_incoherent_impl =
1721
`rustc_allow_incoherent_impl` attribute should be applied to impl items
1822
.label = the only currently supported targets are inherent methods
@@ -29,10 +33,6 @@ passes_attr_application_struct =
2933
attribute should be applied to a struct
3034
.label = not a struct
3135
32-
passes_attr_application_struct_enum_function_method_union =
33-
attribute should be applied to a struct, enum, function, associated function, or union
34-
.label = not a struct, enum, function, associated function, or union
35-
3636
passes_attr_application_struct_enum_union =
3737
attribute should be applied to a struct, enum, or union
3838
.label = not a struct, enum, or union
@@ -583,13 +583,14 @@ passes_remove_fields =
583583
*[other] fields
584584
}
585585
586-
passes_repr_align_function =
587-
`repr(align)` attributes on functions are unstable
588-
589586
passes_repr_align_greater_than_target_max =
590587
alignment must not be greater than `isize::MAX` bytes
591588
.note = `isize::MAX` is {$size} for the current target
592589
590+
passes_repr_align_should_be_align =
591+
`#[repr(align(...))]` is not supported on {$item} items
592+
.help = use `#[align(...)]` instead
593+
593594
passes_repr_conflicting =
594595
conflicting representation hints
595596

compiler/rustc_passes/src/check_attr.rs

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
142142
}
143143
Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
144144
}
145+
Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => {
146+
self.check_align(span, target, *align, *repr_span)
147+
}
148+
145149
Attribute::Parsed(
146150
AttributeKind::BodyStability { .. }
147151
| AttributeKind::ConstStabilityIndirect
@@ -636,6 +640,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
636640
sym::naked,
637641
sym::instruction_set,
638642
sym::repr,
643+
sym::align,
639644
sym::rustc_std_internal_symbol,
640645
// code generation
641646
sym::cold,
@@ -672,7 +677,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
672677
// this check can be part of the parser and be removed here
673678
match other_attr {
674679
Attribute::Parsed(
675-
AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. },
680+
AttributeKind::Deprecation { .. }
681+
| AttributeKind::Repr { .. }
682+
| AttributeKind::Align { .. },
676683
) => {
677684
continue;
678685
}
@@ -1947,6 +1954,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
19471954
}
19481955
}
19491956

1957+
/// Checks if the `#[align]` attributes on `item` are valid.
1958+
fn check_align(&self, span: Span, target: Target, align: Align, repr_span: Span) {
1959+
match target {
1960+
Target::Fn | Target::Method(_) => {}
1961+
Target::Struct | Target::Union | Target::Enum => {
1962+
self.dcx().emit_err(errors::AlignShouldBeReprAlign {
1963+
span: repr_span,
1964+
item: target.name(),
1965+
align_bytes: align.bytes(),
1966+
});
1967+
}
1968+
_ => {
1969+
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
1970+
hint_span: repr_span,
1971+
span,
1972+
});
1973+
}
1974+
}
1975+
1976+
self.check_align_value(align, repr_span);
1977+
}
1978+
19501979
/// Checks if the `#[repr]` attributes on `item` are valid.
19511980
fn check_repr(
19521981
&self,
@@ -1999,23 +2028,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
19992028
match target {
20002029
Target::Struct | Target::Union | Target::Enum => {}
20012030
Target::Fn | Target::Method(_) => {
2002-
if !self.tcx.features().fn_align() {
2003-
feature_err(
2004-
&self.tcx.sess,
2005-
sym::fn_align,
2006-
*repr_span,
2007-
fluent::passes_repr_align_function,
2008-
)
2009-
.emit();
2010-
}
2031+
self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
2032+
span: *repr_span,
2033+
item: target.name(),
2034+
});
20112035
}
20122036
_ => {
2013-
self.dcx().emit_err(
2014-
errors::AttrApplication::StructEnumFunctionMethodUnion {
2015-
hint_span: *repr_span,
2016-
span,
2017-
},
2018-
);
2037+
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
2038+
hint_span: *repr_span,
2039+
span,
2040+
});
20192041
}
20202042
}
20212043

@@ -2073,21 +2095,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
20732095
match target {
20742096
Target::Struct | Target::Union | Target::Enum => continue,
20752097
Target::Fn | Target::Method(_) => {
2076-
feature_err(
2077-
&self.tcx.sess,
2078-
sym::fn_align,
2079-
*repr_span,
2080-
fluent::passes_repr_align_function,
2081-
)
2082-
.emit();
2098+
self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
2099+
span: *repr_span,
2100+
item: target.name(),
2101+
});
20832102
}
20842103
_ => {
2085-
self.dcx().emit_err(
2086-
errors::AttrApplication::StructEnumFunctionMethodUnion {
2087-
hint_span: *repr_span,
2088-
span,
2089-
},
2090-
);
2104+
self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
2105+
hint_span: *repr_span,
2106+
span,
2107+
});
20912108
}
20922109
}
20932110
}

compiler/rustc_passes/src/errors.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,13 +1308,6 @@ pub(crate) enum AttrApplication {
13081308
#[label]
13091309
span: Span,
13101310
},
1311-
#[diag(passes_attr_application_struct_enum_function_method_union, code = E0517)]
1312-
StructEnumFunctionMethodUnion {
1313-
#[primary_span]
1314-
hint_span: Span,
1315-
#[label]
1316-
span: Span,
1317-
},
13181311
}
13191312

13201313
#[derive(Diagnostic)]
@@ -1816,3 +1809,26 @@ pub(crate) enum UnexportableItem<'a> {
18161809
field_name: &'a str,
18171810
},
18181811
}
1812+
1813+
#[derive(Diagnostic)]
1814+
#[diag(passes_repr_align_should_be_align)]
1815+
pub(crate) struct ReprAlignShouldBeAlign {
1816+
#[primary_span]
1817+
#[help]
1818+
pub span: Span,
1819+
pub item: &'static str,
1820+
}
1821+
1822+
#[derive(Diagnostic)]
1823+
#[diag(passes_align_should_be_repr_align)]
1824+
pub(crate) struct AlignShouldBeReprAlign {
1825+
#[primary_span]
1826+
#[suggestion(
1827+
style = "verbose",
1828+
applicability = "machine-applicable",
1829+
code = "#[repr(align({align_bytes}))]"
1830+
)]
1831+
pub span: Span,
1832+
pub item: &'static str,
1833+
pub align_bytes: u64,
1834+
}

src/tools/miri/tests/pass/fn_align.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
//@compile-flags: -Zmin-function-alignment=8
22
#![feature(fn_align)]
33

4-
// When a function uses `repr(align(N))`, the function address should be a multiple of `N`.
4+
// When a function uses `align(N)`, the function address should be a multiple of `N`.
55

6-
#[repr(align(256))]
6+
#[align(256)]
77
fn foo() {}
88

9-
#[repr(align(16))]
9+
#[align(16)]
1010
fn bar() {}
1111

12-
#[repr(align(4))]
12+
#[align(4)]
1313
fn baz() {}
1414

1515
fn main() {
1616
assert!((foo as usize).is_multiple_of(256));
1717
assert!((bar as usize).is_multiple_of(16));
1818

19-
// The maximum of `repr(align(N))` and `-Zmin-function-alignment=N` is used.
19+
// The maximum of `align(N)` and `-Zmin-function-alignment=N` is used.
2020
assert!((baz as usize).is_multiple_of(8));
2121
}

0 commit comments

Comments
 (0)