1use rustc_abi::{Align, Size};
2use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
3use rustc_feature::AttributeStability;
4use rustc_hir::attrs::IntType::{SignedInt, UnsignedInt};
5use rustc_hir::attrs::ReprAttr;
6use rustc_session::diagnostics::feature_err;
7
8use super::prelude::*;
9use crate::session_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(session_diagnostics::InvalidAlignmentValue {
240 span: lit.span,
241 error_part: message,
242 });
243 None
244 }
245 }
246}
247
248fn parse_alignment(node: &LitKind, cx: &AcceptContext<'_, '_>) -> Result<Align, String> {
249 let LitKind::Int(literal, LitIntType::Unsuffixed) = node else {
250 return Err("not an unsuffixed integer".to_string());
251 };
252
253 if !literal.get().is_power_of_two() {
256 return Err("not a power of two".to_string());
257 }
258 let align = literal
260 .get()
261 .try_into()
262 .ok()
263 .and_then(|a| Align::from_bytes(a).ok())
264 .ok_or("larger than 2^29".to_string())?;
265
266 let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64;
268 if align.bytes() > max {
269 return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("alignment larger than `isize::MAX` bytes ({0} for the current target)",
max))
})format!(
270 "alignment larger than `isize::MAX` bytes ({max} for the current target)"
271 ));
272 }
273 Ok(align)
274}
275
276#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignParser {
#[inline]
fn default() -> RustcAlignParser {
RustcAlignParser(::core::default::Default::default())
}
}Default)]
278pub(crate) struct RustcAlignParser(Option<(Align, Span)>);
279
280impl RustcAlignParser {
281 const PATH: &[Symbol] = &[sym::rustc_align];
282 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>"]);
283
284 fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
285 let Some(list) = cx.expect_list(args, cx.attr_span) else {
286 return;
287 };
288
289 let Some(align) = cx.expect_single(list) else {
290 return;
291 };
292
293 let Some(lit) = align.as_lit() else {
294 cx.adcx().expected_integer_literal(align.span());
295 return;
296 };
297
298 match parse_alignment(&lit.kind, cx) {
299 Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))),
300 Err(message) => {
301 cx.emit_err(session_diagnostics::InvalidAlignmentValue {
302 span: lit.span,
303 error_part: message,
304 });
305 }
306 }
307 }
308}
309
310impl AttributeParser for RustcAlignParser {
311 const ATTRIBUTES: AcceptMapping<Self> =
312 &[(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)];
313 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
314 Allow(Target::Fn),
315 Allow(Target::Method(MethodKind::Inherent)),
316 Allow(Target::Method(MethodKind::Trait { body: true })),
317 Allow(Target::Method(MethodKind::TraitImpl)),
318 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
320 ]);
321
322 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
323 let (align, span) = self.0?;
324 Some(AttributeKind::RustcAlign { align, span })
325 }
326}
327
328#[derive(#[automatically_derived]
impl ::core::default::Default for RustcAlignStaticParser {
#[inline]
fn default() -> RustcAlignStaticParser {
RustcAlignStaticParser(::core::default::Default::default())
}
}Default)]
329pub(crate) struct RustcAlignStaticParser(RustcAlignParser);
330
331impl RustcAlignStaticParser {
332 const PATH: &[Symbol] = &[sym::rustc_align_static];
333 const TEMPLATE: AttributeTemplate = RustcAlignParser::TEMPLATE;
334
335 fn parse(&mut self, cx: &mut AcceptContext<'_, '_>, args: &ArgParser) {
336 self.0.parse(cx, args)
337 }
338}
339
340impl AttributeParser for RustcAlignStaticParser {
341 const ATTRIBUTES: AcceptMapping<Self> =
342 &[(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)];
343 const ALLOWED_TARGETS: AllowedTargets<'_> =
344 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
345
346 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
347 let (align, span) = self.0.0?;
348 Some(AttributeKind::RustcAlign { align, span })
349 }
350}