1use rustc_abi::{Align, Size};
2use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
3use rustc_attr_ir::IntType::{SignedInt, UnsignedInt};
4use rustc_attr_ir::ReprAttr;
5use rustc_feature::AttributeStability;
6use rustc_session::diagnostics::feature_err;
7use rustc_span::OrdSpan;
8
9use super::prelude::*;
10use crate::diagnostics;
11
12pub(crate) struct ReprParser;
22
23impl CombineAttributeParser for ReprParser {
24 type Item = (ReprAttr, Span);
25 const PATH: &[Symbol] = &[sym::repr];
26 const CONVERT: ConvertFn<Self::Item> =
27 |items, first_span| AttributeKind::Repr { reprs: items, first_span };
28 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&["C", "Rust", "transparent", "align(...)", "packed(...)",
"<integer type>"]),
one_of: &[],
name_value_str: None,
docs: Some("https://doc.rust-lang.org/reference/type-layout.html#representations"),
}template!(
29 List: &["C", "Rust", "transparent", "align(...)", "packed(...)", "<integer type>"],
30 "https://doc.rust-lang.org/reference/type-layout.html#representations"
31 );
32
33 fn extend(
34 cx: &mut AcceptContext<'_, '_>,
35 args: &ArgParser,
36 ) -> impl IntoIterator<Item = Self::Item> {
37 let Some(list) = cx.expect_list(args, cx.attr_span) else {
38 return ::alloc::vec::Vec::new()vec![];
39 };
40
41 if list.is_empty() {
42 cx.check_target(
43 "()",
44 &AllowedTargets::AllowList(&[
45 Allow(Target::Struct),
46 Allow(Target::Enum),
47 Allow(Target::Union),
48 Warn(Target::MacroCall),
49 ]),
50 );
51
52 let attr_span = cx.attr_span;
53 cx.adcx().warn_empty_attribute(attr_span);
54 return ::alloc::vec::Vec::new()vec![];
55 }
56
57 let mut reprs = Vec::new();
58 for param in list.mixed() {
59 let Some(item) = param.meta_item() else {
60 cx.adcx().expected_identifier(param.span());
61 continue;
62 };
63 reprs.extend(parse_repr(cx, item).map(|r| (r, param.span())));
64 }
65 reprs
66 }
67
68 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::ManuallyChecked;
69 const STABILITY: AttributeStability = AttributeStability::Stable;
70}
71
72fn parse_repr(cx: &mut AcceptContext<'_, '_>, param: &MetaItemParser) -> Option<ReprAttr> {
73 use ReprAttr::*;
74
75 macro_rules! repr_int {
76 ($arg: ident, $constructor: expr) => {{
77 cx.check_target(
78 concat!("(", stringify!($arg), ")"),
79 &AllowedTargets::AllowList(&[Allow(Target::Enum), Warn(Target::MacroCall)]),
80 );
81 cx.expect_no_args(param.args())?;
82 Some($constructor)
83 }};
84 }
85
86 match param.path().word_sym() {
87 Some(sym::align) => {
88 cx.check_target(
89 "(align(...))",
90 &AllowedTargets::AllowList(&[
91 Allow(Target::Struct),
92 Allow(Target::Enum),
93 Allow(Target::Union),
94 Warn(Target::MacroCall),
95 ]),
96 );
97 let l = cx.expect_list(param.args(), param.span())?;
98 parse_repr_align(cx, l, AlignKind::Align)
99 }
100 Some(sym::packed) => {
101 cx.check_target(
102 "(packed)",
103 &AllowedTargets::AllowList(&[
104 Allow(Target::Struct),
105 Allow(Target::Union),
106 Warn(Target::MacroCall),
107 ]),
108 );
109 match param.args() {
110 ArgParser::NoArgs => Some(ReprPacked(Align::ONE)),
111 ArgParser::List(l) => parse_repr_align(cx, l, AlignKind::Packed),
112 ArgParser::NameValue(_) => {
113 cx.adcx().expected_list_or_no_args(param.span());
114 None
115 }
116 }
117 }
118
119 Some(sym::Rust) => {
120 cx.check_target(
121 "(Rust)",
122 &AllowedTargets::AllowList(&[
123 Allow(Target::Struct),
124 Allow(Target::Enum),
125 Allow(Target::Union),
126 Warn(Target::MacroCall),
127 ]),
128 );
129 cx.expect_no_args(param.args())?;
130 Some(ReprRust)
131 }
132 Some(sym::C) => {
133 cx.check_target(
134 "(C)",
135 &AllowedTargets::AllowList(&[
136 Allow(Target::Struct),
137 Allow(Target::Enum),
138 Allow(Target::Union),
139 Warn(Target::MacroCall),
140 ]),
141 );
142 cx.expect_no_args(param.args())?;
143 Some(ReprC)
144 }
145 Some(sym::simd) => {
146 if cx.features.is_some_and(|feats| !feats.repr_simd()) {
147 feature_err(
148 cx.sess(),
149 sym::repr_simd,
150 param.span(),
151 "SIMD types are experimental and possibly buggy",
152 )
153 .emit();
154 }
155 cx.check_target("(simd)", &AllowedTargets::AllowList(&[Allow(Target::Struct)]));
156 cx.expect_no_args(param.args())?;
157 Some(ReprSimd)
158 }
159 Some(sym::transparent) => {
160 cx.check_target(
161 "(transparent)",
162 &AllowedTargets::AllowList(&[
163 Allow(Target::Struct),
164 Allow(Target::Enum),
165 Allow(Target::Union), Warn(Target::MacroCall),
167 ]),
168 );
169 cx.expect_no_args(param.args())?;
170 Some(ReprTransparent)
171 }
172
173 Some(sym::i8) => {
cx.check_target("(i8)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::I8)))
}repr_int!(i8, ReprInt(SignedInt(IntTy::I8))),
174 Some(sym::u8) => {
cx.check_target("(u8)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::U8)))
}repr_int!(u8, ReprInt(UnsignedInt(UintTy::U8))),
175 Some(sym::i16) => {
cx.check_target("(i16)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::I16)))
}repr_int!(i16, ReprInt(SignedInt(IntTy::I16))),
176 Some(sym::u16) => {
cx.check_target("(u16)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::U16)))
}repr_int!(u16, ReprInt(UnsignedInt(UintTy::U16))),
177 Some(sym::i32) => {
cx.check_target("(i32)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::I32)))
}repr_int!(i32, ReprInt(SignedInt(IntTy::I32))),
178 Some(sym::u32) => {
cx.check_target("(u32)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::U32)))
}repr_int!(u32, ReprInt(UnsignedInt(UintTy::U32))),
179 Some(sym::i64) => {
cx.check_target("(i64)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::I64)))
}repr_int!(i64, ReprInt(SignedInt(IntTy::I64))),
180 Some(sym::u64) => {
cx.check_target("(u64)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::U64)))
}repr_int!(u64, ReprInt(UnsignedInt(UintTy::U64))),
181 Some(sym::i128) => {
cx.check_target("(i128)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::I128)))
}repr_int!(i128, ReprInt(SignedInt(IntTy::I128))),
182 Some(sym::u128) => {
cx.check_target("(u128)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::U128)))
}repr_int!(u128, ReprInt(UnsignedInt(UintTy::U128))),
183 Some(sym::isize) => {
cx.check_target("(isize)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(SignedInt(IntTy::Isize)))
}repr_int!(isize, ReprInt(SignedInt(IntTy::Isize))),
184 Some(sym::usize) => {
cx.check_target("(usize)",
&AllowedTargets::AllowList(&[Allow(Target::Enum),
Warn(Target::MacroCall)]));
cx.expect_no_args(param.args())?;
Some(ReprInt(UnsignedInt(UintTy::Usize)))
}repr_int!(usize, ReprInt(UnsignedInt(UintTy::Usize))),
185 _ => {
186 cx.adcx().expected_specific_argument(
187 param.span(),
188 &[
189 sym::align,
190 sym::packed,
191 sym::Rust,
192 sym::C,
193 sym::simd,
194 sym::transparent,
195 sym::i8,
196 sym::u8,
197 sym::i16,
198 sym::u16,
199 sym::i32,
200 sym::u32,
201 sym::i64,
202 sym::u64,
203 sym::i128,
204 sym::u128,
205 sym::isize,
206 sym::usize,
207 ],
208 );
209 None
210 }
211 }
212}
213
214enum AlignKind {
215 Packed,
216 Align,
217}
218
219fn parse_repr_align(
220 cx: &mut AcceptContext<'_, '_>,
221 list: &MetaItemListParser,
222 align_kind: AlignKind,
223) -> Option<ReprAttr> {
224 let Some(align) = list.as_single() else {
225 cx.adcx().expected_single_argument(list.span, list.len());
226 return None;
227 };
228
229 let Some(lit) = align.as_lit() else {
230 cx.adcx().expected_integer_literal(align.span());
231 return None;
232 };
233
234 match parse_alignment(&lit.kind, cx) {
235 Ok(literal) => Some(match align_kind {
236 AlignKind::Packed => ReprAttr::ReprPacked(literal),
237 AlignKind::Align => ReprAttr::ReprAlign(literal),
238 }),
239 Err(message) => {
240 cx.emit_err(diagnostics::InvalidAlignmentValue { span: lit.span, error_part: message });
241 None
242 }
243 }
244}
245
246fn parse_alignment(node: &LitKind, cx: &AcceptContext<'_, '_>) -> Result<Align, String> {
247 let LitKind::Int(literal, LitIntType::Unsuffixed) = node else {
248 return Err("not an unsuffixed integer".to_string());
249 };
250
251 if !literal.get().is_power_of_two() {
254 return Err("not a power of two".to_string());
255 }
256 let align = literal
258 .get()
259 .try_into()
260 .ok()
261 .and_then(|a| Align::from_bytes(a).ok())
262 .ok_or("larger than 2^29".to_string())?;
263
264 let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64;
266 if align.bytes() > max {
267 return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("alignment larger than `isize::MAX` bytes ({0} for the current target)",
max))
})format!(
268 "alignment larger than `isize::MAX` bytes ({max} for the current target)"
269 ));
270 }
271 Ok(align)
272}
273
274#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignParser {
#[inline]
fn default() -> Self {
RustcAlignParser(::core::default::Default::default())
}
}Default)]
276pub(crate) struct RustcAlignParser(Option<(Align, OrdSpan)>);
277
278impl RustcAlignParser {
279 const PATH: &[Symbol] = &[sym::rustc_align];
280 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&["<alignment in bytes>"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["<alignment in bytes>"]);
281
282 fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
283 let Some(list) = cx.expect_list(args, cx.attr_span) else {
284 return;
285 };
286
287 let Some(align) = cx.expect_single(list) else {
288 return;
289 };
290
291 let Some(lit) = align.as_lit() else {
292 cx.adcx().expected_integer_literal(align.span());
293 return;
294 };
295
296 match parse_alignment(&lit.kind, cx) {
297 Ok(literal) => self.0 = Ord::max(self.0, Some((literal, OrdSpan(cx.attr_span)))),
298 Err(message) => {
299 cx.emit_err(diagnostics::InvalidAlignmentValue {
300 span: lit.span,
301 error_part: message,
302 });
303 }
304 }
305 }
306}
307
308impl AttributeParser for RustcAlignParser {
309 const ATTRIBUTES: AcceptMapping<Self> =
310 &[(Self::PATH, Self::TEMPLATE, {
_ = rustc_feature::Features::fn_align;
AttributeStability::Unstable {
gate_name: rustc_span::sym::fn_align,
notes: &[],
}
}unstable!(fn_align), Self::parse)];
311 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
312 Allow(Target::Fn),
313 Allow(Target::Method(MethodKind::Inherent)),
314 Allow(Target::Method(MethodKind::Trait { body: true })),
315 Allow(Target::Method(MethodKind::TraitImpl)),
316 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
318 ]);
319
320 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
321 let (align, span) = self.0?;
322 Some(AttributeKind::RustcAlign { align, span: span.0 })
323 }
324}
325
326#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignStaticParser {
#[inline]
fn default() -> Self {
RustcAlignStaticParser(::core::default::Default::default())
}
}Default)]
327pub(crate) struct RustcAlignStaticParser(RustcAlignParser);
328
329impl RustcAlignStaticParser {
330 const PATH: &[Symbol] = &[sym::rustc_align_static];
331 const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE;
332
333 fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
334 self.0.parse(cx, args)
335 }
336}
337
338impl AttributeParser for RustcAlignStaticParser {
339 const ATTRIBUTES: AcceptMapping<Self> =
340 &[(Self::PATH, Self::TEMPLATE, {
_ = rustc_feature::Features::static_align;
AttributeStability::Unstable {
gate_name: rustc_span::sym::static_align,
notes: &[],
}
}unstable!(static_align), Self::parse)];
341 const ALLOWED_TARGETS: AllowedTargets<'_> =
342 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
343
344 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
345 let (align, span) = self.0.0?;
346 Some(AttributeKind::RustcAlign { align, span: span.0 })
347 }
348}