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